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).
Asked
Active
Viewed 6.6k times
44

Matthew Crumley
- 101,441
- 24
- 103
- 129

Greg
- 21,235
- 17
- 84
- 107
3 Answers
90
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.

Jakub Wieczorek
- 1,849
- 13
- 10
-
9instead of rotating/translating at the end, you could do a `context.save()` and `context.restore()` before and after you do the transformations and drawing. – Matthew Crumley Sep 25 '10 at 15:53
-
41While 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. – Jakub Wieczorek Sep 25 '10 at 17:39
-
1For another example, see this (duplicate) question and answer: [Dial of a Gauge using HTML5 Canvas - Rotate About Arbitrary Point](http://stackoverflow.com/questions/4649836/dial-of-a-gauge-using-html5-canvas-rotate-about-arbitrary-point/4650102#4650102). – Phrogz Jan 10 '11 at 20:52
-
10@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. – s4y Nov 29 '15 at 01:36
-
1what are the performance implication for doing this for a game, ie every game object is animated and is an image? – Nikos Oct 27 '16 at 23:04
-
2-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. – Feuermurmel Aug 25 '21 at 16:04
-
3@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). – Feuermurmel Aug 25 '21 at 16:07
19
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.

unrealsoul007
- 3,809
- 1
- 17
- 33

PetrolHead
- 390
- 4
- 7
15
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 );

Yaume
- 198
- 1
- 6
-
2-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. – Feuermurmel Aug 25 '21 at 16:08