122

I need to know how to draw polygons on a canvas. Without using jQuery or anything like that.

Luc125
  • 5,752
  • 34
  • 35
CyanPrime
  • 5,096
  • 12
  • 58
  • 79
  • 14
    It's good to remember that whatever can be done without a third-party library, should usually be done so. – Rodrigo Mar 05 '16 at 18:21

12 Answers12

200

Create a path with moveTo and lineTo (live demo):

var ctx = canvas.getContext('2d');
ctx.fillStyle = '#f00';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(100,50);
ctx.lineTo(50, 100);
ctx.lineTo(0, 90);
ctx.closePath();
ctx.fill();
phihag
  • 278,196
  • 72
  • 453
  • 469
  • 101
    @Gio Borje: AFAIK, jsFiddle doesn't care about canvas, that's your browser. jsFiddle just feeds your HTML/CSS/JS back to you. – mu is too short Jan 30 '11 at 01:26
  • 2
    Excellent solution. Very neat code. thank u @phihag. Something that I can understand! – bytise Nov 15 '16 at 03:11
  • 1
    can you replace c2 with ctx? I think it's more common use for canvas context. great answer by the way – gididaf Jan 21 '18 at 08:00
  • @user1893354 Thank you very much for the notice. Indeed, there seems to be a problem with jsfiddle there - the error message is completely unrelated to the canvas. Replaced with a very simple live demo site. – phihag Feb 13 '18 at 19:41
49

from http://www.scienceprimer.com/drawing-regular-polygons-javascript-canvas:

The following code will draw a hexagon. Change the number of sides to create different regular polygons.

var ctx = document.getElementById('hexagon').getContext('2d');

// hexagon
var numberOfSides = 6,
    size = 20,
    Xcenter = 25,
    Ycenter = 25;

ctx.beginPath();
ctx.moveTo (Xcenter +  size * Math.cos(0), Ycenter +  size *  Math.sin(0));          

for (var i = 1; i <= numberOfSides;i += 1) {
  ctx.lineTo (Xcenter + size * Math.cos(i * 2 * Math.PI / numberOfSides), Ycenter + size * Math.sin(i * 2 * Math.PI / numberOfSides));
}

ctx.strokeStyle = "#000000";
ctx.lineWidth = 1;
ctx.stroke();
#hexagon { border: thin dashed red; }
<canvas id="hexagon"></canvas>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Andrew Staroscik
  • 2,675
  • 1
  • 24
  • 26
  • 3
    This was great, very elegant, also, if you add: `cxt.save();` `cxt.fillStyle = "#FF000";` `cxt.fill();` `cxt.restore();` You can fill the shape. – samuelkobe Dec 07 '14 at 23:49
  • this is great - i've been sitting playing with it, but cannot work out how i would get the chosen polygon to rotate - any ideas? – eskimomatt Jan 06 '15 at 10:44
  • 1
    There are a few ways to get what you want. One option is to use the built in cxt.rotate() method [along with cxt.save() and cxt.restore()] to rotate parts of the canvas. Alternatively, adding a consistent value to the cos and sin functions will also work. See this jsfiddle for a demonstration: http://jsfiddle.net/kwyhn3ba/ – Andrew Staroscik Jan 07 '15 at 16:02
  • thanks for that - i came across the same solution after reading through the logic on the science primer link you provided. `var angle = i * 2 * Math.PI / shape.currentSides + rotation` added to the cos and sin values worked for me... thanks again – eskimomatt Jan 08 '15 at 09:57
  • If (as in my case) you just want the starting point to be the middle top of the polygon rather than the middle right, flip the `sin` and `cos` calls and change `Ycenter +` to `Ycenter -` on both places (leaving it as a sum rather than a difference of the values results in it starting with a point at the bottom of the resultant shape). I am not a clever man when it comes to trig, so take with a grain of salt; but this achieved what I wanted at least. – Joseph Marikle Oct 22 '15 at 22:13
42
//poly [x,y, x,y, x,y.....];
var poly=[ 5,5, 100,50, 50,100, 10,90 ];
var canvas=document.getElementById("canvas")
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#f00';

ctx.beginPath();
ctx.moveTo(poly[0], poly[1]);
for(let item=2 ; item < poly.length-1 ; item+=2 ){ctx.lineTo( poly[item] , poly[item+1] )}
 
ctx.closePath();
ctx.fill();
Dave Sumter
  • 2,926
  • 1
  • 21
  • 29
