How to Rotate a Single Object on an HTML 5 Canvas

How do I rotate a single object on an html 5 canvas?

Unfortunately in the HTML5 canvas element you can't rotate individual elements.

Animation works like drawing in MS Paint: You draw something, make a screen.. use the eraser to remove some stuff, draw something differently, make a screen.. Draw something else on top, make a screen.. etc etc.

If you have an existing item on the canvas - you'll have to erase it ( use ctx.fillRect() or clearRect() for example ), and then draw the rotated object.

If you're not sure how to rotate it while drawing in the first place:

ctx.save();
ctx.rotate(0.17);
// draw your object
ctx.restore();

HTML 5 Canvas. How to rotate (keep rotating) only one object (of 1)?

To rotate only one image or rendering you need to restore the canvas transform state back to the default.

This function will render your object rotated scaled and translated but not affect any other rendering.

BTW you don't need to use beginPath is the render calls start with fill or stroke such as ctx.fillRect, ctx.strokeRect, ctx.fillText, ctx.strokeText and you only need to use ctx.closePath if you need to create a line from the last path point to the previous ctx.moveTo point or first pathpoint after a ctx.beginPath call

Nor do you need to use save and restore for all rendering. You only use it if you need to get the previous canvas state back.

ctx = canvas.getContext("2d");
function drawBox(col,size,x,y,scale,rot){ ctx.fillStyle = col; // use setTransform as it overwrites the canvas transform rather than multiply it as the other transform functions do ctx.setTransform(scale, 0, 0, scale, x, y); ctx.rotate(rot); ctx.fillRect(-size / 2,-size / 2, size, size); }

function update(time){ ctx.clearRect(0,0,512,256) drawBox("black",100,125,125,1,0); // draw none rotated box drawBox("maroon",50,225,125,1,time / 500); // draw rotating box drawBox("green",25,275,100,1,-time / 250); // draw rotating box drawBox("green",25,275,150,1,-time / 250); // draw rotating box // after using transforms you need to reset the transform to the default // if you plan to render anything in the default coordinate system ctx.setTransform(1, 0 ,0 , 1, 0, 0); // reset to default transform
requestAnimationFrame(update);}
requestAnimationFrame(update);
<canvas id="canvas" width="512" height="256"></canvas>

Rotate individual objects in canvas?

Use local space

Instead of drawing object at the position you want them draw everything around its own origin in its local space. The origin is at (0,0) and is the location that the object rotates around.

So if you have a rectangle that you draw with

function drawRect(){
context.fillRect(200, 40, 100, 100);
}

change it so that it is drawn at its origin

function drawRect(){
context.fillRect(-50,-50 , 100, 100);
}

Now you can easily draw it wherevery you want

Start with the setTransform function as that clears any existing tranforms and is a convenient way to set the location of the center of the object will be

ctx.setTransform(1,0,0,1,posX,posY); // clear transform and set center location 

if you want to rotate it then add the rotation

ctx.rotate(ang);

and scale with

ctx.scale(scale,scale);

if you have two different scales you should scale before the rotate.

Now just call the draw function

drawRect();

and it is drawn with its center at posX,posY rotated and scaled.

You can combine it all into a function that has the x,y position, the width and the height, scale and rotation. You can include the scale in the setTransform

function drawRect(x,y,w,h,scale,rotation){
ctx.setTransform(scale,0,0,scale,x,y);
ctx.rotate(rotation);
ctx.strokeRect(-w/2,-h/2,w,h);
}

It also applies to an image as a sprite, and I will include a alpha

function drawImage(img,x,y,w,h,scale,rotation,alpha){
ctx.globalAlpha = alpha;
ctx.setTransform(scale,0,0,scale,x,y);
ctx.rotate(rotation);
ctx.drawImage(img,-img.width/2,-img.height/2,img.width,img.height);
}

On a 6 year old laptop that can draw 2000 sprites on firefox every 1/60th of a second, each rotated, scaled, positioned, and with a alpha fade.

No need to mess about with translating back and forward. Just keep all the objects you draw around there own origins and move that origin via the transform.

Update
Lost the demo so here it is to show how to do it in practice.

Just draws a lot of rotated, scaled translated, alphaed rectangles.

By using setTransform you save a lot of time by avoiding save and restore

// create canvas and add resize var canvas,ctx;function createCanvas(){    canvas = document.createElement("canvas");     canvas.style.position = "absolute";    canvas.style.left     = "0px";    canvas.style.top      = "0px";    canvas.style.zIndex   = 1000;    document.body.appendChild(canvas); }function resizeCanvas(){    if(canvas === undefined){        createCanvas();    }    canvas.width          = window.innerWidth;    canvas.height         = window.innerHeight;     ctx            = canvas.getContext("2d"); }resizeCanvas();window.addEventListener("resize",resizeCanvas);

// simple function to draw a rectanglevar drawRect = function(x,y,w,h,scale,rot,alpha,col){ ctx.setTransform(scale,0,0,scale,x,y); ctx.rotate(rot); ctx.globalAlpha = alpha; ctx.strokeStyle = col; ctx.strokeRect(-w/2,-h/2, w, h);
}
// create some rectangles in unit scale so that they can be scaled to fit// what ever screen size this is invar rects = [];for(var i = 0; i < 200; i ++){ rects[i] = { x : Math.random(), y : Math.random(), w : Math.random() * 0.1, h : Math.random() * 0.1, scale : 1, rotate : 0, dr : (Math.random() - 0.5)*0.1, // rotation rate ds : Math.random()*0.01, // scale vary rate da : Math.random()*0.01, // alpha vary rate col : "hsl("+Math.floor(Math.random()*360)+",100%,50%)", };}
// draw everything once a framefunction update(time){ var w,h; w = canvas.width; // get canvas size incase there has been a resize h = canvas.height; ctx.setTransform(1,0,0,1,0,0); // reset transform ctx.clearRect(0,0,w,h); // clear the canvas // update and draw each rect for(var i = 0; i < rects.length; i ++){ var rec = rects[i]; rec.rotate += rec.dr; drawRect(rec.x * w, rec.y * h, rec.w * w,rec.h * h,rec.scale + Math.sin(time * rec.ds) * 0.4,rec.rotate,Math.sin(time * rec.da) *0.5 + 0.5,rec.col); } requestAnimationFrame(update); // do it all again}requestAnimationFrame(update);

