Canvas layers and game animation - javascript

I am wondering how to go about this?
draw, wait 1 second
draw frame2 wait 1 second
draw frame3 wait 1 second.
clear animation canvas
now I assume you have to do this on a overlapping canvas
so that it wont be wiped by my current game refresh at 60
fps.
I will also be having the animation frames be a transparent png
so you can see whats behind it also.
think smoke animation.
What is the best way to wait 1 second, use set interval or set Timeout?
I already use set animframerate on my main canvas layer
I need to know how to animate while other animation is going on, as my head tells me that painting to screen is procedural so if a method is getting called to animate something everything else stops while its getting animated.

Drawing onto a separate canvas is a good way to have multiple layers, but there are several ways to do what you would like:
If your PNG image is pre-defined, you can just render it directly onto the canvas with context.drawImage(). The transparency will be preserved. As long as your image is loaded onto the page before it is rendered, there will be little overhead in re-rendering it every frame.
You can draw to a hidden canvas and then draw that canvas onto your main canvas. You can do this by creating a canvas in JavaScript and never writing it to the page. For example: var layer = document.createElement('canvas'); and then call context.drawImage() with layer as the image parameter. I have written an implementation of a Layer class you can use to make this easier.
You can draw directly onto your main canvas using transparency by setting the globalAlpha property of your graphics context object. (Just remember to set it back to 1 after you're done drawing your transparent stuff.)
You can draw onto a secondary, visible canvas that is absolutely positioned over your primary canvas. To do this you need to set the CSS background-color of the secondary canvas to transparent.
Similarly, there are two good ways to wait one second:
Use setTimeout or setInterval. This will wait as close to your delay period as possible (in this case one second) and then execute the callback asynchronously. Use setTimeout if you want to execute something once and setInterval if you want to execute something at a predefined interval indefinitely (i.e. every second).
Keep a variable that holds the last time you executed the function you want to run, and check whether you've waited long enough before running the code again. This will run the code synchronously. For example:
// Outside of your animation loop
var lastRun = Date.now();
// Inside your animation loop
if (lastRun + 1000 < Date.now()) {
/* Run you callback code */
}
Since you are using requestAnimationFrame to run your main animation loop, this will run at the soonest time after your delay (e.g. 1 second) at which the browser is ready to paint a new frame. Note that if you want code using this method to run asynchronously, you can also use web workers.
Unless you're drawing text onto a canvas without caching it or drawing thousands of objects onto your canvas, chances are pretty good that your performance is not draw-bound, so I would opt for the simplest solution even if it's not asynchronous. You can use the stats.js library to test your canvas performance.

Related

How to store HTML5 canvas as an image in a list of frames?

Trying to make a simple animation engine for an HTML5 game. I have a timeline along the top composed of frame previews and a wireframe editor with a stickman for testing on a bigger, bottom canvas.
When I switch frames, I use drawImage() to copy the bottom canvas to a small frame preview along the top.
Is there a way to save the bottom canvas' current state in an array (each item corresponding to each frame) so that I can use drawImage() to place them as onion skins?
Here's the project if you want to see it visually:
https://jsfiddle.net/incon/owrj7e5z/
(If you look at the project, it doesn't really work yet, it's a big work in progress and is very glitchy)
This is what I was trying, and it works for previews, but won't redraw as onion skins on the bottom canvas.
var framePreviews = [undefined,undefined,undefined....];
var c = document.getElementById('bottomCanvas');
var tContext = document.getElementById('timelineCanvas').getContext('2d');
framePreviews[simulation.currentFrame] = c; // save canvas in an array
tContext.drawImage(framePreviews[simulation.currentFrame], simulation.currentFrame*60, 0, 60, 60/c.width*c.height); // draw the canvas 60px wide in the timeline
Don't.
An image is really heavy, whatever the mean of saving it you'll choose, there will always at least be once the ImageBitmap of your full sized canvas that will need to be stored in your browser's memory, and in a few frames, you'll blow it out.
You don't do any heavy calculations in your app, so you don't need to store these as images.
Instead only save the drawing commands, and redo the drawings every time.
Now, for the ones who do have to save computationally heavy frames, then the best is to generate an ImageBitmap from your canvas, but once again, this should be used only if drawing this one frame takes more than 16ms.

Why is GPU render time inconsistent?

I'm building a WebGL application using THREE and am noticing some odd timing on the GPU. I don't have repro code available at the moment but I thought I'd ask the question in case it's a known browser quirk or something common and fixable.
Scene Setup
Scene with ~2,000,000 polygons, 136 Meshes, and 568 Object3D instances.
Using THREE.Composer with the FXAA and Unreal Bloom passes.
Using THREE.OrbitControls.
The scene is only rendered when something is known to have changed. For example, a draw is scheduled when the user drags the scene to move the camera with the controls or something in the scene moves. The scene is often static so we try not to render unnecessarily in those cases.
The Problem
The issue happens when the scene has been static (not drawn for a bit) and then the user changes the camera position by dragging. Once the user starts dragging the framerate is very choppy -- maybe 10-20 fps or lower -- for several frames before smoothing back out to something closer to 60. This happens consistently when leaving the scene alone for several seconds and then dragging again. If the mouse is dragged consistently after the initial stutter then the framerate stays smooth. Nothing different is being rendered for these frames.
This stuttering doesn't happen and the scene remains snappy if it's rendered every frame using requestAnimationFrame.
Here's the performance profiler with the stutter when the scene is only being rendered when something changes. You can see that there is a lot more time spent on the GPU during the frames that stutter before smoothing out again:
And the profiler when the scene is rendered at 60 fps:
Any thoughts? Why is there so much more GPU work happening suddenly on drag? Could the draw be blocked by some other rendering process? Why would it happen so consistently after not rendering for a few seconds? I've profiled using the latest version of Chrome but the stutter is present in Firefox, as well.
Thank you!
without a live sample there is no easy way to know BUT....
1 Three.js can do frustum culling on objects.
That means if some objects are off outside of the view they won't get drawn. So, put the camera in such a way that all objects are visible will run slower than if only some objects are visible
2 Primitive Clipping
Same as above except at the GPU level. The GPU clips primitives (it doesn't draw or compute pixels outside the view) so similar to above, if the lots of the things you're trying to draw happen to be outside the view it will run faster than if everything is inside the view.
3 Depth(Z) Buffer rejection
Similar to above again, if your objects are opaque then if a pixel is is behind an existing pixel via the depth test the GPU will skip calling the pixel shader if it can. This means if you draw 568 things and the first one you draw is the closest thing to the camera and covers up many things behind it than it will run faster than if all those things behind it draw drawn first. Three.js has the option to sort before drawing. Usually sorting is turned on for transparency since transparent objects need to be drawn back to front. For opaque objects though drawing front to back will be faster if any front objects occlude objects further back.
4 Drawing too many frames?
Another question is how are you queuing your draws? ideally you only queue a single draw and until the drawing has happened don't queue any more.
So
// bad
someElement.addEventListener('mousemove', render);
The code above will try to render for every mouse move even if that's > 60 fps
// bad
someElement.addEventListener('mousemove', () => {
requestAnimationFrame(render);
});
The code above may queue up lots and lots of requestAnimationFrames all of which will get executed on the next frame, drawing your scene multiple times per frame
// good?
let frameQueued = false;
function requestFrame() {
if (!frameQueued) {
frameQueued = true;
requestAnimationFrame(render);
}
}
function render(time) {
frameQueued = false;
...
}
someElement.addEventListener('mousemove', () => {
requestFrame();
});
Or something along those lines so that at most you only queue on render and don't queue any more until that render has completed. The code above is just one example of a way to structure your code so that you don't draw more frames than you need to.

How to animate using requestAnimationFrame?

I am trying to use requestAnimationFrame to animate my canvas when I click on + and - buttons to zoom in and zoom out like the Google Maps does (When you click on the + button to zoom in, you see a slight transition or a delay). Unlike the traditional canvas implementations, I have a pretty complicated structure in the application.
When the button is clicked, zoomin() function inside a service is called where the values are calculated and then the resultant value is emitted which is captured by a different component A which in turn calls a different class method redraw() which redraws the canvas.
In every example I saw, the images are being animated by manipulating the pixel values with time and the draw() method is available right there. But could anyone tell me where I should handle this and is there any alternative? And also how could I animate like the google maps does in JavaScript?
Could anyone guide me?
Thank you.
The demos that you have seen are right. requestAnimationFrame is a means and not an end. You use it to make your application more smooth and performant by limiting how often a method can be called, not just for the sake of using it to draw because that will not help.
If you want to use requestAnimationFrame for throttling (ie doing a redraw update every 1/60 of a second instead of whenever zoomin is called), you will have to update your logic and indeed store and update the zoom values in the background in a data service somewhere. The requestAnimationFrame loop should indeed have access to and call redraw so it is called every 1/60 of a second instead of whenever something updates. Redraw should have access to all data needed to draw the map (like the current zoom level).
The only alternative is NOT using requestAnimationFrame and doing the debouncing yourself when receiving mousewheel events to prevent to much draws. It sounds like your application could benefit from a more simple structure that would allow the use of requestAnimationFrame.
For the animation part, I would ask a separate question as that is a whole other topic.

How to separate canvas update logic from rAF drawing updates

I'm working on my second big canvas project (a game) and are really trying to optimize everything.
I have my mouse and keyboard listeners set up so they change the state of my canvas layers but all drawing is done through my rAF call. This works well.
However, I'd like to do a similar thing with the rest of my game. Could I add an event listener or somehow update all my states/variables after each rAF call completes drawing?
For example, if something must move five pixels left I currently do the subtraction then draw. I would like to be able to do the subtraction after each frame outside the rAF and then inside of it just draw, draw, draw. No calculations.
Thanks!
Combine both. Save variables based on events and draw then with rAF.
see the link below. (the 2 sec loop should be your rAF)
Canvas animation frame rendering: Infinite Loop vs. triggering on mousemover()
(not my best code work)

Redraw lots of objects on Canvas HTML

Is there a quick and efficient way to move lots of objects in canvas? Basically if there are around 1000 objects and I want to move all of them at once to emulate scrolling, it is very slow to redraw every single object by calling drawImage() 1000+ times.
Is there anyway to optimize this? I have an example link of the problem (and that's only with 100 objects): http://craftyjs.com/isometric/
Since canvas doesn't provide fast low level bitmap copying it's hard to do stuff in multiple layers and scroll for example the whole background at once and then only render the edges.
So what can you do? In short, nothing. Especially not when scrolling, sure you can do tricks with multiple canvases when you have a more or less static background but for moving objects there are hardly any performance improving tricks.
So, you've go to wait for Hardware Acceleration shipping in all majors browsers, I know this sounds ridiculous but I'm too waiting for that :/
The problem is that the canvas was never designed for game stuff. It was designed as, well, basically some kind of on the fly drawing thing, guess the designers had Photoshop clones in mind, but definitely not games, let alone the fact that there's no fast clear operation proves that, there's not even optimization in place when clearing the whole canvas with the same color.
If the images are already composited, not moving relative to one another, and defined by a rectangular region, then using canvas.drawImage() with a canvas as the first parameter and drawing to a sub-region should be significantly faster than re-drawing all the objects.
You could also just layer multiple canvases and slide the top canvas with the objects in HTML to scroll them.
Edit: Having really looked at your example, it seems to me that it should be implemented similar to Google Maps: create tiles of canvases and slide them left/right on the HTML page; once a canvas has been slid off the screen entirely (for example, off the left edge), move it to the other side (to the right edge) and re-use it for drawing. With this you will only need to re-draw whatever objects overlap the canvases that are moving on the edges.
You can draw all objects on a second, off-screen canvas and then only blit the whole canvas (drawImage() accepts canvas element).
However, if you're targeting desktop browsers, then this shouldn't be necessary. I've implemented tile engine (source) that simply redraws whole scene and naive implementation turned out to be pretty fast.
What I did to solve this problem was I had 10 squares on my screen and I wanted to animate them on a white background. So I drew a white rectangle over the canvas to clear the canvas so the animation would work. Does that make sense?
#Ivo By the way I read on http://www.w3.org/TR/html5/the-canvas-element.html that canvas was made for applications like games because it was a solution to get rid of the dependency on a external engine. Canvas is built in so it's kind of like a flash player built into your browser powered by JavaScript. I think it's fascinating.
You can use tiled rendering.
http://www.gamesfrommars.fr/demojsv2/ (better viewed with Chrome)

Categories

Resources