【譯】用一天入門 canvas 和 JavaScript

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

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

<canvas> 元素只是個容器,你老是須要使用 JavaScript 來準確繪製圖形。有人可能會說,咱們老是能夠添加這些點,也能夠添加SVG,但這又會多麼有趣?html

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

寫法像這樣:git

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

開始

已經作了那麼多介紹,讓咱們專一於使用簡單的原生 JavaScript(不是很舊——ES6)作些有趣的東西。首先,咱們看下初始的文件。github

<!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 作一些事情。web

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 的寬度和高度,分別等於 窗口的寬度和高度。

如今,咱們終於有了畫布,繼續定義畫布的最基本屬性。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 對象有一些很是重要和有用的屬性:

                                               鼠標事件對象

咱們只關注對象裏的 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 的結合中,介紹的只是冰山一角。 我會鼓勵你作更多的研究,使畫布看起來更好。 也許添加幾個按鈕來清除屏幕,或者選擇一種特定的顏色在畫布上繪製。 有不少選擇!

相關文章
相關標籤/搜索