RequestAnimationFrame for multiple canvases - javascript

I’ve got a page with layered <canvas> elements like explained in the answer here. The canvases together make up an animation, so each is cleared and redrawn as necessary.
Now I'm trying to incorporate requestAnimationFrame by using the cross browser shim. But I don’t really know what requestAnimationFrame is doing behind the scenes.
Is it okay to have it update multiple canvases in each loop? Should each canvas have its own loop? Is the answer browser dependent?

Updating all the canvases in a single requestAnimationFrame is perfectly okay.
If the canvases are independent from each other and appear on different sections of the page, then you might want to use individual requestAnimationFrame handlers, passing the canvas element as the second argument. That way, only the currently visible canvases get updated. (Passing an element as the second argument is WebKit-specific, though.)
What requestAnimationFrame does is tell the browser that you would like to update the appearance of the page. The browser calls the callback when the page or element is next up for a redraw. And that’s going to happen when the page/element is visible, and never more often than the screen refresh rate.

Using requestAnimationFrame simply lets the browser control when reflows/repaints on the page happen.
It would be better to alter all the canvases in one callback, so the browser can repaint them in one go.
This explanation of requestAnimationFrame was pretty helpful

The shim merely falls back to a timer if it is not available.
You should be fine. Update all of them in the same single loop.
requestAnimFrame isn't "tied" to a canvas or anything, just the function you pass it. So you can use it with three canvases or zero canvases just fine.

use canvas in this format js file so RequestAnimationFrame for multiple canvas will work
(function(){ ... })();

Related

requestAnimationFrame resource consumption: redrawing whole or part of canvas

As seen from Stuck with SetInterval ,SetTimeOut and Requestanimationframe or the like, requestAnimationFrame repeat "once the browser is ready". In other words, it keeps the browser busy.
I'm creating a "hover" effect using "mousemove" when plotting a chart with many data points. It's easy to do by reploting the whole chart/canvas using requestAnimationFrame repeatedly. Code is short in this case.
Instead of the whole canvas, I tried to replot only the data point under mouse (hover, <1% of the canvas) using requestAnimationFrame. For that several arrays need to be added and the code is longer.
It can be different from case to case, but in general, is requestAnimationFrame a resource-intensive method? Redrawing the whole canvas for the sake of <1% of the area seems not sound economically.
requestAnimationFrame is not resource intensive, its purpose is to adjust the CPU consumption to what the screen can display (in terms of framerate).
You can assume that requestAnimationFrame allows your code to be ran once per frame of the screen. It's up to you to optimize the code of the callback so it doesn't recompute positions, shapes and colors of static things (only the point under the cursor).
Redrawing the whole canvas isn't the problem, the problem is redrawing the same image every frame.
Instead, redraw only when something has changed in your graphic.
You could start an infinite requestAnimationFrame (rAF) loop waiting for the state to change, but this will force the browser to stay in an animated mode, which forces it to enter some branches in the event-loop that it could otherwise ignore (specs). See this answer of mine for more details.
Given that mouse events are now throttled to screen refresh rate, in modern browsers you wouldn't even win by throttling this event in rAF, except that all the browsers still don't do that, looking at you Safari....
So to summarize,
Clear all / Redraw all. Painting only part of the canvas doesn't improve perfs that much, and this way you avoid a lot of trouble at coding.
Redraw only when your graphics changed. Avoid useless renderings.
Avoid keeping a requestAnimationFrame loop active for nothing. It just saves trees.

Multiple canvases on a page. Optimization questions

I have a very long page with multiple canvases. They don't overlap each other, and are mostly used separately for PIXI.js to play spritesheets.
I use requestAnimationFrame to render each canvas.
I have a few questions since I'm unsure how to optimize.
1) When a canvas is offscreen, do I need to cancelAnimationFrame? Or does it not matter because it is offscreen and therefore won't be painted?
2) Should I have all my render functions within the same requestAnimationFrame? Will that improve performance?
Any other suggestions?
Comments provide most of the information, let me still answer all the points:
requestAnimationFrame is naturally a window, not canvas method. Thus it is not aware of canvas visibility. There is "element" parameter in WebKit, but it is not in current spec http://www.w3.org/TR/animation-timing/#requestAnimationFrame. Thus there are no benefits in having multiple handlers, it will improve performance if you have just one requestAnimationFrame taking care of the entire flow.
Browsers (tested on Safari/FF/Chrome) will not call requestAnimationFrame if tab is not visible at all. There's no much benefit in cancelling animation frame request manually.
PIXI is not checking for canvas visibility in renderer.render(stage). You can use getBoundingClientRect to check canvas visibility before rendering. This may increase performance quite a bit. How to tell if a DOM element is visible in the current viewport?
Best way to profile this particular scenario is probably through Chrome timeline view https://developer.chrome.com/devtools/docs/timeline . Animation skipping may reduce browser composite/paint times. Only checking javascript execution time might be a little misleading, especially if we remember that WebGL calls may execute asynchronously.

