How to transform and draw image on html canvas? - html

I wanna transform and draw an image on canvas. First I'm transforming an image over canvas through touch gestures and that transformation is handled through css
and controlled by 4 variables rotation, deltaX, deltaY and scale where deltaX and deltaY are positions of center an image. But after finalizing that transformation, I wanna draw that image with same transformations on the canvas. I tried the following but didn't got expected result:
img.onload = () => {
// this.ctx.setTransform(this.transformBlock.scale, 0, 0, this.transformBlock.scale, x, y);
this.ctx.rotate(this.transformBlock.rotation);
this.ctx.scale(this.transformBlock.scale, this.transformBlock.scale);
this.ctx.translate(this.transformBlock.deltaX, this.transformBlock.deltaY);
this.ctx.drawImage(img, -width / 2, -height / 2);
};
Suppose if my transformation is this: rotation = 90, scale = 2, delX = 180, delY = 320 than my image is 90 deg rotated clockwise with width and height doubled(scale=2) and center of image is at (180px, 320px) (delX, delY). But while drawing on canvas scale is more than 2, most of the image went out of the screen and rotation is not 90 deg. If anybody tell me how to obtain same expected behavior on canvas. It would be great help.
Thanks.

Related

Getting the right width and height of a canvas element

I am having difficulties with setting the correct width and height of my canvas element.
I have a ball, that I'd like to bounce back whenever it hits a screen boundary by changing it's vertical velocity. It works, but instead of moving back as soon as it hits the edge of the screen, it goes on for a couple of seconds and THEN moves back. I have these variables to determine the viewport's size:
var left = 0,
right = canvas.width,
top = 0,
bottom = canvas.height;
If my ball's x or y positions are outside these boundaries, the velocity should be changed to a negative one. However, during my animation I console.log it's x position and by the time it reaches the right edge of the screen the value is around 600, which is really strange, since I'm on a 1366x768px monitor.
Also, it doesnt't fully reach the left screen edge, but bounces off like 50px from it.
Any ideas are really appreciated, because I've been stuck on this for quite some time.
You can see a working example here: http://codepen.io/gbnikolov/pen/puiwk
Update your draw to the following.
Ball.prototype.draw = function(ctx) {
ctx.save();
// you've translated to the x and y position of the ball.
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.scale(this.scaleX, this.scaleY);
ctx.lineWidth = this.lineWidth;
ctx.fillStyle = this.color;
ctx.strokeStyle = this.strokeColor;
ctx.beginPath();
// Draw at 0,0 since we are already translated to the x and y.
ctx.arc(0, 0, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.restore();
}
Live Demo
Your problem is in the draw method, you're translating the context and then making the arc at the x and y of the ball so if you translate to 20, 20 for example and then draw at 20,20 your ball is actually at 40,40.

Gradient stroke along canvas arc disappearing when context translates to center

I am very new to canvas drawings. I am trying to apply gradient along the arc of a circle. I can get it to appear fine when I draw the arc with center offset from the context coordinates. Lets say centerX and centerY denote the center of the canvas. I can get a gradient arc using context.arc(centerX, centerY, radius, ......).
Working example: http://jsfiddle.net/m5Pmb/
But when I try to draw the arc around the context coordinates, the gradient disappears. For example, I take the above working jsfiddle example, do context.translate(centerX, centerY), then do context.arc(0,0,radius,......), the resulting arc does not have any gradient on it.
Example here: http://jsfiddle.net/N6NMB/
In my case, I need to spin the resulting circle around its axis using context.rotate(), so I must translate to the center and draw circle around (0,0). But I cant understand why the gradient disappears when trying to draw arc around context's (0,0) point. Any insight would be really helpful.
Since you are translating the context centerX and centerY are not where you think anymore.
When you translate the context you are saying you want that x and y to be the new 0,0. So now your 0,0 is in the center of the canvas, so centerX and centerY are offset by themselves putting them further away from the center.
One method you can use is the following
var grad = context.createLinearGradient(
-radius,
radius / 2,
radius,
radius / 2
);
live Demo
The above works because its called after you translate the context, so centerX and centerY (like I said previously) are 0,0 meaning they don't need to be referenced in that function.
The gradients that you create will be painted with the actual transform in use.
So if you are not using any transform, no need to wonder anything : define your gradient where you're about to draw, and you'll be fine.
If you are using transform, you must think of the coordinates of the gradient as relative to the point/angle/scale when you'll use them.
To explain further, i modified your example and used a radial gradient.
I created a normalized gradient : it is defined in between 0.0 and 1.0 radius, meaning it will have its x and y in [-1; 1].
var eyeGrad = context.createRadialGradient(0, 0, 0, 0, 0, 1.0);
Then to use the gradient i must :
1) translate to be in the center of the figure i want to draw.
2) scale to have normalized coordinates.
function drawEye(x, y, r) {
context.save();
//translate context to center
context.translate(x, y);
// scale to radius
context.scale(r, r);
context.beginPath();
// draw an arc with radius of 1
context.arc(0, 0, 1, 0, 2 * Math.PI, false);
context.fillStyle = eyeGrad;
context.fill();
context.restore();
}
fiddle is here :
http://jsfiddle.net/gamealchemist/N6NMB/3/
Result for :
drawEye(100, 100, 40);
drawEye(250, 120, 20);

