Canvas學習:繪制正多邊形
到目前為止,我們了解了如何在Canvas中繪制線段、 矩形 、 圓或圓弧線 和貝塞爾曲線等。這些都是Canvas的 CanvasRenderingContext2D 對象自身提供繪制基本圖形。但是,我們肯定需要在Canvas中繪制除此之外的其他圖形,比如前面所說的繪制箭頭或者說我們今天要聊的繪制正多邊形。
正多邊形
維基百科 上是這樣描述的: 正多邊形是所有角都相等、并且所有邊都相等的簡單多邊形,簡單多邊形是指在任何位置都不與自身相交的多邊形 。
正多邊形的特性
正 n 邊形每個內角為 (1 - 2 / n) * 180 或者表示為 (n - 2) * 180 / n 角度。也可以用弧度表示為 (n - 2) * π / n 或者 (n - 2) / 2n 。
正多邊形的所有頂點都在同一個外接圓上,每個正多邊形都有一個外接圓,這也稱為 圓內接正多邊形 。
把一個圓分成相等的一些弧,就可以得到這個圓的內接正多邊形(Regular Polygon),這個圓就是正多邊形的外接圓。外接圓的圓心叫做這個正多邊形的中心,外接圓的半徑叫做正多邊形的半徑(Radius),正多邊形每一邊所對的圓心角叫做正多邊形的中心角(圓心角,Central Angle),中心到正多邊形的一邊的距離叫做正多邊形的邊心距(Apothem)。
一個圓的圓周是 2π ,當邊的數目為 n 時,每一個中心角都是 2π / n 。半徑 r 、邊心距 a 、邊長 s 都存在著固定的關系,已知其中的兩個,都可由上面的公式求出第三個。
當 n 接近 48 這個值時,那這個正多邊形也就接近是一個圓了。如下圖所示:
正多邊形屬性
先上張圖:
上圖描述了正多邊形的相關屬性:
- 正多邊形的中心點正好是一個正多邊形的外接圓的圓心
- 正多邊形每條邊的長度都相等,如上圖中的 x
- 正多邊形的每個內角都相等,如上圖中的 β
- 正多邊形的每個外角都相等,如上圖中的 α
- 正多邊形的中心角都相等,如上圖中的 θ
- 正多邊形的中心點距正多邊形的內切圓的半行為 r
- 正多邊形的頂點數和邊數相等,常用 n 表示
- 正多邊形中心距正多邊形的外接圓(或者正多邊中心點距正多邊形的頂點)就是正多邊形外接圓半么,如上圖中的 R
- 正多邊形中心點距和每條邊的端點構成一個等腰三角形,如上圖中的 A1 。這個三角形的兩條邊長度相等,剛好是正多邊形外接圓半徑 R ,而這個三角形的高,剛好是正多邊形內切圓半徑 r
那么在Canvas中要使用 CanvasRenderingContext2D 對象自帶的方法,比如 moveTo() 和 lineTo() 繪制多邊形,我們就必須知道正多邊形屬性之間的關系。也就是這些屬性之間的三角函數。言外之意,在Canvas中,我們使用 moveTo() 和 lineTo() 方法,再配合一些簡單的三角函數,就可以繪制出任意邊數的多邊形。
既然繪制正多邊形需要一定的三角函數知道,我們在繪制正多邊形之前,先了簡單的了解一下這方面的基礎。
外角(Exterior Angle)
正多邊形的外角是正多邊形任意邊與相鄰邊延長直線構成的角:
正多邊形所有外角之和等于 360° 。也就是說,每個外角 α = 360° / n 。比如 n=8 ,一個正八邊形,它的外角 α = 360° / n = 360° / 8 = 45 。
內角(Interior Angles)
正多邊形相鄰兩條邊構成的夾角就是正多邊形的一個內角。每個內角都有其相鄰的一個外角,它們構成一條直線,也就是說內角加上外角,剛好是 180° 。也就是內角 β = 180° - α 。即: β = 180° - 360° /n 。上面的公式可以轉化為:
β = 180° - 360° / n
= (n × 180° / n) ? (2 × 180° / n)
= (n ? 2) × 180° / n
同樣的拿 n = 8 的正八邊形為例: β = (n - 2) × 180° / n = (8 - 2) × 180° / 8 ,即 β = 135° 。
中心角
正多邊形的中心點與正多邊形頂點構成的角,即正多邊形每條邊對應的夾角稱為正多邊形的中心角 θ 。如果正邊形有 n 條邊,那么就有 n 個中心角 θ ,這樣一來 θ = 360° / n 。
正多邊形每個頂點的坐標
通過前面的介紹,我們可以很容易得到正多邊形的中心角 θ 。但在Canvas中要繪制一個正多邊形,需要知道正多邊形每個頂點的坐標。而這個坐標 (xPos, yPos) 可以通過三角函數得到。
xPos = cos(θ) * R
yPos = sin(θ) * R
而正多邊形所有中心角的和是 360° 也就是 2π ,中心角 θ = 2π / n :
假設我們正多邊形的中點心是 (xCenter, yCenter) ,這樣就可以得到每個頂點坐標位置:
xPos = xCenter + Math.cos(angle) * radius;
yPos = yCenter + Math.sin(angle) * radius;
繪制正多邊形
前面花了很長的篇幅來介紹正多邊形相關知識點。因為只有了解這些基礎,才能更好的繪制正多邊形,這也是磨刀不誤砍柴工。那么我們接下來看怎么繪制一個正多邊形。
我們先來看一個簡單的繪制方法,比如封裝一個繪制正多邊形的函數 drawPolygons() ,給它傳幾個參數:
- ctx :Canvas中繪圖環境
- num :正邊形邊數
- radius :正邊形外接圓半徑
- arc :是否顯示正多邊形的外接圓
這樣我們就可以擼碼了:
function drawPolygons(ctx, num, radius, arc) {
// 先清Canvas畫布,w為Canvas寬度,h為Canvas高度
ctx.clearRect(-w / 2, -h / 2, w, h);
ctx.save();
ctx.beginPath();
// 通過moveTo繪制移到正多邊形起始點
ctx.moveTo(0, -radius);
for (var i = 0; i < num; i++) {
angle = (360 / num) * (i + 1) * Math.PI / 180;
actAngle = angle - Math.PI / 2;
x = Math.cos(actAngle) * radius;
y = Math.sin(actAngle) * radius;
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.fill();
ctx.stroke();
// 畫外接圓
if (arc) {
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI, true);
ctx.stroke();
}
}
為了更好的演示,添加幾個表單,讓我們更好的演示繪制正多邊形,比如:
為了讓我們繪制正多邊形更佳靈活,可以給其傳入更多的參數。并且為了繪制邊框正多邊形和填充正多邊形,我們可以多封裝兩個函數:
// @param {CanvasRenderingContext2D} ctx
// @param {Number} xCenter 中心坐標X點
// @param {Number} yCenter 中心坐標Y點
// @param {Number} radius 外圓半徑
// @param {Number} sides 多邊形邊數
// @param {Number} alpha 角度 默認270度
// @param {Boolean} arc 是否顯示外圓
function drawPolygons(ctx, xCenter, yCenter, radius, sides, alpha, arc) {
var radAngle = Math.PI * 2 / sides;
var radAlpha = (alpha != 'undefined') ? alpha * Math.PI / 180 : -Math.PI / 2;
ctx.save();
ctx.beginPath();
var xPos = xCenter + Math.cos(radAlpha) * radius;
var yPos = yCenter + Math.sin(radAlpha) * radius;
ctx.moveTo(xPos, yPos);
for (var i = 1; i <= sides; i++) {
var rad = radAngle * i + radAlpha;
var xPos = xCenter + Math.cos(rad) * radius;
var yPos = yCenter + Math.sin(rad) * radius;
ctx.lineTo(xPos, yPos);
}
ctx.closePath();
}
// 繪制填充的多邊形
// @param {CanvasRenderingContext2D} ctx
// @param {Number} xCenter 中心點X坐標點
// @param {Number} yCenter 中心點Y坐標點
// @param {Number} radius 外圓半徑
// @param {Number} sides 多邊形邊數
// @param {Number} alpha 角度 默認270度
// @param {Boolean} arc 是否顯示外圓
function drawFillPolygon(ctx, xCenter, yCenter, radius, sides, alpha, arc) {
drawPolygons(ctx, xCenter, yCenter, radius, sides, alpha, arc);
ctx.fill();
// 畫外接圓
if (arc) {
ctx.beginPath();
ctx.arc(xCenter, yCenter, radius, 0, 2 * Math.PI, true);
ctx.stroke();
}
}
// 繪制描邊的多邊形
// @param {CanvasRenderingContext2D} ctx
// @param {Number} xCenter 中心點X坐標點
// @param {Number} yCenter 中心點Y坐標點
// @param {Number} radius 外圓半徑
// @param {Number} sides 多邊形邊數
// @param {Number} alpha 角度 默認270度
// @param {Boolean} arc 是否顯示外圓
function drawStrokePolygon(ctx, xCenter, yCenter, radius, sides, alpha, arc) {
drawPolygons(ctx, xCenter, yCenter, radius, sides, alpha, arc);
ctx.stroke();
// 畫外接圓
if (arc) {
ctx.beginPath();
ctx.arc(xCenter, yCenter, radius, 0, 2 * Math.PI, true);
ctx.stroke();
}
}
其中還省略了部分代碼,全部代碼,可以在CodePen上查看:
上面我們看到的都是繪制正多邊形,但很多時候我們還可以繪制一些星形,比如下面的示例:
// @param {CanvasRenderingContext2D} ctx
// @param {Number} xCenter 中心坐標X點
// @param {Number} yCenter 中心坐標Y點
// @param {Number} radius 外圓半徑
// @param {Number} sides 多邊形邊數
// @param {Number} sideIndent (0 ~ 1)
// @param {Number} alpha 角度 默認270度
// @param {Boolean} arc 是否顯示外圓
function drawStarPolygons(ctx, xCenter, yCenter, radius, sides, sideIndent, alpha, arc) {
var sideIndentRadius = radius * (sideIndent || 0.38);
var radAngle = alpha ? alpha * Math.PI / 180 : -Math.PI / 2;
var radAlpha = Math.PI * 2 / sides / 2;
ctx.save();
ctx.beginPath();
var xPos = xCenter + Math.cos(radAngle) * radius;
var yPos = yCenter + Math.sin(radAngle) * radius;
ctx.moveTo(xPos, yPos);
for (var i = 1; i <= sides * 2; i++) {
var rad = radAlpha * i + radAngle;
var len = (i % 2) ? sideIndentRadius : radius;
var xPos = xCenter + Math.cos(rad) * len;
var yPos = yCenter + Math.sin(rad) * len;
ctx.lineTo(xPos, yPos);
}
ctx.closePath();
}
// 繪制填充的多邊形
// @param {CanvasRenderingContext2D} ctx
// @param {Number} xCenter 中心點X坐標點
// @param {Number} yCenter 中心點Y坐標點
// @param {Number} radius 外圓半徑
// @param {Number} sides 多邊形邊數
// @param {Number} sideIndent (0 ~ 1)
// @param {Number} alpha 角度 默認270度
// @param {Boolean} arc 是否顯示外圓
function drawFillStarPolygon(ctx, xCenter, yCenter, radius, sides, sideIndent, alpha, arc) {
drawStarPolygons(ctx, xCenter, yCenter, radius, sides, sideIndent, alpha, arc);
ctx.fill();
// 畫外接圓
if (arc) {
ctx.beginPath();
ctx.arc(xCenter, yCenter, radius, 0, 2 * Math.PI, true);
ctx.arc(xCenter, yCenter, radius * sideIndent, 0, 2 * Math.PI, true);
ctx.stroke();
}
}
// 繪制描邊的多邊形
// @param {CanvasRenderingContext2D} ctx
// @param {Number} xCenter 中心點X坐標點
// @param {Number} yCenter 中心點Y坐標點
// @param {Number} radius 外圓半徑
// @param {Number} sides 多邊形邊數
// @param {Number} sideIndent (0 ~ 1)
// @param {Number} alpha 角度 默認270度
// @param {Boolean} arc 是否顯示外圓
function drawStrokeStarPolygon(ctx, xCenter, yCenter, radius, sides, sideIndent, alpha, arc) {
drawStarPolygons(ctx, xCenter, yCenter, radius, sides, sideIndent, alpha, arc);
ctx.stroke();
// 畫外接圓
if (arc) {
ctx.beginPath();
ctx.arc(xCenter, yCenter, radius, 0, 2 * Math.PI, true);
ctx.arc(xCenter, yCenter, radius * sideIndent, 0, 2 * Math.PI, true);
ctx.stroke();
}
}
總結
這篇文章介紹了如何借助Canvas中的 moveTo() 和 lineTo() 方法,再配合一些簡單的三角函數的知識繪制正多邊形和星形。為了更好的在Canvas中繪制,將其封裝成對應的函數。如果你有更好的方法,歡迎在下面的評論中與我們分享。
來自:http://www.w3cplus.com/canvas/drawing-regular-polygons.html