I am creating an animation in Canvas. Initially, the Canvas will have a set of images drawn on it. After certain time, say 5 seconds, an image has to be cleared from its original place and drawn at a separate place.
To clear the image, I tried using context.clearRect() to clear the portion, but no luck. Is there any other way to do this?
clearRect is the right way. Note that if you have a transformation applied, it may be clearing a different rectangle in the canvas. You can always remedy this by using:
// I have lots of transforms right now
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
// Will always clear the right space
ctx.clearRect(x, y, width, height);
ctx.restore();
// Still have my old transforms
Related
What I would like to do is draw a circle on a canvas and then when a function is called delete the previous circle and draw a new one. Is this possible without having to redraw the whole canvas?
Take the code below for example
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(95, 50, 40, 0, 2 * Math.PI);
ctx.stroke();
A canvas is a collection of pixels (it's exactly like an image, however it's computed from code instead of being just loaded from file).
The only way to remove the old circle and draw the new one (if you want pixel perfect result) is repainting everything with the circle in the new position.
A possible solution could be using three distinct canvas elements layered one on top of the other:
whatever is behind the changing parts
the changing parts
whatever is covering the changing parts
1 and 3 can be kept fixed, you need to redraw only the second canvas.
So I'm trying to optimize my code by only accessing the DOM's canvas properties when the window is resized but I'm getting this weird problem. Why does the resizing prevent this? P.S. not sure what to include I have 2k lines of code right now.
I'm using
document.getElementById('mycanvas').getContext("2d").clearRect(-2000, -2000, 2000, 2000)
With this in my render loop:
document.getElementById('mycanvas').getContext("2d").height = window.innerHeight
Without it:
Setting either width or height properties of your HTMLCanvasElement will reset all the properties of your context to their default. It will also remove all clipping areas, and more interestingly in your case, it will reset the transformation matrix, and set a new pixel data (all transparent-black). And this is slow, so you are right removing it from your code.
Your call to ctx.clearRect sets the x and y coords to -2000, this means that with a default matrix transform, you are clearing non-existing pixels.
Setting your HTMLCanvasElement.height was what cleared your canvas previously.
We can also see on your second screenshot that you are drawing the grid slightly more on the bottom-right every time, this indicates that you are probably modifying the context matrix (e.g ctx.translate(x, y)).
Now that you don't reset it with the height setting, you need to do it explicitly. This can be done with ctx.setTransform(1,0,0,1,0,1).
So a basic start for a drawing function would look like
function draw() {
// clear
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset the transform matrix (fast)
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear all pixels (fast)
//... start drawing ...
Now, there might be other properties that you assumed where reset to their defaults while they're not, e.g fillStyle etc. Most of them can be simply set to what you want when needed.
If you are using ctx.clip() however (but looking at the screenshot, it seems you're not), you'd need to save() and restore() the context's state to be able to remove the clipping area (bad API design...).
I have the following problem:
I need to transform text on a HTML canvas, e.g. give it a trapezoid shape.
Here's what I tried:
context.beginPath();
context.moveTo(0, 0);
context.lineTo(200, 0);
context.lineTo(100, -100);
context.lineTo(0, -100);
context.closePath();
context.clip();
context.fillText("Hello World", this.x, this.y);
As you might have guessed, the text gets cut off instead of transformed to fit the shape. Below are images of what I am trying to do and what I managed to do.
Any help is appreciated :)
What I managed to do:
What I want to do:
What you can do is get a dummy canvas element in which you put your normal text, then you can apply your own maths to the pixel color values in textCtx.getImageData onto another imageData array that you create, so you can then put that into another canvas with the correct transform with transformedTextCtx.putImageData, and finally draw its canvas onto the original context with ctx.drawImage.
I'm going to assume you know the kind of maths that you want to apply, and if not I suggest you look into skewing and scaling, and find out how you can combine the two.
The mdn resources for the imageData methods are in the "Pixel Manipulation" subsection of the canvas rendering context page: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D#Pixel_manipulation
General Answer: How to transform text
You can transform your text (or anything else) by using canvasContext.setTransform before drawing. You don't need any second canvas.
Examples:
canvasContext.setTransform(1, 0, 0, 1, 0, 0); is the default
canvasContext.setTransform(0.5, 0, 0, 1, 0, 0); reduces width to 50%
canvasContext.setTransform(1, 0, 0, 0.5, 0, 0); reduces height to 50%
Specific Answer:
I couldn't figure this out quickly. I'll leave it up to the next user to figure it out.
I am using an HTML canvas and javascript and I need to clear all of the pixels underneath a shape created by closing a path (for example, I am using flot, and I want to make rounded corners, and to do this, I first need to remove the square corners by drawing a curve on top of the corner to remove the desired pixels).
Right now, I am doing this by just filling the shape with the same color as the background, which can imitate what I want to do, but, it is not ideal as it makes it impossible to place the chart on top of non-solid backgrounds without seeing the square corners. I know that there is a clearRect method that would do what I want to do, but with only rectangles, I need to do it with any closed shape. Is it possible, and if so, how would I do it?
brainjam's code was heading in the right direction, but didn't fully solve the problem. Here's the solution:
context.save();
context.globalCompositeOperation = 'copy';
context.fillStyle = 'rgba(0,0,0,0)';
//draw shape to cover up stuff underneath
context.fill();
context.restore();
Here's an example of a function that will clear a circle from a canvas:
var clearCircle = function(x, y, radius)
{
context.save();
context.globalCompositeOperation = 'destination-out';
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
context.fill();
context.restore();
};
I think what you want is a clipping region, defined by the clip() function. The latter takes a bunch of paths. Here's an example.
This is a little different from what you are specifically asking (which is to remove pixels after drawing them), but actually not drawing the pixels in the first place is probably better, if I understand your requirements correctly.
Edit: I now think I understand that what you want to do is clear pixels to transparent black. To do that, after having defined your paths, do something like this:
context.fillStyle = 'rgba(0,0,0,0)';
context.fill();
The first statement sets the fill color to transparent black.
Use globalCompositeOperation = 'destination-out' instead of 'copy', it will erase all pixels of the shape in the canvas.
See all kinds of composition here
very usefull !
http://gist.github.com/232194
I know it's something wrong with my transformations in drawGuy. Can anyone help me figure out how to rotate just the image? Currently it rotates fine, but I something with the transforms is distorting it, so that it doesn't follow the mouse correctly.
Instead of translating forward and then translating backward again, simply push your current state onto the stack before you traslate/rotate, and when you're done - pop your state back off the stack. This is how most graphical applications make use of translations/rotations.
Also, you're translating by x, y, then additionally calling ctx.drawImage(guy, x, y). This is going to, in effect, double the offset. I'd either get rid of the translate call, or change the position arguments for the drawImage call to 0, 0.
function drawGuy() {
ctx.save();
ctx.translate(x,y);
ctx.rotate(angle * Math.PI / 180);
ctx.drawImage(guy, 0, 0);
ctx.restore();
}
Check out the spec about context.save() and context.restore() (the way canvas does state push/pop), here.