You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

transform-origin为默认50% 50%时实现鼠标指向位置缩放图片的计算方案求助

解决方案

核心逻辑

我们只需要保证缩放前后,鼠标指针指向的图片点位的屏幕坐标完全不变,就能实现指针位置缩放的效果,不需要修改平移、旋转的原有逻辑。因为变换原点改为图片中心,需要在坐标计算中额外叠加图片半宽、半高的偏移量,同时兼容旋转场景的话需要做坐标的逆旋转换算。

完整可运行代码

<!DOCTYPE html>
<html lang="en">
<head>
  <style>
    .container {
      background-color: lightgrey;
      height: 100vh;
      margin: 0;
    }
    .stage {
      height: calc(100% - 1.5rem);
      width: 100%;
      overflow: hidden;
    }
    #image {
      transform-origin: 50% 50%; /* 改为默认中心原点 */
      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 panning = false;
    // 存储图片半宽半高(中心原点偏移量)
    let imgHalfW = 0;
    let imgHalfH = 0;

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

    // 图片加载完成后获取尺寸,设置初始偏移
    img.onload = function() {
      imgHalfW = img.offsetWidth / 2;
      imgHalfH = img.offsetHeight / 2;
      // 初始把图片中心移到屏幕合适位置,和原代码初始展示效果对齐
      ts.translate.x = imgHalfW + 100;
      ts.translate.y = imgHalfH + 100;
      setTransform();
    }

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

    img.onwheel = function(event) {
      event.preventDefault();
      // 先把鼠标屏幕坐标转成缩放、平移前的相对坐标
      let relativeX = (event.clientX - ts.translate.x) / ts.scale;
      let relativeY = (event.clientY - ts.translate.y) / ts.scale;
      
      // 兼容旋转场景:逆旋转得到图片本地坐标(相对于中心原点)
      const rad = -ts.rotate * Math.PI / 180;
      const cos = Math.cos(rad);
      const sin = Math.sin(rad);
      const localX = relativeX * cos - relativeY * sin;
      const localY = relativeX * sin + relativeY * cos;

      // 计算新的缩放比例
      const delta = (event.wheelDelta ? event.wheelDelta : -event.deltaY);
      const oldScale = ts.scale;
      ts.scale = (delta > 0) ? (ts.scale * 1.2) : (ts.scale / 1.2);
      const scaleDiff = ts.scale / oldScale;

      // 计算缩放后该点的相对坐标,再反推新的平移值
      const rotateRad = ts.rotate * Math.PI / 180;
      const newRelativeX = localX * scaleDiff * Math.cos(rotateRad) - localY * scaleDiff * Math.sin(rotateRad);
      const newRelativeY = localX * scaleDiff * Math.sin(rotateRad) + localY * scaleDiff * Math.cos(rotateRad);
      ts.translate.x = event.clientX - newRelativeX;
      ts.translate.y = event.clientY - newRelativeY;
      
      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;
      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) rotate(${ts.rotate}deg) scale(${ts.scale})`;
      img.style.transform = steps;
    }
  </script>
</body>
</html>

修改说明

  1. 调整了CSS中图片的transform-origin为默认的50% 50%
  2. 新增图片加载监听,获取图片半宽半高作为中心原点的偏移基准
  3. 重写了滚轮事件的坐标计算逻辑,加入了旋转变换的逆运算,保证旋转后缩放依然在鼠标指针位置生效
  4. 调整了变换顺序,先平移再旋转最后缩放,符合中心原点下的变换预期

内容的提问来源于stack exchange,提问作者Neyo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 11:36:09