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

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

Related

Is it possible to ansynchronously capture canvas image to ensure WebGL canvas isn't currently blank/black during swapping?

We are trying to take a snapshot of a video that is playing on a WebGL canvas. However, when the user clicks the snapshot button the image captured is sometimes black. YES - I have read all of the threads about how the WebGL canvas works with the swapping so I understand why we are getting a black image. We, unfortunately, cannot set the preserveDrawingBuffer property to true because our video needs to be of the highest quality and cannot afford any slow down.
I would like to see if I could programmatically keep taking snapshots until I get a good image.
The problem is that I can't just use a loop as that blocks the canvas from ever getting a new image drawn to it. So, if I just added a while loop and kept calling canvas.toDataURL, it would constantly get the same black image because the canvas is never being updated.
I'm trying to figure out if there is a way to capture the canvas asynchronously so that the canvas is still being updated.
Any suggestions would greatly be appreciated.
Thank you.
Just repeating the answers you claim you already read
use preserveDrawingBuffer
const gl = someCanvas.getContext('webgl', {preserveDrawingBuffer: true});
capture after drawing
function render() {
drawWithWebGL();
if (needScreenshot) {
takeScreenshot();
}
}
draw before capture (really the same as 2)
function doScreenshot() {
drawWithWebGL(); // force a draw
takeScreenshot();
}
Everything else is some variation of those 3 solutions
So ...
Is it possible to ansynchronously capture canvas image to ensure WebGL canvas isn't currently blank/black during swapping?
Without preserveDrawingBufffer the answer is "no"

HTML5 Canvas Context.drawImage not showing up

I am trying to make a drawing program with HTML canvas. You can see a version of it here: https://naclcaleb.github.io/Draw
My architecture works somewhat like this:
I have a master Canvas object, which contains a list of layers that it draws.
Each layer has a list of "Strokes". Each stroke contains a list of actions required to draw it.
I've had a few problems with lag. First, it was taking too long to draw all the strokes in the list.
I solved this by "rendering" the layer every time a stroke is added, so it's just an image.
That worked fine, except that I still had to draw every stroke when the user pressed Ctrl+Z, so I could reset the render.
So every time they tried to undo, it would take a while to compute - usable, but not ideal.
To fix this, I tried the same approach with each stroke - when it's done being created, "render" it by drawing it on a different hidden canvas, then turning it into an image with a DataURL.
(NOTE: I did try using getImageData and putImageData instead, but then it would replace the previous strokes with empty pixels)
And now I'm getting a pretty big problem:
Whenever I make a stroke, it immediately disappears. But once I press Ctrl+Z, all the previous strokes suddenly become visible.
I've been trying to figure this out for a while now.
Here's basically what happens:
The mouse is released, which triggers this event listener (found in the layer class):
this.el.addEventListener("mouseup", function(e){
//You can ignore this line...
if (active){
//...and this if statement. It just handles the erasers, and has been tested to work
if (that.erasing){
CURRENT_TOOL.currentStroke.actions.push({
func: ctx.setGlobalCompositeOperation,
params: ["source-over"]
});
}
//Have the current stroke create its render - I have used console.log to ensure that this does get run, and that the dataURL produced shows the correct image.
CURRENT_TOOL.currentStroke.createRender();
//The endStroke method returns the currentStroke, which I grab here
var newStroke = CURRENT_TOOL.endStroke(ctx);
//"that" is just a reference to the layer instance
//Add it to the strokes list
that.strokes.push(newStroke);
//Update the render
that.updateRender();
}
});
As you saw in the event listener, it calls an updateRender method. Here is that method (also in the layer class):
//Clear the canvas
this.ctx.clearRect(0, 0, this.el.width, this.el.height);
//Put the LAYER's render onto the screen
this.ctx.putImageData(this.render, 0, 0);
//Draw the stroke - I'll give you the draw function, but I've confirmed it does get run
this.strokes[this.strokes.length-1].draw();
//Now get the image data and update the render
this.render = this.ctx.getImageData(0,0, this.el.width, this.el.height);
As you saw in that function, it calls the stroke's draw function (found in the stroke class):
draw(){
//Ignore this if
if (this.render === undefined){
for (var i = 0;i<this.actions.length;i++){
this.actions[i].func.apply(this.ctx, this.actions[i].params);
}
}
else {
//I've used console.log to confirm that this runs.
//Draw the render
this.ctx.drawImage(this.render, 0, 0);
//I've also console.logged the dataurl of the layer before and after drawing the image, and neither have the render drawn on them.
}
}
This seems to say that the this.ctx.drawImage is not being run? Or that it's being run in the wrong way?
Anyways, that's that, and though I apologize for the longevity of this question, I'm hoping someone can help me. This is really my last resort.
One more thing: I got it to work in the link I gave earlier by rendering the layer using the same method I render the strokes - using an image with a dataURL instead of getImageData and putImageData. I'm not really sure what to think about that...

HTML5 Canvas choppy graphics when animating

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!

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.

Animating a missile in HTML5/WinJS

I'm writing a game using HTML5/WinJS on Windows 8. I'm trying to produce the effect of a bullet or missile firing at something; however, I can't seem to get the object to go through another image in the background without trailing a border. My working theory is that the border I'm seeing is caused by using clearRect. Here's my code:
var moveBullet = function(missile) {
if (missile.Image == undefined || missile.Image == null) {
var image = new Image();
image.src = "/images/missileImg.png";
image.onload = function () {
canvasContext.clearRect(missile.PointX - (image.width / 2), missile.PointY, image.width, image.height);
missile.PointY += BULLET_SPEED;
canvasContext.drawImage(image, missile.PointX - (image.width / 2), missile.PointY);
};
} else {
canvasContext.clearRect(missile.PointX - (missile.Image.width / 2), missile.PointY, missile.Image.width, missile.Image.height);
missile.PointY += BULLET_SPEED;
canvasContext.drawImage(missile.Image, missile.PointX - (missile.Image.width / 2), missile.PointY);
}
}
Is there a way to achieve this effect without using clearRect, or a more efficient way of restoring the background as it moves past?
Make your clearRect area a few pixels larger than the missile image. Drawing on a canvas in general has some built-in anti-aliasing. This means that if you draw a line with one color, then draw the same line with the background color, you'll not remove the original line. Something similar might be happening here, in which case a few extra pixels should help.
That said, there's a caveat to be aware of here.
First, I assume the background is separate element from the canvas? It looks like it as you're not redrawing that part on the canvas itself.
The reason I ask is that making repeated calls to clearRect on the same canvas will eventually show performance problems. What happens is that every call to clearRect accumulates into a complex region within the canvas--essentially its transparency mask. So every time the canvas has to be rendered, which happens any time you change it, it has to process that transparent area. Gradually, as you leave more and more small clearRect trails across the canvas, this region will become more and more complex and performance will drop.
I did this experiment with the Blizzard demo on the IE Test Drive site once, where I wondered why the demo was clearing the entire canvas with every animation frame. So I tried just clearing the trail behind each snowflake (and made each one a little bigger as I suggest above, because I had trails). Seemed like the right thing to do, but the performance plummeted by several orders of magnitude. Asking around within the IE team, they confirmed the region behavior I describe.
So the best thing to do, actually, is to do a clearRect on the entire canvas with every frame, then redraw the missile and any other bits that you're animating. This may seem counter intuitive, but ends up working best and avoids all these glitches with pixel trails.

Categories

Resources