Canvas: 優雅的代碼做圖系列:中國國旗

Canvas: 優雅的代碼做圖系列:中國國旗

有不少個這練手的,好的差的都有。此次,我演示下用極客的代碼,畫出最標準的中國國旗,並詳細說明代碼是怎麼寫出來的html

繪製規範:canvas

1、嚴格按照繪製說明;ide

2、設置基本單位長度,其餘長度全以單位長度的倍數表示;函數

3、座標系取製做樣式上的座標,製做樣式上有的座標,照取,沒有的,所有經過計算;ui

先把繪製說明複製一遍:編碼

  1. 先將旗面劃分爲4個等分長方形,再將左上方長方形劃分長寬15×10個方格。
  2. 大五角星的中心位於該長方形上5下五、左5右10之處。大五角星外接圓的直徑爲6單位長度。
  3. 四顆小五角星的中心點,第一顆位於上2下八、左10右5,第二顆位於上4下六、左12右3,第三顆位於上7下三、左12右3,第四顆位於上9下一、左10右5之處。
  4. 每顆小五角星外接圓的直徑均爲2單位長度。四顆小五角星均有一角尖正對大五角星的中心點。

下面先講下如何做圖。spa

1、設置 <canvas> 元素並取得 canvascontext 對象:

<canvas id="canvas-flag" width="800"></canvas>
var canvas = document.getElementById("canvas-flag");
var context = canvas.getContext("2d");

<canvas>width 能夠根據須要設置,也能夠只設置 height,也能夠不設置,在後面的代碼中再行設置。但不要同時設置 widthheight,由於長寬比是固定的。指針

2、設置長寬及取得基本單位長度

var WIDTH  = canvas.width;
var UNIT   = WIDTH / 30;
var HEIGHT = canvas.height = UNIT * 20;

由於開始在 <canvas> 元素上已經定義了 width="800",因此單位長度等於 WIDTH / 30,高度須要以單位長度自乘以 20。第三句爲連續賦值,把 UNIT * 20 的值賦給 canvas.height 後,再賦給 HEIGHTcode

若是 <canvas> 元素上沒有定義 width 的話:htm

// 只定義了 height
var HEIGHT  = canvas.height;
var UNIT   = WIDTH / 20;
var WIDTH = canvas.width = UNIT * 30;


// width, height 都沒有定義
var WIDTH  = canvas.width = 800;
var UNIT   = WIDTH / 30;
var HEIGHT = canvas.height = UNIT * 20;

這樣設置事後,後面的單位長度就均可以以這三個變量爲基準了。

3、填充背景:

在這裏,我把兩個顏色設置成了常量。這個圖比較簡單,常量的優點不大,但複雜點的圖,尤爲是一種顏色被用在了好幾個塊上面的時候,優點就出來了。反覆被引用而又未賦給常量的字符串叫魔術字符串,在編碼中應儘可能消滅。

var COLOR_RED    = "#de2910";
var COLOR_YELLOW = "#ffde00";
context.fillStyle = COLOR_RED;
context.fillRect(0, 0, WIDTH, HEIGHT);

4、畫星星

畫五角星相對來講要難一些。把五角星的五個角記爲頂點一、頂點二、頂點三、頂點四、頂點5,一般用筆是這樣畫星星的:頂點1連到頂點3,連到頂點5,連到頂點2,連到頂點4,最後再連到頂點1。實際上大於三的奇數角星均可以這樣畫。

如今回到製圖說明上來,每顆五角星的中心(本質上是圓心),半徑都是肯定的。也就是說,只須要明確頂點1(暫時稱做起始頂點)的角度,就明確了五角星。

canvas 畫布上,橫軸的方向是向右的,縱軸的方向是向下的。

對於大五角星,起始頂點是向上的,角度是:

\[ - \pi / 2 \]

對於小五角星,有一個頂點是明確的,就是指向大五角星中心的那個頂點。如今來計算其角度,設置大五角中心的座標是 \((A_x, A_y)\),小五角星中心的座標是\((B_x, B_y)\),那麼,起始角的角度就是兩點連線的斜率的反正切,再加上 \(\pi\)。連線斜率的反正切是橫軸與連線的角度,加 \(\pi\) 是爲了旋轉180度,讓其指向大五角星的中心。

