如何在不旋转整个画布的情况下旋转一个三角形?

5

我对使用 <canvas> 不熟悉,对数学也不擅长。我在我的画布上用这个函数绘制了一个简单的等边三角形,该函数借鉴了别人的代码(请不要讨厌我):

drawTriangle(PosX, PosY, SideLength, Orientation) {
    context.beginPath();

    var sides = 3;

    var a = ((Math.PI * 2) / sides);

    context.moveTo(PosX + SideLength, PosY);

    for (var i = 1; i < sides + 1; i++) {
        context.lineTo(PosX + SideLength * Math.cos(a*i), PosY + SideLength * Math.sin(a*i));
    }

    context.closePath();

    return true;
}

该函数只知道三角形的中心坐标和指向的方向,没有其他信息。
目前它可以成功地绘制三角形,但是"朝向"东方。
如何使用Orientation参数(度数)旋转三角形,而不像其他答案建议的那样旋转整个画布?

如果您旋转画布,然后绘制三角形,然后将画布旋转回来,那么这样不就可以实现您想要的结果,并允许您继续进行进一步的绘制吗?(您不是在谈论旋转先前绘制的现有三角形,对吗?) - nnnnnn
每一帧都会绘制三角形,如果需要的话,将与包含其他形状的画布一起绘制。 - Jared
那么,为什么旋转画布、绘制形状,然后再旋转回来会成为问题呢?这似乎是下面的工作解决方案所做的。 (并不是说这是唯一可能的解决方案,但如果它简单且有效...) - nnnnnn
我现在正在尝试它。 :) - Jared
他的解决方案可行,但实际上并没有回答我的问题,即如何不旋转画布。我是否应该将其标记为已接受? - Jared
@Jared 不,还有很多其他的方法可以做到。 - B''H Bi'ezras -- Boruch Hashem
5个回答

7
下面是一个绘制任意正多边形和指定角度的函数:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var sideCount=3;
var size=40;
var centerX=50;
var centerY=50;
var strokeWidth=4;
var strokeColor='purple';
var fillColor='skyblue';
var rotationDegrees=0;
var rotationIncrement=1;
var nextTime=0;
var delay=1000/60*1;

requestAnimationFrame(animate);

function animate(time){
    if(time<nextTime){requestAnimationFrame(animate);return;}
    nextTime=time+delay;
    ctx.clearRect(0,0,cw,ch);
    drawPolygon(centerX,centerY,sideCount,size,strokeWidth,strokeColor,fillColor,rotationDegrees);
    rotationDegrees+=rotationIncrement;
    requestAnimationFrame(animate);
}

function drawPolygon(centerX,centerY,sideCount,size,strokeWidth,strokeColor,fillColor,rotationDegrees){
    var radians=rotationDegrees*Math.PI/180;
    ctx.translate(centerX,centerY);
    ctx.rotate(radians);
    ctx.beginPath();
    ctx.moveTo (size * Math.cos(0), size * Math.sin(0));          
    for (var i = 1; i <= sideCount;i += 1) {
        ctx.lineTo (size * Math.cos(i * 2 * Math.PI / sideCount), size * Math.sin(i * 2 * Math.PI / sideCount));
    }
    ctx.closePath();
    ctx.fillStyle=fillColor;
    ctx.strokeStyle = strokeColor;
    ctx.lineWidth = strokeWidth;
    ctx.stroke();
    ctx.fill();
    ctx.rotate(-radians);
    ctx.translate(-centerX,-centerY);    }
<canvas id="canvas" width=512 height=512></canvas>
<canvas id="canvas" width=512 height=512></canvas>


1
我尝试添加你的drawPolygon()函数的某些部分。我在某个地方读到画布将东方作为0度的信息。当我添加了这段代码来将我的角度转换为画布角度时,三角形就停止绘制:Orientation = Orientation - 90; if (Orientation < 0) { Orientation = 360 + Orientation; }。当我不添加那段代码时,它可以成功地绘制。 - Jared
1
@Jared。正确!默认情况下,零度指向“东”。在drawPolygon函数中,如果您希望rotationDegrees在设置为零时指向北方,则可以在var radians...之前rotationDegrees-=90;。顺便说一句,您不必通过添加360度来规范化角度... javascript会将超出0-360范围的值规范化为该范围内的值。注意:我已经编辑了我的演示,将三角形的中心移动到[centerX,centerY]内部的drawPolygon中。干杯! - markE
谢谢回复。当我在代码中添加 rotationDegrees -= 90 后,没有多边形被绘制。这是我自己的(相当冗长的)函数,基于您的函数进行修改:http://pastebin.com/htbNrWNg 没有 -90 代码时,它可以正常工作。Orientation 通常为 0,经过 -=90 后变为预期的 -90 - Jared
我需要在哪里添加 context.setTransform(1, 0, 0, 1, x, y); 吗? - Jared
糟糕?当我去查看您的pastbin时,它已被删除。也许可以从我的答案中重新开始使用修订后的drawPolygon代码。它似乎具有您所需的所有选项。因为在drawPolygon中发送到centerX、centerY将在画布上定位三角形的中心点,所以不需要使用setTransform。 - markE
1
我找到了问题所在。我是在反向翻译之后才反转旋转的。我把它们交换了一下,现在绘制正确了。 - Jared

5
请查看这个示例。您可以将方向作为可选参数发送。它接受'东'、'西'、'北'、'南'。请验证。
``` function drawTriangle(PosX, PosY, SideLength, Orientation) { ```
if (!Orientation) {
   Orientation = 'east';
}

context.beginPath();

var sides = 3;

var a = ((Math.PI * 2) / sides);
// the components have negative contributions
if (Orientation === 'west' || Orientation === 'north') {
    SideLength *= -1;
}

