通过使用js的定时器setInterval、setTimeout和requestAnimationFrame来进行动画构建
通过js控制动画一般原理是通过使用定时器来不停的改变DOM样式来实现动画。下面通过使用setInterval、setTimeout实现进度条从0到100%的动画来说明。
效果如下:
公共样式:1
2
3
4
5
6
7
8<style>
.progress {
width: 0;
height: 20px;
background-color: lightblue;
line-height: 20px;
}
</style>
setInterval实现代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<div id="p1" class="progress">0%</div>
<button id="btn1">Run setInterval</button>
function f1() {
let btn1 = document.querySelector("#btn1")
let p1 = document.querySelector("#p1")
btn1.addEventListener("click", function () {
let timer = setInterval(function () {
if (p1.offsetWidth < 500) {
p1.style.width = p1.offsetWidth + 5 + 'px'
p1.innerText = p1.offsetWidth / 5 + '%'
} else {
clearInterval(timer)
}
}, 17)
})
}
通过设定setInterval定时器,间隔响应时间改变进度条长度来实现进度条增长,在满足条件之后清除定时器。
setTimeout实现代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<div id="p2" class="progress">0%</div>
<button id="btn2">Run setTimeout</button>
function f2() {
let btn2 = document.querySelector("#btn2")
let p2 = document.querySelector("#p2")
btn2.addEventListener("click", function () {
let timer = setTimeout(function fn() {
if (p2.offsetWidth < 500) {
p2.style.width = p2.offsetWidth + 5 + 'px'
p2.innerText = p2.offsetWidth / 5 + '%'
timer = setTimeout(fn, 17)
} else {
clearTimeout(timer)
}
}, 17)
})
}
原理跟上面的setInterval比较类似,但是setTimeout是延迟执行,并不能重复执行,所以需要递归调用。
以上两种方法是比较常见的javascript动画实现,但这两种方法也有一定的缺陷,编写动画循环的关键是调节合适的时间间隔,时间间隔太长会导致动画卡顿,间隔太短又会造成频繁渲染消耗资源。
目前主流的显示器依旧是60Hz的刷新率,所以动画最佳的刷新率可以推算是1000ms/60,约等于16.6ms,但是就算我们设定了最佳的间隔时间,但由于setTimeout和setInterval的运行机制,并不一定严格的按照间隔时间执行。所以在html5中引入了一个新的方法requestAnimationFrame,MDN介绍如下:
requestAnimationFrame实现代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<div id="p3" class="progress">0%</div>
<button id="btn3">Run requestAnimationFrame</button>
function f3() {
let btn3 = document.querySelector("#btn3")
let p3 = document.querySelector("#p3")
btn3.addEventListener("click", function () {
requestAnimationFrame(function fn() {
if (p3.offsetWidth < 500) {
p3.style.width = p3.offsetWidth + 5 + 'px'
p3.innerText = p3.offsetWidth / 5 + '%'
requestAnimationFrame(fn)
}
})
})
}
实现方法跟setTimeout比较相似,requestAnimationFrame需要在每一次绘制结束后在调用,直到完成全部动画,绘制时间间隔requestAnimationFrame会自动帮我们设定好,它采用系统时间,会保持一直的绘制帧率,从而是动画更流畅。