When to use requestAnimationFrame?

Having discovered requestAnimationFrame just a moment ago, I have dived into all the information I could find about it. To name just a few of the resources I came across in case there are others looking for more info about it:
http://creativejs.com/resources/requestanimationframe/ - explains the basics about it.
http://www.html5rocks.com/en/tutorials/speed/animations/ - explains how to use it.
Anyway, all of these resources tell me something about how requestAnimationFrame works or how it could/should be used, but none of them tell me when it is right to use it.
Should I use it for animations (repeated changes to the style of an element, much like CSS animations)?
Should I use it when an automated event wants to change the css/classes of one or multiple elements?
Should I use it when an automated event wants to change the text value of one or multiple elements? (e.g. updating the value of a clock once every second)
Should I use it when an automated event wants to modify the DOM?
Should I use it when an automated event needs values like .offsetTop, .offsetLeft and then wants to change styles such as top and left a few lines further?
Should I use it when a user generated event causes any of the above changes?
TL;DR: When is it right to use requestAnimationFrame?
You shouldn't yet. Not really, at least. This is still experimental and may or may not reach full recommendation (it is still a working draft at this point). That said, if you don't care about older browsers, or willing to work with the polyfill available the best time to use it is when you are looking to draw things to the screen that will require the browser to repaint (most animations).
For many simple modifications of the DOM, this method is overkill. This only becomes useful when you are doing animations when you will be drawing or moving items quickly and need to make sure that the browser repainting is keeping up enough to make it smooth. It will allow you to ensure that every frame you calculate will be drawn to the screen. It also provides a utility for more accurate time measurements to your animations. The first argument is the time at which the paint will occur, so you can ensure that you are where you should be at that moment.
You should not use it when you are doing many simple modifications to the DOM, or things that don't need to be smoothly transitioned. This will be more expensive on your users' computers so you want to limit this to making things smoother in transitions, movements and animations. Forcing a frame redraw is not needed every time you make a change on the page, since the response will be fast enough most of the time you don't need to worry about that extra couple milliseconds between draws.
As the previous answer says, you should not use it in the discontinuous animation because that don't need to be smoothly transitioned. In most cases, it's used for properties which vary continuously with time.

Force HTML5 canvas to redraw while doing heavy JavaScript processing?

This question is related to this older one, but I wanted to be sure I had the right answer before I started making major changes to my code.
I'm working on a very computation-intensive JavaScript program that needs to constantly update an image in an HTML5 canvas to draw an animation. As written now, the code draws all the frames of the animation in a tight loop without returning control to the browser, meaning that what's ultimately displayed is just the final frame. I'm pretty sure the only way to fix this is to split the animation code into smaller pieces that can be called reentrantly through a timeout event. Is this correct? Or is there a way to force the canvas to display its contents at a certain point even in the middle of a tight JavaScript loop?
I'm pretty sure the only way to fix this is to split the animation code into smaller pieces that can be called reentrantly through a timeout event. Is this correct?
This is correct.
Javascript is single threaded, so there's no way for anything else to happen while your logic is being executed. Your only choice is to "emulate" threading by splitting your logic up in to micro-chunks to be executed on timeouts.
You could use webworkers for this if they are available. Just calculate everything you need to do in the webworkers and post the result back when it's done. When you post the message back you can just refresh the image.
Calculations will be done in the background and your page only blocks while updating the image.

HTML5: Canvas performs too slow on lower end computers

