视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001 知道1 知道21 知道41 知道61 知道81 知道101 知道121 知道141 知道161 知道181 知道201 知道221 知道241 知道261 知道281
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
canvas实现按住鼠标移动绘制出轨迹的方法实例
2020-11-27 18:49:22 责编:小采
文档

需求

在一块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
}

下载本文
显示全文
专题