正多边形是一种很有意思的图形,但是如果想要绘制一个正多边形并不是那么容易的。最近看书介绍了这一块的内容,但是它只是很简单的描述了一下,并没有详细的过程。所以自己推导了一下,在这里记录一下。这种推导方式是通用的,在其它任何的绘图库中都是可以使用的。在这里我使用canvas来实现绘制。
坐标推导
绘制正多边形的关键点在于需要计算出多边形顶点的位置,这里需要利用到一些三角函数的简单知识。我这里以正八边形为例子进行举例。正多边形的顶点都在其外接圆上,所以
正多边形上任意一个顶点的坐标为:
x = r * cos(θ)
y = r * sin(θ)
这里只是简单的初中三角函数的知识。
canvas 绘制
我们在计算机上进行绘图,它的坐标系是下图所示的。在上一个图中,它是默认坐标原点作为圆心的,不具有一般性。所以我们需要将它转化为下面这样的形式,这张图片是我用canvas绘制的,有点简陋,大家凑合着看吧。
这里介绍一下已知条件:
圆心 (X, Y)
半径 R
角度 θ
所以 点0 的坐标为:
x0 = X + R * cos(θ)
y0 = Y - R * sin(θ)
其它点的坐标也可以由上式进行推导,区别在于 角度θ 值不同,且因为是正多边形,角度是倍数关系。角度θ = 360/边数
通过前面的推导,我们只需要知道一个需要绘制的正多边形外接圆圆心的位置和外接圆的半径,就可以绘制任意一个正多边形了。下面我们就来尝试使用 canvas 进行绘制!
这里我们尝试来绘制一个正八边形:
圆心位置为:(250, 250)
半径为:100
let canvas = document.getElementById("canvas"),
context = canvas.getContext("2d");
let centerX = 250, // 外接圆圆心 x 坐标
cneterY = 250, // 外接圆圆心 y 坐标
r = 100, // 外接圆半径
sides = 8, // 外接圆边数
points = []; // 存储外接圆各个顶点的数组
let angle = 0; // 多边形的角度,弧度制
for (let i = 0; i < sides; i++) {
x = centerX + r * Math.sin(angle);
y = cneterY - r * Math.cos(angle);
angle += 2*Math.PI/sides;
points.push({x:x, y:y});
}
console.log(points);
context.beginPath();
context.moveTo(points[0].x, points[0].y);
for (let i = 1; i < sides; i++) {
context.lineTo(points[i].x, points[i].y);
}
context.closePath();
context.stroke();
这里我在绘制图上增加了数字,数字表示绘制的先后顺序,但是在实际代码中将其删除了。
所以绘制一个正多边形的步骤是:
通过圆心、半径及角度计算出各个顶点的位置,然后一次连线各个顶点即可。从初始点连接到初始点或者连接到最后一条,然后调用 closePath()
方法,这里是采用这种方式。
封装
将绘制正多边形的步骤封装成一个函数,这里会绘制几个例子。
/*
x 外接圆圆心 x坐标
y 外接圆圆心 y坐标
r 外接圆半径
*/
function drawPolygon(centerX, centerY, radius, sides) {
let angle = 0;
let points = []
// 求各个顶点的坐标
for (let i = 0; i < sides; i++) {
x = centerX + radius * Math.sin(angle);
y = centerY - radius * Math.cos(angle);
angle += 2*Math.PI/sides;
points.push({x: x, y: y});
}
console.log(points);
// 顶点坐标连线,先移动到第一个顶点
context.moveTo(points[0].x, points[0].y);
// 依次开始连线
for (let i = 1; i < sides; i++) {
context.lineTo(points[i].x, points[i].y);
}
// 关闭路径,使得最后一点和第一点闭合
context.closePath();
context.stroke();
}
依次绘制三角形、四边形到十边形。
drawPolygon(100, 150, 100, 3);
drawPolygon(300, 150, 100, 4);
drawPolygon(500, 150, 100, 5);
drawPolygon(700, 150, 100, 6);
drawPolygon(100, 400, 100, 7);
drawPolygon(300, 400, 100, 8);
drawPolygon(500, 400, 100, 9);
drawPolygon(700, 400, 100, 10);
旋转
这样绘制的多边形都是在垂直方向上对称的(这个从上面的推导中得出),我们需要的是任意的多边形,这里还不够任意,多边形应该可以在此基础上进行旋转才行。所以,需要给绘制多边形的函数多增加一个起始角度参数。
/*
x 外接圆圆心 x坐标
y 外接圆圆心 y坐标
r 外接圆半径
startAngle 起始旋转角度
*/
function drawPolygon(centerX, centerY, radius, sides, startAngle) {
let angle = startAngle || 0;
let points = []
// 求各个顶点的坐标
for (let i = 0; i < sides; i++) {
x = centerX + radius * Math.sin(angle);
y = centerY - radius * Math.cos(angle);
angle += 2*Math.PI/sides;
points.push({x: x, y: y});
}
console.log(points);
// 顶点坐标连线,先移动到第一个顶点
context.moveTo(points[0].x, points[0].y);
// 依次开始连线
for (let i = 1; i < sides; i++) {
context.lineTo(points[i].x, points[i].y);
}
// 关闭路径,使得最后一点和第一点闭合
context.closePath();
context.stroke();
}
这里将正四边形、正六边形、正八边形和正十边形进行旋转,让他们最上面的边是水平放置的。
drawPolygon(100, 150, 100, 3);
drawPolygon(300, 150, 100, 4, 45/180*Math.PI);
drawPolygon(500, 150, 100, 5);
drawPolygon(700, 150, 100, 6, 30/180*Math.PI);
drawPolygon(100, 400, 100, 7);
drawPolygon(300, 400, 100, 8, 22.5/180*Math.PI);
drawPolygon(500, 400, 100, 9);
drawPolygon(700, 400, 100, 10, 18/180*Math.PI);
结语
好了,看到了这里的时候,想必你已经对如何推导及绘制正多边形有所了解了。一定要尝试自己去经历推导和绘制的过程,那是非常有趣的事情。已经有很多人写了类似的内容,我也参考了其它人写的,但是总是感觉有一些迷惑的地方。索性自己来亲自推导,绘制一遍,并记录这个过程,现在这一块的内容我已经比较了解了。纸上得来终觉浅,绝知此事要躬行。