JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks
、No Compilers
、No Libraries
、No Boilerplate
在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。
另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。
使用 JavaScript 實作出能在 HTML Canvas 上用筆刷畫圖的極簡版小畫家。
在 body
內放入 canvas
標籤作為我們的畫布,這裡雖然指定大小為 800*800,但之後會利用 JS 把它改成跟視窗(window)一樣大。
<canvas id="draw" width="800" height="800"></canvas>
取得 canvas
標籤後,將畫布的寬、高,將其改成跟視窗內部的寬、高一樣的數值。
const canvas = document.querySelector('#draw');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas
只是一個空白的畫布,實際上我們需要"取得畫布的渲染環境
(rendering context)"才能在渲染環境上作畫。
我們呼叫 canvas
的 getContext()
方法並放入 "2d
" 作為參數,順利取得 canvas
的 2D 渲染環境
,若是要取得 3D 渲染環境只要放入 "3d" 作為參數就好。
緊接著,我們調整 context
的 strokeStyle
屬性值,更改作畫時的畫筆顏色。
context
的 lineJoin
是用來設定兩條長度不為0的線段如何在接合處連接,把值設定為 "round
" 則在相連部分以扇形連接。
context
的 lineCap
是用來設定線段末端的呈現方式,把值設定為 "round
" 則在末端部分以扇形呈現。
context
的 lineWidth
用來設定線條的粗細。
const ctx = canvas.getContext('2d');
ctx.strokeStyle = '#BADASS';
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.lineWidth = 100;
我們希望在視窗下按住滑鼠拖曳時作畫,所以在 canvas
上註冊4個事件監聽器。
觸發'mousedown
'事件時,設定 isDrawing = true
,表示現在可以作畫;設定 [lastX,lastY] = [e.offsetX,e.offsetY]
,更新作畫的起始點。
觸發'mousemove
'事件時,用 draw()
方法持續的作畫。
觸發'mouseup
'、'mouseout
'時,皆設定 isDrawing = false
,表示在滑鼠沒被按住和滑鼠離開視窗的情況下都停止作畫。
let isDrawing = false;//flag
let lastX = 0;
let lastY = 0;
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);
事件處理方法 draw()
,在最初就先判斷現在能不能作畫,不能的話(isDrawing = false
)就直接返回。
ctx.beginPath()
用來建立一個新的作畫路徑。ctx.moveTo()
用來移動路徑起始點的座標。ctx.lineTo()
用來指定這條路徑的終點座標。ctx.stroke()
將這條路徑描出來。
[lastX,lastY] = [e.offsetX,e.offsetY]
用來不斷更新"此次"按住滑鼠拖曳作畫過程中持續變動的路徑起始點。
function draw(e){
if(!isDrawing) return;
ctx.beginPath();
//start from
ctx.moveTo(lastX,lastY);
//go to
ctx.lineTo(e.offsetX,e.offsetY);
ctx.stroke();
[lastX,lastY] = [e.offsetX,e.offsetY];
}
HSL
的 H(Hue,色相角度),是由 0~360 為止的色相循環,0是紅色、120是綠色、240是藍色,Hue 本身不加單位,詳細說明可以參考下方補充資料 CSS Coke 大大的文章。
宣告 hue
變數指定畫筆顏色,並持續的將 hue + 1,當 hue >= 360時,代表顏色已經循環過一次,故將其重設為0。
宣告 direction
變數作為目前該加粗畫筆還是讓畫筆變細的判斷,direction
的初始值是 true
也就是讓畫筆變粗。第一個 if 判斷目前的畫筆粗細是不是 >= 100 或 <= 1,如果條件符合就將 direction
變為相反值(true 變 false)。在第二個的 if-else 判斷,如果 direction = true
就加粗畫筆、反之則讓畫筆變得更細。
let hue = 0;
let direction = true;
function draw(e){
/*.......省略*/
ctx.strokeStyle = `hsl(${hue},100%,50%)`;
/*.......省略*/
hue++;
if(hue >= 360){
hue = 0;
}
if(ctx.lineWidth >= 100 || ctx.lineWidth <= 1){
direction = !direction;
}
if(direction){
ctx.lineWidth++;
}else{
ctx.lineWidth--;
}
}
最後來介紹一個特別的東西,ctx
的 globalCompositeOperation
主要用來處理疊圖(線段重疊)的情況,若將其設定為 'multiply
' 則將上層像素與相對應的下層像素相乘,整體效果是讓顏色更加趨於黑色。
ctx.globalCompositeOperation = 'multiply';
globalCompositeOperation
)Canvas 基本用途
CanvasRenderingContext2D.strokeStyle
CanvasRenderingContext2D.lineJoin
CanvasRenderingContext2D.lineCap
CanvasRenderingContext2D.lineWidth
CanvasRenderingContext2D.beginPath()
CanvasRenderingContext2D.moveTo()
CanvasRenderingContext2D.lineTo()
RGB、HSL、Hex 網頁色彩碼,看完這篇全懂了
JS一秒區分clientX,offsetX,screenX,pageX之間關係
CanvasRenderingContext2D.globalCompositeOperation