iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 8
1
Modern Web

寫JS30天系列 第 8

JS 30 - 08 - Fun with HTML5 Canvas

  • 分享至 

  • xImage
  •  

今天要用 canvas 來畫圖,canvas可以用來即時畫圖、畫動畫,還可以用來處理影片,為影片增加特效。今天要來做一個會隨著點擊時間「移動」、「變色」、「縮放」的畫筆

首先要設置一個 <canvas> 來裝我們要畫圖的地方,並設置畫布的大小。要注意是畫布的像素大小,而不是 canvas 的元素大小,元素大小是 canvas.style.width。如果設定了元素大小,會等比例縮放畫布
Canvas width and height in HTML5

<!--產生一個像素是800*800的畫布-->
<canvas id="draw" width="800" height="800"></canvas> 

接著我們使用 cavnas.widthcanvas.height畫布變成全螢幕大小,並設置 canvas.getContext()來讓畫布是 2d 的或 3d 的,這裡我們設置 2d 畫布

const canvas = document.getElementById('draw');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

接著我們要讓滑鼠「拖曳」的時候才會有效果,拖曳就是滑鼠「左鍵點擊」並「移動」,所以我們設置一個 isDrawing 來監測滑鼠是否被點擊,預設是 false。只有在 isDrawingtrue 時才會畫圖。所以只有在 mousedown 事件才會改成 truemouseupmouseout(離開視窗)都會變成 false

let isDrawing = false;

function draw() {
    if (!isDrawing) return;
    //畫圖
}

canvas.addEventListener('mousedown', () => isDrawing = true);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', () => isDrawing = false);
canvas.addEventListener('mouseout', () => isDrawing = false);

接下來就是要設定怎麼畫了, canvas 是原點在左上角,往右往下皆為正的坐標系,知道這點就可以開始畫圖了。首先使用 .beginPath() 表示開始規劃路徑, .moveTo() 移動畫筆, .lineTo() 則是從當前位置畫線到參數的位置, .closePath() 表示結束規劃路徑,最後 .stroke() 才是真的畫圖。

let lastX = 0; //設定初始x位置
let lastY = 0; //設定初始y位置
function draw(e) {
    ctx.beginPath();//開始規劃路徑
    ctx.moveTo(lastX, lastY);//畫筆移動到座標(lastX, lastY)
    ctx.lineTo(e.offsetX, e.offsetY); //畫線到 (e.offetX,e.offetX)
    ctx.closePath();//結束規劃路徑
    ctx.stroke(); //作畫
}

這時候你會看到的圖案是是一個從(0, 0)到滑鼠位置的軌跡線,這是因為我們並沒有隨時更新位置。
我們可以把時間切成極短的時間,這樣「短時間」的線連起來就會是直線

function draw(e) {
    ...
    [lastX, lastY] = [e.offsetX, e.offsetY]; //將結束位置設為下次起始位置
}

這時會發現每次下筆都是從上一次結束的位置,而不是下筆位置,因此我們再對 mousedown 做修改
一點擊就設定成起始位置。

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

這樣我們就完成畫線了。

但是此時看線不太是圓的,所以我們要對他做修改,讓線更圓滑。

ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.lineWidth = 50;

其中 linesJoin 是兩線相接的樣式 ;lineCap 是對末端做修改; lineWidth 是畫筆的粗度。

接著我們要來控制線的粗細變化,誠如上面提到,我們將初始寬度設定在 50,我們希望線條寬度在1~100之間變化。
所以我們設定一個變數 directiontrue 來表示正向, false 來表示反向。
當超出邊界的時候就更改 direction,讓畫筆的寬度的變量變號。

let direction = true;
function draw(e) {
    ...
    if (ctx.lineWidth >= 100 || ctx.lineWidth <= 1) {
        direction = !direction;
    }
    if(direction){
        ctx.lineWidth++;
    } else {
        ctx.lineWidth--;
    }
}

最後是顏色,這邊用的是 hsl 顏色表示法,HSL分別代表 Hue(顏色), Saturation(飽和度), Lightness(亮度)。我們先不管後面兩個,只看顏色表示法。由於 hue 的顏色是360度的方式表示紅橙黃綠藍靛紫,所以我們讓變數 hue雖呼叫函式時增加, 到達360時,就讓它歸0,這樣就可以呈現顏色變化了

let hue = 0;
function draw(e) {
    ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
    ctx.beginPath();
    ...
    hue++;
    if (hue >= 360) {
        hue = 0;
    }
}

完整程式碼
Demo


上一篇
JS 30 - 07 - Array Cardio Part II
下一篇
JS30 - 09 - Dev Tools Domination
系列文
寫JS30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言