如何在鼠标移动事件后围绕其中心旋转Canvas对象?

时间:2016-06-20 05:14:01

标签: javascript canvas rotation

嗨我想在移动鼠标时围绕它的中心旋转这个形状,但是目前它正在绕着(0,0)旋转。如何更改我的代码?

源代码(另见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();

3 个答案:

答案 0 :(得分:3)

所有好的答案,令人沮丧的是没有...他们没有提到解决方案只有在当前变换处于默认状态时才有效。他们没有提到如何恢复默认状态并保存和恢复状态。

获取默认转换状态

ctx.setTransform(1,0,0,1,0,0);

保存并恢复所有状态

ctx.save();
ctx.transform(10,0,0,2,200,100); // set some transform state
ctx.globalAlpha = 0.4;
ctx.restore(); // each save must be followed by a restore at some point

它们可以嵌套

ctx.save();  // save default state
ctx.globalAlpha = 0.4;
ctx.save();  // save state with alpha = 0.4
ctx.transform(10,0,0,2,200,100); // set some transform state
ctx.restore(); // restore to alpha at 0.4
ctx.restore(); // restore to default.

setTransform完全取代当前的转换。同时转换,缩放,旋转,平移,将现有变换与适当的变换相乘。如果你有一个对象附加到另一个对象,并希望第一个的转换应用于第二个,并且另外转换到第二个但不转换到第一个,这很方便。

ctx.rotate(Math.PI /2); // Rotates everything 90 clockwise
ctx.rotate(Math.PI /2); // Rotates everything another 90 clockwise so that
                        // everything is 180 from default

ctx.translate(1,1); // move diagonally down by 1. Origin is now at 1,1
ctx.translate(1,1); // move diagonally down by 1. Origin is now at 2,2
ctx.translate(1,1); // move diagonally down by 1. Origin is now at 3,3
ctx.translate(1,1); // move diagonally down by 1. Origin is now at 4,4

ctx.scale(2,2); // scale by 2 everything twice as big
ctx.scale(2,2); // scale by 2 everything four times as big

另一种不需要ctx

的默认转换状态的替代方案
// scaleX, scaleY are scales along axis x,y
// posX, posY is position of center point
// rotate is in radians clockwise with 0 representing the x axis across the screen
// image is an image to draw.
ctx.setTransform(scaleX,0,0,scaleY, posX, posY);
ctx.rotate(rotate);
ctx.drawImage(image,-image.width / 2, -image.height / 2);

或者如果不是图像而是对象

ctx.setTransform(scaleX,0,0,scaleY, posX, posY);
ctx.rotate(rotate);
ctx.translate(-object.width / 2, -object.height / 2);

答案 1 :(得分:1)

你需要:

  • 首先转换为旋转点(枢轴)
  • 然后旋转
  • 然后:
    • A:使用(-width / 2,-height / 2)作为相对坐标(对于居中的图纸)在(0,0)处绘制
    • B:平移并使用对象的绝对位置并减去居中绘图的相对坐标

修改后的代码:

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.closePath();                               // must come before stroke() btw.
ctx.stroke();

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

<强> Modified Fiddle

奖励提示:您目前正在使用save() / restore()来维护转换矩阵。另一种方法是使用最初替换save() / restore()的绝对值来设置矩阵 - 而不是第一个translate()

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

您还可以为每个样式设置类似样式的内容。无论如何,它并没有改变核心解决方案。

答案 2 :(得分:1)

您必须首先转换到圆心,进行旋转然后翻译

在渲染圆形和方形

之前执行此操作
ctx.translate(this.cx, this.cy);
ctx.rotate(this.toAngle - this.angle);
ctx.translate(-this.cx, -this.cy);

以下jsfiddle: https://jsfiddle.net/1st8Lbu8/2/