Html5 Canvas rotate single element

Assuming that you want to give the illusion of the ball continuing to rotate, you should increase the rotation angle for each frame drawn.

As written, your code will give the ball a fixed rotation of 0.17 radians on each frame.

var frame = 0;

function drawPlayer() {
ctx.save();
ctx.rotate(0.17 * frame);
...
ctx.restore();
}

function draw() {
++frame;
drawPlayer();
...
}

Rotating Objects in HTML5 Canvas

You had some errors. This is why it didn't work. You had a few undeclared variables. Also: I would do the mouse detection differently.

I've putted the rotation in the Circle's draw method. Since the method is called Circle I've clipped your images to a circle. If you don't want this please remove the arc and the clip part.

var canvas = document.querySelector('canvas');
var innerWidth = canvas.width = window.innerWidth;var innerHeight = canvas.height = window.innerHeight;
var c = canvas.getContext('2d');
var radius=30;//var rotation = Math.PI/180;
img = new Image();var randomNumber = getRandomInt(2);var imgArray = ["https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/puppyBeagle300.jpg", "https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/darwin300.jpg"];img.src = imgArray[randomNumber];

function getRandomInt(max) { return Math.floor(Math.random() * Math.floor(max));}

var mouse = { x: undefined, y: undefined}
window.addEventListener('mousemove',function(event) {mouse.x = event.x;mouse.y = event.y;})
function Circle(x, y, dx, dy, radius) { this.x = x; this.y = y; this.dx = dx; this.dy = dy; this.radius = radius; this.rotation = Math.PI*Math.random();
this.draw = function() { c.save(); c.translate(this.x,this.y); c.rotate(this.rotation); c.beginPath(); c.arc(0,0,this.radius,0,2*Math.PI); c.clip(); c.drawImage(img, -this.radius, -this.radius, this.radius*2, this.radius*2); c.restore(); }
this.update = function() {
if (this.x + this.radius > innerWidth || this.x - this.radius < 0) { this.dx = -this.dx; }
if (this.y + this.radius > innerHeight || this.y - this.radius < 0) { this.dy = -this.dy; } this.x += this.dx; this.y += this.dy;
//interactivity if (mouse.x - this.x < 100 && mouse.x - this.x > -100 && mouse.y - this.y < 100 && mouse.y - this.y > -100 ) { this.dx = -dx * 2; this.dy = -dy * 2; } this.draw(); }
}

var circleArray = [];
for (var i = 0; i < 100; i++) { var x = Math.random() * (innerWidth - radius * 2) + radius; var y = Math.random() * (innerHeight - radius * 2) + radius; var dx = (Math.random() - 0.5) * 1; var dy = (Math.random() - 0.5) * 1;

circleArray.push(new Circle(x, y, dx, dy, radius));

}

function animate() { requestAnimationFrame(animate); c.clearRect(0, 0, innerWidth, innerHeight); for (var i = 0; i < circleArray.length; i++){ circleArray[i].update(); }
}
animate();
<div id="container"><canvas></canvas></div>

How can I rotate any shape or point on an HTML5 canvas around an arbitrary point?

It turns out the answer is pretty simple but involves a bit of math that may put some folks off. I'm using the Konvajs HTML5 canvas library but the code is easily transportable to your own lib. Also, this example is described as rotating a shape, but it's really rotating a point - the origin of the shape - so you could use it for any point-rotation-around-a-point case.

The rotateAroundPoint() function does the work - the rest of the code in the snippet is there to make it a working example.

Lifting this function out we can see that the inputs are the shape - although this could be any object with x, y and rotation properties, the rotation angle in degrees, and the rotation point - again an object with x & y values.

When we rotate around the point we are carrying out the equivalent of a rotation-in-place, followed by a translation (or move). These must be done in this sequence. Also, because of how 2d-drawing works, we have to work out the new position for the move and this depends on the drawing origin of the shape.

The calculation of the new x & y positions requires the use of sine & cosine functions which require radians - not degrees. So we multiply degrees by PI / 180 to get that.

// Rotate a shape around any point.
// shape is a Konva shape
// angleDegrees is the angle to rotate by, in degrees
// point is an object {x: posX, y: posY}
function rotateAroundPoint(shape, angleDegrees, point) {
let angleRadians = angleDegrees * Math.PI / 180; // sin + cos require radians

const x =
point.x +
(shape.x() - point.x) * Math.cos(angleRadians) -
(shape.y() - point.y) * Math.sin(angleRadians);
const y =
point.y +
(shape.x() - point.x) * Math.sin(angleRadians) +
(shape.y() - point.y) * Math.cos(angleRadians);

shape.rotation(shape.rotation() + angleDegrees); // rotate the shape in place
shape.x(x); // move the rotated shape in relation to the rotation point.
shape.y(y);

}

That's it! Have a play with the snippet - best viewed full-screen. Select a shape to rotate, then click the rotate button a few times to watch it spin around its origin (the natural point of rotation if we just change the rotation angle and nothing else). Then click the reset button, and click the canvas to move the blue target somewhere else on the canvas or shape, and rotate some more to see the effect.

There's also a codepen version here.

// Code to illustrate rotation of a shape around any given point. The important functions here is rotateAroundPoint() which does the rotation and movement math ! 

let
angle = 0, // display value of angle
startPos = {x: 80, y: 45},
shapes = [], // array of shape ghosts / tails
rotateBy = 20, // per-step angle of rotation
shapeName = $('#shapeName').val(), // what shape are we drawing
shape = null,
ghostLimit = 10,

// Set up a stage
stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
}),


