Search code examples
javascriptjquerycssdraggablemask

Using an overlapped element to create drag-zoom effect on image


I have this mockup:

enter image description here

where the white button lets you drag the iPhone mockup to the left and right, giving a distorted/zoomed perspective of the image in the background.

The drag functionality works, what I'm looking for is a solution that gives the distorted/zoomed view in the iPhone-mockup. I've been fiddling with the mask-property, but can't achieve what I want.

jQuery, JS, CSS, SCSS solutions are welcome.

(The image in the code isn't the exact same as the one in the mockup, ignore that)

Snippet

(works best in full page view)

let draggable = $('.phone-mockup-container'); //element
let dragTrigger = $('.phone-trigger');
dragTrigger.on('mousedown', function(e) {
  let dr = draggable.addClass("drag");
  height = dr.outerHeight();
  width = dr.outerWidth();
  max_left = dr.parent().offset().left + dr.parent().width() - dr.outerWidth();
  min_left = dr.parent().offset().left;
  xpos = dr.offset().left + width - e.pageX;
  $(document.body).on('mousemove', function(e) {
    let ileft = e.pageX + xpos - width;

    if (dr.hasClass("drag")) {
      if (ileft <= min_left) {
        ileft = min_left;
      }
      if (ileft >= max_left) {
        ileft = max_left;
      }
      dr.offset({
        left: ileft
      });
    }
  }).on('mouseup', function(e) {
    dr.removeClass("drag");
  });
});
.phone-mockup-wrapper {
  position: relative;
}

.phone-mockup-container {
  position: absolute;
  top: 20px;
  left: 0;
  right: 0;
  z-index: 2;
  max-width: 355px;
}

.phone-mockup {
  -o-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -webkit-user-drag: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-drag: none;
  user-select: none;
  position: relative;
}

.phone-trigger {
  position: absolute;
  top: 95%;
  left: 50%;
  transform: translate(-50%, -100%);
  max-width: fit-content;
  font-size: 29px;
  padding: 13px;
  border-radius: 50%;
  background-color: white;
  cursor: move;
}

.image {
  max-width: 820px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div class="phone-mockup-wrapper">
  <div class="phone-mockup-container">
    <img class="phone-mockup" src="https://i.ibb.co/NpXQY30/iphone-mockup.png">
    <span class="phone-trigger">drag</span>
  </div>
</div>
<img class="image" src="https://i.ibb.co/MPwGfmc/7e8db8b589fe8defd6b9cc051a676a19.png">


Solution

  • I managed to solve this by using the same image as background-image and a combination of background-size, overflow and the value generated from the function that lets you drag the iPhone-mockup.

    let draggable = $('.phone-mockup-container');
    let dragTrigger = $('.phone-trigger');
    let eller = $('.phone-area');
    
    dragTrigger.on('mousedown', function(e) {
      eller.addClass('dragger');
    
      let dr = draggable.addClass("drag");
      height = dr.outerHeight();
      width = dr.outerWidth();
      max_left = dr.parent().offset().left + dr.parent().width() - dr.outerWidth();
      min_left = dr.parent().offset().left;
      xpos = dr.offset().left + width - e.pageX;
      $(document.body).on('mousemove', function(e) {
        let ileft = e.pageX + xpos - width;
    
        if (dr.hasClass("drag")) {
          if (eller.hasClass("dragger")) {
    
            if (ileft <= min_left) {
              ileft = min_left;
            }
            if (ileft >= max_left) {
              ileft = max_left;
            }
    
            dr.offset({
              left: ileft
            });
            let test = document.querySelector('.drag');
            let left = test.style.left;
            $(eller).css('backgroundPositionX', '-' + left);
          }
        }
      }).on('mouseup', function(e) {
        dr.removeClass("drag");
        eller.removeClass("dragger");
      });
    });
    .phone-mockup-wrapper {
      position: relative;
    }
    
    .phone-mockup-container {
      position: absolute;
      top: 20px;
      left: 0;
      right: 0;
      z-index: 2;
      max-width: 355px;
      overflow: hidden;
    }
    
    .phone-mockup {
      -o-user-select: none;
      -khtml-user-select: none;
      -moz-user-select: none;
      -webkit-user-drag: none;
      -webkit-user-select: none;
      -ms-user-select: none;
      user-drag: none;
      user-select: none;
      position: relative;
      z-index: 2;
    }
    
    .phone-area {
      background-image: url('https://i.ibb.co/MPwGfmc/7e8db8b589fe8defd6b9cc051a676a19.png');
      position: absolute;
      top: 4.3%;
      top: 1.2%;
      left: 5%;
      height: 97%;
      width: 90%;
      z-index: 1;
      border-radius: 10px;
      pointer-events: none;
      background-repeat: no-repeat;
      background-size: 820px 709px;
      background-position: left;
      border-radius: 20px;
    }
    
    .phone-trigger {
      position: absolute;
      top: 95%;
      left: 50%;
      transform: translate(-50%, -100%);
      max-width: fit-content;
      font-size: 29px;
      padding: 13px;
      border-radius: 50%;
      background-color: white;
      cursor: move;
      z-index: 10;
    }
    
    .image {
      max-width: 820px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <div class="phone-mockup-wrapper">
      <div class="phone-mockup-container">
        <img class="phone-mockup" src="https://i.ibb.co/NpXQY30/iphone-mockup.png">
        <div class="phone-area"></div>
        <span class="phone-trigger">drag</span>
      </div>
    </div>
    <img class="image" src="https://i.ibb.co/MPwGfmc/7e8db8b589fe8defd6b9cc051a676a19.png">