Saturday, 21 May 2011

Rotate Image on Canvas

This is where it gets a little bit tricky. Once it's figured out, it's easy from then on in. However, figuring it out in the first place can be a bit of a pain.

It's not as simple as just "rotate image". There's a lot more to it. It's more like rotating the canvas, then setting the image's orientation, then resetting the canvas.
I'll show some example code, then explain that in more detail.

Let's just go with a small 100x100 canvas.
<canvas id="canvas" width="100" height="100" style="background-color: #A9A9A9;">
Browser not compatible with HTML5 canvas
</canvas>

Now here's the javascript:

const FPS = 30;
var playerPosX = 0;
var playerPosY = 0;
var playerRot = 0;
var onGround = 0;
var playerImg = new Image();
playerImg.src = "http://i.imgur.com/nEYjaIe.png";

var canvas = null;
var context2D = null;
var keys = new Array();

window.onload = init;

window.addEventListener('keydown',keyDown,true);
window.addEventListener('keyup',keyUp,true);
function keyDown(evt){
 keys[evt.keyCode] = true;
}
function keyUp(evt){
 keys[evt.keyCode] = false;
}

function init()
{
 canvas = document.getElementById('canvas');
 context2D = canvas.getContext('2d');
 setInterval(draw, 1000 / FPS);
}

function draw()
{
 // detect user input
 if ((37 in keys && keys[37]) || (65 in keys && keys[65])){ //left
  playerRot -= 3;
 }
 if ((39 in keys && keys[39]) || (68 in keys && keys[68])){ //right
  playerRot += 3;
 }
 
 
 context2D.clearRect(0, 0, canvas.width, canvas.height);

  context2D.save();
 context2D.translate(playerPosX+(playerImg.width/2), playerPosY+(playerImg.height/2));
 context2D.rotate(playerRot * Math.PI / 180);
 context2D.drawImage(playerImg, 0, 0, playerImg.width, playerImg.height, -(playerImg.width/2), -(playerImg.height/2), playerImg.width, playerImg.height);
 context2D.restore();
}


Result: Arrow keys or A and D to spin left and right.
Browser not compatible with HTML5 canvas


So, the main bit is this:
context2D.save();
 context2D.translate(playerPosX+(playerImg.width/2), playerPosY+(playerImg.height/2));
 context2D.rotate(playerRot * Math.PI / 180);
 context2D.drawImage(playerImg, 0, 0, playerImg.width, playerImg.height, -(playerImg.width/2), -(playerImg.height/2), playerImg.width, playerImg.height);
 context2D.restore();

- So first it saves the canvas the way it is.
- Then the translate line makes the "start" of the image at the origin you want it to spin (in this case, the center).
- It then rotates the actual canvas the amount you want the player to rotate.
- Then the image is drawn, but because the start of the image has changed place, it needs to start drawing at a different place; which is half of the width of the image to the left, and half of the height of the image to the top.
- It then brings the canvas back to where it was before, leaving the drawn image still showing as rotated.
It took me a while to figure that out at first. I was never able to find a good explanation of how it works. I just hope this helps you readers better.
Although to be honest, it isn't incredibly important that you know how it works. Just that you know how to use it.


EDIT:
This is a function that draws an image, it takes rotation into account. Use it as you wish.
function drawImg(img, pX, pY, oX, oY, w, h, rot) {
 context2D.save();
  context2D.translate(pX+oX, pY+oY);
  context2D.rotate(rot * Math.PI / 180);
  context2D.drawImage(img, 0, 0, w, h, -(oX), -(oY), w, h);
 context2D.restore();
}

Note:
img: the image object
pX: the x position of the image
pY: the y position of the image
oX: how far across the image that the origin is
oY: how far down the image that the origin is
w: width of the image
h: height of the image
rot: angle of rotation