工作以來,寫過vue、react、正規、演算法、小程式等知識,唯獨沒有寫過canvas,因為實在不會啊!
2018年,為自己設定一個小目標:學會canvas,達到的效果是能用canvas實現一些css3不容易實現的動畫。
本文作為學習canvas的第一篇收穫,許多人初學canvas做的第一個demo是實現一個鐘,當然,我也實現了一個,不過不講這個,而是講講一個更有趣、也更簡單的玩意。
滑鼠按住繪製軌跡需求在一塊canvas畫布上,初始狀態畫布什麼都沒有,現在,我想在畫布上加一點滑鼠事件,用滑鼠在畫布上寫字。具體的效果是滑鼠移動到畫布上任意一點,然後按住滑鼠,移動滑鼠的位置,就可以開始寫字囉!
原理先簡單分析下思路,首先我們需要一個canvas畫布,然後計算滑鼠在畫布上的位置,給滑鼠綁定onmousedown事件和onmousemove事件,在移動過程中繪製出路徑,放開滑鼠的時候,繪製結束。
這個想法雖然很簡單,但是裡面有些地方需要小技巧實現。
1.需要一個html文件,包含canvas元素。
這是一個寬度800,高度400的畫布。為什麼沒有寫px呢?哦,暫時沒搞懂,canvas文檔推薦的。
<!doctype html><html class=no-js lang=zh> <head> <meta charset=utf-8> <meta http-equiv=x-ua-compatible content=ie=edge> <title>canvas學習< /title> <meta name=description content=> <meta name=viewport content=width=device-width, initial-scale=1> <link rel=manifest href=site.webmanifest> <link rel=apple-touch-icon href=icon.png> <link rel=stylesheet href=css/main.css> </head> <body> <canvas id=theCanvas width =800 height=400></canvas> <script src=js/main.js></script> </body></html>
2、判斷當前環境是否支援canvas。
在main.js中,我們寫一個自執行函數,下面是相容性判斷的程式碼片段,程式碼主體中將會是實作需求的核心。
(function() { let theCanvas = document.querySelector('#theCanvas') if (!theCanvas || !theCanvas.getContext) { //不相容於canvas return false } else { //程式碼主體}})()
3、獲取2d對象。
let context = theCanvas.getContext('2d')
4.取得目前滑鼠相對於canvas的座標。
為什麼要獲取這個座標呢?因為滑鼠預設是取得目前視窗的相對座標,而canvas可以位於頁面上的任何位置,所以需要透過計算才能得到真實的滑鼠座標。
將取得滑鼠相對於canvas的真實座標封裝成了函數,如果你覺得抽象,可以在草稿紙上畫圖來理解為什麼要這麼運算。
通常情況下,可以是x - rect.left和y - rect.top。但為什麼實際上是x - rect.left * (canvas.width/rect.width)呢?
canvas.width/rect.width表示判斷canvas中存在的縮放行為,求縮放的倍數。
const windowToCanvas = (canvas, x, y) => { //取得canvas元素距離視窗的一些屬性,MDN上有解釋let rect = canvas.getBoundingClientRect() //x和y參數分別傳入的是滑鼠距離視窗的座標,然後減去canvas距離視窗左邊和頂部的距離。 return { x: x - rect.left * (canvas.width/rect.width), y: y - rect.top * (canvas.height/rect.height) }}
5.有了第4步的利器函數,我們可以為canvas加上滑鼠事件了!
先給滑鼠綁定按下onmousedown事件,用moveTo繪製座標起點。
theCanvas.onmousedown = function(e) { //取得滑鼠按下的點相對canvas的座標。 let ele = windowToCanvas(theCanvas, e.clientX, e.clientY) //es6的解構賦值let { x, y } = ele //繪製起點。 context.moveTo(x, y)}
6.移動滑鼠的時候,沒有滑鼠長按事件,又該怎麼監聽呢?
這裡用到的小技巧是在onmousedown內部再執行一個onmousemove(滑鼠移動)事件,這樣就能監聽按住滑鼠並且移動了。
theCanvas.onmousedown = function(e) { //取得滑鼠按下的點相對canvas的座標。 let ele = windowToCanvas(theCanvas, e.clientX, e.clientY) //es6的解構賦值let { x, y } = ele //繪製起點。 context.moveTo(x, y) //滑鼠移動事件theCanvas.onmousemove = (e) => { //移動時取得新的座標位置,用lineTo記錄目前的座標,然後stroke繪製上一個點到目前點的路徑let ele = windowToCanvas(theCanvas, e.clientX, e.clientY) let { x, y } = ele context.lineTo(x, y) context.stroke() }}
7.滑鼠鬆開的時候,不再繪製路徑。
有什麼辦法可以讓onmouseup事件中阻止掉上面監聽的2種事件呢?方法還蠻多的,設定onmousedown和onmousemove為null算是一種,我這裡用到了開關。 isAllowDrawLine設定為bool值,來控制函數是否執行,具體程式碼可以看下面完整的原始碼。
原始碼分成3個文件,index.html、main.js、utils.js,這裡用到了es6的語法,我是使用parcle配置好了開發環境,所以不會有報錯,如果你直接複製程式碼,運行的時候發生錯誤,在無法升級瀏覽器的情況下,可以將es6語法改成es5.
1、index.html
上面已經展示了,不再複述。
2、main.js
import { windowToCanvas } from './utils'(function() { let theCanvas = document.querySelector('#theCanvas') if (!theCanvas || !theCanvas.getContext) { return false } else { let context = thevas.Context = thevas.Context ('2d') let isAllowDrawLine = false theCanvas.onmousedown = function(e) { isAllowDrawLine = true let ele = windowToCanvas(theCanvas, e.clientX, e.clientY) let { x, y } = ele context.moveTo(x, y) theCanvas.onmousemove = (e) => { if (isAllowDrawLine) { let ele = windowToCanvas(theCanvas, e.clientX, e.clientY) let { x, y } = ele context.lineTo(x, y) context.stroke() } } } theCanvas.onmouseup = function() { isAllowDrawLine = false } }}} )()
3、utils.js
/** 取得滑鼠在canvas上的座標* */const windowToCanvas = (canvas, x, y) => { let rect = canvas.getBoundingClientRect() return { x: x - rect.left * (canvas.width/rect .width), y: y - rect.top * (canvas.height/rect.height) }}export { windowToCanvas}總結
這裡有個迷思,我用的是canvas物件綁定事件theCanvas.onmouseup,其實canvas不能綁定事件,真正綁定的是document和window。但由於瀏覽器會自動幫你判斷並且移交事件處理,所以完全不用擔心。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。