在javascript中旋转一个正方形

时间:2015-06-01 15:22:57

标签: javascript canvas rotation geometry

我正试图在javascript中围绕其中心旋转一个正方形。虽然在这种情况下我使用画布绘制正方形,但我需要有一个旋转方块的功能方法,以便在其他地方使用,所以只是旋转画布是不可接受的。这就是我到目前为止所拥有的

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();`

orientation是-90-90度的值。此代码开始旋转方块,但方块继续"挤压"直到它完全消失了90度。显然我的旋转公式在某处被抛弃但我无法弄清楚如何。

1 个答案:

答案 0 :(得分:2)

您可以手动实施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的盒子,我们想要在中心旋转:

将其添加到原型中:

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,您可以使用与上述实现完全相同的方式