canvas實現按住滑鼠移動繪製出軌跡的方法執行個體

來源:互聯網
上載者:User

需求

在一塊canvas畫布上,初始狀態畫布希麼都沒有,現在,我想給畫布加一點滑鼠事件,用滑鼠在畫布上寫字。具體的效果是滑鼠移動到畫布上任意一點,然後按住滑鼠,移動滑鼠的位置,就可以開始寫字啦!

本文作為學習canvas的第一篇收穫,很多人初學canvas做的第一個demo是實現一個“鐘”,當然,我也實現了一個,不過不講這個,而是講講一個更有趣、也更簡單的玩意。

滑鼠按住繪製軌跡

原理

先簡單分析下思路,首先我們需要一個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 = 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

/** 擷取滑鼠在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}
相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.