多点触控手势怎么监听缩放操作?
我在移动端做图片查看器,想实现双指缩放,但 touchstart 和 touchmove 事件里拿到的 touches 长度有时候不对,缩放时经常触发两次甚至更多次处理,逻辑乱了。
我试过用两个 touch 点的距离计算缩放比例,但手指刚接触屏幕时 touches 数量变化太快,导致初始距离算错。下面是我写的部分代码:
let startDistance = 0;
function handleTouchStart(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);
}
}
function handleTouchMove(e) {
if (e.touches.length === 2) {
// 这里有时拿不到正确的 startDistance
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;
console.log('scale:', scale);
}
}
关键问题在于手指刚接触屏幕时,两个 touch 事件不是同时到达的。有时候第一个手指的 touchstart 先触发,这时候 e.touches.length 是 1,第二个手指的 touchstart 才把长度变成 2。
解决方法是用一个标志位来标记是否已经准备好缩放,等两个手指都稳定了才开始计算。我改过的版本是这样的:
let startDistance = 0;
let isReady = false;
function handleTouchStart(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);
isReady = true;
} else {
isReady = false;
}
}
function handleTouchMove(e) {
if (!isReady || e.touches.length !== 2) return;
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;
console.log('scale:', scale);
}
另外建议加点防抖处理,不然缩放时控制台会被 log 刷屏。我当时还加了个最小移动距离的阈值,小于 5px 的移动直接忽略,这样体验会好很多。
这个坑最烦人的是不同机型表现还不一样,有些安卓机会疯狂触发 touchmove。如果要做更复杂的手势识别,建议直接用现成的库比如 hammer.js,自己处理这些边界情况太折磨人了。
记得把三个事件都绑上,touchend 也要处理,不然 isScaling 状态会乱。凌晨三点调试这种问题确实让人头秃。