HTML5 Canvas choppy graphics when animating - javascript

When animating one layer in canvas the graphics become choppy if there are other layers present, see fiddle (click RUN to see animation): http://jsfiddle.net/Q97Wn/
If i change this line:
opts.percentageIndicator.clearRect(0, 0, opts.percentageIndicator.width, opts.percentageIndicator.height);
To:
opts.percentageIndicator.clearRect(0, 0, opts.canvas.width, opts.canvas.height);
Then everything goes smoothly, except that this will remove the other layer completely.
I could solve this issue by having both in one canvas each, but i was hoping for structure-purposes that i could avoid that. Any suggestions?

First of all, canvas.getContext() not generating new context, it returning already existing instance, so the lines:
opts.centerCircle = opts.canvas.getContext('2d');
// ...
opts.percentageIndicator = opts.canvas.getContext('2d');
Would mean the same thing.
So I advice you to do like this:
http://jsfiddle.net/Volter9/Q97Wn/2/
What I did I just changed both contexts to one property and after rendering base added:
opts.ctx.strokeStyle = opts.indicatorColor;
Good luck!

Related

Masked element in Snap SVG doesn't come into view when translated

I have a group of elements that are masked by a rect in SnapSVG and I want to translate the elements, bringing new ones into view (and hiding ones that are currently in view). The code is really simple - here's a codepen: http://codepen.io/austinclemens/pen/ZbpVmX
As you can see from the pen, box1, which starts outside the mask element (clip) should cross through it when animated, but it never appears. Moreover, box2, which should move out of the clipping area, remains visible.
This example seems to do a similar thing and has no problems: http://svg.dabbles.info/snaptut-masks2
Here's the code from codepen:
var t = Snap('#target')
var clip=t.rect(200,200,200,200).attr({fill:'#fff'})
var box1=t.rect(300,100,50,50).attr({fill:'#000'})
var box2=t.rect(300,300,50,50).attr({fill:'#000'})
var boxgroup=t.group(box1,box2)
boxgroup.attr({mask:clip})
boxgroup.animate({transform:'t100,300'},2000)
I notice that the svg.dabbles examples translates the clip region by 0,0 at one point, but adding something like that doesn't seem to get me anywhere.
Ok, I figured this out thanks in part to this really great article about SVG transforms: http://sarasoueidan.com/blog/svg-transformations/
The upshot is that when I translate the box group, it takes the mask with it. This is a little confusing to me still - I guess the mask attribute is causing this somehow? Anyways, the solution is to apply an opposite translation to the mask to keep it in place. Check the pen to see it in action but basically I just had to add:
clip.animate({transform:'t-100,-300'},2000)
The tricky part of this is that you now need to synchronize the movement of the mask and the movement of the box group.
edit - I now demonstrate how synchronization can be achieved using snap's set.animate method on the codepen.

My HTML5 canvas isn't clearing - no paths are involved

I've run into an issue with a game I am developing where a canvas is not clearing, although the function is being called to clear that specific context.
I am moving an object from left to right, and to do so I run this code:
onKeyboardKeyDown(){
canUpdateBack = true;
drawX++;
}
onKeyboardKeyUp(){
canUpdateBack = false;
}
if (canUpdateBack) {
console.log("CLEARING contextBack");
contextBack.clearRect(0, 0, canvasBack.width, canvasBack.height);
contextBack.drawImage(img, drawX, 0, img.naturalWidth, img.naturalHeight);
}
I have tried this with hard-coded numbers for the width and height of the canvas and get the same result.
I know this works because I can see the boxObj moving across the canvas when I press a key, canUpdateBack is set to true. It is only set to false on a "keyup" event so that I only clear / draw on the canvas whilst moving boxObj.
I am getting the "CLEARING contextBack" console log, so I know the correct context is being passed. However, the context simply isn't clearing.
Thanks to anyone that could provide or point me toward a solution.
I am NOT using any transforms, I believe. I am drawing my images at an X-coordinate updated by my key presses. Or are those still considered transforms, me saying "paint over there"?
I discovered the bug in Chrome but am unable to replicate on mobile, Safari, or Firefox. It's looking entirely possible it's a Chrome bug.
From your given Fiddle in the comments, it looks like the problem is you are not clearing the contextBug canvas and just clearing the backCanvas. Make sure you clear both of them (or either or depending on how your program works):
function update() {
contextBack.clearRect(0, 0, canvasBack.width, canvasBack.height);
contextBug.clearRect(0, 0, canvasBug.width, canvasBug.height);
moveObj(boxItem);
...
}
Fiddle Example

