Konva舞台捏合缩放时如何基于Pinch点居中且避免位置跳变?
解决Konva舞台捏合缩放时基于中心点定位的问题
这个问题我之前在做Konva交互式项目时也踩过坑——默认缩放以舞台左上角为原点,再加上draggable: true的自动位置更新,确实会导致缩放时位置跳变。核心矛盾是draggable的自动位置调整和我们手动基于捏合中心的位置修正冲突,下面是一套亲测可行的解决方案:
核心思路
- 在捏合缩放开始时,临时禁用舞台的
draggable,避免自动位置干扰 - 计算捏合中心在舞台本地坐标系下的位置
- 根据缩放比例,反向计算舞台需要偏移的距离,保证捏合中心始终在屏幕原位置
- 捏合结束后,恢复舞台的
draggable功能
具体实现代码
const stage = new Konva.Stage({ container: 'container', width: window.innerWidth, height: window.innerHeight, draggable: true }); // 记录捏合状态和初始参数 let isPinching = false; let initialScale = 1; let initialStagePos = { x: 0, y: 0 }; let initialPinchCenter = { x: 0, y: 0 }; // 监听touchstart,判断是否进入捏合状态 stage.container().addEventListener('touchstart', (e) => { if (e.touches.length === 2) { isPinching = true; // 临时禁用draggable,避免自动位置逻辑干扰 stage.draggable(false); // 保存缩放前的初始状态 initialScale = stage.scaleX(); initialStagePos = { x: stage.x(), y: stage.y() }; // 计算初始捏合中心的屏幕坐标,再转成舞台本地坐标 const touch1 = e.touches[0]; const touch2 = e.touches[1]; const globalCenterX = (touch1.clientX + touch2.clientX) / 2; const globalCenterY = (touch1.clientY + touch2.clientY) / 2; initialPinchCenter = stage.getPointerPosition({ clientX: globalCenterX, clientY: globalCenterY }); } }); // 监听touchmove,处理缩放和位置调整 stage.container().addEventListener('touchmove', (e) => { if (!isPinching || e.touches.length !== 2) return; e.preventDefault(); // 阻止页面默认滚动行为 // 计算当前捏合的距离与初始距离的比例,得到缩放倍数 const touch1 = e.touches[0]; const touch2 = e.touches[1]; const currentDistance = Math.sqrt( Math.pow(touch2.clientX - touch1.clientX, 2) + Math.pow(touch2.clientY - touch1.clientY, 2) ); const initialDistance = Math.sqrt( Math.pow(touch2.clientX - touch1.clientX, 2) + Math.pow(touch2.clientY - touch1.clientY, 2) ); const scale = initialScale * (currentDistance / initialDistance); // 计算缩放后,为保持捏合中心位置不变,舞台需要偏移的量 const newStageX = initialStagePos.x + initialPinchCenter.x * (initialScale - scale); const newStageY = initialStagePos.y + initialPinchCenter.y * (initialScale - scale); // 更新舞台的缩放比例和位置 stage.scale({ x: scale, y: scale }); stage.position({ x: newStageX, y: newStageY }); stage.draw(); }); // 监听touchend,恢复draggable功能 stage.container().addEventListener('touchend', (e) => { if (isPinching) { isPinching = false; stage.draggable(true); } });
关键细节解释
- 临时禁用draggable:捏合过程中必须关掉自动拖拽,否则Konva内置的draggable逻辑会自动修改stage的x/y值,和我们手动计算的位置冲突,直接导致跳变。
- 坐标转换:
stage.getPointerPosition()是核心——它能把屏幕上的捏合中心坐标转换成舞台内部的相对坐标,只有拿到这个值,才能准确计算缩放后的舞台偏移量。 - 偏移量计算逻辑:
initialPinchCenter.x * (initialScale - scale)的本质是:缩放后,捏合中心在舞台上的相对位置会跟着缩放比例变化,我们需要反向移动舞台,让这个中心在屏幕上的视觉位置保持不变。
内容的提问来源于stack exchange,提问作者Anthony
相关产品推荐
相关产品推荐

