摘要:鼠標(biāo)按住繪制軌跡需求在一塊畫布上,初始狀態(tài)畫布什么都沒(méi)有,現(xiàn)在,我想給畫布加一點(diǎn)鼠標(biāo)事件,用鼠標(biāo)在畫布上寫字。獲取當(dāng)前鼠標(biāo)相對(duì)于的坐標(biāo)。的解構(gòu)賦值繪制起點(diǎn)。但是由于瀏覽器會(huì)自動(dòng)幫你判斷并且移交事件處理,所以完全不用擔(dān)心。
概要
工作以來(lái),寫過(guò)vue、react、正則、算法、小程序等知識(shí),唯獨(dú)沒(méi)有寫過(guò)canvas,因?yàn)閷?shí)在不會(huì)啊!
2018年,給自己設(shè)定一個(gè)小目標(biāo):學(xué)會(huì)canvas,達(dá)到的效果是能用canvas實(shí)現(xiàn)一些css3不容易實(shí)現(xiàn)的動(dòng)畫。
本文作為學(xué)習(xí)canvas的第一篇收獲,很多人初學(xué)canvas做的第一個(gè)demo是實(shí)現(xiàn)一個(gè)“鐘”,當(dāng)然,我也實(shí)現(xiàn)了一個(gè),不過(guò)不講這個(gè),而是講講一個(gè)更有趣、也更簡(jiǎn)單的玩意。
鼠標(biāo)按住繪制軌跡 需求在一塊canvas畫布上,初始狀態(tài)畫布什么都沒(méi)有,現(xiàn)在,我想給畫布加一點(diǎn)鼠標(biāo)事件,用鼠標(biāo)在畫布上寫字。具體的效果是鼠標(biāo)移動(dòng)到畫布上任意一點(diǎn),然后按住鼠標(biāo),移動(dòng)鼠標(biāo)的位置,就可以開(kāi)始寫字啦!
原理先簡(jiǎn)單分析下思路,首先我們需要一個(gè)canvas畫布,然后計(jì)算鼠標(biāo)在畫布上的位置,給鼠標(biāo)綁定onmousedown事件和onmousemove事件,在移動(dòng)過(guò)程中繪制出路徑,松開(kāi)鼠標(biāo)的時(shí)候,繪制結(jié)束。
這個(gè)思路雖然很簡(jiǎn)單,但是里面有些地方需要小技巧實(shí)現(xiàn)。
1、需要一個(gè)html文件,包含canvas元素。
這是一個(gè)寬度800,高度400的畫布。為什么沒(méi)有寫px呢?哦,暫時(shí)沒(méi)搞懂,canvas文檔推薦的。
canvas學(xué)習(xí)
2、判斷當(dāng)前環(huán)境是否支持canvas。
在main.js中,我們寫一個(gè)自執(zhí)行函數(shù),下面是兼容性判斷的代碼片段,“代碼主體”中將會(huì)是實(shí)現(xiàn)需求的核心。
(function() { let theCanvas = document.querySelector("#theCanvas") if (!theCanvas || !theCanvas.getContext) { //不兼容canvas return false } else { //代碼主體 } })()
3、獲取2d對(duì)象。
let context = theCanvas.getContext("2d")
4、獲取當(dāng)前鼠標(biāo)相對(duì)于canvas的坐標(biāo)。
為什么要獲取這個(gè)坐標(biāo)呢?因?yàn)槭髽?biāo)默認(rèn)是獲取當(dāng)前窗口的相對(duì)坐標(biāo),而canvas可以位于頁(yè)面上的任何位置,所以需要通過(guò)計(jì)算才能得到真實(shí)的鼠標(biāo)坐標(biāo)。
將獲取鼠標(biāo)相對(duì)于canvas的真實(shí)坐標(biāo)封裝成了一個(gè)函數(shù),如果你覺(jué)得抽象,可以在草稿紙上畫圖來(lái)理解為什么要這么運(yùn)算。
通常情況下,可以是x - rect.left和y - rect.top。但為什么實(shí)際上卻是x - rect.left * (canvas.width/rect.width)呢?
canvas.width/rect.width表示判斷canvas中存在的縮放行為,求出縮放的倍數(shù)。
const windowToCanvas = (canvas, x, y) => { //獲取canvas元素距離窗口的一些屬性,MDN上有解釋 let rect = canvas.getBoundingClientRect() //x和y參數(shù)分別傳入的是鼠標(biāo)距離窗口的坐標(biāo),然后減去canvas距離窗口左邊和頂部的距離。 return { x: x - rect.left * (canvas.width/rect.width), y: y - rect.top * (canvas.height/rect.height) } }
5、有了第4步的利器函數(shù),我們可以給canvas加上鼠標(biāo)事件了!
先給鼠標(biāo)綁定按下onmousedown事件,用moveTo繪制坐標(biāo)起點(diǎn)。
theCanvas.onmousedown = function(e) { //獲得鼠標(biāo)按下的點(diǎn)相對(duì)canvas的坐標(biāo)。 let ele = windowToCanvas(theCanvas, e.clientX, e.clientY) //es6的解構(gòu)賦值 let { x, y } = ele //繪制起點(diǎn)。 context.moveTo(x, y) }
6、移動(dòng)鼠標(biāo)的時(shí)候,沒(méi)有鼠標(biāo)長(zhǎng)按事件,又該怎么監(jiān)聽(tīng)呢?
這里用到的小技巧是在onmousedown內(nèi)部再執(zhí)行一個(gè)onmousemove(鼠標(biāo)移動(dòng))事件,這樣就能監(jiān)聽(tīng)按住鼠標(biāo)并且移動(dòng)了。
theCanvas.onmousedown = function(e) { //獲得鼠標(biāo)按下的點(diǎn)相對(duì)canvas的坐標(biāo)。 let ele = windowToCanvas(theCanvas, e.clientX, e.clientY) //es6的解構(gòu)賦值 let { x, y } = ele //繪制起點(diǎn)。 context.moveTo(x, y) //鼠標(biāo)移動(dòng)事件 theCanvas.onmousemove = (e) => { //移動(dòng)時(shí)獲取新的坐標(biāo)位置,用lineTo記錄當(dāng)前的坐標(biāo),然后stroke繪制上一個(gè)點(diǎn)到當(dāng)前點(diǎn)的路徑 let ele = windowToCanvas(theCanvas, e.clientX, e.clientY) let { x, y } = ele context.lineTo(x, y) context.stroke() } }
7、鼠標(biāo)松開(kāi)的時(shí)候,不再繪制路徑。
有什么辦法可以讓onmouseup事件中阻止掉上面監(jiān)聽(tīng)的2種事件呢?方法挺多的,設(shè)置onmousedown和onmousemove為null算是一種,我這里用到了“開(kāi)關(guān)”。isAllowDrawLine設(shè)置為bool值,來(lái)控制函數(shù)是否執(zhí)行,具體代碼可以看下面完整的源碼。
源碼分為3個(gè)文件,index.html、main.js、utils.js,這里用到了es6的語(yǔ)法,我是使用parcle配置好了開(kāi)發(fā)環(huán)境,所以不會(huì)有報(bào)錯(cuò),如果你直接復(fù)制代碼,運(yùn)行的時(shí)候出現(xiàn)錯(cuò)誤,在無(wú)法升級(jí)瀏覽器的情況下,可以將es6語(yǔ)法改成es5.
1、index.html
上面已經(jīng)展示了,不再?gòu)?fù)述。
2、main.js
import { windowToCanvas } from "./utils" (function() { let theCanvas = document.querySelector("#theCanvas") if (!theCanvas || !theCanvas.getContext) { return false } else { let context = theCanvas.getContext("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
/* * 獲取鼠標(biāo)在canvas上的坐標(biāo) * */ 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 }總結(jié)
這里有個(gè)誤區(qū),我用的是canvas對(duì)象綁定事件 theCanvas.onmouseup,其實(shí)canvas不能綁定事件,真正綁定的是document和window。但是由于瀏覽器會(huì)自動(dòng)幫你判斷并且移交事件處理,所以完全不用擔(dān)心。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/107157.html
摘要:支持動(dòng)畫狀態(tài)的,在動(dòng)畫開(kāi)始,執(zhí)行中,結(jié)束時(shí)提供回調(diào)函數(shù)支持動(dòng)畫可以自定義貝塞爾曲線任何包含數(shù)值的屬性都可以設(shè)置動(dòng)畫倉(cāng)庫(kù)文檔演示功能介紹一定程度上,也是一個(gè)動(dòng)畫庫(kù),適用所有的屬性,并且實(shí)現(xiàn)的能更方便的實(shí)現(xiàn)幀動(dòng)畫,替代復(fù)雜的定義方式。 動(dòng)畫調(diào)研-V1 前言:動(dòng)畫從用途上可以分為兩種,一種是展示型的動(dòng)畫,類似于一張GIF圖,或者一段視頻,另一種就是交互性的動(dòng)畫。這兩種都有具體的應(yīng)用場(chǎng)景,比如...
摘要:表示不一定是原生形式的數(shù)據(jù)。接口基于,繼承了的功能并將其擴(kuò)展使其支持用戶系統(tǒng)上的文件。讀取操作完成的時(shí)候,會(huì)變成已完成,并觸發(fā)事件,同時(shí)屬性將包含一個(gè)格式的字符串編碼以表示所讀取文件的內(nèi)容。 溫馨提示:這里除了一些幼稚的小組件啥也沒(méi)有溫馨提示-續(xù):這是一個(gè)新的系列,寫一些實(shí)際開(kāi)發(fā)中遇到的一些常用的功能,想法笨拙,代碼亂套 寫在前面 圖片上傳,作為web端一個(gè)常用的功能,在不同的項(xiàng)目中有...
閱讀 2639·2021-07-26 23:38
閱讀 3514·2019-08-30 13:10
閱讀 2406·2019-08-29 18:33
閱讀 2408·2019-08-29 16:12
閱讀 1094·2019-08-29 10:59
閱讀 1862·2019-08-26 17:40
閱讀 912·2019-08-26 11:59
閱讀 895·2019-08-26 11:41