如何用JavaScript实现捏合缩放及双指平移功能?
实现带平移的双指捏合缩放功能
需要用JavaScript实现捏合缩放功能,目前已能通过touch事件检测手势,但需要一套完善的算法,同时处理缩放和平移,确保缩放中心始终在双指之间;另外当双指保持等距移动时,要实现和单指拖拽一样的纯平移效果。
完整实现代码
HTML 结构
<div id="zoom-container"> <img id="zoom-img" src="替换为你的图片地址" alt="可缩放图片" /> </div>
CSS 样式
#zoom-container { width: 100vw; height: 100vh; overflow: hidden; position: relative; } #zoom-img { position: absolute; transform-origin: center center; transition: transform 0.05s ease-out; /* 让缩放/平移更平滑 */ cursor: grab; }
JavaScript 核心逻辑
const zoomContainer = document.getElementById('zoom-container'); const zoomImg = document.getElementById('zoom-img'); // 记录手势状态,避免每次操作从头计算 let initialScale = 1; let currentScale = 1; let initialTranslate = { x: 0, y: 0 }; let currentTranslate = { x: 0, y: 0 }; let initialFingerDistance = 0; let initialMidPoint = { x: 0, y: 0 }; // 获取元素在视口中的位置信息 function getElementRect(el) { return el.getBoundingClientRect(); } // 计算双指的中点坐标 function getMidPoint(touch1, touch2) { return { x: (touch1.pageX + touch2.pageX) / 2, y: (touch1.pageY + touch2.pageY) / 2 }; } // 计算双指之间的距离 function getFingerDistance(touch1, touch2) { return Math.hypot(touch1.pageX - touch2.pageX, touch1.pageY - touch2.pageY); } // 更新元素的变换样式 function updateTransform() { zoomImg.style.transform = `translate(${currentTranslate.x}px, ${currentTranslate.y}px) scale(${currentScale})`; } // 触摸开始:初始化状态 zoomContainer.addEventListener('touchstart', (e) => { e.preventDefault(); // 阻止默认的页面滚动行为 const touches = e.targetTouches; if (touches.length === 2) { // 双指触摸,初始化缩放相关参数 initialFingerDistance = getFingerDistance(touches[0], touches[1]); initialMidPoint = getMidPoint(touches[0], touches[1]); initialScale = currentScale; initialTranslate = { ...currentTranslate }; // 把变换原点设为双指中点,确保缩放围绕这个点进行 const imgRect = getElementRect(zoomImg); const relativeMidX = (initialMidPoint.x - imgRect.left) / imgRect.width; const relativeMidY = (initialMidPoint.y - imgRect.top) / imgRect.height; zoomImg.style.transformOrigin = `${relativeMidX * 100}% ${relativeMidY * 100}%`; } else if (touches.length === 1) { // 单指触摸,初始化平移参数 initialTranslate = { ...currentTranslate }; initialMidPoint = { x: touches[0].pageX, y: touches[0].pageY }; } }); // 触摸移动:处理缩放和平移 zoomContainer.addEventListener('touchmove', (e) => { e.preventDefault(); const touches = e.targetTouches; if (touches.length === 2) { const currentFingerDistance = getFingerDistance(touches[0], touches[1]); const currentMidPoint = getMidPoint(touches[0], touches[1]); // 计算当前缩放比例:新距离/初始距离 * 初始缩放值 const scaleFactor = currentFingerDistance / initialFingerDistance; currentScale = initialScale * scaleFactor; // 计算平移偏移:让图片跟随双指中点移动 const deltaX = currentMidPoint.x - initialMidPoint.x; const deltaY = currentMidPoint.y - initialMidPoint.y; currentTranslate.x = initialTranslate.x + deltaX; currentTranslate.y = initialTranslate.y + deltaY; updateTransform(); } else if (touches.length === 1) { // 单指拖拽:纯平移逻辑 const deltaX = touches[0].pageX - initialMidPoint.x; const deltaY = touches[0].pageY - initialMidPoint.y; currentTranslate.x = initialTranslate.x + deltaX; currentTranslate.y = initialTranslate.y + deltaY; updateTransform(); } }); // 触摸结束:重置变换原点,避免后续操作异常 zoomContainer.addEventListener('touchend', (e) => { if (e.targetTouches.length === 0) { zoomImg.style.transformOrigin = 'center center'; } });
关键逻辑说明
- 状态持久化:用变量记录每次手势的初始状态,确保缩放和平移都是基于上一次操作的结果计算,不会出现跳变
- 缩放中心锁定:双指触摸时将
transformOrigin设为双指中点,让缩放始终围绕双指中间进行,符合直觉 - 平移同步处理:双指移动时,同步计算中点的偏移量并叠加到平移值上,当双指等距移动时,缩放比例不变,自然实现纯平移效果
- 单指兼容:保留单指拖拽逻辑,和双指操作无缝切换,提升交互体验
内容的提问来源于stack exchange,提问作者shruti shende
相关产品推荐
相关产品推荐

