缩放比例>1时Touchmove pinch zoom位置跳动问题优化咨询
修复图片Pinch Zoom的位置跳动问题
问题根源
- 状态变量
_translateX/_translateY未与实际transform同步,导致再次触发pinch时初始位移值错误。 - 初始触摸中心的坐标转换逻辑错误,未结合当前缩放和位移计算图片坐标系下的对应点,导致缩放时位置跳变。
- 从transform提取参数的方式不可靠,且未正确获取当前translate值。
优化方案
以下是修复后的完整代码,核心修改点已标注:
HTML(无修改)
<div id='container'> <img src='https://i.pinimg.com/originals/f3/40/bf/f340bf0e86d65b5aaa709a0e98eae829.jpg'> </div>
CSS(无修改)
#container { width: 100%; overflow: hidden; border:solid 1px grey; } img { max-height: 100vh; max-width: 400px; -webkit-transition: all 0.05s ease-out; -moz-transition: all 0.05s ease-out; -ms-transition: all 0.05s ease-out; transition: all 0.05s ease-out; }
修复后的JavaScript
var _img = document.querySelector('img'); var _container = document.querySelector('#container'); var _pinching = false; // 维护实时同步的状态变量 var _currentScale = 1; var _currentTranslateX = 0; var _currentTranslateY = 0; // 辅助函数:解析当前transform的参数 function getTransformParams() { const transform = window.getComputedStyle(_img).transform; if (transform === 'none') { return { scale: 1, translateX: 0, translateY: 0 }; } const matrix = transform.match(/matrix\(([^)]+)\)/)?.[1]?.split(',') || []; if (matrix.length === 6) { // 对于scale和translate的组合,matrix格式为 [scaleX, 0, 0, scaleY, translateX, translateY] const scale = parseFloat(matrix[0]); const translateX = parseFloat(matrix[4]); const translateY = parseFloat(matrix[5]); return { scale, translateX, translateY }; } return { scale: 1, translateX: 0, translateY: 0 }; } _container.addEventListener('touchstart', (event) => { event.preventDefault(); if (event.touches.length === 2) { const touch1 = event.touches[0]; const touch2 = event.touches[1]; _initialDistance = Math.hypot(touch2.clientX - touch1.clientX, touch2.clientY - touch1.clientY); // 从当前样式中提取正确的初始参数 const { scale: initialScale, translateX: initialTranslateX, translateY: initialTranslateY } = getTransformParams(); _currentScale = initialScale; _currentTranslateX = initialTranslateX; _currentTranslateY = initialTranslateY; // 计算触摸中心在容器内的坐标 const containerRect = _container.getBoundingClientRect(); const touchCenterX = (touch1.clientX + touch2.clientX) / 2 - containerRect.left; const touchCenterY = (touch1.clientY + touch2.clientY) / 2 - containerRect.top; // 将触摸中心转换为图片坐标系下的点(核心修复:解决跳变问题) _initialImgCenterX = (touchCenterX - _currentTranslateX) / _currentScale; _initialImgCenterY = (touchCenterY - _currentTranslateY) / _currentScale; _pinching = true; } }); document.addEventListener('touchmove', (event) => { if (_pinching && event.touches.length === 2) { const touch1 = event.touches[0]; const touch2 = event.touches[1]; const distance = Math.hypot(touch2.clientX - touch1.clientX, touch2.clientY - touch1.clientY); const scaleFactor = distance / _initialDistance; const newScale = _currentScale * scaleFactor; // 计算当前触摸中心在容器内的坐标 const containerRect = _container.getBoundingClientRect(); const currentTouchCenterX = (touch1.clientX + touch2.clientX) / 2 - containerRect.left; const currentTouchCenterY = (touch1.clientY + touch2.clientY) / 2 - containerRect.top; // 计算新的位移:确保图片坐标系下的初始中心始终对应当前触摸中心 const newTranslateX = currentTouchCenterX - (_initialImgCenterX * newScale); const newTranslateY = currentTouchCenterY - (_initialImgCenterY * newScale); // 限制最小缩放为1,重置位移 let finalScale = newScale; let finalTranslateX = newTranslateX; let finalTranslateY = newTranslateY; if (finalScale <= 1) { finalScale = 1; finalTranslateX = 0; finalTranslateY = 0; } // 更新transform并同步状态变量 _img.style.transform = `translate(${Math.round(finalTranslateX)}px, ${Math.round(finalTranslateY)}px) scale(${finalScale})`; _currentScale = finalScale; _currentTranslateX = finalTranslateX; _currentTranslateY = finalTranslateY; } }); document.addEventListener('touchend', () => { _pinching = false; });
关键修改说明
- 状态同步:使用
getTransformParams函数从计算样式中提取当前的transform参数,确保初始值完全匹配实际显示状态。 - 坐标转换修复:将触摸中心转换为图片自身坐标系下的点,缩放时通过反向计算位移,保证该点始终对应触摸中心,彻底解决跳变问题。
- 实时状态更新:每次touchmove更新transform后,立即同步
_currentScale/_currentTranslateX/_currentTranslateY,避免状态不一致。
内容的提问来源于stack exchange,提问作者abe1432181
相关产品推荐
相关产品推荐