clearRect issue in animation

Im having issue with clearRect, i have an image u can move up and down and which follow the angle where the mousse is but in some frames of the animation the clearRect let a small edge of the previous image state ( 'this' reference to the image and 'ctx' is the 2d context, 'this.clear()' is called each frame before redrawing the image at the new coordinates )
this.clear = function(){
game.ctx.save();
game.ctx.translate(this.x+this.width/2, this.y+this.height/2);//i translate to the old image center
game.ctx.rotate(this.angle);//i rotate the context to the good angle
game.ctx.clearRect(this.width/-2, this.height/-2, this.width, this.height);//i clear the old image
game.ctx.restore();
};
if i replace the clearRect line by
game.ctx.clearRect(this.width/-2-1, this.height/-2-1, this.width+2, this.height+2);
it works but its not the logical way
The problem is that you are only clearing at position half the width/height, not position minus half the width/height.
Regarding anti-aliasing: when you do a rotation there will be anti-aliased pixels regardless of the original position being integer values. This is because after the pixels relative positions are run through the transformation matrix their offsets will in most cases be float values.
Try to change this line:
game.ctx.clearRect(this.width/-2, this.height/-2, this.width, this.height);
to this instead including compensation for anti-aliased pixels (I'll split the lines for clearity):
game.ctx.clearRect(this.x - this.width/2 - 1, /// remember x and y
this.y - this.height/2 - 1,
this.width + 2,
this.height + 2);

html5 canvas - emulate 3d rotation of image

I have an image which has square shape. I want to rotate and squeeze it to get 3d effect like on the pictures below:
Source image:
Rotate to 0 degrees and squeeze:
Rotate to 45 degrees and squeeze:
Something like this.
I have played around Math and tried to change Width and Height of the image by multiplying to Sin and Cos of angle.
var w = image.width*Math.cos(angle* TO_RADIANS);
var h = image.height*Math.sin(angle* TO_RADIANS);
h=h*2/3; //squeezing the height
ctx.drawImage(image, 0, 0, w, h);
But I am not good at mathematics, so I hope somebody may help me to solve this issue.
I've resolved my problem. I squeezed my image in photoshop. So, the image became 300x150 size and looked like the second picture above. Then I applied a function below anytime, when I needed to redraw the image according to the angle:
var TO_RADIANS = Math.PI/180;
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle * TO_RADIANS);
var w = image.width-Math.abs((image.width/2)*Math.sin(angle* TO_RADIANS));
var h = image.height+Math.abs((image.height)*Math.sin(angle * TO_RADIANS));
ctx.translate(-w/2, -h/2);
ctx.drawImage(image, 0, 0, w, h);
ctx.restore();
Now it works pretty well.
You should look into a transform matrix.
Something like this:
ctx.transform(1, 0.6, -.8, .5, 20, 0);
http://jsfiddle.net/ericjbasti/nNZLC/
some links for you :
http://www.w3schools.com/tags/canvas_transform.asp
https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/MatrixTransforms/MatrixTransforms.html

How to get visual corner (eg. topLeft) of rotated displayObject in actionscript 3?

When rotating a display object (around its center) the visual corner of the element moves (the actual x and y of the "box" remains the same). For example with 45 degrees of rotation the x coordinate will have increased and the y coordinate will have decreased as the top left corner is now at the top center of the "box".
I've tried to use displayObject.getBounds(coordinateSpace).topLeft however this method is simply returning the x and y of the box and thus doesn't change after an object has been rotated.
So, how do you get the x and y of a visual corner of a rotated display object?
Update: this is what I mean with the position of a visual corner after rotation -->
alt text http://feedpostal.com/cornerExample.gif
You simply need to translate the point to its parent's coordinate space.
var box:Shape = new Shape();
box.graphics.beginFill(0xff0099);
box.graphics.drawRect(-50, -50, 100, 100); // ... the center of the rectangle being at the middle of the Shape
addChild(box);
box.x = 100; // note: should be 100 + box.width * .5 in case you want to use the topleft corner to position
box.y = 100;
box.rotation = 45;
// traces the result (Point)
trace( box.parent.globalToLocal(box.localToGlobal(box.getBounds(box).topLeft)) );