HTML 5 Canvas - Rotate, Scale, Translate and drawImage - javascript

I'm trying to place an html5 image inside canvas. The image is translated, rotated and scaled using the css transform property. The main problem is how to copy the properties of the image and apply them on the canvas.
You can try the jsfiddle link below: the image can be scaled, moved and rotated and whatever appears in the greenbox should be the same as the one in the red.
https://jsfiddle.net/q7y1py5f/3/. Here I can move and scale the image, but when the rotation kicks in nothing works. Here is the code that bugs me (58 line in the javascript jsfiddle) :
context.save();
context.translate(image.width / 2, image.height / 2);
context.rotate(angle * Math.PI/180);
context.translate(((-image.width) / 2), ((-image.height) / 2));
context.translate(-x, -y);
context.scale(scale, scale);
context.drawImage(image, 0, 0);
context.restore();
Here the x and y are the difference between the top left of the green container and the top left of the image. Any help would be appreciated.
I've managed to get each property to work individually, but when combining them all together it doesn't work. When I only translate and scale the image and apply to the canvas context everything is fine, but when I add the rotation with the image is not where it's supposed to be.
I tried the following - calculated the top-left of the element with the angle to get the right value and then translating and rotating with it.

In function canvasCropping you write
var left = cropper.offset().left - img.offset().left,
top = cropper.offset().top - img.offset().top,
but rotating and scaling will change element offset, so you got wrong coordinats for translation. Change it to:
var left = -(cropper.offset().left - transform.translate.x-initialOffset.left),
top = -(cropper.offset().top -transform.translate.y-initialOffset.top),
initialOffset - initial offset of your image.
And some changes in drawRotatedImage
`
var drawRotatedImage = function (context, image, x, y, angle, width, height, scale) {
context.save();
// Move registration point to the center of the canvas
context.translate(x+image.width/2,y+image.height/2);
context.rotate(angle* Math.PI / 180);// angle must be in radians
context.scale(scale, scale);
// Move registration point back to the top left corner of canvas
context.translate((-image.width)/2, (-image.height)/2);
context.drawImage(image, 0, 0);
context.restore();
};
`

Related

Zooming on a HTML <canvas>

I'm creating a webpage that draws a trail along a path as the page is scrolled, using canvas to draw the trail and SVGs to determine the path coordinates. (Similar to this demo - https://tympanus.net/Development/StorytellingMap/)
I have a hidden canvas set up as a 'room' which contains the full map background, then draws from this to the main canvas (using a 'camera' to get the position). I've managed to get the background to zoom using drawImage to scale the selected area as follows:
ctx.drawImage(
image,
camera.position.x + ((canvas.width - (canvas.width / zoom)) / 2),
camera.position.y + ((canvas.height - (canvas.height / zoom)) / 2),
canvas.width / zoom,
canvas.height / zoom,
0,
0,
canvas.width,
canvas.height
);
The line is drawn by looping through an array of points like this:
ctx.beginPath();
for (var i = 0; i < points.length; i++) {
ctx.lineTo(
points[i].x - camera.position.x,
points[i].y - camera.position.y
);
}
ctx.stroke();
However I can't adapt the line to account for zoom - I can multiply all the coordinates by the zoom factor and the size appears correct, but I lose the position set by the camera, so it's not aligned to the background. Can anyone tell me how I can account for this?
I hope I've explained this well enough, I can try to upload the whole thing if anyone needs to see it in context!
Please let me know any questions, I'd really appreciate any advice or guidance.
Edit:
Link: http://staging.clicky.co.uk/map-scroll/ (Scroll to see the map update!)

Why is my canvas creating ellipses when I want to create circles?

Everytime I try to make a circle by this method (where board is the canvas board which has the context of 2d):
board.lineWidth = 4;
board.beginPath();
board.arc(canvas.width/2, canvas.height/2, 10,0,360);
board.strokeStyle = "blue";
board.stroke();
board.closePath();
It always created more of a ellipse than a circle and it never go by the coordinates I give it. If I say canvas.width/2 and with height (to center it) it even goes outside the canvas itself. When I give it something else like 30 it goes much further than 30 pixels aswell.
Sidenote: My width and height of my canvas are both 500 pixels.
5th parameter of arc expected to be an angle in radians, not degrees.
360 degrees = 2 * PI
So you have to change your code to:
...
board.arc(canvas.width/2, canvas.height/2, 10, 0, Math.PI * 2);

Rotate image in the correct coordinates (x,y) in a canvas

The question is related with that other question: https://stackoverflow.com/questions/26948677/translate-coordinates-to-its-equivalent-in-scale1 that no one gave response but it does not matter anymore because it's solved, at least that part. I get the correct x and y values.
I put you in situation, the app I'm developing allows the user to create a image with 6 differents backgrounds with funny characters without face so that the user can put a photo of his/her face. To do this, they can drag, pinch to zoom and rotate the image. Drag and pinch to zoom are under control but when the image is rotated I can not match the image in the exact coordinates. The images always go up a little.
An example, I get as values that:
x: 30,
y: 80,
scale: 0.5,
deg: 40.
So, when I draw into a canvas the rotated image the y seems to go up a little depending on the degrees. Less degrees, less go up.
I'm using this function to draw the image rotated:
var TO_RADIANS = Math.PI / 180;
var drawRotatedImage = function (context, image, x, y, angle) {
context.save();
context.translate(x, y);
context.rotate(angle * TO_RADIANS);
context.drawImage(image, -(image.width / 2), -(image.height / 2));
context.restore();
}
PD: To point it out, if I do not rotate the image, it matches perfectly in the correct position.
How I can get the correct x and y values for the rotated image?

