我有特殊的节能技巧,处理事件的时候我会装作四处看风景

本文主要讲的是基于requestAnimationFrame的函数节流技巧。

如果你还不熟悉requestAnimationFrame,你可以看一下msdn上的这篇文章基于脚本的动画的计时控制

requestAnimationFrame有助于我们创建丝滑柔顺的动画,同时也有利于优化性能和节约电能。然而我们创建的很多动画都要依赖于DOM事件,像mousemove,resize,scroll等这些事件调用函数的频率却远远比浏览器重绘的频率快,这就造成了和使用setTimeout或setInterval创建动画相似的弊端。举个栗子,比如在一个画布里,我们要创建一个跟随鼠标雪花不断飘落的动画效果。鼠标每移动一下,我们就通过科赫曲线绘制若干随机大小雪花来模拟新产生的雪花。使用科赫曲线画雪花无疑是密集型计算,而mousemove每秒触发上百次,结果就是看到巨卡无比的动画。当然这个方法有很多需要优化的地方,比如你可以缓存一个雪花,用drawImage来替换每次重新绘制路径,然后通过放缩来产生不同大小的雪花;或者用webgl来加速……本文就聚焦于优化mousemove事件。

先做个试验,看看mousemove事件每秒触发多少次。

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <script>
        var time = [], i = 0;
        function listener() {
            time[time.length] = Date.now();
            if (time[time.length - 1] - time[0] > 1000) {
                console.log(i);
                document.removeEventListener("mousemove", listener);
            }
            i++;
        }
        document.addEventListener("mousemove", listener);
    </script>
</body>
</html>

用浏览器打开这个HTML文档,鼠标晃动个一两秒,打开控制台,在我这里显示的是125。

结果就是在短短的一秒内listener函数调用了高达125次,远超浏览器每秒60次的重绘频率。

下面来看一个特殊的节流函数。

function throttle(fn) {
    var handle;
    return function () {
        var context = this, args = arguments;
        cancelAnimationFrame(handle);
        handle = requestAnimationFrame(function () {
            fn.apply(context, args);
            handle = null;
        });
    };
}

我们先来看这个节流函数对优化mousemove事件的作用,然后再解释它是怎么工作的。

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <script>
        
        var time = [], i = 0, lis = throttle(listener);
        document.addEventListener("mousemove", lis);
        function throttle(fn) {
            var handle;
            return function () {
                var context = this, args = arguments;
                cancelAnimationFrame(handle);
                handle = requestAnimationFrame(function () {
                    fn.apply(context, args);
                    handle = null;
                });
            };
        }
        function listener() {
            time[time.length] = Date.now();
            if (time[time.length - 1] - time[0] > 1000) {
                console.log(i);
                document.removeEventListener("mousemove", lis);
            }
            i++;
        }
    </script>
</body>
</html>

重复一样的步骤,控制台上这次显示的是61,接近完美的数字。

现在我们来看看throttle函数的原理。用一个比喻来阐释一下:你要调戏一下KFC,重复给宅急送打电话订餐。假设电话老是占线,你平均1分钟才能打进一次。一个固定的快递负责你们小区。如果客服发现快递还没送到你又打电话进来,客服想这个王八蛋又来催了,直接就挂了你的电话。结果就是一小时你最多只能拿到60份。如果快递1分钟内送不到你手上,你连60份也拿不到。如果快递30秒就送到了,你电话1分钟才能打进一次,也只能拿到60份。你打电话就好比throttle被调用,无论被调用的有多快,requestAnimationFrame只能以接近60fps的速度调用你给定的函数。如果你的给定函数还要执行很长时间,那么一秒内它总共执行的次数就少于60次;如果你给定的函数执行很快,一秒内总执行次数也不会多于60。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值