【譯】用一天入門 canvas 和 JavaScript

liaozeen發表於2019-02-28

【譯】用一天入門 canvas 和 JavaScript

在最近的 JavaScript 30天挑戰中,我有機會了解 HTML 內建 canvas的特性。相對適宜的等級和學習曲線,使我寫下整個過程。

HTML canvas 用最簡單的方式,使web 開發者能夠通過JavaScript在網頁上繪製圖形。這樣,HTML 元素變得更加有趣。

<canvas> 元素只是個容器,你總是需要使用 JavaScript 來準確繪製圖形。有人可能會說,我們總是可以新增這些點,也可以新增SVG,但這又會多麼有趣?

回到 <canvas> 元素:canvas 在 HTML 頁面上是一個矩形。canvas 是預設沒有邊框和內容的。

寫法像這樣:

<canvas id="canvas" width="200" height="100"></canvas>複製程式碼

開始

已經做了那麼多介紹,讓我們專注於使用簡單的原生 JavaScript(不是很舊——ES6)做些有趣的東西。首先,我們看下初始的檔案。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>HTML5 Canvas</title>
    <link rel="stylesheeet" href="style.css" />
</head>
<body>
<canvas id="canvas" width="800" height="800"></canvas>
<script src="app.js"></script>
</body>
</html>複製程式碼


讓我們慢慢講。我們有個叫 style.css 的樣式層疊表。然後我們定義一個寬800和高800的 canvas 。最後,我們在 script 標籤裡引用了 app.js ,所有魔法都在這裡。我們開始使用 app.js 做一些事情。

const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');

canvas.width = window.innnerWidth;
canvas.height = window.innerHeight;

ctx.strokeStyle = "#BADA55"
ctx.lineWidth = 1;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';複製程式碼


  • 我們一開始,在第一行選擇 canvas 元素儲存 到 叫 canvas 的常量裡。
  • 然後,我們在同一個 canvas 中獲取2D上下文,並儲存到指定的變數中。
  • 設定 canvas 的寬度和高度,分別等於 視窗的寬度和高度。

現在,我們終於有了畫布,繼續定義畫布的最基本屬性。

  • ctx.strokeStyle 設定或返回用於描繪的顏色,漸變色或圖案。是的,你理解的對:預設顏色是 #BADASS
  • ctx.lineWidth 設定或返回 當前線條的寬度。我們把它設定為 1,稍後我們會講到這個。
  • ctx.lineJoin 設定或返回 兩條線相匯時所建立的邊角的型別。我們設定當兩條線交匯時的邊角為圓角。
  • ctx.lineCap 設定或返回線的端點的樣式。我們將它設定為圓形,這樣當我們不遇到其他線時,我們仍然會得到相同的整齊的圓形,具體取決於之前定義的線的寬度。

線我們已經完成了所有這些工作,讓我們看看如何在畫布上繪圖。

首先,我們需要為畫布上的滑鼠移動新增事件偵聽器,然後觸發一個繪製內容的函式。我們來看看 app.js 檔案中可能新增的內容。

let isDrawing = fasle;

function draw(e){
    if(!isDrawing) return;
    console.log(e)
}

canvas.addEventListener('mousemove',draw);
canvas.addEventListener('mousedown', () => isDrawing = true);
canvas.addEventListener('mouseup', () => isDrawing = fasle);
canvas.addEventListener('mousedown', () => isDrawing = fasle);複製程式碼

我們來講解下:

  • 我們開始定義一個叫 isDrawing 的變數,來判斷使用者是否要在畫布上畫圖。我們會在後面再講到這個。
  • 現在,我們有一個叫 draw 的函式,它會被觸發,並負責完成整個繪製動作。
  • 最後,我們增加一個約束條件,確保我們捕獲正確的事件,並且只在需要的時候執行 draw 函式。

通過宣告 isDrawing 變數,並將其值設定為 false,我們在 canvas 元素被載入後設定 canvas 的初始狀態,但還不繪製。然後在每個後續事件偵聽器中,我們使用內嵌函式,並每次根據觸發的事件型別更改 變數 isDrawing 變數的值。

