iT邦幫忙

2023 iThome 鐵人賽

DAY 8
0
Modern Web

JS30 x 鐵人30 x MDN doc系列 第 8

[Day8] - Fun with HTML5 Canvas(JS30 x 鐵人 30 x MDN)

  • 分享至 

  • xImage
  •  

實做一個 Canvas 畫布,會隨著使用者滑鼠軌跡自動變換畫筆顏色及粗度

觀察 index-Start.html,嗯...很乾淨就只有一個 canvas 元素

Canvas 元素是什麼,如英文單字字面上意思「畫布」透過 Javascript Canvas API 操作繪製產生畫面,兩者缺一不可,Canvas 應用非常廣泛,可用於網頁遊戲、動畫、地圖繪製、檔案電子簽名,可以說是你想的到的,canvas 都做得出來,前端知名設計軟體Figma底層也是使用 Canvas。還有另一個 Web API 也搭配 canvas 元素使用,那就是:WebGL API。這邊簡潔整理兩者差異:Canvas 著重 2D 畫面,若要進行 3D 設計則使用 WebGL API。

  1. 第一步先創造一個 canvas 元素並設定畫布尺寸(這邊原作者已經幫我們建好了)接著都是 javascript 的事了。
<canvas id="draw" width="800" height="800"></canvas>
  1. 要操作 canvasm 元素不外乎一樣要取得元素節點,然後存放於變數中,接著使用CanvasElement: getContext() method設定這個畫布使用 2d 接口(CanvasRenderingContext2D)並存於變數ctx中,接下來我們的任何操作都是使用ctx操作2D stroke() method了。
const canvas = document.querySelector("#draw");
const ctx = canvas.getContext("2d");
  1. 那要如何從滑鼠軌跡變成 canvas 中畫出線條呢,這就要用到滑鼠事件了,其實 MDN mousemove 下方範例已經有展示透過滑鼠畫 canvas 的範例,有點像是文件電子簽名功能,與本日目標只差別在不會變動顏色變化及線條粗度,那麼我們就要先弄懂2D stroke() methodElement: mousemove event存在著什麼關係讓我們能完成這個效果,之後再來使用這個範例。
  • 要在 canvas 中要畫出一條線需要以下步驟
//清除上次路徑
ctx.beginPath();
//設定線頭 ctx.moveTo(X座標,Y座標)
ctx.moveTo(0, 0);
//設定線尾 ctx.lineTo(X座標,Y座標)
ctx.lineTo(100, 100);
// 繪製至cnavas上顯示
ctx.stroke();
  • 而 mouseEvent 觸發後印出 Event 物件後可以發現裡面有不少屬性有都有跟 X、Y 相關,有些值看似相同其實各有差異,我們這裡要使用clientXclientY,代表的是鼠標在這個視窗內的座標
MouseEvent {
clientX: 77,
clientY: 127,
}

那我們何不每次記住上次事件的 X、Y 值當作線頭,這次觸發事件的 X、Y 值當作線尾持續畫線呢

  1. 於是我們將畫線步驟先包成以下函式:drawLine
function drawLine(ctx, pastX, pastY, currentX, currentY) {
  ctx.beginPath();
  ctx.lineWidth = lineWidth;
  ctx.moveTo(pastX, pastY);
  ctx.lineTo(currentX, currentY);
  ctx.stroke();
}
  1. 接著我們在全域宣告XY變數拿來儲存上次觸發滑鼠滑動的事件,以及isDrawing變數來判定要不要畫線,因為需求是滑鼠按著狀態下的滑動才會出現線條,可以想像成 true = 打該筆蓋/false = 蓋上筆蓋。
let isDrawing = false;
let x;
let y;

// 當滑鼠按下,賦值當前坐標進X、Y,切換成isDrawing5狀態成true(打開筆蓋)
canvas.addEventListener("mousedown", (e) => {
  isDrawing = true;
  x = e.clientX;
  y = e.clientY;
});
// 當滑鼠滑動,判斷是否繪畫,如果true 執行畫線函式後,再將這次事件座標賦改進X、Y值
canvas.addEventListener("mousemove", (e) => {
  if (isDrawing) {
    drawLine(ctx, x, y, e.clientX, e.clientY);
    x = e.clientX;
    y = e.clientY;
  }
});

//如果滑鼠彈起或滑鼠離開畫布,則切換isDrawing5狀態成false(蓋上筆蓋)
canvas.addEventListener("mouseup", (e) => (isDrawing = false));
canvas.addEventListener("mouseleave", (e) => (isDrawing = false));
  1. 最後只要補上線條顏色與粗度切換邏輯就能達到題目要求囉
//全域宣告線條粗度變數供遞增遞減
let lineWidth = 0;
//全域宣告hsv色相變數供遞增遞減
let color = 0;
function drawLine(ctx, pastX, pastY, currentX, currentY) {
  ctx.beginPath();
  //線條頭尾補上半圓形
  ctx.lineCap = "round";
  //hsv色相 0~360 來回遞增遞減達成彩虹效果
  if (color === 0) colorDistance = 3;
  if (color === 360) colorDistance = -3;
  color += colorDistance;
  //筆畫粗細 0~100 來回遞增遞減
  if (lineWidth === 0) distance = 1;
  if (lineWidth === 100) distance = -1;
  lineWidth += distance;

  ctx.strokeStyle = `hsl(${color},100%,50%)`;
  ctx.lineWidth = lineWidth;
  ctx.moveTo(pastX, pastY);
  ctx.lineTo(currentX, currentY);
  ctx.stroke();
}

👉Github Demo 頁面 👈

👉 好想工作室 15th 鐵人賽看板 👈

參考資料

  1. Javascript 30 官網
    https://javascript30.com/
  2. MDN 官網
    https://developer.mozilla.org/en-US/
  3. 色相 - 維基百科
    https://zh.wikipedia.org/zh-tw/%E8%89%B2%E7%9B%B8

上一篇
[Day7] - Array Cardio part2(JS30 x 鐵人 30 x MDN)
下一篇
[Day9] - Dev Tools Domination(JS30 x 鐵人 30 x MDN)
系列文
JS30 x 鐵人30 x MDN doc30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言