寫在系列開篇
在學(xué)習(xí)了基本的 HTML / CSS / JavaScript 之后沒有啥 side project 就直接開始學(xué) Angular 做項目。遇到最近很火的JavaScript30這個項目后決心好好實踐一下 Vanilla JavaScript。
對于每個挑戰(zhàn),我會自己在看過視頻了解原理后自己實現(xiàn)一遍并在 blog 里記錄過程、想法和相關(guān)資料。源碼放在我的 Github上,demo 通過 Github Pages 部署,點擊這里或 Github 的 readme 中連接可以訪問。
Objective
實現(xiàn)一個簡單的彩虹效果的 Canvas 畫筆。

Steps
- 獲取 canvas 以及其 context 并設(shè)置初始屬性;
- 設(shè)置需要的4個事件(mouseup, mousedown, mouseout, mousemove)的監(jiān)聽器;
- 在畫圖函數(shù)中設(shè)置 context 的狀態(tài)并完成畫圖。
Details and Things learned
<canvas> 可以完成圖像的繪制,功能十分強大。MDN 有非常好的教程,今天這個小 demo 只使用了 <canvas> 的線段繪制的一部分 API。
Prepare
首先要獲取元素以及 context:
const canvas = document.querySelector('#draw');
const ctx = canvas.getContext('2d');
這里我們只需要 2d 的繪制功能,如果需要繪制 3d 圖形,可以 getContext('webgl')。
然后設(shè)置幾個屬性:
-
ctx.strokeStyle = 'hsl(50, 100%, 50%)';strokeStyle 可以設(shè)置 color, gradient, pattern -
ctx.lineJoin = 'round';指定畫出的線段如何相交,提供了三種選項
lineJoin -
ctx.lineCap = 'round';指定畫出的線段端點如何截止,同樣三種我們選擇 round
lineCap -
ctx.lineWidth = 10;畫線段的寬度,in space units。
程序相關(guān)的
-
let isDrawing = false;用于標明是否處于繪圖狀態(tài),在鼠標按下松開移出繪圖區(qū)時更新。 -
let lastX, lastY;記錄每次移動鼠標前點的位置,從而可以畫出光滑曲線。 -
let hue = 0;使用 hsl 來表征顏色,從而可以得到連續(xù)的顏色變化。 -
let widthGrow = true;用來讓寬度來回變大變小。
畫圖過程
canvas 可以畫出多種圖形以及 animation 等特效,最簡單的畫路徑根據(jù) MDN 教程 分為以下幾步:
-
beginPath()- 清空context的所有狀態(tài)開始新的畫圖過程。 -
Path methods- 操作畫圖的點的移動,包括moveTo(),lineTo(),bezierCurveTo(),quadraticCurveTo()等等。 -
closePath()- 結(jié)束 path 的規(guī)劃,之后的指令重新對 context 起作用。 -
stroke()- 畫! -
fill()- 填充,這次沒用到。
加入顏色,寬度調(diào)整后的完整代碼:
canvas.addEventListener('mousemove', (e) => {
if (!isDrawing) return false;
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.closePath();
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
hue = (hue + 1) % 360;
if (ctx.lineWidth == 66 || ctx.lineWidth == 1) {
widthGrow = !widthGrow;
}
ctx.lineWidth = ctx.lineWidth + (widthGrow ? 1 : -1);
})
Notice
如果不使用 [lastX, lastY] = [e.offsetX, e.offsetY];,每次畫圖的線似乎斷斷續(xù)續(xù)的,原因是每次只畫一個點,而不是一段線。
HSL
以前只會 RGBA 顏色表示,實際上 "Hue-saturation-lightness model (HSL)" 表示方法更為直觀,hue 是 0 - 360 的數(shù)字,然后選擇飽和度和亮度,可以方便的猜測出結(jié)果的顏色。這個網(wǎng)站可以方便預(yù)覽選取的顏色。