I know the requestAnimationFrame be invoked before the re-rendering of the browser, and the frequency is about 60fps, but if there is a very long javascript in the callback of requestAnimationFrame does it can block the re-rendering of the Browser? and therefore stuck the next requestAnimationFrame?
And move over, Why the browser re-rendering in 60fps, if there no content or style changed?
Yes.
As with any event-based function call (requestAnimationFrame, setTimeout, addEventListener, etc.) if the JS engine is busy running another function (even the same function from an earlier iteration) then the new function can't run until the previous one is finished.
I'm a game developer and quite familiar with this. Any frame that takes too long to render will not only drop the frame-rate but will also lock up the DOM.
I strongly suggest you look into webworkers and offscreen canvas'. This allows you to push heavy loads into background processes that won't interfere with animation frames or the DOM.
An couple of excellent demos can be found here: https://developers.google.com/web/updates/2018/08/offscreen-canvas
While what you said is true regarding 60fps, only sometimes as per the docs
requestAnimationFrame() calls are paused in most browsers when running in background tabs or hidden iframes in order to improve performance and battery life.
Related
I've studied requestanimationframe documentation and looked for many posts about the usage of it, but i still haven't get a clear answer on one of my thought.
I understand that requestanimationframe is scheduling a task to be executed right at the beginning of the next frame, so the code that does dom manipulations will have a better chance to be finished and painted before the pain cyle. (unless setInterval or setTimeout which usually executes a lot later, causing the well known 'running out of time before the frame gets painted' => dropping frames).
1. The recursive way
The simplest example to use requestanimation frame is the following:
function animate() {
requestAnimationFrame(animate);
// drawing code comes here
}
requestAnimationFrame(animate);
This will give you a smooth animation if you have something that needs to be updated frequently, and also giving you the benefit of not dropping any frames during your animations. This will usually gives you 60fps animations, but if your browser and screen supports 144hz/fps, then you can easily end up having 144fps animations (6.95 ms cycle).
2. Fps limited animations
Other examples also introduce ways to limit the fps to a certain number. The following code snippnet shows how to limit your fps to 30 fps:
const fpsInterval = 1000 / 30;
let previousTime = 0;
function animate(time) {
requestAnimationFrame(animate);
const deltaTime = time - previousTime;
if (deltaTime > fpsInterval) {
// Get ready for next frame by setting then=now, but also adjust for your
// specified fpsInterval not being a multiple of RAF's interval (16.7ms)
previousTime = time - (deltaTime % fpsInterval);
}
// drawing code comes here
}
requestAnimationFrame(animate);
3. One-off animations
I've been wondering a third case, when you just want your animation to be scheduled precisely, even if you have 1 or just a few amount of updates in each second.
A best example is when you have a websocket connection and each update will introduce a dom manipulation, but the update rate is far too low to do it in a recursive way.
// setting up websocket connection
ws.onmessage = (data) => {
// changing application state
myApplicationState = JSON.parse(data);
requestAnimationFrame(animate);
}
function animate () {
// drawing code comes here
}
Now here is my question for you all:
Does this make sense to call requestanimationframe right from the callback of a websocket onmessage function, or should i be using the recursive way?
So far I haven't tested it (in progress), but i have a feeling it does still going to give you the benefit of well-timed animations that can be executed without dropping a frame.
My real-life example is similar, i only have 5 messages in a second and i'd like to call requestanimationframe ONLY 5 times in a second.
My thought of doing this vs the recursive way:
Using requestanimation frame in a recursive way will incredibly increase the script execution time when measured in chrome profiling tools.
Only calling requestanimationframe when a websocket comes will still make sure to have the benefit of the feature, yet not polluting the callstack and reducing execution time
My initial measures were the following. I've spin up chrome profiling and run it for 10 seconds and measured the script execution times (we're not measuring render or paint since they are basically identical):
Script execution times:
recursive way: 4500ms
fps limited way: 4300ms
one-off animated way: 1700ms
While recursive requestanimationframe solution is giving you a super smooth and good user experience, it's also very costy for your CPU and execution times.
If you have multiple components doing animations with recursive requestanimationframe, you're going to hit a CPU bottleneck pretty soon.
Oddly this last case causing some fps drops, which I do not understand. My understanding is that you can call requestanimationframe whenever you want and it'll only execute the begginning of the next frame. But it seems there is something i dont know about.
Here is a picture of what is happening. I still don't understand it. requestanimationframe function was called before the end of the frame, but somehow because it was part of a bigger function call, it's marked as 'dropped' in chrome. Wonder if that's just a bug in the chrome profiling or was it for real dropped.
I wonder what you guys thinking about this topic. I'll update this post with some chrome performance metrics soon.
There seems to some misconception about requestAnimationFrame (rAF) magically preventing dropped frames by ensuring that whatever is executed in it will somehow run fast enough. It doesn't.
requestAnimationFrame is just a timer for "right before the next paint"*.
Its main goal is to limit the number of callbacks to just what is needed, avoiding to waste drawing operations which won't even be rendered on screen.
It does actually allow to drop frames smartly, so if one execution took the time of 3 frames to render, it won't stupidly try to execute the 3 missing frames as soon as possible, instead it will nicely discard them and get your script ready to get back from this hiccup.
So using it for updating something that doesn't match the screen refresh rate is not very useful.
One should remember that calling requestAnimationFrame is not free, this will mark the document as animated and force the event-loop to enter the update-the-rendering steps, which in itself has some performance costs. So if what you are doing in there is not going to update the rendering of the page, it can actually be detrimental to wrap your callback in a rAF callback.
Still, there are cases where it could make sense, for instance in complex pages it may be good to have a method that batches all the changes to the DOM in a rAF callback, and have all the scripts that need to access the CSSOM boxes before these changes take effect, thus avoiding useless and costly reflows.
An other case is to avoid executing scripts when the page is in the background: rAF is heavily throttled when in background and if you have some script running that doesn't need to run when the page is hidden (e.g a clock or similar), it may make sense to wrap a timer in a rAF callback to take advantage of this heavy throttling.
*Actually both Chrome and Firefox have broken this conception. In these browsers if you call requestAnimationFrame(cb) from a non-animated document (a document where no animation frame callback is scheduled, and no mouse-event occurred in the last frame), they will force the update the rendering steps to fire immediately, making this frame not synced with the monitor (and, in Chrome, sometimes not even rendered on the screen at all).
I'm working on a fairly resource hungry web application which heavily relies on Raphael.js for roughly 50% of the animations used, the rest I have rolled my own animation method which uses webkitRequestAnimationFrame in conjunction with the the Web Audio API's context.currentTime to sync animations with the audio component.
I am experiencing pretty terrible performance at the moment and looking through Raphael's source I see that it also uses requestAnimationFrame. Most of the lag I am experiencing seems to occur when both my animations and Raphael's are running concurrently. Is this because requestAnimationFrame is essentially being called twice per draw cycle?
Essentially what I'm asking is basically do I have to re-roll my own implementation of an animate for raphael objects and stick it in with my existing requestAnimationFrame?
Hmmm as far as I know the whole point of RAF is to sync everything so that its ready for the next render update. I would be doing exactly the same as you as the this is the whole point of it.
As per the spec:
The expectation is that the user agent will run tasks from the animation task source at at a regular interval matching the display's refresh rate. Running tasks at a lower rate can result in animations not appearing smooth. Running tasks at a higher rate can cause extra computation to occur without a user-visible benefit.
So I would say NO it shouldn't be a performance hit.
I'm having a similar issue with sluggish SVG animation. My understanding of RAF is that it batches updates together wherever they come from, so I dont think that was your problem.
I've found that most of my cycles are taken up by repainting. There's not much you can do JS-wise to speed up repainting. But I think you can make it easier on the browser by cutting down on transparency effects, filters, and large areas of the screen changing. Also, repainting is a function of the the amount of pixels that you're updating. I'm making a full-screen site and when I double the viewport size, it doubles my paint time.
I was wondering what is the best way to implement a renderer in JavaScript. It's not the content part of the rendering that's really important here - I would rather like to hear when and how to effectively run the renderer code.
Currently, I have window.setInterval(renderFunc, 1000 / 20), which will just render a frame each 50 ms (i.e., fps = 20).
The point is that faster computers won't render more frames, moreover slower computers will not be able to catch up with 20 fps, so the function is called more than the computer might be able to handle.
I was thinking of a while(true) loop, but this uses 100% CPU and will freeze the computer itself - so actually my game (the renderer is of my 3D game) won't be playable anymore at all since you cannot click on buttons anymore.
What is the most efficient option in this scenario, or is there a better method that has not come to my mind?
Thanks in advance.
There's a feature that is made specifically for animation, called window.requestAnimationFrame. This means that the browsers chooses when to call your animation function instead of you hardcoding intervals. Lots of benefits from using it:
Faster computers will get higher frame rates
Windows/tabs that aren't visible are updated much less often
Varying frame rate depending on CPU usage
This article explains how to use it in a cross browser fashion:
http://paulirish.com/2011/requestanimationframe-for-smart-animating/
The article also links to http://jsfiddle.net/paul/XQpzU/2/
You could time how long a frame render takes, and adjust frame spacing to achieve an acceptable load. E.g., if your current frame took 5ms to render, and you want 50% load, wait 5ms before the next frame. You'll want to put some sanity checks on it, and also probably use times from the last several frames.
Look into trying the while(true) loop inside of a web worker thread. This should prevent the browser from locking up. Note that you can't directly manipulate a <canvas> or any other part of the DOM from within the worker thread.
https://developer.mozilla.org/En/Using_web_workers
"setInterval() will pass the number of milliseconds late the callback was called"
-- https://developer.mozilla.org/en/window.setInterval
You could use this to dynamically adjust your interval time.
N.B. MDC docs are for Javascript as implemented by Mozilla, not ECMA Script or other implementations. You should check if this works in other browsers.
I'm creating a game for the iPad using Phonegap, which means I'm using JavaScript/ CSS/ HTML for iPad's Safari. Basically, I'm moving around lots of img elements (sometimes changing their src) on 1024x768 resolution, just local files without any net connection. On the desktop Safari things work smoothly, but on the iPad, my setInterval feels delayed and flickering. Do you have any speed optimization tips for me that I could try? Thanks!
PS: I'm aware that switching to the iOS's native Objective-C would likely be much, much faster, but I'd really love to try it with standard JS/HTML/CSS.
You've run into one of the most common browser scripting issue with animated webpages.
The reason why your application slows down is because the browser is a single-threaded environment. As soon as you forget that you'll get into trouble.
setInterval makes you believe that your actions will happen in parallel like in a multi-threaded environment. But what really happens is that setInterval pushes the action to the UI stack to be handled at a later time. But if too many things are getting in to this stack at one time some actions will lag. The setInterval will keep pushing new actions, but the old ones will be still there, and the whole rendering becomes a sluggish mess.
As to when it happens, it depends on the hardware/software capabilities. iPad has much lower horse power than a desktop PC, that is pretty obvious.
Things you can do in order to avoid lag.
Trade smoothness for speed: raise your delay between intervals, so as to avoid cumulative actions in the UI stack.
setTimeout: this alternative is very similar to setInterval, except that it doesn't ensures a given interval between repetition, rather focuses on how long the browser should wait until repeating the action. So in order to make it more like setInterval you may need to keep track of the elapsed time between actions and calculate the measure of the change that has to be taken care of.
Group animations: you can have one interval for some related animations (you manage a mini-queue for them) and so you decrease the actual setInterval calls, and gain more power over controlling race conditions.
Also make sure to read this piece of article:
Making an iPad HTML5 App & making it really fast (Thomas Fuchs the creator of script.aculo.us)
Use CSS3 animations that use GPU acceleration... This will have a huge effect on any animations.
I'm starting on a javascript MMORPG that will actually work smoothly. Currently, I created a demo to prove that I can move characters around and have them chat with each other, as well as see eachother move around live.
http://set.rentfox.net/
Now Javascript timers are something I have not used extensively, but from what I know, correct me if I'm wrong, is that having multiple setIntervals happening at the same time doesn't really work well b/c it's all on a single thread.
Lets say I wanted to have 10 different people nuking fireballs at a monster by using sprite background positioning with setInterval -- that animation would require 10 setIntervals doing repainting of the DOM for sprite background-position shifts. Wouldn't that be a big buggy?
I was wondering if there was a way around all this, perhaps using Canvas, so that animations can all happen concurrently without creating an event queue and I don't have to worry about timers.
Hope that makes sense, and please let me know if I need to clarify further.
The issue with multiple setIntervals is twofold. The first is as you indicate, since all Javascript on browsers is (currently) single-threaded, one timer's execution may hold up the next timer's execution. (Worker threads are coming, though; Firefox already has them, as does Safari 4 [and maybe others].) The second is that the timer happens at a set interval, but if your handler is still running when that interval expires, the second interval is completely skipped. E.g., the timer can interfere with itself.
That last part needs more explanation: Say you have a setInterval at 10ms (which is the fastest you can reasonably expect any implementation to do it; may are clamped so that they don't go faster than that). If your handler takes 13ms, the interval that should have happened 10ms after it began will be completely skipped.
I usually use setTimeout for this kind of thing. When my handler is triggered, I do my work and then schedule the next event at the end of the handler. Then (within the bounds of what you can be certain of), I know the next event will happen at that interval.
For what you're doing, it seems like a single "pulse" timer would be best, working through whatever it needs to do on the pulse. Whether that pulse timer uses setInterval or setTimeout is a judgment call based on what you're seeing with your actual code.
+1 to T. J. Crowder, the answer was perfect. I strongly recommend learning to use Canvas over DOM nodes for game animation; the latter is slow and buggy, and will hang the browser in any non-trivial situation. OTOH, Canvas is much faster and can be hardware accelerated, and even has a 3D context if you need it.