canvastag
  • 508
  • 3
  • 3
  • This is why I wish I could fundamentally understand the JavaScript vanilla `for` method. That one line of code simplified things so much. I typically use the jQuery `.each()` but it's application is much less versatile. – Alexander Dixon Mar 21 '16 at 15:22
  • 10
    @AlexanderDixon The javascript above is really not a good example. *All* variables need `var`, in the above code `item` is a polluting the global namespace. Everything is on one line, which reduces readability. If you don't care about readability then you might as well remove the curly brackets. – AnnanFay Feb 08 '18 at 14:55
  • @canvastag Nice work dynamic job. This answer is better from accepted answer for me. I don't understand "Query .each()" ... this is some magic function who does take memory. Also for global namespace ;) funny this is just example make it like class if you want that. – Nikola Lukic Mar 14 '18 at 13:31
10
//create and fill polygon
CanvasRenderingContext2D.prototype.fillPolygon = function (pointsArray, fillColor,     strokeColor) {
    if (pointsArray.length <= 0) return;
    this.moveTo(pointsArray[0][0], pointsArray[0][1]);
    for (var i = 0; i < pointsArray.length; i++) {
        this.lineTo(pointsArray[i][0], pointsArray[i][1]);
    }
    if (strokeColor != null && strokeColor != undefined)
        this.strokeStyle = strokeColor;

    if (fillColor != null && fillColor != undefined) {
        this.fillStyle = fillColor;
        this.fill();
    }
}
//And you can use this method as 
var polygonPoints = [[10,100],[20,75],[50,100],[100,100],[10,100]];
context.fillPolygon(polygonPoints, '#F00','#000');
Jignesh Variya
  • 1,869
  • 16
  • 12
  • 1
    Interesting... Actually does a moveTo AND a lineTo for the first point... but now that I think about it... who cares? – James Newton Feb 10 '19 at 22:06
5

Here is a function that even supports clockwise/anticlockwise drawing do that you control fills with the non-zero winding rule.

Here is a full article on how it works and more.

// Defines a path for any regular polygon with the specified number of sides and radius, 
// centered on the provide x and y coordinates.
// optional parameters: startAngle and anticlockwise

function polygon(ctx, x, y, radius, sides, startAngle, anticlockwise) {
  if (sides < 3) return;
  var a = (Math.PI * 2)/sides;
  a = anticlockwise?-a:a;
  ctx.save();
  ctx.translate(x,y);
  ctx.rotate(startAngle);
  ctx.moveTo(radius,0);
  for (var i = 1; i < sides; i++) {
    ctx.lineTo(radius*Math.cos(a*i),radius*Math.sin(a*i));
  }
  ctx.closePath();
  ctx.restore();
}

