目的
由于前端的浏览器兼容性问题,很多比较优雅的动画往往只在某些浏览器中可以使用,而在其它浏览器无效。所有浏览器都兼容的requestAnimationFrame
便是用来解决这种问题。它能够按照规定的"路线"对帧进行更新,从而实现动画效果。
函数原型
window.requestAnimationFrame(callback)
callback(DOMHighResTimeStamp)
, 参考MDN,该函数告诉浏览器执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
该方法需要传入一个回调函数作为参数,回调函数会在浏览器下一次重绘之前执行。回调函数的参数为DOMHighResTimeStamp,十进制数值,表示网页加载完成后至今的毫秒数(即执行回调函数时的时间戳),与performace.now()
的返回值相同。
具体来说,回调函数的内容一般是
callback(timestamp){
根据timestamp计算需要更新的属性值
更新属性值
获得动画执行进度 ( 经历的时间值 = timestamp - 起始时间戳 )
还需要继续动画 -> requestAnimationFrame(callback)
不需要继续动画 -> return
}
调用requestAnimationFrame
后浏览器的执行流程为:
收到动画请求 -> 调用回调函数,得到下一帧 -> 执行动画,从当前帧过渡到下一帧 -> (回调函数调用requestAnimationFrame -> 重复) -> 结束
使用回调函数的时间戳参数能够计算出从开始请求动画为止经历的时间,然后据此执行相应程度的更新,就可以实现动画效果了。
使用
回调函数的调用间隔很短,因此一般需要在回调函数内部再次调用requestAnimationFrame
,以实现特定时长的动画,即
function callback(timeStamp){
// ....... 更新元素属性
requestAnimationFrame(callback);
}
requestAnimationFrame(callback);
如果是均匀的动画,那么只需根据执行时间百分比(由回调函数参数计算得到)更新属性,即在 x% 时刻更新元素的偏移为 x%·totalOffset。效果图如下
如果是非均匀的动画,那么在回调函数中需要手动计算下一帧的属性,然后执行更新即可。如下为使用函数
f
(
x
)
=
36
1
+
e
−
x
+
4
f(x)=\frac{36}{1+e^{-x+4}}
f(x)=1+e−x+436在[0, 6 + 6]
区间的值进行更新的效果图:
代码如下:
<!DOCTYPE html>
<html>
<head>
<script>
let totalTime = 850, zeroTimeStamp = 0;
function moveByPercent() {
let moveDistance = 600;
let div = document.getElementById("container");
function callback(timeStamp) {
timeStamp = timeStamp - zeroTimeStamp;
let x = timeStamp;
let y = x / totalTime;
let tmpLeft = y * moveDistance;
div.style.left = (tmpLeft + 20) + 'px';
if (timeStamp < totalTime) requestAnimationFrame(callback);
};
zeroTimeStamp = performance.now();
requestAnimationFrame(callback);
}
function move() {
let div = document.getElementById("container");
// function: a / (1 + e^(-x + b))
let moveDistance = 600;
let a = 16, b = 4;
let x_max = b + 6, y_max = a;
function callback(timeStamp) {
timeStamp = timeStamp - zeroTimeStamp;
let x = timeStamp / totalTime * x_max;
let y = a / (1 + Math.exp(-x + b));
let tmpLeft = y / y_max * moveDistance;
div.style.left = (tmpLeft + 20) + 'px';
if (timeStamp < totalTime) requestAnimationFrame(callback);
};
zeroTimeStamp = performance.now();
requestAnimationFrame(callback);
}
</script>
</head>
<body style="padding: 20px;margin: 0px;">
<input type="button" value="moveByPercent" style="position: absolute;" onclick="moveByPercent()">
<input type="button" value="move" style="position: absolute;margin-left: 200px;" onclick="move()">
<div id="container"
style="width: 300px;height: 300px;box-shadow: gray 1px 1px 6px;margin-top: 40px;position: absolute;background: cyan;">
</div>
</body>
</html>