前端系列——canvas實現按住鼠標移動繪製出軌跡

概要

工做以來,寫過vue、react、正則、算法、小程序等知識,惟獨沒有寫過canvas,由於實在不會啊!css

2018年,給本身設定一個小目標:學會canvas,達到的效果是能用canvas實現一些css3不容易實現的動畫。html

本文做爲學習canvas的第一篇收穫,不少人初學canvas作的第一個demo是實現一個「鍾」,固然,我也實現了一個,不過不講這個,而是講講一個更有趣、也更簡單的玩意。vue

鼠標按住繪製軌跡

需求

在一塊canvas畫布上,初始狀態畫布什麼都沒有,如今,我想給畫布加一點鼠標事件,用鼠標在畫布上寫字。具體的效果是鼠標移動到畫布上任意一點,而後按住鼠標,移動鼠標的位置,就能夠開始寫字啦!java

圖片描述

原理

先簡單分析下思路,首先咱們須要一個canvas畫布,而後計算鼠標在畫布上的位置,給鼠標綁定onmousedown事件和onmousemove事件,在移動過程當中繪製出路徑,鬆開鼠標的時候,繪製結束。react

這個思路雖然很簡單,可是裏面有些地方須要小技巧實現。css3

一、須要一個html文件,包含canvas元素。es6

這是一個寬度800,高度400的畫布。爲何沒有寫px呢?哦,暫時沒搞懂,canvas文檔推薦的。web

<!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>

二、判斷當前環境是否支持canvas。算法

在main.js中,咱們寫一個自執行函數,下面是兼容性判斷的代碼片斷,「代碼主體」中將會是實現需求的核心。canvas

(function() {
    let theCanvas = document.querySelector('#theCanvas')
    if (!theCanvas || !theCanvas.getContext) {
        //不兼容canvas
        return false
    } else {
        //代碼主體
    }
})()

三、獲取2d對象。

let context = theCanvas.getContext('2d')

四、獲取當前鼠標相對於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)
    }
}

五、有了第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)
}

六、移動鼠標的時候,沒有鼠標長按事件,又該怎麼監聽呢?

這裏用到的小技巧是在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()
    }
}

七、鼠標鬆開的時候,再也不繪製路徑。

有什麼辦法可讓onmouseup事件中阻止掉上面監聽的2種事件呢?方法挺多的,設置onmousedown和onmousemove爲null算是一種,我這裏用到了「開關」。isAllowDrawLine設置爲bool值,來控制函數是否執行,具體代碼能夠看下面完整的源碼。

源碼

分爲3個文件,index.html、main.js、utils.js,這裏用到了es6的語法,我是使用parcle配置好了開發環境,因此不會有報錯,若是你直接複製代碼,運行的時候出現錯誤,在沒法升級瀏覽器的狀況下,能夠將es6語法改爲es5.

一、index.html
上面已經展現了,再也不復述。

二、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
        }
    }
})()

三、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。可是因爲瀏覽器會自動幫你判斷而且移交事件處理,因此徹底不用擔憂。

相關文章
相關標籤/搜索