Javascript pinch zoom image

969 Views Asked by At

I have the following script to zoom and pan image. Mouse functionality works ok including wheel, but I want to add touch support for mobile. I can figure out how to calculate current scale when pinch starts and retain image position without any ind of weird jumps.

const img = document.getElementById('image');
const stage = document.querySelector('.stage');
const container = document.querySelector('.container');
let mouseX;
let mouseY;
let mouseTX;
let mouseTY;
let startX = 0;
let startY = 0;
let panning = false;

const ts = {
  scale: 1,
  translate: {
    x: 0,
    y: 0
  }
};


stage.onwheel = function(event) {
  event.preventDefault();

  var func = stage.onwheel;
  stage.onwheel = null;

  let rec = stage.getBoundingClientRect();
  let x = (event.clientX - rec.x) / ts.scale;
  let y = (event.clientY - rec.y) / ts.scale;

  let delta = (event.wheelDelta ? event.wheelDelta : -event.deltaY);
  ts.scale = (delta > 0) ? (ts.scale + 0.2) : (ts.scale - 0.2);
  if (ts.scale < 1) ts.scale = 1

  let m = (delta > 0) ? 0.1 : -0.1;
  ts.translate.x += (-x * m * 2) + (stage.offsetWidth * m);
  ts.translate.y += (-y * m * 2) + (stage.offsetHeight * m);

  setTransform();
  stage.onwheel = func;
};

stage.onmousedown = function(event) {
  event.preventDefault();
  panning = true;
  mouseX = event.clientX;
  mouseY = event.clientY;
  mouseTX = ts.translate.x;
  mouseTY = ts.translate.y;
};

stage.onmouseup = function(event) {
  panning = false;
};
document.onmouseup = function(event) {
  panning = false;
};

stage.onmousemove = function(event) {
  event.preventDefault();
  let rec = stage.getBoundingClientRect();

  const x = event.clientX;
  const y = event.clientY;

  if (!panning) {
    return;
  }
  ts.translate.x = mouseTX + (x - mouseX);
  ts.translate.y = mouseTY + (y - mouseY);

  setTransform();
};

function setTransform() {
  const steps = `translate(${ts.translate.x}px,${ts.translate.y}px) scale(${ts.scale}) translate3d(0,0,0)`;

  stage.style.transform = steps;
}

function reset() {
  ts.scale = 1;
  ts.translate = {
    x: 0,
    y: 0
  };
  stage.style.transform = 'none';
}

setTransform();


if ("ontouchstart" in window) {


  let start = {};

  // Calculate distance between two fingers
  const distance = (event) => {
    return Math.hypot(event.touches[0].pageX - event.touches[1].pageX, event.touches[0].pageY - event.touches[1].pageY);
  };

  stage.addEventListener('touchstart', (event) => {

    if (event.touches.length === 2) {
      event.preventDefault(); // Prevent page scroll

      // Calculate where the fingers have started on the X and Y axis
      start.x = (event.touches[0].pageX + event.touches[1].pageX) / 2;
      start.y = (event.touches[0].pageY + event.touches[1].pageY) / 2;
      start.distance = distance(event);
    }
  });

  stage.addEventListener('touchmove', (event) => {

    if (event.touches.length === 2) {
      event.preventDefault(); // Prevent page scroll

      // Safari provides event.scale as two fingers move on the screen
      // For other browsers just calculate the scale manually
      let scale;
      if (event.scale) {
        scale = event.scale;
      } else {
        const deltaDistance = distance(event);
        scale = deltaDistance / start.distance;
      }
      var imageElementScale = Math.min(Math.max(1, scale), 4);

      // Calculate how much the fingers have moved on the X and Y axis
      const deltaX = (((event.touches[0].pageX + event.touches[1].pageX) / 2) - start.x); 
      const deltaY = (((event.touches[0].pageY + event.touches[1].pageY) / 2) - start.y); 

      ts.translate.x = deltaX;
      ts.translate.y = deltaY;

      ts.scale = imageElementScale

      setTransform();

    }
  });

  stage.addEventListener('touchend', (event) => {
  
   panning = false;
  });



}
.container {
  position: relative;
  width: 600px;
  border: 1px solid;
  overflow: hidden;
}

.stage {
  transform-origin: 50% 50%;
  cursor: grab;
  position: relative;
  width: 100%;
}

#image {
  width: 100%;
  height: auto;
  display: block;
}
<div class="container">
  <div class="stage">
    <img id="image" src="https://cdn.pixabay.com/photo/2018/01/14/23/12/nature-3082832__480.jpg" />

  </div>
</div>

0

There are 0 best solutions below