Web Design Guide 2023-12-11 10:29:21 | 2.0.0
<div class="image-compare column" data-vertical="true">
    <div class=" media ">
        <img class="media__item" src="/demo/teaser/teaser6.jpg" alt=>
    </div>

    <div class="second-image vertical">
        <div class="handle">
            <div class="caption first">Caption of first image</div>
            <div class="comparator"></div>
            <div class="caption second">Caption of first image</div>
        </div>
        <div class="wrapper">
            <div class=" media ">
                <img class="media__item" src="/demo/teaser/teaser5.jpg" alt=>
            </div>

        </div>
    </div>
</div>
<div class="image-compare column" data-vertical="true">
  {{render '@atoms-media--image' image1}}
  <div class="second-image vertical">
    <div class="handle">
      <div class="caption first">{{caption1}}</div>
      <div class="comparator"></div>
      <div class="caption second">{{caption1}}</div>
    </div>
    <div class="wrapper">
      {{render '@atoms-media--image' image2}}
    </div>
  </div>
</div>
{
  "image1": {
    "src": "/demo/teaser/teaser6.jpg"
  },
  "image2": {
    "src": "/demo/teaser/teaser5.jpg"
  },
  "caption1": "Caption of first image",
  "caption2": "Caption of second image"
}
  • Content:
    .image-compare {
      position: relative;
      padding: 0;
      overflow: hidden;
      user-select: none;
      -webkit-touch-callout: none;
    
      .media {
        font-size: 1px;
      }
    
      .second-image {
        position: absolute;
        top: 0;
        right: 0;
        // display: none;
        width: 50%;
        height: 100%;
        padding: 0 0 0 2.4rem;
        text-align: right;
    
        .wrapper {
          position: relative;
          width: 100%;
          height: 100%;
          overflow: hidden;
        }
    
        .media {
          display: inline-block;
          float: right;
        }
    
        &.vertical {
          top: inherit;
          right: inherit;
          bottom: 0;
          left: 0;
          width: 100%;
          height: 50%;
          padding: 2.4rem 0 0;
    
          .handle {
            top: 0;
            left: inherit;
            width: 100%;
            height: 4.8rem;
            cursor: n-resize;
    
            .caption {
              left: 0;
    
              &.first {
                top: auto;
                right: auto;
                bottom: 2.6rem;
              }
    
              &.second {
                top: 2.6rem;
                bottom: auto;
                left: auto;
              }
            }
    
            &::before,
            &::after {
              top: -15px;
              left: 50%;
              width: 30px;
              height: 20px;
              margin: 0 0 0 -15px;
              background-position-x: -43px;
              text-align: left;
            }
    
            &::before {
              $symbol: map-get($icons, 'chevron-up');
              @include icon-content-extended($symbol, block, 3rem);
              line-height: 2rem;
            }
    
            &::after {
              $symbol: map-get($icons, 'chevron-down');
              @include icon-content-extended($symbol, block, 3rem);
              top: auto;
              bottom: -15px;
              line-height: 2rem;
            }
    
            &:hover,
            &.active {
              &::before {
                top: -5px;
              }
    
              &::after {
                bottom: -5px;
              }
            }
    
            .comparator {
              width: 100%;
              height: 4px;
              margin: 2.2rem 0;
            }
          }
    
          .media {
            position: absolute;
            bottom: 0;
            left: 0;
          }
        }
      }
    
      .handle {
        position: absolute;
        left: 0;
        z-index: 20;
        width: 4.8rem;
        height: 100%;
        cursor: w-resize;
    
        &::before,
        &::after {
          position: absolute;
          top: 50%;
          display: block;
          width: 2rem;
          height: 3rem;
          margin-top: -1.5rem;
          font-size: 3rem;
          color: $white;
          transition: all 0.25s linear;
        }
    
        &::before {
          $symbol: map-get($icons, 'chevron-left');
          @include icon-content-extended($symbol, block, 3rem);
          left: -1.5rem;
        }
    
        &::after {
          $symbol: map-get($icons, 'chevron-right');
          @include icon-content-extended($symbol, block, 3rem);
          right: -1.5rem;
          text-align: left;
        }
    
        &:hover,
        &.active {
          &::before {
            left: -0.5rem;
          }
    
          &::after {
            right: -0.5rem;
          }
        }
    
        .comparator {
          width: 4px;
          height: 100%;
          margin: 0 auto;
          background-color: $primary;
        }
    
        .caption {
          position: absolute;
          top: 0;
          min-width: 100px;
          padding: 10px;
          border: 0;
    
          &.first {
            right: 2.6rem;
            background-color: $gray;
            color: $white;
          }
    
          &.second {
            left: 2.6rem;
            background-color: $white;
            color: $primary;
            text-align: left;
          }
        }
      }
    }
    
  • URL: /components/raw/molecules-image-compare/_image-compare.scss
  • Filesystem Path: src/components/02-ui-components/02-molecules/25-image-compare/_image-compare.scss
  • Size: 3.4 KB