// add a layer to draw on
layer = new Konva.Layer(),

// create the rotation target point cross-hair marker
lineV = new Konva.Line({points: [0, -20, 0, 20], stroke: 'cyan', strokeWidth: 1}),
lineH = new Konva.Line({points: [-20, 0, 20, 0], stroke: 'cyan', strokeWidth: 1}),
circle = new Konva.Circle({x: 0, y: 0, radius: 10, fill: 'transparent', stroke: 'cyan', strokeWidth: 1}),
cross = new Konva.Group({draggable: true, x: startPos.x, y: startPos.y});

// Add the elements to the cross-hair group
cross.add(lineV, lineH, circle);
layer.add(cross);

// Add the layer to the stage
stage.add(layer);

$('#shapeName').on('change', function(){
shapeName = $('#shapeName').val();
shape.destroy();
shape = null;
reset();
})

// Draw whatever shape the user selected
function drawShape(){

// Add a shape to rotate
if (shape !== null){
shape.destroy();
}

switch (shapeName){
case "rectangle":
shape = new Konva.Rect({x: startPos.x, y: startPos.y, width: 120, height: 80, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;

case "hexagon":
shape = new Konva.RegularPolygon({x: startPos.x, y: startPos.y, sides: 6, radius: 40, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;

case "ellipse":
shape = new Konva.Ellipse({x: startPos.x, y: startPos.y, radiusX: 40, radiusY: 20, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;

case "circle":
shape = new Konva.Ellipse({x: startPos.x, y: startPos.y, radiusX: 40, radiusY: 40, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;

case "star":
shape = new Konva.Star({x: startPos.x, y: startPos.y, numPoints: 5, innerRadius: 20, outerRadius: 40, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
};
layer.add(shape);

cross.moveToTop();

}

// Reset the shape position etc.
function reset(){

drawShape(); // draw the current shape

// Set to starting position, etc.
shape.position(startPos)
cross.position(startPos);
angle = 0;
$('#angle').html(angle);
$('#position').html('(' + shape.x() + ', ' + shape.y() + ')');

clearTails(); // clear the tail shapes

stage.draw(); // refresh / draw the stage.
}

// Click the stage to move the rotation point
stage.on('click', function (e) {
cross.position(stage.getPointerPosition());
stage.draw();
});

// Rotate a shape around any point.
// shape is a Konva shape
// angleRadians is the angle to rotate by, in radians
// point is an object {x: posX, y: posY}
function rotateAroundPoint(shape, angleDegrees, point) {
let angleRadians = angleDegrees * Math.PI / 180; // sin + cos require radians

const x =
point.x +
(shape.x() - point.x) * Math.cos(angleRadians) -
(shape.y() - point.y) * Math.sin(angleRadians);
const y =
point.y +
(shape.x() - point.x) * Math.sin(angleRadians) +
(shape.y() - point.y) * Math.cos(angleRadians);

shape.rotation(shape.rotation() + angleDegrees); // rotate the shape in place
shape.x(x); // move the rotated shape in relation to the rotation point.
shape.y(y);

shape.moveToTop(); //
}

$('#rotate').on('click', function(){

let newShape = shape.clone();
shapes.push(newShape);
layer.add(newShape);

// This ghost / tails stuff is just for fun.
if (shapes.length >= ghostLimit){
shapes[0].destroy();
shapes = shapes.slice(1);
}
for (var i = shapes.length - 1; i >= 0; i--){
shapes[i].opacity((i + 1) * (1/(shapes.length + 2)))
};

// This is the important call ! Cross is the rotation point as illustrated by crosshairs.
rotateAroundPoint(shape, rotateBy, {x: cross.x(), y: cross.y()});

cross.moveToTop();

stage.draw();

angle = angle + 10;
$('#angle').html(angle);
$('#position').html('(' + Math.round(shape.x() * 10) / 10 + ', ' + Math.round(shape.y() * 10) / 10 + ')');
})

// Function to clear the ghost / tail shapes
function clearTails(){

for (var i = shapes.length - 1; i >= 0; i--){
shapes[i].destroy();
};
shapes = [];

}

// User cicks the reset button.
$('#reset').on('click', function(){

reset();

})

// Force first draw!
reset();
body {
margin: 10;
padding: 10;
overflow: hidden;
background-color: #f0f0f0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/konva@^3/konva.min.js"></script>
<p>1. Click the rotate button to see what happens when rotating around shape origin.</p>
<p>2. Reset then click stage to move rotation point and click rotate button again - rinse & repeat</p>
<p>
<button id = 'rotate'>Rotate</button>
<button id = 'reset'>Reset</button>
<select id='shapeName'>
<option value='rectangle'>Rectangle</option>
<option value='hexagon'>Polygon</option>
<option value='ellipse' >Ellipse</option>
<option value='circle' >Circle</option>
<option value='star' selected='selected'>Star</option>

</select>
Angle : <span id='angle'>0</span>
Position : <span id='position'></span>
</p>
<div id="container"></div>


Related Topics



Leave a reply



Submit