使用带有变换原点中心的滚轮在鼠标点上放大/缩小图像。在计算中需要帮助 [已解决]

Posted

技术标签:

【中文标题】使用带有变换原点中心的滚轮在鼠标点上放大/缩小图像。在计算中需要帮助 [已解决]【英文标题】:Zoom image in/out on mouse point using wheel with transform origin center. Need help in calculation [ SOLVED ] 【发布时间】:2022-01-09 14:01:17 【问题描述】:

上下文:

我的要求是通过用户对图像的操作进行图像转换,例如缩放、旋转和平移。 使用变换原点 50% 50%(默认)在其中心完成图像的旋转和平移。 为了使用鼠标滚轮缩放鼠标点上的图像,我能够使其与变换原点 0 0 一起使用 如果我更改原点,为了适应缩放行为,我需要重新计算相对于新原点的其他转换。

问题:

如果变换原点默认为 50% 50%,如何在鼠标点上缩放图像? 以下小提琴适用于变换原点 0 0 以供参考。我需要帮助来使用转换原点 50% 50% 优化此计算。

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

<!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.xpx,$ts.translate.ypx) scale($ts.scale) rotate($ts.rotatedeg)`;
        img.style.transform = steps;
      
    </script>
  </body>
</html>

【问题讨论】:

我不完全理解为什么缩放应该涉及重新定位 - 你能解释一下你想在这里实现什么吗? 如上所述,要求是在图像上的鼠标点上使用鼠标滚轮启用放大/缩小。当我们缩放具有特定原点的图像时,鼠标点下方的图像会移动到不同的位置。因此,为了模拟该特定点上的图像缩放,我们需要通过适当的平移来补偿它。如果您可以检查小提琴,您就会理解这种行为。 我确实检查了小提琴,但我不明白这种行为,这就是我问这个问题的原因。好的,所以鼠标要停留在图像的同一点上,明白了。这就是我们需要将 CSS 缩放属性与转换的其余部分分开的地方 - 即将推出,但尚未出现在所有浏览器中。 鼠标点的比例仅在原点设置为 0 0 时有效。但我希望旋转发生在图像的中心。因此,为了使所有内容都适应 50% 50%(默认)的共同起源,如何更改“onwheel”回调的计算以使其按预期工作? 【参考方案1】:

如果变换原点默认为50% 50%,如何在鼠标点上缩放图像?

要将原点移动到 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.xpx,$ts.translate.ypx) scale($ts.scale) rotate($ts.rotatedeg) 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%,您可以在中心旋转图像。

【讨论】:

感谢您的解决方案。非常感谢您花时间解决这个问题。 乐于助人!我希望 devtools 有元素坐标指示器。 :( 只是一个想法,如果您可以探索使用transform: translate(10px, 10px) scale(2) translate(-5px, 10px) rotate(30deg); 的可能性,我不知道您一次可以堆叠多少。此外,您可以在绝对定位的图像上设置顶部和左侧而不是平移(x,y) . 我的意思是使用 top, left 进行拖动和平移以补偿缩放。这可以使计算变得简单。 我最初使用top left 来翻译元素。但是为了在更新位置时减少绘制周期数并在移动元素时达到一致的 60 fps,我使用了变换。从而减少每个鼠标事件的重新计算。

以上是关于使用带有变换原点中心的滚轮在鼠标点上放大/缩小图像。在计算中需要帮助 [已解决]的主要内容,如果未能解决你的问题,请参考以下文章

LabVIEW操作鼠标滚轮放大/缩小图像

LabVIEW操作鼠标滚轮放大/缩小图像

LabVIEW操作鼠标滚轮放大/缩小图像

word滚动鼠标滑轮会放大缩小怎么办

Java AWT 图形界面编程使用鼠标滚轮放大缩小 Canvas 画布 ( 鼠标滚轮事件监听器 MouseWheelListener )

在网页里点开放大了一张图片后,怎么实现随鼠标滚轮放大缩小?