定时器使用目前有三个方法可以使用:
setTimeout()(设置某个时间后执行某个动作,表示延时执行某个动作)
setInterval()(设置每隔多久执行一次某个动作,它是循环的,如果想重复执行使用该方法)
requestAnimationFrame()(不需要设置时间间隔)
前言:
计时器一直是javascript动画的核心技术。而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化
大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms
而setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行
requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果
特点
【1】requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率【2】在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量
【3】requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销
HTML5提供了一个专门用于请求动画的API,那就是requestAnimationFrame,顾名思义就是请求动画帧。动画原理
根据上面的原理我们知道,你眼前所看到图像正在以每秒60次的频率刷新,由于刷新频率很高,因此你感觉不到它在刷新。而动画本质就是要让人眼看到图像被刷新而引起变化的视觉效果,这个变化要以连贯的、平滑的方式进行过渡。 那怎么样才能做到这种效果呢?
刷新频率为60Hz的屏幕每16.7ms刷新一次,我们在屏幕每次刷新前,将图像的位置向左移动一个像素,即1px。这样一来,屏幕每次刷出来的图像位置都比前一个要差1px,因此你会看到图像在移动;由于我们人眼的视觉停留效应,当前位置的图像停留在大脑的印象还没消失,紧接着图像又被移到了下一个位置,因此你才会看到图像在流畅的移动,这就是视觉效果上形成的动画。
与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。具体一点讲,如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,换句话说就是,requestAnimationFrame的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。
优势:
CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。
函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。一个刷新间隔内函数执行多次时没有意义的,因为显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来。
requestAnimationFrame()
requestAnimationFrame的用法与settimeout很相似,requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame用于取消这个函数的执行。
例如:
var requestID=requestAnimationFrame(callback);
console.log(requestID);当前读取的就是requestAnimationFrame的定时器编号。
var mm = function() {
console.log('aa');
}
var timer = requestAnimationFrame(mm);
console.log(timer);
控制台打印结果如下:
cancelAnimationFrame()
可以用来取消requestAnimationFrame。
例如:
var mm = function() {
console.log('aa');
}
var timer = requestAnimationFrame(mm);
cancelAnimationFrame(timer);
此时控制台什么也不输出。也可以使用cancelAnimationFrame(1);直接取消,默认第一个编号是1。
为了兼容可以这样使用:
简单兼容:
if (!window.requestAnimationFrame) {
requestAnimationFrame = function(fn) {
setTimeout(fn, 17);
};
}
严格兼容:
if(!window.requestAnimationFrame){
var lastTime = 0;
window.requestAnimationFrame = function(callback){
var currTime = new Date().getTime();
var timeToCall = Math.max(0,16.7-(currTime - lastTime));
var id = window.setTimeout(function(){
callback(currTime + timeToCall);
},timeToCall);
lastTime = currTime + timeToCall;
return id;
}
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
如下图使用requestAnimationFrame和cancelAnimationFrame来制作一个进度条。
代码如下:
<template>
<div>
<div style="border:1px solid #ddd; width:500px;">
<div id="myDiv"></div>
</div>
<button @click="run">点击</button>
</div>
</template>
<script>
export default {
data() {
return {};
},
methods: {
run() {
var progressbar = document.getElementById("myDiv");
progressbar.style.width = 0;
var mypro = progressbar.style.width;
var myfun = function() {
var timer;
if (parseInt(mypro) < 500) {
mypro = parseInt(mypro) + 5 + 'px';
progressbar.style.width = mypro;
progressbar.innerHTML = parseInt(mypro) / 5 + "%";
timer = requestAnimationFrame(myfun);
} else {
cancelAnimationFrame(timer);
}
}
myfun();
}
},
mounted() {}
};
</script>
<style>
#myDiv {
height: 24px;
background-color: #ff5200;
color: #fff;
width: 0;
line-height: 24px;
}
</style>
setTimeout() clearTimeout()
定义和用法:
setTimeout()方法用于在指定的毫秒数后调用函数或计算表达式。
clearTimeout()方法来阻止函数的执行。
语法:
setTimeout(fun,milliseconds,param1,param2,…);
fun参数,必填,可以调用一个代码串,也可以是一个函数。
milliseconds参数,可选,执行或调用参数1需要等待的时间,以毫秒计,默认为0。
param1,param2…参数,可选,传给执行函数的其他参数。
例如:
var m1=function(){
alert("hello world!");
}
setTimeout(m1,4000);
使用clearTimeout()方法来阻止函数执行,如下:
var m1=function(){
alert("hello world!");
}
var a=setTimeout(m1,1000);
clearTimeout(a);
例如,制作一个时钟:
var starttime = function() {
var today = new Date(); //实例化一个时间对象
var h = today.getHours(); //获取当前小时
var m = today.getMinutes(); //获取当前分钟
var s = today.getSeconds(); //获取当前秒
m = checktime(m); //如果分钟小于10,则在前面补个0
s = checktime(s); //如果秒小于10,则在前面补个0
document.getElementById("txt").innerHTML = h + ":" + m + ":" + s; //把当前时分秒放到id为txt的容器中
setTimeout(function() {
starttime();
}, 500); //500毫秒后执行starttime()方法,这就相当于一个循环,因为当前就在starttime()方法中,相当于每隔500毫秒后就调用一次它自己
};
//checktime方法的目的是在分秒前面加个0
var checktime = function(i) {
if (i < 10) {
i = "0" + i;
}
return i;
};
starttime();
setInterval() clearInterval()
定义和用法:
setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。
setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。
语法:
setInterval(function, milliseconds, param1, param2, …)
参数说明:
code/function 必需。要调用一个代码串,也可以是一个函数。
milliseconds 必须。周期性执行或调用 code/function 之间的时间间隔,以毫秒计。
param1, param2, … 可选。 传给执行函数的其他参数(IE9 及其更早版本不支持该参数)。
例如每2s执行一次m1方法:
var m1=function(){
console.log(123);
}
setInterval(m1,2000);
使用clearInterval()来停止setInterval()的执行:
如下:
<template>
<div>
<p id="txt"></p>
<button @click="mystop">点击停止</button>
</div>
</template>
<script>
export default {
data() {
return {
myVar: undefined
};
},
methods: {
mytimer() {
var d = new Date();//实例化一个时间对象
var t = d.toLocaleTimeString();//获取本地时间
document.getElementById("txt").innerHTML = t;//把获取的时间放到p标签中
},
mystop() {
clearInterval(this.myVar);//myVar中存储了计时器的编码,调用myVar可以停用当前计时器
}
},
mounted() {
this.myVar = setInterval(this.mytimer, 1000);//把当前计时器编码赋值给myVar
}
};
</script>