您的位置:首页 > Web前端 > JavaScript

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

2014-03-22 18:58 330 查看
本文主要讲的是基于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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息