if (Orientation === 'east' || Orientation === 'west') {
    context.moveTo(PosX + SideLength, PosY);

    for (var i = 1; i < sides + 1; i++) {
        context.lineTo(PosX + SideLength * Math.cos(a*i), PosY + SideLength * Math.sin(a*i));
    }
}
else if (Orientation === 'south' || Orientation === 'north') {
    context.moveTo(PosY, PosX + SideLength);

    for (var i = 1; i < sides + 1; i++) {
        context.lineTo(PosY + SideLength * Math.sin(a*i), PosX + SideLength * Math.cos(a*i));
    }
}
context.stroke();
context.closePath();

}`


当我将方向设置为北或南时,三角形被绘制得非常远。当它是东或西时,它看起来像预期的那样。你知道这是什么原因吗? - Jared

4

如果只旋转特定的形状而不是整个画布 - 您应该手动旋转它所包含的每个点,然后使用旋转后的点进行绘制。

请查看我创建的此示例,它不使用任何canvas API函数进行旋转或平移,并且可以绘制任何正多边形。

var ctx = document.getElementById("cnv").getContext('2d');
var id = 0;

var Point2d = function(x, y) {
  this.x = x || 0;
  this.y = y || 0;
}

Point2d.prototype.set = function(x, y) { 
  this.x = x;
  this.y = y;
};

Point2d.prototype.translate = function(p) { 
  this.x += p.x;
  this.y += p.y;
  return this;
};

//rotation around origin
Point2d.prototype.rotate = function(phi) {
  this.set(
    this.x*Math.cos(phi) - this.y*Math.sin(phi),
    this.x*Math.sin(phi) + this.y*Math.cos(phi)
  );
  return this;
};


function getRegularPolygonPoints(center, numSides, sideLength) {
  var points = [];
  var alpha = 2*Math.PI / numSides;  
  for (var i = 0; i < numSides; i++) {
    points.push(new Point2d( 
      center.x + sideLength*Math.cos(alpha*i),
      center.y + sideLength*Math.sin(alpha*i))
    )
  }  
  return points;
}


function drawPolygon(points) {
  ctx.beginPath();
  ctx.moveTo(points[0].x, points[0].y); 
  for (var i = 1; i < points.length; i++) {
    ctx.lineTo(points[i].x, points[i].y);
  }
  ctx.lineTo(points[0].x, points[0].y);//  close the shape
  ctx.lineWidth = 1;
  ctx.fillStyle = "#899";
  ctx.fill();
  ctx.stroke();
  ctx.closePath();
}

function rotatePolygon(polygonPoints, phi, pointAround) {
  var pointAroundInv= new Point2d(-pointAround.x, -pointAround.y);
  
  for (var i = 0; i < polygonPoints.length; i++) {
    polygonPoints[i].translate(pointAroundInv);//  translate to origin
    polygonPoints[i].rotate(phi);//  rotate
    polygonPoints[i].translate(pointAround);// translate back to it's original position
  }
}

var center = new Point2d(250, 120);
var regPolPoints = getRegularPolygonPoints(center, 3, 50);
var currentFrame = (new Date).getTime();
var lastFrame = currentFrame;
var dt = 0;

var render = function() {
  ctx.clearRect(0, 0, 400, 400);
  currentFrame = (new Date).getTime();
  dt = currentFrame - lastFrame;
  lastFrame = currentFrame; 
  
  rotatePolygon(regPolPoints, -dt/600, center);
  drawPolygon(regPolPoints);
  id = requestAnimationFrame(render);
}

id = requestAnimationFrame(render);
<canvas id="cnv" width="400" height="400"></canvas>


1
完美的答案,正是我在寻找的.. 应该被接受。 - B''H Bi'ezras -- Boruch Hashem

3
这里的数学很简单。我们从半径为radius(见下面的参数)的圆上取等角点之间画出线段。此外,需要记住SideLength不是多边形的实际边长。我简化了初始代码并添加了图形旋转功能。下面是最终函数及示例:

        /* here, radius means the radius of circle, inside of which polygon is drawn */
        function drawTriangle(context, PosX, PosY, radius, rotate) {
            context.beginPath();

            /* number of vertices for polygon */
            var sides = 3;
            /* angle between vertices of polygon */
            var a = ((Math.PI * 2) / sides);

            for (var i = 0; i < sides; i++) {
                context.lineTo(PosX + radius * Math.cos(a*i+rotate), PosY + radius * Math.sin(a*i+rotate));
            }

            context.closePath();
            context.stroke();

            return true;
        }

        var canvas = document.getElementById("demo")
        if (canvas.getContext) {
            var ctx = canvas.getContext('2d');
            var PosX = 50;
            var PosY = 50;
            var radius = 40;
            drawTriangle(ctx, PosX, PosY, radius, Math.PI / 4);
        }
canvas#demo
{
  border: thin solid green;
}
<canvas id="demo" width="200" height="200">
    This browser or document mode doesn't support canvas
</canvas>


2

旋转三角形

drawTriangle(PosX, PosY, SideLength, Orientation) {
    context.setTransform(1,0,0,1,PosX,PosY); // Set position
    context.rotate(Orientation);  // set rotation in radians
    context.beginPath();
    var sides = 3;
    var a = ((Math.PI * 2) / sides);
    context.moveTo(SideLength,0);

    for (var i = 1; i < sides + 1; i++) {
        context.lineTo(SideLength * Math.cos(a*i), SideLength * Math.sin(a*i));
    }

    context.closePath();
    context.fill()
    context.setTransform(1,0,0,1,0,0);// reset the transform

    return true;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接