I am having hard time understanding what are frames in JavaScript. Is frame a data structure? Is it frames as in FPS or refresh rate? Is it a fixed-time slice of window like preemptive scheduling? Also, it the same article it says:
rAFs aren’t throttled for you if they take too long to execute.
What does this means?
I understand how setInterval can cause performance issues when callbacks are long running by basically pilling up too many callback in the queue to execute. Also, What is the relation of frame with event loop of call stack/thread of execution?
Every code which is dedicated to an animation should be executed with requestAnimationFrame. When you say requestAnimationFrame that means: as soon as a frame is ready to be rendered, execute the callback (which should move or transform elements based on the timestamp parameter), and then render the frame. That allows the code which is dedicated to the animation to be run exactly at the same rate than the screen framerate.
The CPU consumption of the animation is then adjusted to what the screen can display.
An usual pattern is to call requestAnimationFrame recursively:
function render(timestamp) {
// Move and transform things
requestAnimationFrame(render);
}
requestAnimationFrame(render);
Related
I am trying to get a game to refresh canvas regularly with a main loop comprising a function to replay a screen and a function to have the computer calculate a move (if it is its turn). The player can select a move 1-9 and then, if the computer has not gone since the player last made a move, it will calculate its move using a very deep tree. The computer move takes several seconds to make its move. Despite having the canvas update before the computer makes a move, it doesn't ever get refreshed until after the computer has gone, meaning both the player and the computer move are shown simultaneously rather than sequentially. What can I do to ensure the canvas is updated every 100 milliseconds and not just after when the computer has calculated its move?
function playGame() {
function doKeyDown(evt) {
console.log(evt.keyCode)
if (evt.keyCode > 48 && evt.keyCode < 58) {
game.makeMove(evt.keyCode-48); // 1-9 are user actions
}
}
function mainLoop(ctx, game, board) {
board.showBoard(ctx); // display board
game.makeMove(0); // zero is computer move
}
Canvas.canvas = document.getElementById("myCanvas");
let game = new Game();
let board = new Board(game);
window.addEventListener( "keydown", doKeyDown, true);
Canvas.ctx = Canvas.canvas.getContext('2d');
setInterval(() => {mainLoop(Canvas.ctx, game, board)}, 100);
};
document.addEventListener('DOMContentLoaded', playGame);
There are a few things that can definitely help you with ensuring your canvas refreshes when you expect.
move computationally intensive tasks into Web Workers.
utilize window.requestAnimationFrame() instead of setInterval() for rendering.
Web Workers
Web Workers are great for moving computationally intensive tasks into; the MDN documentation for Web Workers states that, "The worker thread can perform tasks without interfering with the user interface." This will allow the computer's move to be calculated without blocking the rendering thread.
For more information on Web Workers see: Web Workers (MDN Web Docs)
window.requestAnimationFrame()
The window.requestAnimationFrame() method is the best current method to achieve animations with. Essentially it causes the browser to execute the provided callback just before the next repaint. This is an improvement over the previous setTimeout() and setInterval() methods as it only calls the callback just before the browser repaint, in other words, it reduces unnecessary computations that can be caused by using setTimeout() and setInterval().
For more information on window.requestAnimationFrame() see: window.requestAnimationFrame() (MDN Web Docs)
For a nice short article on why to choose window.requestAnimationFrame() over setTimeout() and setInterval() see this article by Matt West: Efficient Animations with requestAnimationFrame
Working Example:
I have put together a small working example using codesandbox.io; it uses Web Workers and requestAnimationFrame() as I have described. Note that it uses a timer in place of a computationally intensive task, it uses a 5 second timer to simulate a computer move, and a 1 second timer to simulate a player move.
Code Sandbox: Web Worker & requestAnimationFrame() Example
If I animate changes in the DOM from JS (e.g. changing the value of a node's textContent from 1 to 500), would it be better to use requestAnimationFrame or requestAnimationFrame within a requestIdleCallback?
If you want to animate some code, then use only requestAnimationFrame, if you want to perform an action only when the browser has nothing else to do anymore, then use requestIdleCallback.
To simplify things, let's consider that both requestAnimationFrame and requestIdleCallback are setTimeout calls, with variables timeout arguments.
requestAnimationFrame would then be setTimeout(fn, time_until_next_screen_refresh). fn will be called right before the page is painted to the screen. This is currently the best timer we have to make animations since it ensures we'll only do the visual modifications only once per actual frame, at the speed of what the monitor is able to render, thus at every frame, if our code is fast enough.
requestIdleCallback would be setTimeout(fn, time_until_idle). fn will be called as soon as the browser has nothing more to do. This can be right away, or in a few event loops iterations.
So while both have a similar API, they provide completely different features, and the only viable one for doing an animation is requestAnimationFrame, since requestIdleCallback has basically no link whatsoever with the rendering.
For as long as I have been writing for the canvas, I've always done the following:
function animate() {
//do stuff in the animation
requestAnimationFrame(animate);
}
Lately though, I have frequently seen it done this way:
function animate() {
requestAnimationFrame();
//do stuff in the animation
}
While I can of course see benefits for doing it my way, (mainly, if there's a bug it won't continue to call more frames) I have been unable to find any benefits for calling for the next frame first.
Does anyone have an explanation for this/possible benefits for doing it this way? Or, can you prove it shouldn't be done this way? A source is definitely needed, as I've seen it all over the web but no one can really give a concrete reason for it.
The placement of the rAF call is clearer if you keep in mind that requestAnimationFrame is just requesting a ride on the next animation processing cycle -- it's not actually triggering the animation processing cycle.
Calling for the next rAF frame immediately will give you the best chance of catching the very next rAF cycle execution.
For example, if your "stuff" takes about 3ms to execute and the next rAF cycle starts in 4ms then you might catch the next cycle in 4ms rather than the subsequent cycle in 4+16ms. It might seem impossible that you would miss the next rAF cycle since you have 4-3==1ms to spare (nearly an eternity for the cpu), but the system might schedule other things that eat up your 1ms margin (like garbage collection).
On the other hand, if your "stuff" averages 15ms to complete then put your rAF call at the end. Putting it last might miss an execution once & a while, but it is probably better than risking multiple "stuff"s piling up.
With this in mind, very generally, putting the rAF call at the end is safer at the occasional cost of a missed rAF cycle.
Currently i have 2 animations in two rAF loops. For performance reasons i'm considering moving both to 1 loop. The constantly running animation is a small 3d wireframe in the site header, this is not stoppable and is present the moment the site is loaded. However i also have oscilloscopes and spectrum analysers that are only requesting animation frames when audio is played. The moment i pause, cancelAnimationFrame is called.
I have read that 1 rAF is usually better, but i'm having some second thoughts how i should achieve this. Should the constantly running rAF loop check 60 times a second for a variable having a boolean value, and when the value is true it should also run a function that holds the user triggered animations? Or are there any better solutions? What are your experiences?
I'm doing some animation with Canvas now, and will be preparing a system for the artists to use to make interactive animations. I'll be using my own timeline as the scenes will be created from some declarative non-js input. My question is: what's the right way to handle the per frame callback and time measurement? In audio (my real-time background), the rule is that there should be only only one master callback method called by the audio system, and any other objects register with it somehow. And all time calculations are done by counting sample ticks of this callback so there is one and only one true clock source (no asking the system clock for anything, just count samples). I assumed this is what I should do in my canvas app but I'm seeing examples in books and sites where multiple objects use requestAnimationFrame, and then check the frame rate by using date objects to measure elapsed time. Am I off base in thinking one master callback is still the most elegant way to go? And can I rely on measuring time in frame ticks assuming I'm getting really 60fps if using requestAnimationFrame?
Your instinct is valid...route all your animation through one requestAnimationFrame loop to keep your animations well coordinated.
The current version of requestAnimationFrame in modern browsers automatically receives a highly accurate timestamp parameter based on the performance object. That timestamp is accurate to 1/1000th of a millisecond.
You cannot rely on counting the number of calls ("ticks") to the animation loop. The loop will be deferred if the prior loop's animation code has not completed or if the system is busy. Therefore, you are not guaranteed 60fps. You are guaranteed the browsers best efforts to get you 60fps.
Bottom line: requestAnimationFrame is not guarenteed to be called at 60fps intervals so you are left with 2 basic animation alternatives:
Use the timestamp to calculate an elaped time and position your objects based on elapsed time.
Increment a counter with each call to the animation loop and postion your objects based on the counter.