随手记 - 疯狂触发滚轮事件的Mac触控板

头几天官网刚上线,就接到投诉说有问题。过去一看,我靠什么鬼?!Mac下用触控板一滑到底,——首页上用iscroll写的翻页效果直接全军覆没。

这个bug来的莫名其妙,问了一圈人也没什么思路,后来自己上网搜,在一个页面上找到一段关于Mac的触控板的手势滑动会疯狂触发滚轮事件的记录,但是轮到具体的解决方案就语焉不详了。没辙,靠天没用,还是靠自己吧~

这里先简单介绍下。出问题的首页用的是iscroll插件,用snap属性做的整屏翻页的效果,翻页用鼠标滚轮驱动,这块用的是MDN上的一个滚轮事件的兼容代码,回调使用iscroll的接口完成向上/向下翻页的效果。

回到这个问题上。一开始我想用事件防抖解决,于是用setTimeout()自己写了个:触发事件后先进入延时,延时后执行函数;如果在延时内仍有事件触发,则取消原有的延时重新计时。

// 打底用的zepto.js,addWheelListener是滚轮事件的兼容插件,下同~
var wheelTimer = false;
var wheelSlide = function (e) {
    e.preventDefault();
    clearTimeout(timer);
    if (e.deltaY > 0) {
        wheelTimer = setTimeout(function(){
            iScroll.next();
        }, 100);
    } else if (e.deltaY < 0 && iScroll.currentPage.pageY != 0) {
        wheelTimer = setTimeout(function(){
            iScroll.prev();
        }, 100);
    }
}
addWheelListener($('body')[0], wheelSlide);

我本意是用延时抵消掉重复触发的滚轮事件,最后合成一个事件触发,没想到测试之后,Mac上的问题并没有解决。

于是我想,用事件防抖的思路处理应该还是不对,即便是延迟时间较短,如果事件持续触发的话肯定翻页还是会被无限的延迟阻塞掉,至此我想换用事件节流再试试。在找资料的时候,意外发现了Underscore.js这个工具库,里边不仅有现成的节流和防抖(中文文档里用的是“防反跳”)函数可以用,而且还支持链式调用,并且压缩版本也才十几k,正合我意。

说干就干,马上用Underscore撸了个事件节流版的:

var wheelSlide = _(function (e) {
    e.preventDefault();
    if (e.deltaY > 0) {
        iScroll.next();
    } else if (e.deltaY < 0 && iScroll.currentPage.pageY != 0) {
        iScroll.prev();
    }
}).throttle(400);//这里毫秒数用了400,大概相当于一个短动画的执行时间
addWheelListener($('body')[0], wheelSlide);

链式写法看上去还挺不错的!进本机浏览器(PC)……嗯?为啥最后会跳一下?赶紧翻文档,又加了个参数上去:

var wheelSlide = _(function (e) {
    e.preventDefault();
    if (e.deltaY > 0) {
        iScroll.next();
    } else if (e.deltaY < 0 && iScroll.currentPage.pageY != 0) {
        iScroll.prev();
    }
}).throttle(400, {trailing: false});
addWheelListener($('body')[0], wheelSlide);

这回PC上倒是正常了,Mac也从一滑到底变成了有“段落感”的跳动,但结果还是不对……
一狠心把毫秒数改成了5000,结果呢:还、是、不、对、、、。。。
(//陷入循环懵逼状态ing……)
痛定思痛,一定是文档看的不够多!于是又啃了一遍Underscore.js的文档(虽然是翻译的,囧……),发现防抖居然有个[immediate]参数,是可以优先执行的!大喜过望~接着撸:

var wheelSlide = _(function (e) {
    e.preventDefault();
    if (e.deltaY > 0) {
        wScroll.next();
    } else if (e.deltaY < 0 && wScroll.currentPage.pageY != 0) {
        wScroll.prev();
    }
}).debounce(600, true);// 本来想改回400的,有点心虚所以又加了200……
addWheelListener($('body')[0], wheelSlide);

居然PC和Mac都能一页页的翻页了有!没!有!不过翻页的动作还有点迟滞,于是果断把毫秒数改小:400、200、100,……Bingo!

最终代码:

var wheelSlide = _(function (e) {
    e.preventDefault();
    if (e.deltaY > 0) {
        wScroll.next();
    } else if (e.deltaY < 0 && wScroll.currentPage.pageY != 0) {
        wScroll.prev();
    }
}).debounce(50, true);
addWheelListener($('body')[0], wheelSlide);

总结:

  1. Mac触控板bug踩坑 +1;

  2. 理解了事件节流和事件防抖的概念;

  3. Underscore.js真好用;

  4. 感谢git把每次的修改都记了下来。

相关推荐