I have a number of objects that represent an image that I want to draw onto a canvas. Each object has a X, Y, Scale property as well as a render method that gets passed a canvas object. then the canvas tag is passed to each object's render method and they all need to render themselves to the canvas.
The problem I have is that scaling is being done from the origin of the canvas and not the origin of the image. To fix this I've tried the following:
canvas.translate(-this.X, -this.Y);
canvas.scale(this.Scale, this.Scale);
canvas.translate(this.X, this.Y);
The problem with the above code is that it looks like you can only have 1 translation and the 3rd line which is supposed to move the object back to its original position becomes the translation when calling drawImage. If I remove the 3rd line I get the correct scaling origin but the image is moved to 0,0 and not rendered in the correct location.
What is the proper way to do this type of scaling?
Try:
canvas.translate(this.X, this.Y);
canvas.scale(this.Scale, this.Scale);
canvas.translate(-this.X, -this.Y);
But keep in mind, doing this you will need to do alot of ctxt.save();s and ctxt.restore();s if you got the width and height of the imagens you could simple do ctxt.drawImage(this.image, this.X, this.Y, this.Width*this.Scale, this.Height*this.Scale); since you don't need to mess with the context state stack it may be faster.
Edit:
The transformations don't affect the origin. What happen is that they must be applied in reverse order. Remember this methods are just shortcuts for stacking matrices in the transformation stack.
Related
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 am trying to create an online image editor using HTML5 and getting issue while perform image move. pixelization happens everytime when I try to move, zoom, drag, rotate. I think issue is clearRect function.
function clear() {
element.clearRect(0, 0, window.innerWidth, window.innerWidth);
}
Here is my Fiddle Project
You are playing with the matrix transformations of the canvas' context.
These also apply to the clearRect method.
You need either to calculate the current transformations and apply their negative value to the clearRect method, or to reset the transformation matrix.
One easy way to do so is by using the setTransform method and the default values for the transformation matrix : ctx.setTransform(1,0,0,1,0,0);
Updated fiddle, with a quick fix.
very interesting topic. Nice exercise, too. I picked your fiddle.
Here is the result:
https://jsfiddle.net/dh6y18yh/8/
var loopRunner = function() {
drawImage();
globalID = requestAnimationFrame(loopRunner);
};
Basically the main big difference is, that I decoupled ALL the parameters for the image positioning into variables. Then do the translate(x,y) in drawImage()
and let drawImage() be run in a window.requestAnimationFrame() loop.
This way in is rendered correctly at 60 fps.
The mouse dragging is now calculating the distance dragged, and set this value to the position values.
Let's say I have a canvas with stuff on it, and I want to carve an ellipse in it's center, whose all of the pixels have an alpha of 0, to let everything behind the canvas to pass through, and given that previously the area now filled by the ellipse was something else, and said that, for performance reasons, I don't want to use putImageData, how would I do so?
And if there isn't a way, how would I carve an ellipse using putImageData?
Just to make it clear: I don't want to draw the html elements on the canvas, I want them to simply be in html behind it
you can use context.globalCompositeOperation = 'destination-out' after you draw the frame to fill the clipped shape(s).
example: http://jsfiddle.net/rlemon/6nEpc/
I have a polygon object (say a car) drawn inside a HTML5 canvas with help of methods moveTo and lineTo. I want to repeatedly draw that object at different positions in the canvas (simulating a moving object). My problem is that the previous drawn object is not getting cleared. Instead, multiple images are drawn on the canvas. How can I fix this issue?
You have to clear the canvas at the start of every draw frame
context.clearRect(0, 0, canvas.width, canvas.height);
Canvases are just arrays of pixels, they know nothing of the shapes you have drawn.
There are animation tricks that used to be used on bitmapped displays (e.g. "xor drawing") that can be used to remove the old shape before you draw the new one, but on modern machines it's generally far simpler (and perfectly fast) to just erase the canvas and start again for each frame.
Given your comments to other answers, I'd suggest just using two Canvases - one for the static background and one for the car. If the background image is static it could even be an <img> element instead of a Canvas.
If the car image is static you could also just draw that once, and then use CSS positioning to set its position relative to the background for each frame.
suppose your shape is car then you first have to assign a new graphic like:
car.graphics = new createjs.Graphics();
car.graphics
.setStrokeStyle(1)
.beginStroke("#000000")
.moveTo()
.lineTo()
.lineTo()
I'm making a top-down shooter game that relies on the avatar always being rotated pointing to the mouse cursor. I achieve rotation like this:
//Rendering.
context.save(); //Save the context state, we're about to change it a lot.
context.translate(position[0] + picture.width/2, position[1] + picture.height/2); //Translate the context to the center of the image.
context.rotate(phi); //Rotate the context by the object's phi.
context.drawImage(picture.image, -picture.width/2, -picture.height/2); //Draw the image at the appropriate position (center of the image = [0, 0]).
context.restore(); //Get the state back.
When the phi is zero, the image is rendered in its normal quality, with sharp edges and detectable pixels. But, when I set the phi to a nonzero value (actually, when it's not 0, Pi/2, Pi, Pi+Pi/2 or 2Pi), the image looses it's sharpness and the individual pixels can't be seen anymore, because they are blurred out.
Here's a screenshot (sorry about the general bad quality of the screenshot, but I think that the difference is more than noticeable):
This is, well, a bit unacceptable. I can't have the images always blurred out! Why is this happening and can I solve it?
You could try
context.imageSmoothingEnabled = false;
See docs:
context.imageSmoothingEnabled [ = value ]
Returns whether pattern fills and the drawImage() method will attempt to smooth images if they have to rescale them (as opposed to just rendering the images with "big pixels").
Can be set, to change whether images are smoothed (true) or not (false).
If you want a true pixel-art retro style effect, you'd need to manually create rotated sprite images for several angles, look up the appropriate sprite for the current value of phi, and draw it without rotation. This obviously requires a fair amount of art work!
IF you are rotating images around their center point, make sure the image itself has an even number of pixels. Once you end up on odd coordinates the image data needs to be interpolated for the target canvas. Apple has some nice documentation on translating and rotating the canvas.
So for any image, as suggested above use rounding to snap to full pixels.
context.translate(Math.floor(img.width/2), Math.floor(img.height/2));
This way every source pixel of your image will always be drawn exactly into a pixel inside the canvas and blurring does not occur. This however is only true for multiples of 90 degrees.
It seems that all browsers do, to some extend, antialiasing in image drawing so you will probably have to provide rotated images as sprites.
According to this Chromium bug report you might be lucky there if they haven't fixed it yet. Read through and you'll learn that Ian Hickson likely opposed making antialiased image drawing optional.
(picture.width/2, picture.height/2) point won't always work.
(Math.floor(picture.width/2) + 0.5, Math.floor(picture.height/2) + 0.5) should help.
Well, actually it is something you cannot get around
If you rotate an image by a multiple of 90 degrees, your library should smart enough so that no interpolation is applied.
But as soon as you rotate an image by an angle different from a multiple of 90 degrees, you need to interpolate. As a consequence, you get that smoothing. If you are interested in the theory, you may look for a book on computer graphics or image processing.
For the concrete case of image rotation you may have a look at this paper,
http://bigwww.epfl.ch/publications/unser9502.html