html动画一般会采用css3的形式去做,当然也比较建议用css去做动画。但是有时候一些动画只能使用js来完成,常用的js动画方案是使用计时器来完成。编写动画循环的一个关键在于我们要延迟多长时间比较合适,如何设置时间才能让动画显得平滑顺畅,这个时间并不是越小越好,因为要遛狗足够的时间间隔来让浏览器产生渲染变化,否则就会变成跳跃感。
目前大多数的电脑显示器的刷新频率是60Hz,也就是一秒钟绘制60次,一般我们成为fps。大多数的浏览器都会对重绘操作进行限制,但是不会超过浏览器的频率,即使超过了,用户体验也不会有所提升。因此,最佳的循环时间就是1000/60,大约16.6毫秒。
了解过事件循环机制的朋友应该知道,siteTimeout和setInterval并不是精准的时间间隔,他们要等待其他优先的执行队列执行完成以后才能继续执行。
于是就引入了一个新的动画执行方式-- window.requestAnimationFrame()。它告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
它采用的是系统时间间隔,以保证最佳的绘制效率,不会因为时间过短造成过度绘制,也不会因为时间间隔太长,产生动画卡顿的现象。让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。
具体兼容性如下图所示
requestAnimationFrame的用法与settimeout很相似,只是不需要设置时间间隔而已。requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame用于取消这个函数的执行
let retID = requestAnimationFrame(callback);
取消的话可直接使用canceAnimationFrame来进行取消即可
cancelAnimationFrame(retID)
调用一次只会执行一次,所以正确的循环调用方式如下:
var count = 0;
function step() {
count++;
console.log(count)
if (count > 6000) {
window.cancelAnimationFrame(step);
}else{
window.requestAnimationFrame(step);
}
}
step();
因为requestAnimationFrame有一定的兼容性,所以我们需要对齐进行兼容处理
window.requestAnimat = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame || function(callback){
setTimeout(callback,1000/60);
}
})();
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
下面来看一下具体的应用效果,下面的示例是模拟一个进度条
<div id="myDiv" style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
myDiv.style.width = '0';
cancelAnimationFrame(timer);
timer = requestAnimationFrame(function fn(){
if(parseInt(myDiv.style.width) < 500){
myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
myDiv.innerHTML = parseInt(myDiv.style.width)/5 + '%';
timer = requestAnimationFrame(fn);
}else{
cancelAnimationFrame(timer);
}
});
}
</script>