// Example using the function.
// Define a path in the shape of a pentagon and then fill and stroke it.
context.beginPath();
polygon(context,125,125,100,5,-Math.PI/2);
context.fillStyle="rgba(227,11,93,0.75)";
context.fill();
context.stroke();
John R
  • 126
  • 1
  • 3
  • That article is rather long to say "your drawing a circle with less edges". You might want to cache the results to avoid calling cos and sin so much (forgive me if its doing it already, I'm not a JavaScript programmer). – QuantumKarl Dec 21 '15 at 12:28
3

In addition to @canvastag, use a while loop with shift I think is more concise:

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var poly = [5, 5, 100, 50, 50, 100, 10, 90];

// copy array
var shape = poly.slice(0);

ctx.fillStyle = '#f00'
ctx.beginPath();
ctx.moveTo(shape.shift(), shape.shift());
while(shape.length) {
  ctx.lineTo(shape.shift(), shape.shift());
}
ctx.closePath();
ctx.fill();
Koen.
  • 25,449
  • 7
  • 83
  • 78
2

You can use the lineTo() method same as: var objctx = canvas.getContext('2d');

        objctx.beginPath();
        objctx.moveTo(75, 50);
        objctx.lineTo(175, 50);
        objctx.lineTo(200, 75);
        objctx.lineTo(175, 100);
        objctx.lineTo(75, 100);
        objctx.lineTo(50, 75);
        objctx.closePath();
        objctx.fillStyle = "rgb(200,0,0)";
        objctx.fill();

if you not want to fill the polygon use the stroke() method in the place of fill()

You can also check the following: http://www.authorcode.com/draw-and-fill-a-polygon-and-triangle-in-html5/

thanks

ankur
  • 21
  • 1
1

For the people looking for regular polygons:

function regPolyPath(r,p,ctx){ //Radius, #points, context
  //Azurethi was here!
  ctx.moveTo(r,0);
  for(i=0; i<p+1; i++){
    ctx.rotate(2*Math.PI/p);
    ctx.lineTo(r,0);
  }
  ctx.rotate(-2*Math.PI/p);
}

Use:

//Get canvas Context
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

ctx.translate(60,60);    //Moves the origin to what is currently 60,60
//ctx.rotate(Rotation);  //Use this if you want the whole polygon rotated
regPolyPath(40,6,ctx);   //Hexagon with radius 40
//ctx.rotate(-Rotation); //remember to 'un-rotate' (or save and restore)
ctx.stroke();
Azurethi
  • 68
  • 7
1

Here is a function approach at three different ways to draw polygons.

  1. Origin, size and radius
  2. Points in a flat array [x1, y1, x2, y2, ..., xN, yN]
  3. Points in a point array { x: int, y: int }[]

Stroke is the default, but fill can also be called. These could either be moved outside the function, or boolean parameters can be added. Alternatively, there can be two methods fillPolygon or strokePolygon, but these are now limited in functionality.

const ctx = document.getElementById('drawing').getContext('2d');

const main = () => {
  ctx.strokeStyle = '#000';
  ctx.lineWidth = 1;

  drawPolygon(ctx, 25, 25, 20);      // Triangle (default)
  drawPolygon(ctx, 75, 25, 20, 6);   // Hexagon
  drawPolygon(ctx, 125, 25, 20, 20); // Icosahedron

  ctx.strokeStyle = '#070';
  ctx.fillStyle = '#ff0';
  ctx.lineWidth = 3;

  drawFreePolygon(ctx, [30, 80, 30, 100, 70, 100, 70, 80, 50, 60]);
  ctx.fill();
  drawFreePolygon2(ctx, [{ x: 90, y: 80 }, { x: 90, y: 100 }, { x: 130, y: 100 }, { x: 130, y: 80 }, { x: 110, y: 60 }]);
};

const drawPolygon = (ctx, x, y, radius, sides = 3) => {
  ctx.beginPath();
  ctx.moveTo(x + radius * Math.cos(0), y + radius * Math.sin(0));
  for (let i = 1; i <= sides; i += 1) {
    ctx.lineTo(x + radius * Math.cos(i * 2 * Math.PI / sides), y + radius * Math.sin(i * 2 * Math.PI / sides));
  }
  ctx.closePath();
  ctx.stroke(); // Could be called outside
};

const drawFreePolygon = (ctx, points) => {
  ctx.beginPath();
  ctx.moveTo(points[0], points[1]);
  for (let i = 2; i < points.length - 1; i += 2) {
    ctx.lineTo(points[i], points[i + 1]);
  }
  ctx.closePath();
  ctx.stroke(); // Could be called outside
};

const drawFreePolygon2 = (ctx, points) => {
  const [{ x: startX, y: startY }] = points;
  ctx.beginPath();
  ctx.moveTo(startX, startY);
  points.forEach(({ x, y }) => {
    ctx.lineTo(x, y);
  });
  ctx.closePath();
  ctx.stroke(); // Could be called outside
};

main();
#hexagon { border: thin dashed red; }
<canvas id="drawing"></canvas>

Related: https://stackoverflow.com/a/74694484/1762224

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
0

To make a simple hexagon without the need for a loop, Just use the beginPath() function. Make sure your canvas.getContext('2d') is the equal to ctx if not it will not work.

I also like to add a variable called times that I can use to scale the object if I need to.This what I don't need to change each number.

     // Times Variable 

     var times = 1;

    // Create a shape

    ctx.beginPath();
    ctx.moveTo(99*times, 0*times);
    ctx.lineTo(99*times, 0*times);
    ctx.lineTo(198*times, 50*times);
    ctx.lineTo(198*times, 148*times);
    ctx.lineTo(99*times, 198*times);
    ctx.lineTo(99*times, 198*times);
    ctx.lineTo(1*times, 148*times);
    ctx.lineTo(1*times,57*times);
    ctx.closePath();
    ctx.clip();
    ctx.stroke();
Sabba Keynejad
  • 7,895
  • 2
  • 26
  • 22
0

Let's do that with HTML and get that down to this:

<!DOCTYPE html>
 <html>
 <head>
   <title> SVG hexagon </title>
 </head>

 <body>
   <svg width="300" height="110" >
     <polygon point="50 3, 100 28, 100 75, 50 100, 3 75, 3 25" stroke="red" fill="lime" stroke-width="5"/>
   </svg>
 </body>
</html>
-1

var ctx = document.getElementById('hexagon').getContext('2d');

// hexagon
var numberOfSides = 4,
    size = 25,
    Xcenter = 40,
    Ycenter = 40;

ctx.beginPath();
ctx.moveTo (Xcenter +  size * Math.cos(0), Ycenter +  size *  Math.sin(0));          

for (var i = 1; i <= numberOfSides;i += 1) {
  ctx.lineTo (Xcenter + size * Math.cos(i * 2 * Math.PI / numberOfSides), Ycenter + size * Math.sin(i * 2 * Math.PI / numberOfSides));
}

ctx.strokeStyle = "#000000";
ctx.lineWidth = 1;
ctx.stroke();
#hexagon { border: thin dashed red; }
<canvas id="hexagon"></canvas>