newguy newguy - 5 months ago 32
Javascript Question

How to rotate a Canvas object around its center following mouse move event?

Hi I want to rotate this shape around its center when I move my mouse, but currently it's rotating around (0, 0). How to change my code?

Source code (also see jsfiddle):

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

class Circle {
constructor(options) {
this.cx = options.x;
this.cy = options.y;
this.radius = options.radius;
this.color = options.color;
this.angle = 0;
this.toAngle = this.angle;

this.binding();
}

binding() {
const self = this;
window.addEventListener('mousemove', (e) => {
self.update(e.clientX, e.clientY);
});
}

update(nx, ny) {
this.toAngle = Math.atan2(ny - this.cy, nx - this.cx);
}

render() {
ctx.clearRect(0,0,canvas.width,canvas.height);

ctx.save();

ctx.beginPath();

ctx.lineWidth = 1;

if (this.toAngle !== this.angle) {
ctx.rotate(this.toAngle - this.angle);
}

ctx.strokeStyle = this.color;
ctx.arc(this.cx, this.cy, this.radius, 0, Math.PI * 2);

ctx.stroke();
ctx.closePath();

ctx.beginPath();

ctx.fillStyle = 'black';
ctx.fillRect(this.cx - this.radius / 4, this.cy - this.radius / 4, 20, 20);

ctx.closePath();
ctx.restore();
}
}

var rotatingCircle = new Circle({
x: 150,
y: 100,
radius: 40,
color: 'black'
});

function animate() {
rotatingCircle.render();
requestAnimationFrame(animate);
}

animate();

K3N K3N
Answer

You need to:

  • first translate to the point of rotation (pivot)
  • then rotate
  • then:
    • A: draw in at (0,0) using (-width/2, -height/2) as relative coordinate (for centered drawings)
    • B: translate back and use the object's absolute position and subtract relative coordinates for centered drawing

Modified code:

ctx.beginPath();
ctx.lineWidth = 1;

ctx.translate(this.cx, this.cy);               // translate to pivot

if (this.toAngle !== this.angle) {
  ctx.rotate(this.toAngle - this.angle);
}

ctx.strokeStyle = this.color;
ctx.arc(0, 0, this.radius, 0, Math.PI * 2);    // render at pivot

ctx.stroke();
ctx.closePath();

ctx.beginPath();
ctx.fillStyle = 'black';
ctx.fillRect(-this.radius / 4, -this.radius / 4, 20, 20);  // render at pivot

Modified Fiddle