我开始用这个方法学习requestAnimationFrame,但是我发现它有点难理解(至少对我来说是这样)。我知道setInterval/setTimeout是如何工作的,但是当我尝试输入一些简单的代码进行测试时:
function message(){console.log('hello');}
requestAnimationFrame(message);这不会使控制台中出现hello一次的任何动画,但是当我在消息函数中调用requestAnmiationFrame(message)时,循环是无限的(因此它是工作的)。
但以另一种方式,我可以使用setInterval/setTimeout在任何地方调用这个消息函数,而不是在消息函数中调用。
那么,我可以理解requestAnimationFrame()方法必须在函数内部运行吗?或者我应该说,在使用requestAnimiationFrame()方法时,函数有一个默认的for循环,用于循环自身无穷大的时间?
这个问题可能很愚蠢,但真的希望有人能启发我。
发布于 2017-05-02 18:51:46
requestAnimationFrame实际上并不为您呈现任何东西,它只是将浏览器的呈现循环同步到您的代码上,因此您可以使用它在屏幕上以编程方式呈现动画。
它所做的是激发每次浏览器呈现框架时提供给它的回调函数。下面是一个很好的例子,说明了它的用途:制作一个javascript游戏
发布于 2018-09-13 16:13:29
rAF(请求动画帧)和timers (setTimeout / setInterval)有着巨大的区别。
两者都在创建运行异步的任务(您的回调),即浏览器对它们进行排队,并在特定时间点调用它们。
最大的区别在于它们何时得到执行和优先级。
我们需要了解谁在管理Que,在什么条件下可以运行回调。
我会尽量保持简单。
同步JavaScript
JavaScript是“单线程”,这意味着您不能在普通JavaScript中运行异步操作。
browser API提供了一些运行代码异步的方法:
timersrAFDOM事件promises (这是一种不同的任务)xhr当您使用这些方法之一时,实际上将一个函数引用传递给浏览器,以便(希望)在所需的时间点调用它。
事件循环
浏览器将根据您使用的API完成它的工作,并在任务Que中进行回调(除了承诺之外,这些都是微任务)。
最大的问题是什么时候将它传递回"javascript-land“到调用堆栈中?
这是事件循环的工作,它将检查两个条件:
如果它满足这两个条件,它将从Que中提取下一个任务并将其推送到调用堆栈。
现在让我们以setTimeout为例。
鉴于这一守则:
setTimeout(() => console.log('timeout'), 0);
console.log('start');
console.log('end');setTimout应该等待0ms,然后再记录timeout,所以我们应该认为日志顺序是:
timeoutstartend但实际上是:
startendtimeout原因是我们将回调传递给浏览器,浏览器等待0ms,然后将其推送到任务que。
现在,事件循环的任务是检查上面提到的两个条件:
这两个问题的答案都是否定的,因为我们仍然有两个console.log行要在主执行上下文上运行。
所以它等待,当我们完成日志时,它将回调从que传递到调用堆栈,然后记录timeout。
大多数异步回调都是这样工作的。
注意到,promises是不同的,它们是微任务,具有比常规回调任务更高的优先级。
好吧,这些话我还没回答你的问题。
但在回答之前还有一件事。
requestanimationFrame
是另一个允许您在浏览器即将运行呈现步骤之前运行内容的浏览器API:
因此,例如,如果您想更新元素在下一个帧上的left位置,这就是您要做的地方。
这是一个可能的实施:
requestAnimationFrame(moveBox)如果您希望它继续在每个帧上移动,您应该递归地调用它:
function callback() {
moveBox();
requestAnimationFrame(callback);
}
callback(); 那么为什么或何时使用rAF over timers呢?
setInterval(moveBox, 0)这将比rAF实现运行得更快。其运行速度较快的原因是,在浏览器有机会重新绘制(处理呈现步骤)之前,回调将被多次调用,因此您将多次“更新”left位置,但浏览器只会重新绘制您传递的最后一个值。
例如,它将从0px跳转到15px,而不是一次增加一个像素。
基本上,你能做的是“猜”帧之间会传递多少时间。最常见的方法是计算“每秒60帧”,因为大多数屏幕都支持这一比率。
setInterval(moveBox2, 1000 / 60) 但是,即使这样也不会顺利或一致,因为它不是真正的60 fps,最重要的是,也许当前的设备和屏幕刷新的速度不像你想象的那样。
因此,处理DOM动画、批处理更新和度量内容的最佳API是requestAnimationFrame。
运行示例
我举了一个用rAF和setInterval的2种解决方案移动一个盒子的小例子,希望它能有所帮助。
const showBox2 = location.search.includes('box2');
const showBox3 = location.search.includes('box3');
const box1 = document.getElementById('box1');
const box2 = document.getElementById('box2');
const box3 = document.getElementById('box3');
// move by 1 px
function move1px(el) {
let left = 0;
return function() {
// infinite looping logic - if off screen start over
if (document.documentElement.clientWidth < left) {
left = 0;
} else {
left++;
}
// move left by 1 px
el.style.left = left + 'px';
}
}
// raf solution
const moveBox1 = move1px(box1);
function callback() {
moveBox1();
requestAnimationFrame(callback);
}
callback();
// setInterval solution
const moveBox2 = move1px(box2);
setInterval(moveBox2, 0);
// setInterval hack
const moveBox3 = move1px(box3);
setInterval(moveBox3, 1000 / 60);body {
overflow: hidden;
}
#root {
display: flex;
flex-direction: column;
}
.box {
display: flex;
justify-content: center;
align-items: center;
position: relative;
height: 30px;
width: 150px;
border: 1px solid #333;
margin: 10px 0;
color: #fff;
font-size: 1em;
}
#box1 {
background-color: slateblue;
}
#box2 {
background-color: brown;
}
#box3 {
background-color: green;
}<div id="root">
<div class="box" id="box3">setInterval hack</div>
<div class="box" id="box1">rAF</div>
<div class="box" id="box2">setInterval</div>
</div>
https://stackoverflow.com/questions/43745088
复制相似问题