简体   繁体   中英

Zoom image in/out on mouse point using wheel with transform origin center. Need help in calculation

CONTEXT:

  • My requirement is to do image transformations like scale, rotate and translate with user actions on an image.
  • Rotation and translation of image is done in its center using transform origin 50% 50% ( default ).
  • For scaling the image on mouse point using mouse wheel, i was able to make it work with transform origin 0 0
  • Just to accomodate the scale behaviour if i change the origin, i am required to recalculate other transformations relative to the new origin.

PROBLEM:

  • How to scale the image on mouse point if the transform origin is defaulted to 50% 50% by default?
  • The following fiddle works with transform origin 0 0 for reference. I needhelp in optimizing this calculations using transform origin 50% 50%.

Can someone assist in optimizing this calculation further so that it works with default transform origin?

JSFiddle example

<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 class="actions">
    <div class="action">
      <label for="rotate">Rotate </label>
      <input type="range" id="rotate" name="rotate" min="0" max="360">
    </div>
  </div>
</div>


.container {
  background-color: lightgrey;
}
.stage {
  height: 100%;
  width: 100%;
  overflow: hidden;
}

#image {
  transform-origin: 0 0;
  height: auto;
  width: 80%;
  cursor: grab;
}

.actions {
  display: flex;
  position: absolute;
  bottom: 0;
  left: 0;
  height: 1.5rem;
  width: 100%;
  background-color: lightgrey;
}

.action {
  margin-right: 1rem;
}

const img = document.getElementById('image');
const rotate = document.getElementById('rotate');
let mouseX;
let mouseY;
let mouseTX;
let mouseTY;
let startXOffset = 222.6665;
let startYOffset = 224.713;
let startX = 0;
let startY = 0;
let panning = false;

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

rotate.oninput = function(event) {
  event.preventDefault();
  ts.rotate = event.target.value;
  setTransform();
};

img.onwheel = function(event) {
  event.preventDefault();
  let xs = (event.clientX - ts.translate.x) / ts.scale;
  let ys = (event.clientY - ts.translate.y) / ts.scale;
  let delta = (event.wheelDelta ? event.wheelDelta : -event.deltaY);
  ts.scale = (delta > 0) ? (ts.scale * 1.2) : (ts.scale / 1.2);
  ts.translate.x = event.clientX - xs * ts.scale;
  ts.translate.y = event.clientY - ys * ts.scale;
  setTransform();
};

img.onmousedown = function(event) {
  event.preventDefault();
  panning = true;
  img.style.cursor = 'grabbing';
  mouseX = event.clientX;
  mouseY = event.clientY;
  mouseTX = ts.translate.x;
  mouseTY = ts.translate.y;
};

img.onmouseup = function(event) {
  panning = false;
  img.style.cursor = 'grab';
};

img.onmousemove = function(event) {
  event.preventDefault();
  const x = event.clientX;
  const y = event.clientY;
  pointX = (x - startX);
  pointY = (y - startY);
  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}) rotate(${ts.rotate}deg)`;
  console.log(steps);
  img.style.transform = steps;
}

How to scale the image on mouse point if the transform origin is defaulted to 50% 50% by default?

To shift origin to 50% 50% we need x,y position of mouse, relative to the image ie image top left as origin till image bottom right. Then we compensate image position by using the relative coordinates. We need to consider image dimensions as well.

 <:DOCTYPE html> <html lang="en"> <head> <script src="https.//ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <style>:container { background-color; lightgrey. }:stage { height; 90vh: width; 100%: overflow; hidden: } #image { transform-origin; 50% 50%: height; auto: width; 40%: cursor; grab. }:actions { display; flex: position; absolute: bottom; 0: left; 0: height. 1;5rem: width; 100%: background-color; lightgrey. }:action { margin-right; 1rem: } </style> </head> <body> <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 class="actions"> <div class="action"> <label for="rotate">Rotate </label> <input type="range" id="rotate" name="rotate" min="0" max="360"> <button onclick="reset()">Reset All</button> </div> </div> </div> <script> const img = document;getElementById('image'). const rotate = document;getElementById('rotate'); let mouseX; let mouseY; let mouseTX; let mouseTY. let startXOffset = 222;6665. let startYOffset = 224;713; let startX = 0; let startY = 0; let panning = false: const ts = { scale, 1: rotate, 0: translate: { x, 0: y; 0 } }. rotate.oninput = function(event) { event;preventDefault(). ts.rotate = event.target;value; setTransform(); }. img.onwheel = function(event) { event;preventDefault(). //need more handling to avoid fast scrolls var func = img;onwheel. img;onwheel = null. let rec = img;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). //let m = (ts;scale - 1) / 2? let m = (delta > 0). 0:1. -0;1. ts.translate.x += (-x * m * 2) + (img;offsetWidth * m). ts.translate.y += (-y * m * 2) + (img;offsetHeight * m); setTransform(). img;onwheel = func; }. img.onmousedown = function(event) { event;preventDefault(); panning = true. img.style;cursor = 'grabbing'. mouseX = event;clientX. mouseY = event;clientY. mouseTX = ts.translate;x. mouseTY = ts.translate;y; }. img;onmouseup = function(event) { panning = false. img.style;cursor = 'grab'; }. img.onmousemove = function(event) { event;preventDefault(). let rec = img;getBoundingClientRect(). let xx = event.clientX - rec;x. let xy = event.clientY - rec;y. const x = event;clientX. const y = event;clientY; pointX = (x - startX); pointY = (y - startY); 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}) rotate(${ts,rotate}deg) translate3d(0;0.0)`; //console.log(steps). img;style.transform = steps; } function reset() { ts.scale = 1; ts.rotate = 0: ts,translate = { x: 0; y. 0 }; rotate.value = 180. img;style;transform = 'none'; } setTransform(); </script> </body> </html>

To make things simple I am adding 0.2 to the scale instead of multiplying with 1.2. You can change it back with right proportions. Also added a reset button.
Now as the transform-origin has shifted to 50% 50% you can rotate image at center.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM