javascript - 在javascript中旋转一个正方形

标签 javascript canvas rotation geometry

我正在尝试在 JavaScript 中围绕其中心旋转一个正方形。虽然在本例中我使用 Canvas 来绘制正方形,但我需要一种旋转正方形的功能方法以供其他地方使用,因此仅旋转 Canvas 是 Not Acceptable 。这就是我到目前为止所拥有的

var thecanvas = this.myCanvas;      

  var canvaswidth = thecanvas.width;
  tlx = (0 - ((canvaswidth * 0.6) / 2));
  tly = (0 - ((canvaswidth * 0.6) / 2));
  trx = (0 + ((canvaswidth * 0.6) / 2));
  tryy = (0 - ((canvaswidth * 0.6) / 2));
  blx = (0 - ((canvaswidth * 0.6) / 2));
  bly = (0 + ((canvaswidth * 0.6) / 2));
  brx = (0 + ((canvaswidth * 0.6) / 2));
  bry = (0 + ((canvaswidth * 0.6) / 2));

  tlx = (((tlx) * (this._cosD(orientation))) - ((tly) * (this._sinD(orientation))));
  tly = ((tlx) * (this._sinD(orientation)) + (tly) * (this._cosD(orientation)));
  trx = (((trx) * (this._cosD(orientation))) - ((tryy) * (this._sinD(orientation))));
  tryy = ((trx) * (this._sinD(orientation)) + (tryy) * (this._cosD(orientation)));
  blx = ((blx) * (this._cosD(orientation)) - (bly) * (this._sinD(orientation)));
  bly = ((blx) * (this._sinD(orientation)) + (bly) * (this._cosD(orientation)));
  brx = ((brx) * (this._cosD(orientation)) - (bry) * (this._sinD(orientation)));
  bry = ((brx) * (this._sinD(orientation)) + (bry) * (this._cosD(orientation)));


  tlx = (tlx + (canvaswidth / 2));
  tly = (tly + (canvaswidth / 2));
  trx = (trx + (canvaswidth / 2));
  tryy = (tryy + (canvaswidth / 2));
  blx = (blx + (canvaswidth / 2));
  bly = (bly + (canvaswidth / 2));
  brx = (brx + (canvaswidth / 2));
  bry = (bry + (canvaswidth / 2));


  var c2 = thecanvas.getContext('2d');
  c2.fillStyle = '#f00';
  c2.beginPath();
  c2.moveTo(tlx, tly);
  c2.lineTo(trx, tryy);
  c2.lineTo(brx, bry);
  c2.lineTo(blx, bly);
  c2.closePath();
  c2.fill();`

方向是 -90-90 度之间的值。此代码开始旋转正方形,但正方形继续“挤压”,直到完全旋转 90 度。显然我的旋转公式在某个地方被抛弃了,但我不知道如何。

最佳答案

您可以手动实现 transformation matrix 。这允许您设置用于读取和写入的矩阵,然后将其应用于返回具有新实际值的绝对点的点,而无需或麻烦地为每个用例编写特殊代码。

使用 3x3 矩阵的(2D 仿射)公式为:

3x3 affine matrix

或用 JavaScript 简化:

var newX = x * a + y * c + e,    // e (or tx) x 1 => e
    newY = x * b + y * d + f;    // f (or ty) x 1 => f

现在您需要将矩阵与另一个矩阵相乘以添加旋转、平移、缩放等。执行此操作的常见方法是这样的 - 假设我们有一个保存我们的值的对象,您可以这样做:

function Matrix() {
   this.a = 1;  // 1,0,0,1,0,0 = identity matrix (untransformed)
   this.b = 0;
   this.c = 0;
   this.d = 1;
   this.e = 0;
   this.f = 0;
}

Matrix.prototype = {

  // we need to be able to multiply matrices to accumulate values:
  transform: function(a2, b2, c2, d2, e2, f2) {

    var a1 = this.a,
        b1 = this.b,
        c1 = this.c,
        d1 = this.d,
        e1 = this.e,
        f1 = this.f;

    /* matrix order (canvas compatible):
    * ace
    * bdf
    * 001
    */
    this.a = a1 * a2 + c1 * b2;
    this.b = b1 * a2 + d1 * b2;
    this.c = a1 * c2 + c1 * d2;
    this.d = b1 * c2 + d1 * d2;
    this.e = a1 * e2 + c1 * f2 + e1;
    this.f = b1 * e2 + d1 * f2 + f1;
  }
}

设置核心后,您现在可以添加方法来执行例如旋转:

rotation

rotate: function(angle) {
    var cos = Math.cos(angle),
        sin = Math.sin(angle);
    this.transform(cos, sin, -sin, cos, 0, 0);
}

规模:

scale: function(sx, sy) {
    this.transform(sx, 0, 0, sy, 0, 0);
}

翻译:

translate: function(tx, ty) {
    this.transform(1, 0, 0, 1, tx, ty);
}

等等,具体取决于您的需要。请注意,这些将像普通 Canvas /SVG 矩阵一样累积。重置为身份以删除所有转换。

现在您需要做的就是在设置转换后输入点以获得绝对点 - 假设我们有一个 100x100 的盒子,我们想要在中心旋转:

将其添加到原型(prototype)中:

applyToPoint: function(p) {
    return {
      x: p.x * this.a + p.y * this.c + this.e,
      y: p.x * this.b + p.y * this.d + this.f
    }
}

将允许我们做:

var m = new Matrix();
m.translate(50, 50);
m.rotate(0.3);
m.translate(-50, 50);

var points = [
      {x: 0, y: 0},      // upper-left
      {x: 100, y: 0},    // upper-right
      {x: 100, y: 100},  // bottom-right
      {x: 0, y: 100}     // bottom-left
    ],
    result = [],
    i = 0, p;

// transform points
while(p = points[i++]) result.push(m.applyToPoint(p));

演示

红色框是原始坐标,蓝色是我们现在可以访问的变换后的点:

snapshot

function Matrix() {
  this.a = 1; // identity matrix
  this.b = 0;
  this.c = 0;
  this.d = 1;
  this.e = 0;
  this.f = 0;
}

Matrix.prototype = {

    applyToPoint: function(p) {
      return {
        x: p.x * this.a + p.y * this.c + this.e,
        y: p.x * this.b + p.y * this.d + this.f
      }
    },

    transform: function(a2, b2, c2, d2, e2, f2) {

      var a1 = this.a,
          b1 = this.b,
          c1 = this.c,
          d1 = this.d,
          e1 = this.e,
          f1 = this.f;

      /* matrix order (canvas compatible):
       * ace
       * bdf
       * 001
       */
      this.a = a1 * a2 + c1 * b2;
      this.b = b1 * a2 + d1 * b2;
      this.c = a1 * c2 + c1 * d2;
      this.d = b1 * c2 + d1 * d2;
      this.e = a1 * e2 + c1 * f2 + e1;
      this.f = b1 * e2 + d1 * f2 + f1;
    },

    rotate: function(angle) {
      var cos = Math.cos(angle),
          sin = Math.sin(angle);
      this.transform(cos, sin, -sin, cos, 0, 0);
    },

    scale: function(sx, sy) {
      this.transform(sx, 0, 0, sy, 0, 0);
    },

    translate: function(tx, ty) {
      this.transform(1, 0, 0, 1, tx, ty);
    }
};

// apply some transformation:
var m = new Matrix();     // our manual transformation-matrix
m.translate(50, 50);      // center of box
m.rotate(0.3);            // some angle in radians
m.translate(-50, -50);    // translate back

var points = [
      {x: 0, y: 0},       // upper-left
      {x: 100, y: 0},     // upper-right
      {x: 100, y: 100},   // bottom-right
      {x: 0, y: 100}      // bottom-left
    ],
    result = [], i = 0, p;

// transform points
while(p = points[i++]) result.push(m.applyToPoint(p));

// draw boxes to canvas:
var ctx = document.querySelector("canvas").getContext("2d");
ctx.translate(30, 30);    // give some room for rotation for this demo

drawPolygon(points, "red");
drawPolygon(result, "blue");
 
drawCoord(points[0]);     // plot old point
drawCoord(result[0]);     // plot resulting point

// Compare using ordinary canvas: -------------------

ctx.translate(150, 0);     // give some space
ctx.fillText("Regular canvas:", 0, -20);

drawPolygon(points, "red");

ctx.translate(50, 50);      // center of box
ctx.rotate(0.3);            // some angle in radians
ctx.translate(-50, -50);    // translate back

drawPolygon(points, "blue");


// plot result:
function drawPolygon(pts, color) {
  ctx.beginPath();
  ctx.strokeStyle = color;
  ctx.moveTo(pts[0].x, pts[0].y);
  for(var i = 1, p; p = pts[i++];) ctx.lineTo(p.x, p.y);
  ctx.closePath();
  ctx.stroke();
}

function drawCoord(p) {
  ctx.fillStyle = "#0c0"; ctx.fillRect(p.x - 2, p.y - 2, 4, 4);
  ctx.fillStyle = "#000";
  ctx.fillText(p.x.toFixed(1) + "," + p.y.toFixed(1), p.x, p.y - 2);
}
<canvas><canvas>

如果您不想自己实现此功能,或者想要更广泛的解决方案,请随时查看我的(免费)matrix solution here .

future will give us currentTransform在上下文中(目前仅在 Chrome 中可用,Firefox 与 bug 作斗争)返回 SVGMatrix object您可以按照与上述实现几乎相同的方式使用它。

关于javascript - 在javascript中旋转一个正方形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30577269/

相关文章:

javascript - 如何让我的 "planets"围绕我制作的太阳旋转?

javascript - 根据使用 jquery/javascript 选择的复选框更新 url 而不重新加载

javascript - 选中多个复选框时,表单字段应相应地与公共(public)字段一起显示

javascript - HTML 5 Canvas 填充颜色

android - 你能解释一下 TextView + Gravity + SingleLine 和 Canvas 的行为吗?

iPad 上的 iPhone 应用程序 : UILabel pixelated during rotation?

javascript - 用JS修改或删除一个textarea标签的显示属性值

javascript - 将值从 image.onload 传递到周围的函数

android - 更新 Canvas ?无效()?

css - 仅显示旋转图像的特定部分