移动端捏合手势怎么监听才准确?
我在做一个移动端的图片查看器,想实现双指捏合缩放功能。试了用 touchstart 和 touchmove 监听两个 touch 点的距离变化,但经常误触发,比如滑动时也会被识别成捏合。而且有时候手指稍微一动就疯狂触发缩放。
我现在的判断逻辑是检测 touch 数量为 2 时计算距离差,代码大概这样:
let startDistance = 0;
element.addEventListener('touchstart', (e) => {
if (e.touches.length === 2) {
const dx = e.touches[0].pageX - e.touches[1].pageX;
const dy = e.touches[0].pageY - e.touches[1].pageY;
startDistance = Math.sqrt(dx * dx + dy * dy);
}
});
element.addEventListener('touchmove', (e) => {
if (e.touches.length === 2) {
const dx = e.touches[0].pageX - e.touches[1].pageX;
const dy = e.touches[0].pageY - e.touches[1].pageY;
const currentDistance = Math.sqrt(dx * dx + dy * dy);
const scale = currentDistance / startDistance;
// 应用缩放...
}
});
但体验很不稳,有没有更靠谱的做法?是不是该用 gesture 事件?不过听说现在基本被废弃了……
根本原因是你没区分"滑动"和"捏合"两种操作。两个手指同时放在屏幕上移动,既有可能是捏合缩放,也有可能是在滑动切换图片,你的代码把所有情况都当成了捏合。
gesture事件确实早就废弃了,别想了。
更靠谱的做法是加个判断:只有当两个手指都在移动且方向基本一致时,才认定为捏合。我给你一个改进后的代码:
几个关键点你注意一下:
第一,
distanceDiff < MIN_PINCH_DISTANCE这个阈值判断很重要,10px 以内的变化直接忽略,不然手指稍微一动就触发,体验很差。第二,
startDistance = currentDistance这个更新逻辑不能少。你之前是固定用 startDistance 算比例,时间长了累积误差会越来越大,导致缩放越来越快。改成每次移动后更新基准距离会平滑很多。第三,
e.preventDefault()要加上,不然捏合的时候页面会跟着滚。第四,
{ passive: false }必须设置,否则 preventDefault 无效。还有个进阶技巧:如果你的场景里同时有左右滑动切换图片的需求,可以在 touchstart 时记录两个手指的中心点位置,如果后续移动时中心点偏移过大,就判断为滑动而不是捏合。不过对于纯图片查看器来说,上面这套应该够用了。