从零打造一个Vue插件并解决开发中的常见问题
又踩坑了,touchmove滚动失效
最近在开发一个移动端插件时遇到个特别棘手的问题。这个插件的核心功能是实现一个可滑动的菜单栏,类似于手机上的tab切换效果。本来以为用touch事件简单处理下就能搞定,结果发现touchmove的时候页面滚动直接卡死了。
这里我踩了个坑,最初以为是CSS样式的问题,折腾了半天把position、overflow这些属性都试了一遍,发现根本不是样式导致的。后来试了下发现,问题出在touch事件的默认行为被阻止了。
三种方案对比,我选了最简单的
查了一圈资料,发现主要有三种解决方案:
- 方案一:完全自己实现滚动逻辑
- 方案二:使用iScroll这类现成库
- 方案三:保留系统默认滚动,只在必要时阻止默认行为
第一个方案我试了一下,太复杂了,要考虑惯性滚动、边界回弹各种情况,代码量直线上升。第二个方案倒是能解决问题,但引入一个第三方库感觉有点杀鸡用牛刀。最后选择了第三种方案,既保持了系统的原生滚动体验,又能实现我的功能需求。
核心代码就这几行
最终的解决方案其实很简单,就是在touchmove事件里判断是否需要阻止默认行为。这里要注意的是,不能一股脑地调用event.preventDefault(),而是要根据实际情况来决定。
let isDragging = false;
let startX = 0;
const handleTouchStart = (e) => {
startX = e.touches[0].pageX;
isDragging = true;
};
const handleTouchMove = (e) => {
if (!isDragging) return;
const currentX = e.touches[0].pageX;
const deltaX = currentX - startX;
// 只有当水平滑动距离大于垂直滑动距离时才阻止默认行为
if (Math.abs(deltaX) > Math.abs(e.touches[0].pageY - startY)) {
e.preventDefault();
}
};
const handleTouchEnd = () => {
isDragging = false;
};
document.querySelector('.menu').addEventListener('touchstart', handleTouchStart);
document.querySelector('.menu').addEventListener('touchmove', handleTouchMove, { passive: false });
document.querySelector('.menu').addEventListener('touchend', handleTouchEnd);
这里有几个关键点需要注意:
- 必须设置{ passive: false },否则浏览器会忽略preventDefault()
- 通过比较水平和垂直方向的位移来判断用户意图
- 使用isDragging标志位来避免误触
踩坑提醒:这三点一定注意
折腾了大半天,总结出几个容易踩坑的地方:
首先是passive事件监听器的问题。现在的浏览器为了性能优化,默认把touch事件设置为passive:true,这就导致preventDefault()不起作用。必须显式地设置{ passive: false }才能正常工作。
其次是事件绑定范围。我一开始把事件绑在document上,结果发现性能很差,特别是快速滑动的时候会有明显的卡顿。后来改成只在实际需要的元素上绑定事件,性能立马提升了一个档次。
最后是边界处理。上面的代码中省略了具体的菜单切换逻辑,但在实际项目中发现,当菜单滑到最左边或最右边时,如果不做特殊处理会出现抖动的情况。我的解决方法是在边界处加一个缓冲距离:
if (currentPosition <= 0 && deltaX > 0) {
// 左边界缓冲
deltaX = deltaX / 3;
} else if (currentPosition >= maxPosition && deltaX < 0) {
// 右边界缓冲
deltaX = deltaX / 3;
}
还有一些小瑕疵
虽然主要问题解决了,但现在还存在一个小问题:当用户快速点击时,偶尔会出现菜单项错位的情况。初步判断是touchend事件触发时机的问题,暂时还没找到完美的解决方案。不过考虑到这种情况出现的概率很低,暂时就这样用了。
另外,不同机型的表现还是有些细微差别,特别是在一些老版本Android机上,滑动的流畅度不如iOS。这部分可能需要后续针对性优化。
以上是我个人对这个touchmove事件处理的完整讲解,有更优的实现方式欢迎评论区交流。插件开发这块还有很多可以深挖的地方,后面我准备写一篇关于性能优化的文章,专门聊聊如何提升移动端交互体验。

暂无评论