\[ \arctan \frac{B_y - A_y}{B_x - A_x} + \pi \]

也就是說,每顆星星的中點(本質上是圓心)是肯定的,半徑是肯定的,起始角度也能夠計算出來。

總共要畫六個星星,咱們能夠寫個函數 drawStar,接收如下參數:context 對象,中心橫座標,中心縱座標,半徑,幾角星,填充顏色,起始角。

因爲大於三的奇數角星均可以用這個方法來繪製,全部添加了一個幾角星的參數。還能夠給起始角添加一個向上\(- \pi / 2\)的缺省值。

function drawStar(context, cx, cy, radius, countSides, fillColor, startAngle) {}

在函數體中,首先設置一個畫筆指針,用來鏈接各個頂點。畫筆指針初始化在起始頂點;畫線(其實是路徑,是看不見的線,顯示須要 fill()stroke() )後,畫筆指針移動到相間的頂點,再畫線,再移動,重複 countSides 次。至於填充,這不是這個方法的事,這個方法不負責。

function drawStar(context, cx, cy, radius, countSides, fillColor, startAngle) {

  var pointer = startAngle == null ? - Math.PI / 2 : startAngle;
  // 設置畫筆指針,初始點爲起始頂點
  // 用 startAngle == null 的緣由是爲了防止 startAngle = 0 的狀況。

  var angle = 2 * Math.PI / countSides;
  // 取得相鄰頂點的角度

  context.beginPath();

  for (var i = 0; i < countSides; i++) {  // 須要重複 countSides 次

    context.lineTo(cx + radius * Math.cos(pointer), cy + radius * Math.sin(pointer));
    // 畫筆指針所在點的座標:(圓心加上半徑乘以正弦值, 圓心加上半徑乘以餘弦值)
    // beginPath() 後不調用moveTo() 而直接 lineTo(),至關於 moveTo()
    // 就是說,第一次進循環,畫筆指針點在動起始頂點,之後就是把畫筆指針所在的點連起來

    pointer += 2 * angle;
    // 畫筆指針移動到相間的頂點

  }

  context.fillStyle = fillColor;
  context.fill();
  // 填充

  context.closePath();
}

好了,這樣就好了!

完整的代碼以下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Flag of the people's republic of china</title>
</head>

<body>

  <canvas id="canvas-flag" width="800"></canvas>

  <script>

    (function() {

      // Flag of the people's republic of china, made by KilArmd

      var COLOR_RED    = "#de2910";
      var COLOR_YELLOW = "#ffde00";

      var canvas = document.getElementById("canvas-flag");
      var context = canvas.getContext("2d");

      var WIDTH  = canvas.width;
      var UNIT   = WIDTH / 30;
      var HEIGHT = canvas.height = UNIT * 20;

      context.fillStyle = COLOR_RED;
      context.fillRect(0, 0, WIDTH, HEIGHT);

      drawStar(context, 5 * UNIT, 5 * UNIT, 3 * UNIT, 5, COLOR_YELLOW);
      [ [10, 2], [12, 4], [12, 7], [10, 9] ].forEach(function(coors) {
        drawStar(context, coors[0] * UNIT, coors[1] * UNIT, UNIT, 5, COLOR_YELLOW,
             Math.atan((coors[1] - 5) / (coors[0] - 5)) + Math.PI);
      });

      function drawStar(context, cx, cy, radius, countSides, fillColor, startAngle) {
        var pointer = startAngle == null ? - Math.PI / 2 : startAngle;
        var angle = 2 * Math.PI / countSides;
        context.beginPath();
        for (var i = 0; i < countSides; i++) {
          context.lineTo(cx + radius * Math.cos(pointer), cy + radius * Math.sin(pointer));
          pointer += 2 * angle;
        }
        context.fillStyle = fillColor;
        context.fill();
        context.closePath();
      }

    }());

  </script>

</body>
</html>

本文章由 KilArmd 原創,轉載請註明出處

相關文章
相關標籤/搜索