draw 函式 的開頭,如果 isDrawing 的值為 false ,函式會執行 return 語句。如果 isDrawing 的值為 true, 會執行draw 函式。

繪製函式

我們來擴充套件下 draw 函式:

let isDrawing = false;
let lastX = 0;
let lastY = 0;

function draw(e){
    if(!isDrawing) return;
    console.log(e);
    ctx.beginPath();
    ctx.moveTo(lastX,lastY);
    ctx.lineTO(e.offsetX,e.offsetY);
    ctx.stroke();
}複製程式碼

  • 我們在開頭定義兩個全域性變數:lastXlastY,並設定他們的初始值為 0.
  • 如果你現在進入瀏覽器的控制檯,你會發現你已經記錄了所有滑鼠移動。這個 MouseEvent 物件有一些非常重要和有用的屬性:【譯】用一天入門 canvas 和 JavaScript

                                               滑鼠事件物件

我們只關注物件裡的 offsetXoffsetY 屬性。

  • 每次執行 ctx.beginPath ,我們新建一個路徑,或者重置當前的路徑。每次事件被觸發,我們希都會執行這個動作。
  • ctx.moveTo 移動路徑到畫布指定的點上,但還沒繪製線。在例子中,在函式外面的全域性環境裡定義 lastXlastY
  • ctx.lineTo 增加新的點,並從上一個點到新的點之間繪製一條線。
  • ctx.stroke() 真正繪製你已經定義的路徑 —— 辛苦了,夥計們!

ctx.lineTo 裡,我們利用事件的屬性 offsetXoffsetY 獲得 在畫布上的最新點的 XY ,只用 ctx.lineTo 繪製一條線。

我們幾乎所有的東西都準備好了。在頁面上的每個滑鼠事件都會在畫布上繪製一條線——但還有些問題,沒有太多酷的東西。所以,讓我們加點酷的東西。

酷!

現在,所有線都是從畫布中的(0,0)點繪製的。 我們將其設定為載入畫布或執行 draw 函式時開始繪製的初始點。

讓我們解決這個問題,以獲得更好的實時體驗。 如果考慮一下,答案非常簡單:每次執行draw函式時,我們都希望初始點始終是 MouseEvent 物件的 offsetXoffsetY 屬性。

通過使用 ES6 的陣列解構,我們可以將變數 lastXlastY 分別重置為 滑鼠事件物件的屬性 offsetXoffsetY,我們在 draw 函式的最後執行。我們來看看加了新東西后的 app.js

function draw(e){
    if(!isDrawing) return;
    console.log(e);
    ctx.beginPath();
    ctx.moveTo(lastX,lastY);
    ctx.lineTo(e.offsetX,e.offsetY);
    ctx.stroke();
    [lastX,lastY] = [e.offsetX,e.offsetY];
}