Example code snippet from coremedia9:

import $ from 'jquery';
import * as nodeDecorationService from '@coremedia/js-node-decoration-service';
import {EVENT_LAYOUT_CHANGED} from '@coremedia/js-basic';

const IMAGE_COMPARE_SELECTOR = '.image-compare';
const IMAGE_SELECTOR = '.media';
const SECOND_BLOCK_SELECTOR = '.second-image';
const HANDLE_SELECTOR = '.handle';
const $document = $(document);

$(function() {
  nodeDecorationService.addNodeDecoratorBySelector(IMAGE_COMPARE_SELECTOR, function ($target) {
    $.each($target, function () {
      const options = {
        $element: $target
      };

      init();

      function init() {
        options.vertical = options.$element.data('vertical') || false;
        options.vertical && getSecondBlock().addClass('vertical');
        options.dimension = 50;
        options.mousePosition = {x: 0, y: 0};
        options.proxyFunction = null;
        getSecondBlock().show();
        resize();
        bindEvents();
        $document.trigger(EVENT_LAYOUT_CHANGED);
      }
      function bindEvents() {
        let comparator = getHandle()[0];
        comparator.addEventListener('mousedown', function(e) {
          options.mousePosition = {x: e.x, y: e.y};
          options.proxyFunction = mouseMove;
          getHandle().addClass('active');
          document.addEventListener('mousemove', options.proxyFunction, false);
        }, false);
        document.addEventListener("mouseup", function(e) {
          options.proxyFunction && (document.removeEventListener('mousemove', options.proxyFunction, false), options.proxyFunction = null, getHandle().removeClass('active'));
        }, false);
        comparator.addEventListener('touchstart', function(e) {
          e.preventDefault();
          if (e.touches.length > 1) { // skip multitouch
            return;
          }
          const touch = e.touches[0];
          options.mousePosition = {x: touch.clientX, y: touch.clientY};
          options.proxyFunction = touchMove;
          getHandle().addClass('active');
          document.addEventListener("touchmove", options.proxyFunction, false);
        }, false);
        document.addEventListener("touchend", function(e) {
          options.proxyFunction && (document.removeEventListener("touchmove", options.proxyFunction, false), options.proxyFunction = null, getHandle().removeClass('active'));
        }, false);
      }
      function mouseMove(e) {
        e && e.preventDefault();
        moveComparator({x: e.x, y: e.y});
      }
      function touchMove(e) {
        e.preventDefault();
        if (e.touches.length > 1) {
          return;
        }
        var touch = e.touches[0];
        moveComparator({x: touch.clientX, y: touch.clientY});
      }
      function moveComparator(newPosition) {
        const delta = (options.mousePosition[options.vertical ? 'y' : 'x'] - newPosition[options.vertical ? 'y' : 'x']) / options.$element[options.vertical ? 'height' : 'width']() * 100;
        options.dimension += delta;
        options.dimension < 0 && (options.dimension = 0);
        options.dimension > 100 && (options.dimension = 100);
        options.mousePosition = newPosition;
        getSecondBlock()[options.vertical ? 'height' : 'width'](getSecondImage()[options.vertical ? 'height' : 'width']() * options.dimension / 100);
      }
      function getHandle() {
        return options.$handle || (options.$handle = $(HANDLE_SELECTOR, options.$element));
      }
      function getFirstImage() {
        return options.$firstImage || (options.$firstImage = $(IMAGE_SELECTOR + ':first', options.$element));
      }
      function getSecondBlock() {
        return options.$secondBlock || (options.$secondBlock = $(SECOND_BLOCK_SELECTOR, options.$element));
      }
      function getSecondImage() {
        return options.$secondImage || (options.$secondImage = $(IMAGE_SELECTOR, getSecondBlock()));
      }
      function resize() {
        getSecondImage().width(getFirstImage().width());
        getSecondBlock()[options.vertical ? 'height' : 'width'](options.dimension + '%');
      }

      $document.on(EVENT_LAYOUT_CHANGED, function() {
        resize();
      });
    });
  });
});