My problem is that my javascript/canvas performs very slowly on lower end computers (Even though they can run even more challenging canvas scripts smoothly).
I'm trying to do a simple animation depending on user selection.
When drawing on the canvas directly proved to be too slow, I draw on a hidden canvas and saved all frames (getImageData) to data and then called animate(1); to draw on my real canvas.
function animate(i){
if(i < 12){
ctx2.putImageData(data[i], 0, 0);
setTimeout(function(){animate(i+1)},1);
}
}
But even this is too slow. What do I do?
Do not use putImageData if you can help it. The performance on FF3.6 is abysmal:
(source: phrogz.net)
Use drawing commands on off-screen canvases and blit sprites to sub-regions using drawImage instead.
As mentioned by #MartinJespersen, rewrite your frame drawing loop:
var animate = function(){
// ...
setTimeout(animate,30); //Max out around 30fps
};
animate();
If you're using a library that forces a clearRect every frame, but you don't need that, stop using that library. Clear and redraw only the portions you need.
Use a smaller canvas size. If you find it sufficient, you could even scale it up using CSS.
Accept that slow computers are slow, and you are standing on the shoulders of a great many abstraction layers. If you want to eek out performance for low-end computers, write in C++ and OpenGL. Otherwise, set minimum system requirements.
The timeout you specified is 1 millisecond. No browser can update the canvas that fast. Change it to 1000 - that'll be 1 second, i.e:
setTimeout(function(){animate(i+1)}, 1000)
UPD. Another thing to try is to prepare as many canvases as there are frames in your animation, set all of them to display:none, then turn display:block on them sequentially. I doubt it's going to be faster than putImageData, but still worth trying.
As already mentioned timeouts with 1 millisecond interval are doomed to fail, so the first step is to stop that.
You are calling setTimeout recursivly which is not ideal for creating animations. Instead initiate all the setTimeouts you need for the entire animation at the same time with increasing delays in a loop and let them run their course, or better yet use setInterval which is the better way of doing animations, and how for instance jQuery's animations work.
It looks like you are trying to redraw the entire canvas at each step of your animation - this is not optimal, try only manipulation the pixels that change. The link you have given to "more challanging canvas scripts" are actually a lot simpler than what you are trying to do, since it's all vector based math - which is what the canvas element is optimized for - it was never made to do full re-rendering every x milliseconds, and it likely never will be.
If what you really need to do is changing the entire image for every frame in your animation - don't use canvas but normal image tags with preloaded images, then it will run smoothly in ie6 on a singlecore atom.
I've got an app that works kind of like Google maps - it lets you click and pan over a large image. I redraw my Canvas heavily, sampling and scaling from a big image each redraw.
Anyway, I happened to try a dual canvas approach - drawing to a (larger) buffer one when needed, then doing a canvas_display.drawImage(canvas_buffer) to output a region to the screen. Not only did I not see a performance gain, but it got significantly slower with the iPhone. Just a datapoint...
OK, first things first. What else is happening while you're doing this animation? Any other javascript, any other timers, any other handlers? The answer, by the way, cannot be nothing. Your browser is repainting the window - the bits you're changing, at least. If other javascript is 'running', remember, that's not strictly true. Javascript is single-threaded by design. You can only queue for execution, so if some other javascript is hogging the thread, you won't get a look in.
Secondly, learn about how timers work. http://ejohn.org/blog/how-javascript-timers-work/ is my personal favorite post on this. In particular, setTimeout is asking the browser to run something after at least the specified time, but only when the browser has an opening to do that.
Third, know what you're doing with function(){animate(i+1);}. That anonymous function can only exist within the scope of its parent. In other words, when you queue up a function like this, the parent scope still exists on the callstack, as #MartinJespersen pointed out. And since that function queues up another, and another, and another... each is going to get progressively slower.
I've put everything discussed in a little fiddle:
http://jsfiddle.net/KzGRT/
(the first time I've ever used jsfiddle, so be kind). It's a simple 10-frame animation at (nominally) 100ms, using setTimeout for each. (I've done it this way instead of setInterval because, in theory, the one that takes longer to execute should start lagging behind the others. In theory - again, because javascript is single-threaded, if one slows down, it would delay the others as well).
The top method just has all ten images drawn on overlapping canvases, with only one showing at a time. Animation is just hiding the previous frame and showing the next. The second performs the putImageData into a canvas with a top-level function. The third uses an anonymous function as you tried. Watch for the red flash on frame zero, and you'll see who is executing the quickest - for me, it takes a while, but they eventually begin to drift (in Chrome, on a decent machine. It should be more obvious in FF on something lower-spec).
Try it on your low-end test machine and see what happens.
I did the setTimeout this way, hope it helps somebody at boosting application:
var do = true;
var last = false;
window.onmousemove = function(evt){
E.x = evt.pageX - cvs.offsetLeft;
E.y = evt.pageY - cvs.offsetTop;
if(do){
draw();
do = false;
//in 23 ms drawing enabled again
var t = setTimeout(function(){do = true;},23);
}else{
//the last operation must be done to catch the cursor point
clearTimeout(last );
last = setTimeout(function(){draw();},23);
}
};

Categories

Resources