有不少個這練手的,好的差的都有。此次,我演示下用極客的代碼,畫出最標準的中國國旗,並詳細說明代碼是怎麼寫出來的。html
繪製規範:canvas
1、嚴格按照繪製說明;ide
2、設置基本單位長度,其餘長度全以單位長度的倍數表示;函數
3、座標系取製做樣式上的座標,製做樣式上有的座標,照取,沒有的,所有經過計算;ui
先把繪製說明複製一遍:編碼
下面先講下如何做圖。spa
<canvas>
元素並取得 canvas
和 context
對象:<canvas id="canvas-flag" width="800"></canvas>
var canvas = document.getElementById("canvas-flag"); var context = canvas.getContext("2d");
<canvas>
的 width
能夠根據須要設置,也能夠只設置 height
,也能夠不設置,在後面的代碼中再行設置。但不要同時設置 width
和 height
,由於長寬比是固定的。指針
var WIDTH = canvas.width; var UNIT = WIDTH / 30; var HEIGHT = canvas.height = UNIT * 20;
由於開始在 <canvas>
元素上已經定義了 width="800"
,因此單位長度等於 WIDTH / 30
,高度須要以單位長度自乘以 20
。第三句爲連續賦值,把 UNIT * 20
的值賦給 canvas.height
後,再賦給 HEIGHT
。code
若是 <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;
這樣設置事後,後面的單位長度就均可以以這三個變量爲基準了。
在這裏,我把兩個顏色設置成了常量。這個圖比較簡單,常量的優點不大,但複雜點的圖,尤爲是一種顏色被用在了好幾個塊上面的時候,優點就出來了。反覆被引用而又未賦給常量的字符串叫魔術字符串,在編碼中應儘可能消滅。
var COLOR_RED = "#de2910"; var COLOR_YELLOW = "#ffde00";
context.fillStyle = COLOR_RED; context.fillRect(0, 0, WIDTH, HEIGHT);
畫五角星相對來講要難一些。把五角星的五個角記爲頂點一、頂點二、頂點三、頂點四、頂點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>