20

I am making an HTML5 canvas game, and I wish to rotate one of the images.

var link = new Image();
link.src='img/link.png';
link.onload=function(){
    ctx.drawImage(link,x,y,20,20); // draws a chain link or dagger
}

I wish to rotate this image. The standard way of rotating image was to set a rotation on the canvas context object. However, that rotates the entire game! I don't want to do that, and only wish to rotate this one sprite. How do I do that?

pimvdb
  • 151,816
  • 78
  • 307
  • 352
Razor Storm
  • 12,167
  • 20
  • 88
  • 148

6 Answers6

11

Use .save() and .restore() (more information):

link.onload=function(){
    ctx.save(); // save current state
    ctx.rotate(Math.PI); // rotate
    ctx.drawImage(link,x,y,20,20); // draws a chain link or dagger
    ctx.restore(); // restore original states (no rotation etc)
}
pimvdb
  • 151,816
  • 78
  • 307
  • 352
  • Cool thanks! Do you have an idea about the maths for redoing the dimensions of the image when I am doing arbitrary angle rotation? – Razor Storm Sep 21 '11 at 08:29
  • @Razor Storm: You'd need to translate the midpoint and translate it back, but you managed to do so already as you posted. – pimvdb Sep 21 '11 at 11:06
  • 1
    I believe it is better performing to do an inverse rotation after drawImage, instead of saving and restoring. – wallabra Jul 11 '18 at 13:28
  • @Gustavo6046 thats what everybody else is saying everywhere else that ive looked so far – oldboy Dec 16 '20 at 05:34
  • 2
    is there any way to rotate the `drawImage` element without rotating the `canvas` itself? – oldboy Dec 16 '20 at 05:34
  • @oldboy I think it's easier to rotate the canvas and then unrotate right after you're done with your stuff? Maybe extract this functionality into a function to be extra sure issues don't arise from this. Even a decorator if you care so much, like f(g()) { return function new_g() { do_stuff_before(); g(); do_stuff_after(); } } – wallabra Dec 16 '20 at 10:34
6

You might want to put a translate(); there because the image is going to rotate around the origin and that is in the top left corner by default so you use the translate(); to change the origin.

link.onload=function(){
    ctx.save();
    ctx.translate(x, y); // change origin
    ctx.rotate(Math.PI);
    ctx.drawImage(link,-10,-10,10,10);
    ctx.restore()
}
JJJ
  • 32,902
  • 20
  • 89
  • 102
Carb0n1c
  • 192
  • 2
  • 7
  • Hmm, the positions are still messed up – Razor Storm Sep 21 '11 at 08:52
  • 1
    ok so u have a position u want to draw a image which will be (x, y). so what u want to do is to translate the origin (0, 0) to the position (x, y) so that position is now (0, 0) u and draw ur image right on top of the origin. so if ur image is 20px high and wide ctx.drawImage(link,-10,-10,10,10); and u will be able to rotate the image around its center. – Carb0n1c Sep 21 '11 at 09:04
5

Your original "solution" was:

ctx.save();
ctx.translate(x,y);
ctx.rotate(-this.angle + Math.PI/2.0);
ctx.translate(-x, -y); 
ctx.drawImage(this.daggerImage,x,y,20,20);
ctx.restore();

However, it can be made more efficient (with no save or restore) by using this code:

ctx.translate(x,y);
ctx.rotate(-this.angle + Math.PI/2.0);
ctx.drawImage(this.daggerImage,x,y,20,20);
ctx.rotate(this.angle - Math.PI/2.0);
ctx.translate(-x, -y); 
tckmn
  • 57,719
  • 27
  • 114
  • 156
5

Look at my solution. It's full example and the easiest to understand.

    var drawRotate = (clockwise) => {
        const degrees = clockwise == true? 90: -90;
        let canvas = $('<canvas />')[0];
        let img = $(".img-view")[0];

        const iw = img.naturalWidth;
        const ih = img.naturalHeight;

        canvas.width = ih;
        canvas.height = iw;

        let ctx = canvas.getContext('2d');

        if(clockwise){
            ctx.translate(ih, 0);
        } else {
            ctx.translate(0, iw);
        }

        ctx.rotate(degrees*Math.PI/180);
        ctx.drawImage(img, 0, 0);

        let rotated = canvas.toDataURL();        
        img.src = rotated;
    }
3

Here i made a working example from one of my games. u can get the image from Here.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=utf-8 />
<title>Test</title>
</head>
<body>
<canvas id="canvas" width="100" height="100"></canvas>
<script type="text/javascript">

var ctx = document.getElementById('canvas').getContext('2d');
var play = setInterval('Rotate()',16);
var i = 0;
var shipImg = new Image();
shipImg.src = 'ship.png';

function Rotate() {
  ctx.fillStyle = '#000';
  ctx.fillRect(0,0,100,100);

  ctx.save();
  ctx.translate(50, 50);
  ctx.rotate(i / 180 / Math.PI);
  ctx.drawImage(shipImg, -16, -16);
  ctx.restore();
  i += 10;
};

</script>
</body>
</html>
Carb0n1c
  • 192
  • 2
  • 7
1

I ended up having to do:

ctx.save();
ctx.translate(x,y);
ctx.rotate(-this.angle + Math.PI/2.0);
ctx.translate(-x, -y); 
ctx.drawImage(this.daggerImage,x,y,20,20);
ctx.restore();
Razor Storm
  • 12,167
  • 20
  • 88
  • 148
  • you just need the x and y value in the first translate and you can remove the second translate and stop putting the x and y values in the `drawImage();` you are just doubling the values, instead `drawImage(this.daggerImage,-10,-10,10,10);`. There is more info on https://developer.mozilla.org/en/Canvas_tutorial/Transformations about this stuff – Carb0n1c Sep 23 '11 at 08:48