WesCritch
WesCritch

Reputation: 31

Pinch and zoom combined with dragging with mouse and touch screen

I'm trying to create the ability, on a touchscreen monitor, to be able to zoom in to a part of an image and be able to drag it around and zoom out from the point the drag ended.

I'm using the GSAP library for the drag and zoom along with Hammer JS to aid in using touch screen.

From the basic codepen I created I have got it working. However if I zoom in then drag to the opposite end of the image, when I zoom out the image will not scale from where my mouse is. I think it has something to do with the transform-origin and translate3D() clashing as if I zoom in and out on the same spot it is perfect.

Am I being really dumb and missing something incredibly obvious or is it something else?

Appreciate any help and/or suggestions given.

https://codepen.io/wescritch98/pen/wBwmvmm

Also thought I might add, if you're gong to recreate this, you will need to add both the Draggable and Observer plugins as well as the main gsap library

<img src="https://img.freepik.com/free-photo/painting-mountain-lake-with-mountain-background_188544-9126.jpg?t=st=1736502806~exp=1736506406~hmac=55191bc567a6b56fa91081c238a86595d5a85ff785179070a8deb52b1ba05041&w=1380" class="zoom-item">
body {
  display: flex;
  justify-content: center;
  align-ites: center;
  overflow: hidden;
}

.box {
  width: 300px;
  height: 300px;
  background: red;
}
let image = document.querySelector(".zoom-item"),
  hammer = new Hammer(image),
  mousePosArr = [],
  pinchPosArr = [];

hammer.get("pinch").set({
  enable: true
});

hammer.on("pinchstart", (e) => {
  let pinchX = e.srcEvent.offsetX,
    pinchY = e.srcEvent.offsetY;

  pinchPosArr.push(pinchX, pinchY);

  zoomDrag[0].disable();
});

hammer.on("pinch", (e) => {
  console.log(pinchPosArr);

  let roundedX = Math.ceil(pinchPosArr[0]),
    roundedY = Math.ceil(pinchPosArr[1]);

  switch (e.additionalEvent) {
    case "pinchout":
      //round the numbers to nearest whole one
      zoom_control("+=0.03", roundedX, roundedY);
      break;
    case "pinchin":
      if (gsap.getProperty(e.target, "scale") >= 1.2) {
        zoom_control("-=0.03", roundedX, roundedY);
      }
      break;
  }
});

hammer.on("pinchend", () => {
  pinchPosArr = [];
  zoomDrag[0].enable();
});

let zoomObserver = Observer.create({
    target: ".zoom-item",
    type: "wheel", // Will only fire on the wheel scroll
    ease: "linear", // makes the animations more slower
    wheelSpeed: 0.1, // Changed how fast the zoom in happens
    onUp: (self) => {
      mousePosArr = [];
      let mouseX = self.event.offsetX,
        mouseY = self.event.offsetY;

      mousePosArr.push(mouseX, mouseY);
      // Creates an array and pushes each instance of mouseX and mouseY into the array

      const [posA, posB] = mousePosArr.slice(-2); // slice() helps me grab the last two added items into the array

      //using the posA and posB means that it zoom into wherever my mouse currently is
      console.log(image.style.scale);
      zoom_control("+=0.3", posA, posB);
    },
    onDown: (self) => {
      // Will only fire if the scale is above the 1.2
      if (gsap.getProperty(self.target, "scale") >= 1.2) {
        zoom_control("-=0.3", mousePosArr[0], mousePosArr[1]);
      }
    }
  }),
  zoomDrag = Draggable.create(".zoom-item", {
    zIndexBoost: false, // zIndexBoost set to false to stop it creasing on each click
    onDrag: () => {
      zoomObserver.disable();
    },
    onDragEnd: () => {
      zoomObserver.enable();
    }
  });

function zoom_control(scaleNum, transformX, transformY) {
  gsap.set(image, {
    transformOrigin: `${transformX}px ${transformY}px`
  });
  let scaleSetter = gsap.quickSetter(image, "css");
  scaleSetter({
    scale: scaleNum
  });
}

Upvotes: 0

Views: 39

Answers (0)

Related Questions