使用具有变换原点中心的滚轮在鼠标点上缩放图像 in/out。需要帮助计算

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

语境:

问题:

有人可以协助进一步优化此计算,使其适用于默认变换原点吗?

<!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.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>
    <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>

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

要将原点移动到 50% 50%,我们需要鼠标相对于图像的 x、y 位置,即图像左上角作为原点,直到图像右下角。然后我们使用相对坐标补偿图像位置。我们还需要考虑图像尺寸。

<!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>

为简单起见,我将比例加 0.2 而不是乘以 1.2。你可以用正确的比例把它改回来。还添加了一个重置​​按钮。
现在,由于变换原点已移动到 50% 50%,您可以在中心旋转图像。