08 - Fun with HTML5 Canvas
俗話說的好,一天一蘋果,醫生遠離我
一天一 JS,What the f*ck JavaScript?
small steps every day - 記錄著新手村日記
完成目標
- 功能
- 畫面
- 邊畫邊換色、粗細
- 停下來,再繼續畫,粗細和顏色與上次停筆相同
index_START.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>HTML5 Canvas</title> </head> <body> <canvas id="draw" width="800" height="800"></canvas> <script> </script>
<style> html, body { margin: 0; } </style>
</body> </html>
|
JS - step by step
首先,這次的範例只是Canvas的基本、滑鼠的操作,如果很喜歡 Canvas 的人,歡迎來我的 Codepen 看更多範例
繪圖基礎語法與動畫原理:https://tinyurl.com/yxftfxoz
畫布的座標系操作:https://tinyurl.com/y4s8fxjr
從事件的角度,先來寫使用者的觸發事件吧!分別是 mousedown
、mousemove
、mouseup
、mouseleave
四種事件,記得 mousemove
這個事件是會一直觸發,使用者在脫離 Canvas 這個 800x800 畫布的時候,也會觸發脫離框框事件,分別有兩種作法:一個是 mouseout
、mouseleave
,而兩者的差別在 DOM 的層級。
另外,因為要使用者按下去才能畫,因此在 mousemove
方法中,我們要假設如果狀態不能畫畫的 if(!drawing) return
,就不能畫畫
mouveseout v.s mouse leave:https://tinyurl.com/y282qc3g
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script> const canvas = document.querySelector("#draw"); let drawing = false;
canvas.addEventListener("mousedown",()=> { drawing = true; }) canvas.addEventListener("mousemove",()=> { if(!drawing) return; console.log("move"); }) canvas.addEventListener("mouseup",()=> { drawing = false; }) canvas.addEventListener("mouseleave",()=> { drawing = false; }) </script>
|
透過 CanvasRenderingContext2D
來操作 Canvas 的 Context,記得不是操作它的 DOM!處理一下要畫畫線條的顏色 ctx.fillStyle
,它是透過HSL 色環來計算顏色 "hsl(0,100%,50%)"
,各欄位分別意思是(顏色、飽和度、明亮度)、調整筆畫粗細 linewidth
、筆畫收尾的形狀 lineCap
、轉折角 lineJoin
CanvasRenderingContext2D:https://tinyurl.com/y39wgnms
CanvasRenderingContext2D.fillStyle:https://tinyurl.com/y5j8zu5g
CanvasRenderingContext2D.lineWidth:https://tinyurl.com/q3pm3aw
CanvasRenderingContext2D.lineCap:https://tinyurl.com/y63rq9rf
CanvasRenderingContext2D.lineJoin:https://tinyurl.com/yxesyrbw
1 2 3 4 5 6 7
| <script> let ctx = canvas.getContext('2d'); ctx.strokeStyle = `hsl(0,100%,50%)`; ctx.lineWidth = 100; ctx.lineCap = 'round'; </script>
|
現在來製作畫畫吧!(點對點的概念,按下去會是一個點,然後會連到結束時候的點成為一條線),必須將按下時的點及結束的點存起來,透過 x、y 變數存下 offsetX、offsetY
MouseEvent.offsetX:https://tinyurl.com/phhseue
MouseEvent.offsetY:https://tinyurl.com/yys9e773
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <script> const canvas = document.querySelector("#draw"); let ctx = canvas.getContext('2d'); ctx.strokeStyle = `hsl(0,100%,50%)`; ctx.lineWidth = 50; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; let x = 0; let y = 0;
let drawing = false; canvas.addEventListener("mousedown",(e)=> { drawing = true; [x,y] = [e.offsetX, e.offsetY]; }) canvas.addEventListener("mousemove",()=> { if(!drawing) return; console.log("move"); }) canvas.addEventListener("mouseup",()=> { drawing = false; }) canvas.addEventListener("mouseleave",()=> { drawing = false; }) </script>
|
畫製 Canvas 圖的規則滿前顯易懂的,可以參考下面的範例連結(切記在製作Canvas的時候程式結束一定要有分號,不然都會出錯喔!)
CanvasRenderingContext2D.beginPath():https://tinyurl.com/ycrkznet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <script> const canvas = document.querySelector("#draw"); let ctx = canvas.getContext('2d'); ctx.strokeStyle = `hsl(0,100%,50%)`; ctx.lineWidth = 50; ctx.lineCap = 'round'; ctx.lineJoin = 'round';
let drawing = false let x = 0, y = 0;
canvas.addEventListener("mousedown",(e)=> { drawing = true; [x, y] = [e.offsetX, e.offsetY]; }) canvas.addEventListener("mousemove",(e)=> { if(!drawing) return; console.log("move");
ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(e.offsetX, e.offsetY); ctx.stroke(); [x, y] = [e.offsetX, e.offsetY]; }) canvas.addEventListener("mouseup",()=> { drawing = false; }) canvas.addEventListener("mouseleave",()=> { drawing = false; }) </script>
|
讓顏色會隨著紅橙黃綠藍靛紫 & 粗細會持續變化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <script> const canvas = document.querySelector("#draw"); let ctx = canvas.getContext('2d');
colorDeg = 0; lineWidth = 50; dir = 1; ctx.strokeStyle = `hsl(${colorDeg},100%,50%)`; ctx.lineWidth = lineWidth; ctx.lineCap = 'round'; ctx.lineJoin = 'round';
let drawing = false let x = 0, y = 0;
canvas.addEventListener("mousedown",(e)=> { drawing = true; [x, y] = [e.offsetX, e.offsetY]; }) canvas.addEventListener("mousemove",(e)=> { if(!drawing) return; console.log("move");
ctx.beginPath(); colorDeg = colorDeg < 360 ? colorDeg + 1 : 0; ctx.strokeStyle = `hsl(${colorDeg},100%,50%)`; if (lineWidth < 1 || lineWidth > 50) { dir *= -1; } lineWidth -= dir ; ctx.lineWidth = lineWidth; ctx.moveTo(x, y); ctx.lineTo(e.offsetX, e.offsetY); ctx.stroke(); [x, y] = [e.offsetX, e.offsetY]; }) canvas.addEventListener("mouseup",()=> { drawing = false; }) canvas.addEventListener("mouseleave",()=> { drawing = false; }) </script>
|
就大功告成啦!
JS - Final
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <script> const canvas = document.querySelector("#draw"); let ctx = canvas.getContext('2d');
colorDeg = 0; lineWidth = 50; dir = 1; ctx.strokeStyle = `hsl(${colorDeg},100%,50%)`; ctx.lineWidth = lineWidth; ctx.lineCap = 'round'; ctx.lineJoin = 'round';
let drawing = false let x = 0, y = 0;
canvas.addEventListener("mousedown",(e)=> { drawing = true; [x, y] = [e.offsetX, e.offsetY]; }) canvas.addEventListener("mousemove",(e)=> { if(!drawing) return; console.log("move");
ctx.beginPath(); colorDeg = colorDeg < 360 ? colorDeg + 1 : 0; ctx.strokeStyle = `hsl(${colorDeg},100%,50%)`; if (lineWidth < 1 || lineWidth > 50) { dir *= -1; } lineWidth -= dir ; ctx.lineWidth = lineWidth; ctx.moveTo(x, y); ctx.lineTo(e.offsetX, e.offsetY); ctx.stroke(); [x, y] = [e.offsetX, e.offsetY]; }) canvas.addEventListener("mouseup",()=> { drawing = false; }) canvas.addEventListener("mouseleave",()=> { drawing = false; }) </script>
|
本刊同步於個人網站:http://chestertang.site/
本次範例程式碼原作者來源:https://tinyurl.com/yxhsxcnl