简体   繁体   English

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

[英]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).嗨,当我移动鼠标时,我想围绕它的中心旋转这个形状,但目前它正在围绕 (0, 0) 旋转。 How to change my code?如何更改我的代码?

Source code (also see jsfiddle ):源代码(另见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();

All good answers, well frustratingly no... they fail to mention that the solution only works if the current transform is at it default.所有好的答案,令人沮丧的是没有......他们没有提到该解决方案仅在当前转换处于默认状态时才有效。 They fail to mention how to get back to the default state and save and restore states.他们没有提到如何恢复到默认状态以及保存和恢复状态。

To get the default transformation state获取默认转换状态

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

To save and restore all states保存和恢复所有状态

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

and they can be nested它们可以嵌套

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 completely replaces the current transformation. setTransform 完全取代了当前的转换。 while transform, scale, rotate, translate, multiply the existing transform with the appropriate transform.而变换、缩放、旋转、平移、将现有变换与适当的变换相乘。 This is handy if you have an object attached to another, and want the transformation of the first to apply to the second, and additional transforms to the second but not to the first.如果您将一个对象附加到另一个对象,并且希望将第一个对象的变换应用于第二个对象,并将其他变换应用于第二个对象而不是第一个对象,则这很方便。

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

And an alternative that does not require the default transform state of ctx以及不需要 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);

Or if not a image but a object或者如果不是图像而是对象

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

You need to:你需要:

  • first translate to the point of rotation (pivot)首先平移到旋转点(枢轴)
  • then rotate然后旋转
  • then either:那么要么:
    • A: draw in at (0,0) using (-width/2, -height/2) as relative coordinate (for centered drawings) A: 在 (0,0) 处绘制,使用 (-width/2, -height/2) 作为相对坐标(对于居中绘图)
    • B: translate back and use the object's absolute position and subtract relative coordinates for centered drawing B:平移回来,使用对象的绝对位置减去相对坐标进行居中绘图

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.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修改后的小提琴

Bonus tip: you're currently using save() / restore() calls to maintain the transformation matrix.额外提示:您目前正在使用save() / restore()调用来维护转换矩阵。 Another way could be to set the matrix using absolute values initially replacing the save() / restore() - so instead of the first translate() :另一种方法是使用最初替换save() / restore()绝对值来设置矩阵 - 所以而不是第一个translate()

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

You can also set things like styles on an individual basis for each.您还可以为每个人单独设置样式之类的东西。 Regardless, it doesn't change the core solution though.无论如何,它不会改变核心解决方案。

You have to first translate to the circle centre, make the rotation and then translate back您必须先平移到圆心,进行旋转,然后再平移回来

Do this before rendering the circle and the square在渲染圆形和正方形之前执行此操作

ctx.translate(this.cx, this.cy);
ctx.rotate(this.toAngle - this.angle);
ctx.translate(-this.cx, -this.cy);

jsfiddle below: https://jsfiddle.net/1st8Lbu8/2/下面的jsfiddle: https ://jsfiddle.net/1st8Lbu8/2/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM