Canvas學習:繪制正多邊形

xiaotech 7年前發布 | 32K 次閱讀 前端技術 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

 

 本文由用戶 xiaotech 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!