最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Zoom image inout on mouse point using wheel with transform origin center. Need help in calculation - Stack Overflow

programmeradmin1浏览0评论

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 acodate 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 ?

<!DOCTYPE html>
<html lang="en">
  <head>
    <style>
      .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;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="stage">
        <img id="image" src=".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>
    <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();
        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)`;
        img.style.transform = steps;
      }
    </script>
  </body>
</html>

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 acodate 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 ?

<!DOCTYPE html>
<html lang="en">
  <head>
    <style>
      .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;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="stage">
        <img id="image" src="https://cdn.pixabay./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>
    <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();
        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)`;
        img.style.transform = steps;
      }
    </script>
  </body>
</html>

Share Improve this question edited Apr 28, 2022 at 19:38 General Grievance 5,04338 gold badges37 silver badges56 bronze badges asked Dec 3, 2021 at 6:25 NeyoNeyo 6441 gold badge13 silver badges27 bronze badges 4
  • I don't fully understand why scaling should involve repositioning - could you explain what you want to achieve here? – A Haworth Commented Dec 3, 2021 at 7:03
  • As stated the requirement is to enable zoom in/out using mouse wheel on the mouse point over the image. When we scale an image with a specific origin, the image below the mouse point moves to different location. So to emulate the image scaling over that specific point we need to pensate that with proper translations. If you can check the fiddle you would undertand the behaviour. – Neyo Commented Dec 3, 2021 at 7:07
  • 1 I did check the fiddle but I didn't understand the behavior which is why I asked the question. OK, so the mouse is to stay on the same point of the image, got it. This is where we need a CSS scale prooperty separated out from the rest of transform - ing but not in all browsers yet. – A Haworth Commented Dec 3, 2021 at 7:18
  • The scale on mouse point works only when the origin is set to 0 0. But i want the rotation to happen at center of image. So to acodate everything to a mon origin of 50% 50% ( default ), how to change the calculation of "onwheel" callback so that it works as expected ? – Neyo Commented Dec 3, 2021 at 7:23
Add a ment  | 

1 Answer 1

Reset to default 6 +50

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 i.e. image top left as origin till image bottom right. Then we pensate 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./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./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.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论