Canvas roulette wheel - using background images as each slice

I have a roulette wheel created with HTML5 canvas and currently each slice is a plain color generated by using the fillStyle(), beginPath(), stroke(), then fill() methods.
I would like to use images that that crop appropriately in the shape of the slice, but I'm not sure how to implement this using drawImage().
Here's the jsfiddle
http://jsfiddle.net/pyD2q/2/
Any help or resources is appreciated.
Instead of filling the color in you can use it as a clipping mask instead. For each arc, set clip, draw the image and repeat.
Something like this (untested):
for (i = 0; i < s.members.length; i++) {
angle = s.startAngle + i * s.arc;
ctx.beginPath();
ctx.arc(s.width / 2, s.height / 2, s.outsideRadius, angle, angle + s.arc, false);
ctx.arc(s.width / 2, s.height / 2, s.insideRadius, angle + s.arc, angle, true);
ctx.save(); /// store current clip-state
ctx.clip(); /// set current arc as clipping mask
ctx.drawImage(someImageArray[i], x, y, width, height);
ctx.restore(); /// restore clip-state
ctx.stroke(); /// stroke arc
}
You will of course also need to place the images correctly relative to the section and if you want to rotate the image you would need to rotate it at this point.
In that regard I would recommend creating this wheel on an off-screen canvas. This way you only need to draw the off-screen canvas into the main canvas rotated and what have you (saves you the trouble of calculating each angle as well and the performance will be better).

Rotate Moving Object on Canvas

I'm trying to draw an object based on it's rotate property, but it seems to get confused when the object is moving.
ctx.save();
ctx.translate(this.width * 0.5, this.height*0.5);
ctx.rotate(DegToRad(45));
ctx.translate(-this.width*0.5,-this.height*0.5);
ctx.drawImage(this.img, this.spriteOffset, 0, this.width, this.height, this.x, this.y,this.width,this.height);
ctx.restore();
The image is drawn rotated 45 degrees, however it now moves in a down-left direction, when the object should only be moving downwards. The movement code is simply handled by incrementing the this.y position. Is there a simpler way to accomplish this?
This is because the whole canvas is being rotated.
What you could try is looking into a framework like Kinetic JS, which creates a sort of SVG API for the canvas.
This site has tons of information on how to use it as well, including rotation, transition, transformation, and pretty much anything else that you might need in working with it.
This should suit your needs rather well.
I think I should provide a low-level, framework-free option as well. Basically, using raw javascript and HTML to pull this off.
Now, as I understood your question, you are trying to make an object (let's assume a black square) move downwards AND have it spin. The only way I can think of to spin it in the canvas without going into path hell is to rotate the entire rendering context. BUT you can also import an image into canvas, for instance, a transparent black diamond (i.e. that same square rotated). So you'd use a separate canvas to render each step of the rotation for the square.
Essentially something like this:
var canvas2 = document.createElement('canvas'), ctx2 = canvas2.getContext('2d');
//do something with the second canvas
//let's assume the second canvas is the same size as the square
ctx.drawImage(canvas2, squareX, squareY);
See my attempt
As you can see, it is a bit wonky, but it does do essentially what the question asks; it moves the square down, and rotates it. It also plots the result of that rotation below the actual canvas so you can see what's happening under the hood, but the square cuts out due to the "center" being on the top left of the square, and not in the middle.
In the end, it really comes down to how you want to do it.
I was playing around with the API and found it was easier to just keep track of where the object should be. here's an example of a square moving diagonally across the screen and rotating.
<canvas id="canvas" style="width: 100%; height: 100%;">
</canvas>
<script>
var DELAY = 15; // ms
var RECT_WIDTH = 100; // px
var RECT_HEIGHT = 100; // px
var canvas = document.getElementById('canvas');
// set intrinsic dimensions
canvas.width = 1000;
canvas.height = 1000;
var ctx = canvas.getContext('2d')
ctx.fillStyle = 'teal';
var step = 0
var vx = 2
var animate = setInterval(function () {
ctx.resetTransform()
ctx.clearRect(0, 0, 1000, 1000);
ctx.translate(vx * step, vx * step);
// rotation in place, translate to center of square and back
ctx.translate(RECT_WIDTH / 2, RECT_HEIGHT / 2);
ctx.rotate((Math.PI / 180) * step);
ctx.translate(-(RECT_WIDTH / 2), -(RECT_HEIGHT / 2));
// Draw the rectangle
ctx.fillRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
step = step + 1
}, DELAY)
setTimeout(function () {
clearInterval(animate);
}, 5000);
</script>
Here, I use vx and keep track of the steps to translate, and calculate what the rotation will be in radians based on the steps a new not caring what the previous state was. Make sure you rotate across the center of where you're square will be as well.
Use ctx.translate to set the object's position before applying the rotation. This should fix the problem.
ctx.save();
ctx.translate(this.x, this.y);
ctx.translate(this.width * 0.5, this.height*0.5);
ctx.rotate(DegToRad(45));
ctx.translate(-this.width*0.5,-this.height*0.5);
ctx.drawImage(this.img, this.spriteOffset, 0, this.width, this.height, 0, 0,this.width,this.height);
ctx.restore();

Categories

Resources