iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 8
0
自我挑戰組

新手村-30 Day JS Coding Challenge系列 第 8

新手村08 - Fun with HTML5 Canvas

08 - Fun with HTML5 Canvas

俗話說的好,一天一蘋果,醫生遠離我

一天一 JS,What the f*ck JavaScript?

small steps every day - 記錄著新手村日記

完成目標

  • 功能
    • 可以畫圖的書布
    • 粗細、顏色變化
  • 畫面
    • 邊畫邊換色、粗細
    • 停下來,再繼續畫,粗細和顏色與上次停筆相同

index_START.html

<!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

從事件的角度,先來寫使用者的觸發事件吧!分別是 mousedownmousemovemouseupmouseleave 四種事件,記得 mousemove 這個事件是會一直觸發,使用者在脫離 Canvas 這個 800x800 畫布的時候,也會觸發脫離框框事件,分別有兩種作法:一個是 mouseoutmouseleave,而兩者的差別在 DOM 的層級。

另外,因為要使用者按下去才能畫,因此在 mousemove 方法中,我們要假設如果狀態不能畫畫的 if(!drawing) return,就不能畫畫

mouveseout v.s mouse leave:https://tinyurl.com/y282qc3g

<script>
  const canvas = document.querySelector("#draw");
  let drawing = false;

  canvas.addEventListener("mousedown",()=> {
    drawing = true;
  })
  canvas.addEventListener("mousemove",()=> {
    if(!drawing) return;
    console.log("move"); // 假設console視為畫畫
  })
  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

<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

<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

<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>

讓顏色會隨著紅橙黃綠藍靛紫 & 粗細會持續變化:

<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();
    
    // 讓角度持續的在 0-360 變化
    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

<!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>
  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>

<style>
  html, body {
    margin: 0;
  }
  canvas{
    border: 1px solid #000;
  }
</style>

</body>
</html>

本刊同步於個人網站:http://chestertang.site/

本次範例程式碼原作者來源:https://tinyurl.com/yxhsxcnl


上一篇
新手村07 - Array Cardio Day 2
下一篇
新手村09 - Dev Tools Domination
系列文
新手村-30 Day JS Coding Challenge30

尚未有邦友留言

立即登入留言