window.requestAnimationFrame()方法,参考MDN的介绍,其作用是:用于告诉浏览器,希望它执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。这意味着,该方法需要传入一个回调函数作为参数,并且该回调函数会在浏览器下一次重绘之前执行。
如何使用requestAnimationFrame?
MDN给出了使用的备注信息,
意思是:如果想要完成一个循环动画的效果,那么,在回调函数的函数体中,必须再次调用window.requestAnimationFrame()方法。
举个栗子:循环动画
如下代码实现的效果,如上图所示,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>requestAnimationFrame-例子</title>
<style>
*,body,html{
padding:0;
margin: 0;
box-sizing: border-box;
overflow-x: hidden;
}
#animate {
margin: 1px 0px;
position: absolute;
left: 0;
top: 0;
width: 100px;
height: 2px;
background-color: rgb(0, 164, 230);
}
</style>
</head>
<body>
<div id="animate"></div>
</body>
<script>
const element = document.getElementById("animate");
console.log()
const { clientWidth } = element; //解构:DOM元素的宽度
//启动滑块动画
function startAnimate(blockWidth){
let start = 0;//起始时间
/**
* @param {timeStamp} 会被window.requestAnimationFrame()注入调用时间,而实现隐式自增
* */
function exectute(timeStamp){
if(!start) start = timeStamp;
// console.log(`start=${start}`);
// console.log(`timeStamp=${timeStamp}`)
// console.log(timeStamp - start);
let progress = timeStamp - start; //progress持续增加
//元素持续右移-最大不超过200px
element.style.left = Math.min(progress/10,window.innerWidth) +'px';
if(element.offsetLeft > (window.innerWidth - blockWidth)){
start = 0;
}
//浏览器会自动为window.requestAnimationFrame()的回调函数exectute注入一个double类型的DOMHighResTimeStamp时间值,它表示最近一次浏览器调用execute()函数的时间值
window.requestAnimationFrame(exectute);//将页面刷新放在下一次重绘之前执行
}
exectute(0);
}
//启动动画
startAnimate(clientWidth);
</script>
</html>
循环动画:代码分析
我们看下这个startAnimate函数的实现,
初看时,可能感觉到很奇怪,因为在调用startAnimate()函数之后,内部的start变量看似是个常量,恒等于0,而且execute()函数的参数也是一个常量0,那么,这个函数体中是如何完成对element滑块的left属性值的修改的呢?
我们看MDN官方的介绍:
重点在callback这个参数,在通过window.requestAnimationFrame(execute)的方式执行回调函数时,window.requestAnimationFrame()它会自动向回调函数传入一个参数值——“DOMHighResTimeStamp参数”,它用来表示requestAnimationFrame最近一次执行回调函数execute的毫秒级时间(double类型)——这意味着这个数值是自增的,因此,这个毫秒级的时间就回去主动更新execute()方法的timeStamp参数值。
那么,我们再来看,滑块是如何回到初始位置呢?关键在于如下的判断逻辑,
当 element滑块的offsetLeft(等价于left属性去掉px)大于(当前浏览器视口宽度-滑块宽度)时,表示滑块一次循环运动结束,将其置为0。那么,在下一次更新element滑块的left属性时,start被更新,progress结果值为0,element滑块的left=0px,自然就回到初始位置了。
浏览器兼容性
如下为,MDN官方的介绍,可以放心使用window.requestAnimationFrame。