canvas.addEventListener('mousemove',draw);
canvas.addEventListener('mousedown',(e) => {
    isDrawing = true;
    [lastX,lastY] = [e.offsetX,e.offsetY];
};

canvas.addEventListener('mouseup',()=> isDrawing = false);
canvas.addEventListener('mousemove',()=> isDrawing = false);複製程式碼
  • mousemove 事件發生時,我們會執行 draw 函式。然後繼續執行,在 draw 函式裡使用 ES6 解構 設定 變數 lastXlastY
  • mousedown 事件發生時,首先我們執行巢狀函式,如你所見,我們再一次將 變數 lastXlastY 設定為當前事件的偏移屬性。這是為了確保當我們在畫布上從一個點移動到另一個點時,我們可以在畫布上看到這條線。

讓它變得豐富多彩,並在筆畫中新增一些動態元素。

let hue = 0;
let direction = true;

function draw(e){    
    if(!isDrawing) return;
    console.log(e);
    ctx.strokeStyle = `hsl(${hue},100%,50%)`;
    ctx.beginPath();
    ctx.moveTo(lastX,lastY);
    ctx.lineTo(e.offsetX,e.offsetY);
    ctx.stroke();
    [lastX,lastY] = [e.offsetX,e.offsetY];
    hue++;
    if(hue>=360){
        hue = 0;
    }
    if(ctx.lineWidth >= 75 || ctx.lineWidth <= 1){
        direction = !direction;
    }
    if(direction){
        ctx.lineWidth++;
    } else {
        ctx.lineWidth = 1;
    }
}

canvas.addEventListener('mousemove',draw);
canvas.addEventListener('mousedown',(e) => {
    isDrawing = true;
    [lastX,lastY] = [e.offsetX,e.offsetY];
};

canvas.addEventListener('mouseup',()=> isDrawing = false);
canvas.addEventListener('mousemove',()=> isDrawing = false);複製程式碼

神聖時刻!!

這還有很多事要處理,我們一一分解:

  • 我定義一個新的變數 hue ,並設定其值為 0
  • 如果你還不知道色調,為什麼會很棒,那就去谷歌試試吧,或者只需點選這裡就可以了。

在其最簡單的形式,hsl 讓我們在從0到360範圍裡使用相同的彩虹的顏色。 每個數字都有一個亮度和透明度。 定義 hsl 看起來像這樣:hsl(173,99%,50%)。 此處 173 表示顏色 , 99% 是亮度,50% 是透明度。

同樣,通過使用 ES6 的模板字串,來修改 hsl 的值,像這樣:

ctx.strokeStyle = `hsl( ${hue}, 100%, 50%)`

接下來,我們增加 hue 變數的值,該變數在每個mousemove事件中更改筆觸的顏色。 一旦色調值增加到360,我們將在上述要點的第14行上將 hue 的值重置為0。 但即使我們不這樣做,我們仍然會有同樣的結果。 即便如此,我們還是做正確的事吧

if(hue > 360){
    hue = 0
}複製程式碼

下一步,我們加點動態的東西,使筆畫的寬度實時變化,像這樣:

if(ctx.linewidth >= 75 || ctx.lineWidth <= 1){
    direction = ! direction;
}

if(direction){
    ctx.lineWidth++
}else {
    ctx.lineWidth = 0
}複製程式碼

這裡我們所做的就是首先檢查當前的線寬是大於75還是小於1。 如果是,則將初始值為 true 的變數 direction 取反。

接下來,我們檢查變數 direction 的值是否為true。 如果是,則將 lineWidth 的值增加1,否則將 lineWidth 重置為0

沒有很多JavaScript。 如果你正確跟著做,你應該有個漂亮的畫布了。

讓我們快速地看一下最終的檔案結構是什麼樣子的。 因為我們只更改了app.js檔案,所以我將只向你展示這一點,因為index.html從一開始就幾乎沒有變化。

canvas.width = window.innerWidth;canvas.height = window.innerHeight;ctx.strokeStyle = '#BADA55';ctx.lineWidth = 1;ctx.lineJoin = 'round';ctx.lineCap = 'round';let isDrawing = false;let lastX = 0;let lastY = 0;let hue = 0;let direction = true;function draw(e) {    if(!isDrawing) {        return; // 滑鼠沒有按下時不執行.    }    console.log(e);    ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;    ctx.beginPath();    ctx.moveTo(lastX, lastY);    ctx.lineTo(e.offsetX, e.offsetY);    ctx.stroke();    [lastX, lastY] = [e.offsetX, e.offsetY];    hue++;    if(hue>=360){        hue = 0;    }    if(ctx.lineWidth >= 75 || ctx.lineWidth <= 1) {        direction = !direction;    }    if(direction){        ctx.lineWidth++;    } else {        ctx.lineWidth = 1;    }}canvas.addEventListener('mousemove', draw);canvas.addEventListener('mousedown', (e) => {    isDrawing = true;    [lastX, lastY] = [e.offsetX, e.offsetY];});canvas.addEventListener('mouseup', ()=> isDrawing = false);canvas.addEventListener('mouseout', () => isDrawing = false);複製程式碼

在 canvas 和 JavaScript 的結合中,介紹的只是冰山一角。 我會鼓勵你做更多的研究,使畫布看起來更好。 也許新增幾個按鈕來清除螢幕,或者選擇一種特定的顏色在畫布上繪製。 有很多選擇!


相關文章