Javascript & Canvas: Draw and delete lines to create a "breathing" circle

I would like to create an element, that shows a red circle. Once the user clicks on it, she can record her voice. In order to show the LIVE mode, I'd like to make the circle "breath" according to the incoming frequencies.
I'm experimenting with a <canvas> element. That means it creates a circle that gets bigger and smaller, depending on the variable arcrad. However, the lines are being drawn correctly, but they do not disappear afterwards. I tried to apply .clip() but can't get it to work...
if (arcrad <= 10) arcrad = 10;
analyserContext.beginPath();
analyserContext.arc(100,120,arcrad,0,2*Math.PI);
analyserContext.closePath();
analyserContext.lineWidth = 2;
analyserContext.strokeStyle = 'red';
analyserContext.stroke();
Any ideas - or completely different strategies for this use case?
Canvas will overdraw by default. For your animation you’ll need to clean the canvas at the start of each frame. Use something the following at the start of your drawing function:
analyserContext.clearRect(0,0,200,200);
assuming your canvas is 200 pixels wide and high. It’s worth pointing out that sometimes you don’t want to completely clear the animation field every frame. For example, if you were to draw a semi transparent rectangle over the frame at the beginning (instead of clearing it) then you’d end up with a basic ‘bullet time’ style effect.
It's a normal behavior. Once something it's drawn on the canvas, it's there forever. You have to think like if you were painting something: what has been done cannot be undone.
Luckily, you still have solutions:
1) redraw another circle on top of the first one with the background color. It's really not the recommend way, but it still can be useful
2) use clearRect method (see How to clear the canvas for redrawing)
There are numerous ways to clear a canvas pre drawing to create animation:
How to clear the canvas for redrawing
simplest in my mind:
canvas.width=canvas.width;
though can equally use clearRect (which is actually quicker and won't reset the entire canvas if that is an issue regarding transforms etc!) over the region or whole canvas.
Get the likes of:
http://jsfiddle.net/dw17jxee/

Why is animation speed increasing each time the target element is deleted and recreated?

I've created a jQuery plugin based on somebody else's Chrome experiment that inserts a canvas element into your target element, and draws an interactive starfield in the canvas.
Each time you resize the window, the canvas element is removed and then restored so that its size matches its parent element and everything animates properly; it's responsive.
However, whenever it's restored, the speed of the animation increases. Why does it do this? I thought all the variables (including speed) were reset to their defaults with the this.start() method.
You can see the code (and demo) on CodePen; you can also fork it on Github, though I think the Github version is several commits behind my own.
(Also, this is my first real jQuery plugin, so if you see any issues, by all means, let me know.)
Any clues?
Using cancelAnimationFrame alone won't necessary stop the animation loop (it turns out).
To be absolute sure you will need to use a conditional check as well - a generic example:
var isPlaying; /// our condition
function loop() {
/* funky stuff here */
If (isPlaying === true)
requestId = requestAnimationFrame(loop);
}
Then starting it:
functiom start() {
isPlaying = true;
loop();
}
Now when you want to stop the animation you need to do:
function stop() {
isPlaying = false;
/// kill any request in progress
if (requestId) cancelAnimationFrame(requestId);
}
If you don't do this you risk stacking calls to the loop - for example:
If you resize and cAF isn't stopping rAF from re-trigger the loop, the old loop will still run in the background and you will start a new loop on top of that.
That is why you see speed increases as the old and the new loop will both increment the positions before the stars are drawn to screen.
On the third re-size yet another loop is started, and eventually the whole thing will block the browser.
However
Instead of utilizing start and stop of the loop I would recommend you the following approach:
Create canvas once
Start the loop only once
In this case, a re-factoring of the whole re-size mechanism could be beneficial (for instance, separate needed initializations (width and height of element) from first time initializations that can be re-used later).
There is no need to re-init the stars for each re-size as you will use the width and height to check their boundaries (canvas will do the clipping).
When resizing you can consider using a conditional flags to prevent render while re-sizing.
Although generally, a condition to block rendering while canvas changes size is really not necessary due to the single-thread nature of JavaScript and in cases such as this you do boundary check on current element size. Canvas itself will take care of the clipping for you.
And that being said: there should be no need to re-create the canvas element each time.
This creates an unnecessary overhead. Simple set new width and height on its properties if canvas is already created:
if (typeof canvas === 'undefined')
canvas = /* only create if it doesn't exist */
canvas.width = width;
canvas.height = height;
PS: I "hampered" a version with some brute-force-ish implementations of the above. It's far from complete or of high quality but takes out some of the pain for the sake of example to give you a couple of pointers.
Please adopt to suit your needs.
Update:
To include more info from the additional comments:
when a new size is set on a canvas element its context is reset to default (fillStyle becomes transparent, strokeStyle black, transform is reset and so on).
This means that all non-default settings must be set again after each new size is set.
Setting a new size may (and typically do) clear the content of the canvas as well so that all pixels becomes transparent black.
For anybody struggling with manually updating a canvas element's dimensions:
Resizing the canvas element results in it discarding anything that's been drawn to it up to the point of the resize.
This script's animation should have continued to draw to the canvas after resize, but the only thing that would update was the fillRect of the background; the stars disappeared.
The only way to get the stars back after changing the dimensions of the canvas element: an extra call to context.strokeStyle. I have no idea why; if anybody could shed some light on the matter, I'd be much obliged.
Edit: As per comments below, everything in the canvas resets—including stroke and fill style (both default to black, apparently). So as the resize fires, I had to re-define stroke and fill styles.

HTML Canvas Layering Frameworks

I'm building an app using the HTML 5 canvas tag, and so far it works great. The next step for the app is layering, so that I have a background image and them some more layers on top of that. Right now I'm doing this:
this.drawMarkers = function(markers) {
selectedImage = null;
map.width = map.width;
for(var i in markers) {
for(var j in markers[i]) {
var holder = markers[i][j];
if(holder !== null) {
var marker = new Marker(new Cell(holder.row, holder.column), holder.type);
marker.src = holder.src;
marker.draw();
}
}
}
initGrid();
}
where i is the layer and j is the things on that layer. The initial code for this just drew everything on one layer and it worked great, and this one does too if everything downloads in the correct order. If not, things get stacked incorrectly. For example, if the background is large and ends up downloading last, it ends up clearing out everything on top of it due to everything being asynchronous.
Are there any frameworks out there for adding layering to canvas to avoid this? I'm fine if things don't load in the correct order, so long as the stacking is preserved.
First question to ask yourself:
If you draw it repeatedly, say every 100 milliseconds, does the problem go away?
If no, the "drawing in the wrong order" is something you are doing and not an async error. I suspect this might be the case.
Images may not draw, but nothing should draw in the wrong order, assuming your draw routine is correct and that you are redrawing everything each time. According to the canvas spec, a call to drawImage does nothing if the image isn't loaded, so incorrect stacking should be impossible unless it is the order of your own calls.
In any case, you should wait until all of the images you are going to use have loaded before you draw. Use their onload events and/or $(window).load() or jQuery(document).ready, etc.
Some other notes:
Depending on how interactive your canvas is and how the layers work, sometimes it is better to use multiple canvases stacked on top of each other to increase performance, especially if a top layer changes very little and a layer underneath changes often.
If your background is a static file such as a png, set the CSS background-image of the canvas to that image to make the drawing easier on yourself.

Categories

Resources