javascript requestAnimationFrame 以有限的帧率
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9308257/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
requestAnimationFrame at a limited framerate
提问by fredrikekelund
As I understand it, usage of the JS requestAnimationFrame
API is intended for cases where the framerate isn't in need of being controlled, but I have a use case where it's essential that a <canvas>
only updates at a certain fps interval that may be anywhere between 1 and 25 (between 1 and 25 frames per second, that is). Can I then somehow still effectively use rAF to get at the optimizations it offers?
据我了解,JS requestAnimationFrame
API 的使用适用于不需要控制帧率的情况,但我有一个用例,其中必须<canvas>
仅以特定的 fps 间隔更新,该间隔可能介于 1和 25(即每秒1 到 25帧之间)。那么我能否以某种方式仍然有效地使用 rAF 来获得它提供的优化?
This questionhas similarities to mine, but the accepted answer there made close to zero sense to me in the context of that question.
这个问题与我的相似,但在那个问题的背景下,那里接受的答案对我来说几乎为零。
I have two possible solutions for this. The first one involves using a while
loop to halt the execution of the script for a specified delay before calling requestAnimationFrame
from within the callback. In the example where I saw this, it effectively limited the fps of the animation, but it also seemed to slow down the entire tab. Is this still actually a good solution? The second alternative, as mentioned in the question I linked to above, calls requestAnimationFrame
within a setInterval
. To me that seems a bit convoluted, but it could be that's the best option?
我有两种可能的解决方案。第一个涉及while
在requestAnimationFrame
从回调内部调用之前使用循环在指定的延迟内暂停脚本的执行。在我看到的示例中,它有效地限制了动画的 fps,但它似乎也减慢了整个选项卡的速度。这实际上仍然是一个很好的解决方案吗?正如我在上面链接的问题中提到的,第二种选择requestAnimationFrame
在setInterval
. 对我来说这似乎有点令人费解,但这可能是最好的选择吗?
Or is there a better alternative to accomplish this?
或者有没有更好的选择来实现这一目标?
采纳答案by fredrikekelund
Yoshi's answer is probably the best code solution to this problem. But still I'll mark this answer as correct, because after some research I basically found that my question was invalid. requestAnimationFrame
is really meant to keep frame rates as high as possible, and it optimizes for scenarios where animation is meant to be kept consistent and smooth.
Yoshi 的回答可能是这个问题的最佳代码解决方案。但是我仍然会将这个答案标记为正确,因为经过一些研究,我基本上发现我的问题无效。requestAnimationFrame
实际上是为了保持尽可能高的帧速率,并且它针对动画旨在保持一致和流畅的场景进行了优化。
Worth noting though is that you don't need requestAnimationFrame
to get optimization (even though rAF was touted as a great performance booster) since browsers still optimize regular drawing of a <canvas>
. For example, when a tab isn't focused, Chrome for one stops drawing its canvases.
不过值得注意的是,您不需要requestAnimationFrame
进行优化(即使 rAF 被吹捧为出色的性能助推器),因为浏览器仍然会优化<canvas>
. 例如,当一个选项卡没有获得焦点时,Chrome for one 会停止绘制它的画布。
So my conclusion was that this question was invalid. Hope this helps anyone who was wondering something similar to me.
所以我的结论是这个问题是无效的。希望这可以帮助任何想知道与我类似的事情的人。
回答by yckart
This is just a proof of concept.
这只是一个概念证明。
All we do is set our frames per second and intervals between each frame. In the drawing function we deduct our last frame's execution time from the current time to check whether the time elapsed since the last frame is more than our interval (which is based on the fps) or not. If the condition evaluates to true, we set the time for our current frame which is going to be the “last frame execution time” in the next drawing call.
我们所做的就是设置每秒的帧数和每帧之间的间隔。在绘图函数中,我们从当前时间中减去上一帧的执行时间,以检查自上一帧以来经过的时间是否大于我们的时间间隔(基于 fps)。如果条件评估为真,我们为当前帧设置时间,这将是下一次绘图调用中的“最后一帧执行时间”。
var Timer = function(callback, fps){
var now = 0;
var delta = 0;
var then = Date.now();
var frames = 0;
var oldtime = 0;
fps = 1000 / (this.fps || fps || 60);
return requestAnimationFrame(function loop(time){
requestAnimationFrame(loop);
now = Date.now();
delta = now - then;
if (delta > fps) {
// Update time stuffs
then = now - (delta % fps);
// Calculate the frames per second.
frames = 1000 / (time - oldtime)
oldtime = time;
// Call the callback-function and pass
// our current frame into it.
callback(frames);
}
});
};
Usage:
用法:
var set;
document.onclick = function(){
set = true;
};
Timer(function(fps){
if(set) this.fps = 30;
console.log(fps);
}, 5);
回答by Yoshi
What you can do, though I don't really know if this is really any better is:
你能做的是,虽然我真的不知道这是否真的更好:
- render to an invisible context with
requestAnimationFrame
- update a visible context with
setInterval
using a fixed fps
- 渲染到一个不可见的上下文
requestAnimationFrame
setInterval
使用固定的 fps更新可见上下文
Example:
例子:
<canvas id="canvas"></canvas>?
<script type="text/javascript">
(function () {
var
ctxVisible = document.getElementById('canvas').getContext('2d'),
ctxHidden = document.createElement('canvas').getContext('2d');
// quick anim sample
(function () {
var x = 0, y = 75;
(function animLoop() {
// too lazy to use a polyfill here
webkitRequestAnimationFrame(animLoop);
ctxHidden.clearRect(0, 0, 300, 150);
ctxHidden.fillStyle = 'black';
ctxHidden.fillRect(x - 1, y - 1, 3, 3);
x += 1;
if (x > 300) {
x = 0;
}
}());
}());
// copy the hidden ctx to the visible ctx on a fixed interval (25 fps)
setInterval(function () {
ctxVisible.putImageData(ctxHidden.getImageData(0, 0, ctxHidden.canvas.width, ctxHidden.canvas.height), 0, 0);
}, 1000/40);
}());
</script>