I am experimenting with animation in <canvas>
and can't work out how to draw an image at an angle. The desired effect is a few images drawn as usual, with one image rotating slowly. (This image is not at the centre of the screen, if that makes any difference).
HTML5 canvas drawImage with at an angle
Asked Answered
You need to modify the transformation matrix before drawing the image that you want rotated.
Assume image points to an HTMLImageElement object.
var x = canvas.width / 2;
var y = canvas.height / 2;
var width = image.width;
var height = image.height;
context.translate(x, y);
context.rotate(angleInRadians);
context.drawImage(image, -width / 2, -height / 2, width, height);
context.rotate(-angleInRadians);
context.translate(-x, -y);
The x, y coordinates is the center of the image on the canvas.
While using save()/restore() may seem cleaner, it would be much more inefficient. The engine will have to store/restore all the context properties, not just the ones we're manipulating here. That matters in a sensitive code path. –
Chirpy
For another example, see this (duplicate) question and answer: Dial of a Gauge using HTML5 Canvas - Rotate About Arbitrary Point. –
Sapienza
@JakubWieczorek No, the engine could easily keep "dirty" flags on each context property and lazily save (and then restore) only the ones that get touched between the
save()
and the restore()
. Hand-tuning this is premature optimization. –
Pinette what are the performance implication for doing this for a game, ie every game object is animated and is an image? –
Philae
-1 Using
translate()
and rotate()
to restore the transformation matrix is very bad advice. This will not reliably restore the matrix to its original value due to rounding of floating-point operations. –
Quinonoid @JakubWieczorek Do you have measured performance of these two approaches? I'd be surprised if two method calls (where
rotate()
needs to call two trigonometric functions) is faster than copying the whole transformation matrix (48 bytes, if implemented as 6 64-bit-floating point numbers). –
Quinonoid It is interesting that the first solution worked for so many people, it didn't give the result I needed. In the end I had to do this:
ctx.save();
ctx.translate(positionX, positionY);
ctx.rotate(angle);
ctx.translate(-x,-y);
ctx.drawImage(image,0,0);
ctx.restore();
where (positionX, positionY)
is the coordinates on the canvas that I want the image to be located at and (x, y)
is the point on the image where I want the image to rotate.
I had to do
ctx.translate(positionX + x, positionY + y);
, then rotate, and then translate by ctx.translate(-x,-y);
–
Woodworker I have written a function (based on Jakub's answer) that allows user to paint an image in a X,Y position based on a custom rotation in a custom rotation point:
function rotateAndPaintImage ( context, image, angleInRad , positionX, positionY, axisX, axisY ) {
context.translate( positionX, positionY );
context.rotate( angleInRad );
context.drawImage( image, -axisX, -axisY );
context.rotate( -angleInRad );
context.translate( -positionX, -positionY );
}
Then you can call it like this:
var TO_RADIANS = Math.PI/180;
ctx = document.getElementById("canvasDiv").getContext("2d");
var imgSprite = new Image();
imgSprite.src = "img/sprite.png";
// rotate 45º image "imgSprite", based on its rotation axis located at x=20,y=30 and draw it on context "ctx" of the canvas on coordinates x=200,y=100
rotateAndPaintImage ( ctx, imgSprite, 45*TO_RADIANS, 200, 100, 20, 30 );
-1 Using
translate()
and rotate()
to restore the transformation matrix is very bad advice. This will not reliably restore the matrix to its original value due to rounding of floating-point operations. –
Quinonoid © 2022 - 2024 — McMap. All rights reserved.
context.save()
andcontext.restore()
before and after you do the transformations and drawing. – Facial