在Web应用中,实现动画效果的方法比较多,Javascript 中能够经过定时器 setTimeout或者setInterval 来实现,css3 可使用 transition 和 animation 来实现,html5 中的 canvas 也能够实现。除此以外,html5 还提供一个专门用于请求动画的API,那就是 requestAnimationFrame,顾名思义就是请求动画帧。javascript
可是传统的经过setTimeout或者setInterval实现的动画,存在两个问题,第一个就是动画的循时间环间隔很差肯定,设置长了动画显得不够平滑流畅, 设置短了浏览器的重绘频率会达到瓶颈,推荐的最佳循环间隔是17ms(大多数电脑的显示器刷新频率是60Hz,1000ms / 60),第二个问题是定时器第二个时间参数只是指定了多久后将动画任务添加到浏览器的UI线程队列中,若是UI线程处于忙碌状态,那么动画不会马上执行,为了解决这个问题,H5中加入了requestAnimationFrame。css
完成一个简单的移动动画,咱们可使用setInterval和requestAnimationFrame两种方式实现。html
<html>
<head>
<title></title>
<style type="text/css">
#box {
margin: 200px;
width: 200px;
height: 200px;
background: green;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
<script type="text/javascript">
var element = document.getElementById('box')
var left = 0;
var animateCallback = function() {
element.style.marginLeft = (++left)+ 'px';
if (left === 500) {
clearInterval(interval);
}
}
var interval = setInterval(animateCallback, (1000 / 60));
</script>
复制代码
使用setInterval咱们成功的实现了一个平滑的动画效果,可是咱们会发现setInterval的执行时间并不肯定,在javascript中,setInterval任务被放进了异步队列中,只有当主线程上的任务执行完之后,才会去检查该队列里的任务是否须要开始执行,因此setInterval的实际执行时机通常要比其设定的时间晚一些。使用setInterval实现动画容易失帧。html5
例如咱们使用setInterval进行颜色的切换java
var color = ['green', 'red', 'blue', 'yellow'];
var element = document.getElementById('box');
var index = 0;
var animateCallback = function() {
index++;
element.style.backgroundColor = color[index];
if (index === 3){
clearInterval(interval);
}
}
var interval = setInterval(interval, 1000 / 100);
复制代码
上面的动画切换咱们设置了间隔10切换一次,可是此时的屏幕刷新频率为16.7,css3
从上面的执行过程当中,咱们能够看出,在执行到20ms和30ms直接,setInterval切换了两次颜色,可是屏幕并无执行一次刷新,就会出现,在34.7ms时,屏幕上直接展现的就是blue颜色,而跳过了red颜色。这就是丢帧现象,这种现象也会引发页面卡顿。canvas
<html>
<head>
<title></title>
<style type="text/css">
#box {
margin: 200px;
width: 200px;
height: 200px;
background: green;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
<script type="text/javascript">
var start = null;
var element = document.getElementById('box');
var left = 0;
var raf_id = null;
function animateCallback() {
element.style.marginLeft = (++left) + 'px';
if (left === 500) {
cancelAnimationFrame(raf_id);
} else {
raf_id = requestAnimationFrame( animateCallback );
}
}
raf_id = window.requestAnimationFrame(animateCallback);
</script>
复制代码
requestAnimationFrame返回请求的id(整数),咱们可使用这个id来取消请求(cancelAnimationFram(id)),从而中止动画的执行。 和setInterval相比,requestAnimationFrame最大的优点是由系统来决定回调函数的执行时机,requestAnimationFrame的步伐跟着系统的刷新步伐走,它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引发丢帧。浏览器
使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout仍然在后台执行动画任务,此时页面处于不可见或者不可用的状态,刷新动画是没有意义的,并且还浪费CPU资源,而rAF则彻底不一样,当页面处于未激活的状态下,该页面的屏幕绘制任务也会被系统暂停,所以跟着系统步伐走的rAF也会中止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。bash
在高频率事件(resize, scroll)中,为了防止在一个刷新间隔内发生屡次函数执行,使用rAF可保证每次绘制间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销,一个绘制间隔内函数执行屡次是没有意义的,由于显示器每16.7ms绘制一次,屡次绘制并不会在屏幕上体现出来。异步