I am creating a html5 canvas game and the main idea is that there is a loop at every animation that redraws everything on my canvas in every frame.
For some reason the objects drawn don't appear in the order i want them to. i want the background image first, then a rectangle and finally an other image to be shown on top of each other.However, the rectangle blocks the view of the second image not other way around.
my relevant code:
function playerdraw(p){
ctx.rect(p.x,p.y,100,150);
ctx.stroke();
//irrelevant stuff here...
ctx.drawImage(p.im,p.x,p.y+25,100,100);
}
I run the whole thing on window.onload so image loading shoudn't be a problem.
Why is this happening?
(as you haven't provided enough code, I assume this could be the issue.)
You need to redraw the background image each time you run the playerdraw() function (if you're clearing the entire canvas each time). So, the following code should work :
function playerdraw(p) {
// clear entire canvas
ctx.clearRect(...);
// draw background image
ctx.drawImage(...);
// draw rectangle
ctx.rect(p.x, p.y, 100, 150);
ctx.stroke();
// irrelevant stuff here...
// draw other image
ctx.drawImage(p.im, p.x, p.y + 25, 100, 100);
}
Note: You need to clear the entire canvas at the very beginning (if you're doing so). There's is also a possibility that you're doing some kind of canvas translating or scaling stuff which if done in wrong order, things can get pretty messed up.
I now know what my issue was. I forgot to add
ctx.beginPath();
in my code. I called my function again and again and it drew more rectangles then I wanted to. I feel so silly.
Related
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...
I have the start of a project here and ran into a little issue.
https://jsfiddle.net/kylebrown219/gn10Lh8g/
In the jsFiddle you can click and create as many circles as you wish. When you press 'Spawn' you will see a Rectangle move across the screen. How do I get rid of the trail, without erasing the circles behind it?
I think this is where the issue is:
context.clearRect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height);
Thanks!
Depending on your needs; if you just are after animation you can use this little trick:
Set the current content of canvas as background to the element before drawing the rectangle:
canvas.style.backgroundImage = "url(" + canvas.toDataURL() + ")";
Then, when animating, clear the whole canvas, then redraw rectangle at current position:
context.clearRect(0,0,canvas.width,canvas.height);
drawRectangle(myRectangle, context);
Modified fiddle
To clear everything just reset the background style ("none") and clear.
You will of course have to limit the spawn button, or store the circles (as objects) for redraw (which in that case you could have used to redraw the background).
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/
I am trying to achieve grass like bend on a straight line created by using Cubic bezier curve.I have created a straight line using Bezier curve and now i want to bend it from top.But the problem I am facing is I am not able to do it dynamically.The moment I use mousemove for creating the bend,series of curves appear giving the canvas a paint like look which is somthing I dont want.I just want a single curved line.My question is how do I clear the previous lines that have been drawn and restore the latest bended curve??
My CODE:here is my code if you want to have a look at it
Create two canvases stacked on top of each other:
The first one containing the static content, the other only the dynamic content.
This way you will only need to be concerned about clearing the area the grass was drawn in (and this is much faster too as there is no need to redraw static all the time).
Place the two canvases inside a div which have position set to relative, then place the canvases using position set to absolute.
Now you can make a function to draw the static content (you will need to redraw it if the browser window gets resized etc.).
Record the area the grass is drawn within and clear only this (for each grass) in the next frame.
If this last is too complicated, just use a simple clear on the "dynamic" canvas:
ctxDyn.clear(0, 0, canvasDyn.width, canvasDyn.height);
This will also preserve transparency of this canvas, or "layer" if you like.
Use requestAnimationFrame to do the actual animation.
var isPlaying = true;
function animate() {
ctxDyn.clear(0, 0, canvasDyn.width, canvasDyn.height);
drawGrass(); //function to draw the actual grass
if (isPlaying) requestAnimationFrame(animate);
}
animate(); // start
The isPlaying is a flag you can toggle with a button on screen or something similar, to be able to stop the animation. See the link for requestAnimationFrame on how to make this cross-browser as this is still a "young" implementation.
dynCtx and dynCanvas can of course be called what you want.
You need to erase the current contents of the canvas before redrawing your updated "grass". This would involve redrawing everything else that is "static" on the canvas. For example:
function redraw() {
// Erase the current contents
ctx.fillStyle = 'white';
ctx.fill(0, 0, canvas.width, canvas.height);
// Draw the stuff that does not change from frame to frame
drawStaticContent();
// Draw the dynamic content
drawGrass();
}
In your mousemove event, update your grass curve information, then 'redraw'.
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.