iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 8
0
Modern Web

新手也能懂的JS30系列 第 8

JS30-Day8-Fun with HTML5 Canvas

JS30-Day8-Fun with HTML5 Canvas

Day8-課題內容

進入JS30的第八天,今天我們要透過HTML的<canvas>元素,在頁面上創造出一塊畫布,並讓我們在上面畫出彩色的線條。
在範例當中,當我們在畫面上按下滑鼠拖曳,就會沿著鼠標軌跡出現一條線,而這條線又會隨著過程有粗細及顏色的變化。
實作連結-1
實作連結-2

canvas元素

<canvas>元素,可以在HTML中創造一個類似畫布的區域,再利用程式腳本在上面繪出圖形。

  1. canvas屬性:
  • height:單位為pixel,表示此元素座標的空間高度,預設為150px。
  • width:單位為pixel,表示此元素座標的空間寬度,預設為300px。

要修改<canvas>元素大小的話,需要直接使用在元素中加入屬性,如果在CSS屬性中強制修改的話,<canvas>的網格大小將被縮放來符合元素尺寸。

//方法一
<canvas id="canvas" width="300" height="300">
//方法二
let canvas = document.querySelector('canvas);
canvas.width = 300;
canvas.height = 300;
  1. canvas方法:
  • getContext()<canvas>元素所創造的區域一開始為空白,透過此方法可以取得渲染環境來產生或操作顯示內容,而畫布上可以有一或多個渲染環境,此方法中的值則代表渲染環境的類型(2D or 3D)。
  1. 繪製圖形:
    以下將簡介幾個在canvas中繪製出不同東西的方法:
  • 矩型:
context.fillRect(x,y,width,height);
x:矩形左上角的起始點X軸座標,往右為正。
y:矩形左上角的起始點Y軸座標,往下為正。
width:寬。
height:高。

矩形範例:

let canvas = document.getElementById('mycanvas');
let ctx = canvas.getContext('2d');
//設定填滿圖形用的顏色
ctx.fillStyle='blue';
ctx.fillRect(25, 50, 120, 150);

  • 線:
//beginPath()方法:產生一條路徑,或重置目前的路徑。
context.beginPath();
//moveTo():該線的起點座標。
context.moveTo(X-Start, Y-Start);
//lineTo():該線的終點座標。
context.lineTo(X-End, Y-End);
//線條寬度
context.lineWidth = number;
//端點樣式
context.lineCap =  'butt'<切平> or 'round'<圓形> or 'square'<方形>;
//交叉點樣式
context.lineJoin = 'bevel'<斜角> or 'round'<圓形> or 'miter'<尖角>;
//線條顏色
context.strokeStyle = color;
//繪製路徑
context.stroke();

線範例:

let canvas = document.getElementById('mycanvas');
let ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(75,75);
ctx.lineTo(225, 75);
ctx.lineTo(75, 225);
ctx.lineTo(225, 225);
ctx.lineWidth = 20;
ctx.lineCap = 'round';
ctx.lineJoin = 'miter';
ctx.strokeStyle = 'green';
ctx.stroke();

  • 圓形:

context.beginPath();
context.arc(x,y,r,sAngle,eAngle);
x:圓心X軸座標,往右為正。
y:圓心Y軸座標,往下為正。
r:圓的半徑。
sAngle:起始角度。
eAngle:結束角度。

圓形範例:

let canvas = document.getElementById('mycanvas');
let ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(150, 150, 100, 0, 2*Math.PI);
ctx.fillStyle='rgba(0, 0, 0, 0.5)';
//填滿圖形
ctx.fill();
ctx.lineWidth= 4;
ctx.strokeStyle='blue';
//畫出曲線
ctx.stroke();

  • 字體:
context.strokeText(text,x,y);
context.fileText(text,x,y);
text:要呈現的字。
x:字串左上起始點X軸座標。
y:字串左上起始點Y軸座標。

字體範例:

// stroke font
let canvas = document.getElementById('mycanvas');
let ctx = canvas.getContext('2d');
ctx.beginPath();
//字體大小及字型
ctx.font = '50px Arial';
ctx.strokeStyle = 'red';
ctx.strokeText('Hello World', 25, 75);

// fill font
let canvas = document.getElementById('mycanvas');
let ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.font = '50px Arial';
ctx.fillStyle = 'blue';
ctx.fillText('Hello World', 25, 225);

  • 漸層
    透過Gradient我們可以創造一個有漸層的顏色物件,可用來填入我們繪製的圖形。
//線性漸層
context.createLinearGradient(x0,y0,x1,y1);
x0:漸層起點的X軸座標。
y0:漸層起點的y軸座標。
x1:漸層終點的X軸座標。
y1:漸層終點的y軸座標。
//圓形漸層
context.createRadialGradient(x0,y0,r0,x1,y1,r1);
x0:漸層起點圓心的X軸座標。
y0:漸層起點圓心的y軸座標。
r0:漸層起點圓心的半徑大小。
x1:漸層終點圓心的X軸座標。
y1:漸層終點圓心的y軸座標。
r1:漸層終點圓心的半徑大小。
//新增顏色
gradient.addColorStop(stop,color);
stop:代表漸層起點到終點間的位置。該值從0(起點)到1(終點)。
color:代表漸層在該位置上的顏色。

線性漸層範例:

let canvas = document.getElementById('mycanvas');
let ctx = canvas.getContext('2d');
let grd = ctx.createLinearGradient(50,50, 150, 50);
grd.addColorStop(0, 'red');
grd.addColorStop(0.5, 'orange');
grd.addColorStop(1, 'yellow');
ctx.fillStyle=grd;
ctx.fillRect(50, 50, 150, 150);

圓形漸層範例:

let canvas = document.getElementById('mycanvas');
let ctx = canvas.getContext('2d');
let grd = ctx.createRadialGradient(150, 150, 0, 150, 150, 120);
grd.addColorStop(0, 'blue');
grd.addColorStop(0.25, 'green');
grd.addColorStop(0.5, 'yellow');
grd.addColorStop(0.75, 'orange');
grd.addColorStop(1, 'red');
ctx.beginPath();
ctx.fillStyle = grd;
ctx.arc(150 ,150, 120, 0, 2*Math.PI);
ctx.fill();

進入JS30-Day8

要完成今天的課題,我們需要依序完成以下的步驟:

  1. 利用<canvas>元素創造畫布區域。
  2. 監聽滑鼠事件。
  3. 執行繪圖函數來畫出線條。
  4. 線條縮放及變色。

步驟一

首先我們要創造畫布區域,目標是整個頁面,因此先透過CSS屬性,將bodymarginpadding歸零。然後建立一個<canvas>元素,並利用前面所學的innerHeight以及innerWidth將其寬跟高指定為整個頁面。

html,body {
    margin: 0;
    padding: 0;
}
var canvas = document.querySelector('#draw');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

步驟二

完成畫布的設定之後,再來要監聽事件。當我們按下滑鼠按鍵之後,隨著滑鼠的移動會繪製出線條,當放開按鍵的時候,又失去繪圖的功能。因此我們要監聽的事件包含mousedownmouseup以及mousemove。而當mousedown事件觸發的時候,我們要加上監聽mousemove這個事件,而當放開滑鼠按鍵的時候,我們要解除監聽mousemove這個事件:

canvas.addEventListener('mousedown', checkClick);
canvas.addEventListener('mouseup', removeClick);

//按下滑鼠的時候
function checkClick(event) {
    //取得滑鼠按下時的位置
    lastX = event.offsetX;
    lastY = event.offsetY;
    canvas.addEventListener('mousemove', createCircle);
};

function removeClick(event) {
    canvas.removeEventListener('mousemove', createCircle);
};

步驟三

從上面的canvas介紹,我們已經知道如何畫出一條線,透過將按下滑鼠的位置作為原點,移動之後滑鼠的位置為終點,可以畫出第一條線,而滑鼠下一次移動的線條終點又以上一次的終點為起點持續畫出新的線條,如此一來就能完成初步的畫線。

function createLine(event) {
    //取得滑鼠移動後的位置座標    
    var newX = event.offsetX;
    var newY = event.offsetY;
    var getContext = canvas.getContext('2d');
    getContext.beginPath();
    //指定剛開始滑鼠按下的位置作為線的起點
    getContext.moveTo(lastX, lastY);
    //指定滑鼠移動之後的位置作為線的終點
    getContext.lineTo(newX, newY);
    getContext.lineCap = 'round';
    getContext.lineJoin = 'round';
    getContext.stroke();
    //原本線條的終點作為下一條線的起點
    lastX = event.offsetX;
    lastY = event.offsetY;
};

步驟四

最後我們要透過getContext.strokeStyle以及getContext.lineWidth這兩種方法來改變我們線條的顏色以及粗細。

  1. 顏色
    作者在這邊使用到一個特別的顏色屬性HSL Colors。[3]
    此屬性可以透過改變色相環上的角度來取得不同顏色,大家可以由作者提供的參考網站嘗試看看。[4]
hsl(hue, saturation, lightness);
Hue:色相。以紅色為0度(360度);黃色為60度;綠色為120度;青色為180度;藍色為240度;最後又回到紅色,形成一個色相環。
Saturation:飽和度。
Lightness:亮度。

我們要讓.strokeStyle指定為hsl(),並讓其中hue的值在函數執行的同時隨著增加,如此一來就能使線條的顏色持續變化。

  1. 寬度
    當然我們也可以利用改變顏色的方法來改變寬度。但是我們怎麼讓他脹大到某個寬度之後就逐漸縮小,細到某個程度之後又逐漸變大?
    透過設定一個布林值為True的元素並給一判斷式,讓他在超過上下限的時候修改為相反的布林值,我們就能利用該元素的布林值來判斷目前的寬度需要增加還是減少。

將以上兩個條件加入我們code,完成之後就能完成今天的課題囉:

//指定初始的色相為0度角的紅色
var hue = 0;
//增加或縮減的判斷條件
var increase = true;
//指定初始的線寬為1px
var radius = 1;

function createLine(event) {
    var newX = event.offsetX;
    var newY = event.offsetY;
    var getContext = canvas.getContext('2d');
    getContext.beginPath();
    getContext.moveTo(lastX, lastY);
    getContext.lineTo(newX, newY);
    //將繪出的線寬指定為變數'radius'                        
    getContext.lineWidth = radius;
    getContext.lineCap = 'round';
    getContext.lineJoin = 'round';
    //將繪出的線顏色指定為hsl                     
    getContext.strokeStyle = `hsl(${hue}, 100%, 50%)`;
    getContext.stroke();
    lastX = event.offsetX;
    lastY = event.offsetY;
    //隨著函式執行色相角度增加而改變顏色                        
    hue++;
    //當radius >= 100 或 radius <1 判斷值改為相反
    if(radius >= 100  || radius <1) {
        increase = !increase;
    };
    //布林值為True的時候radius持續增加
    if(increase) {
        radius++;
    //布林值為False的時候radius持續減少           
    } else {
        radius--;
    }
};

總結

今天雖然只有學到<canvas>元素,但裡頭包含了許多有趣的屬性,還有如何在上面繪製出各種圖形,而這邊也嘗試利用圓型取代線做出類似滴水感覺,有興趣的讀者可以從實作二的連結玩玩看。感謝您的閱讀。

參考資料

  1. javascript30
  2. MDN-canvas
  3. w3School-HSL
  4. Mother-effing hsl()

上一篇
JS30-Day7-Array Cardio 2
下一篇
JS30-Day9-Dev Tools Domination
系列文
新手也能懂的JS3030

尚未有邦友留言

立即登入留言