最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to rotate a triangle without rotating the entire canvas? - Stack Overflow

programmeradmin1浏览0评论

I'm new to working with <canvas> and terrible at maths. I'm drawing a simple equilateral triangle on my canvas with this function that has borrowed code from somebody else (don't hate me):

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;
}

The function would only know the centre co-ordinates of the triangle and the orientation to point it at, nothing else.

It currently draws the triangle successfully but "points" east.

How do I rotate the triangle using the Orientation parameter (degrees) without rotating the entire canvas like other answers suggest?

I'm new to working with <canvas> and terrible at maths. I'm drawing a simple equilateral triangle on my canvas with this function that has borrowed code from somebody else (don't hate me):

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;
}

The function would only know the centre co-ordinates of the triangle and the orientation to point it at, nothing else.

It currently draws the triangle successfully but "points" east.

How do I rotate the triangle using the Orientation parameter (degrees) without rotating the entire canvas like other answers suggest?

Share Improve this question edited Jul 7, 2016 at 5:58 Mohit Bhardwaj 10.1k7 gold badges40 silver badges65 bronze badges asked Jul 7, 2016 at 5:47 JaredJared 3,0164 gold badges30 silver badges47 bronze badges 6
  • If you rotate the canvas, then draw the triangle, then rotate the canvas back, won't that achieve your desired result and allow you to go on with further drawing? (You're not talking about rotating an existing triangle that was drawn previously, are you?) – nnnnnn Commented Jul 7, 2016 at 5:50
  • The triangle is drawn every frame with the rest of the canvas that contains other shapes if that helps. – Jared Commented Jul 7, 2016 at 5:58
  • Right, so why is rotating the canvas, drawing the shape, then rotating back a problem? It's what the working solution below seems to do. (Not that this is the only possible solution, but if it's simple and it works...) – nnnnnn Commented Jul 7, 2016 at 6:04
  • I'm trying it out now. :) – Jared Commented Jul 7, 2016 at 6:07
  • So his solution works but it's not actually an answer to my question about not rotating the canvas. Should I mark it as accepted anyway? – Jared Commented Jul 8, 2016 at 8:24
 |  Show 1 more comment

5 Answers 5

Reset to default 7

Here's a function that draws any regular polygon and a specified angle:

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>

Please look at this fiddle. You can send the orientation as an optional paramenter. It accepts 'east', 'west', 'north','south'. Please validate.

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

}`

To rotate only the particular shape but not the entire canvas - you should rotate manually every point it consists of then draw it with rotated points.

check this example I created, it does not use any canvas API functions for rotation or translation also draws any regular polygon.

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>

The math here is very simple. We are drawing lines between the equiangular points taken from the circle with radius radius (see argument below). Also, we should keep in mind that SideLength is not real side length of the polygon. I've simplified initial code and added figure rotation feature. The resulting function together with example is presented below:

        /* 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>

To rotate the triangle

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;
}
发布评论

评论列表(0)

  1. 暂无评论