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>
修改说明
- 调整了CSS中图片的
transform-origin为默认的50% 50% - 新增图片加载监听,获取图片半宽半高作为中心原点的偏移基准
- 重写了滚轮事件的坐标计算逻辑,加入了旋转变换的逆运算,保证旋转后缩放依然在鼠标指针位置生效
- 调整了变换顺序,先平移再旋转最后缩放,符合中心原点下的变换预期
内容的提问来源于stack exchange,提问作者Neyo
相关产品推荐
相关产品推荐

