对于想了解如何利用canvas实现按住鼠标移动绘制出轨迹的读者,本文将提供新的信息,我们将详细介绍canvas记录鼠标移动轨迹,并且为您提供关于canvas火焰动态效果随鼠标移动、Canvas入门-利
对于想了解如何利用canvas实现按住鼠标移动绘制出轨迹的读者,本文将提供新的信息,我们将详细介绍canvas记录鼠标移动轨迹,并且为您提供关于canvas 火焰动态效果 随鼠标移动、Canvas入门-利用Canvas绘制好玩的电子时钟、canvas制作鼠标拖动绘制图形、canvas实现按住鼠标移动绘制出轨迹的方法实例的有价值信息。
本文目录一览:- 如何利用canvas实现按住鼠标移动绘制出轨迹(canvas记录鼠标移动轨迹)
- canvas 火焰动态效果 随鼠标移动
- Canvas入门-利用Canvas绘制好玩的电子时钟
- canvas制作鼠标拖动绘制图形
- canvas实现按住鼠标移动绘制出轨迹的方法实例
如何利用canvas实现按住鼠标移动绘制出轨迹(canvas记录鼠标移动轨迹)
本篇文章主要介绍了canvas实现按住鼠标移动绘制出轨迹的示例代码,内容挺不错的,现在分享给大家,也给大家做个参考。概要
工作以来,写过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 = 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 }
总结
这里有个误区,我用的是canvas对象绑定事件 theCanvas.onmouseup,其实canvas不能绑定事件,真正绑定的是document和window。但是由于浏览器会自动帮你判断并且移交事件处理,所以完全不用担心。
canvas 火焰动态效果 随鼠标移动
效果图
从上图可以看出,主要分为 4 个部分:
Fire 单词字样
随鼠标而动的火光
呈现黄白红色的火焰
不断盘旋往上的火花
除了 Fire 字样,其他三样都随鼠标移动。
那么,就从如何实现这四样效果入手。
全局变量的声明
//更新页面用requestAnimationFrame替代setTimeout
window.requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
var canvas = document.getElementById(''fire'');
var ctx = canvas.getContext(''2d'');
var w = canvas.width = canvas.offsetWidth;
var h = canvas.height = canvas.offsetHeight;
1. 实现 Fire 单词字样,这里用到一个字体,css 代码中引入
@import url(https://fonts.googleapis.com/css?family=Amatic+SC);
//Fire单词
ctx.font = "15em Amatic SC";
ctx.textAlign = "center";
ctx.strokeStyle = "rgb(50, 20, 0)";
ctx.fillStyle = "rgb(120, 10, 0)";
ctx.lineWidth = 2;
ctx.strokeText("Fire", w / 2, h * .72);
ctx.fillText("Fire", w / 2, h * .72);
这里 Filre 字样是填充效果,外面还包着一层未填充的 Fire 单词效果,颜色为 rgb (50, 20, 0)。
2. 实现随鼠标移动的火光。
//火光阴影
var grd = ctx.createRadialGradient(this.x, this.y - 200, 200,
this.x, this.y - 100, 0);
grd.addColorStop(0, "rgb(15, 5, 2)");
grd.addColorStop(1, "rgb(30, 10, 2)");
ctx.beginPath();
ctx.arc(this.x, this.y - 100, 200, 0, 2 * Math.PI);
ctx.fillStyle = grd;
ctx.fill();
这里用到了渐变色,原理可查看 http://www.runoob.com/tags/canvas-createradialgradient.html 。
具体如何随鼠标移动,放到最后面讲。
3. 实现火焰,由图可以看出,就是很多不同颜色的圆,因为要画很多个圆,这里就定义一个对象,方便多次生成实例。
function flame(x, y) {
this.cx = x;
this.cy = y;
this.x = rand(this.cx - 25, this.cx + 25);
this.y = rand(this.cy - 5, this.cy + 5);
this.vx = rand(-1, 1);
this.vy = rand(1, 3);
this.r = rand(20, 30);
this.life = rand(3, 6);
this.alive = true;
this.c = {
h: Math.floor(rand(2, 40)),
s: 100,
l: rand(80, 100),
a: 0,
ta: rand(0.8, 0.9)
}
this.update = update;
function update() {
this.y -= this.vy;
this.vy += 0.05;
this.x += this.vx;
if (this.x < this.cx) {
this.vx += 0.1;
} else {
this.vx -= 0.1;
}
if (this.r > 0) {
this.r -= 0.1;
} else {
this.r = 0;
}
this.life -= 0.15;
if (this.life <= 0) {
this.c.a -= 0.05;
if (this.c.a <= 0) {
this.alive = false;
}
} else if (this.life > 0 && this.c.a < this.c.ta) {
this.c.a += .08;
}
}
this.draw = draw;
function draw(ctx) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r * 3, 0, 2 * Math.PI);
ctx.fillStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + (this.c.a / 20) + ")";
ctx.fill();
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.fillStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + this.c.a + ")";
ctx.fill();
}
}
其中 HSLA (H,S,L,A) 为一种色彩模式 https://www.html.cn/book/css/values/color/hsla.htm,为了实现火焰效果,这些圆的颜色各不相同,色调亮度在一定范围内随机生成。而从下面代码中可以看出
this.life -= 0.15;
if (this.life <= 0) {
this.c.a -= 0.05;
if (this.c.a <= 0) {
this.alive = false;
}
} else if (this.life > 0 && this.c.a < this.c.ta) {
this.c.a += .08;
}
透明度 a 值是先不断增加,再不断减小直到 a <= 0 火焰 alive 为 false,这个火焰就可以退休了,会从火焰数组中删除这个实例。
4. 火花,可以看出火花是由很多线条组成的,这里也要定义一个对象
function spark(x, y) {
this.cx = x;
this.cy = y;
this.x = rand(this.cx - 40, this.cx + 40);
this.y = rand(this.cy, this.cy + 5);
this.lx = this.x;
this.ly = this.y;
this.vx = rand(-4, 4);
this.vy = rand(1, 3);
this.r = rand(0, 1);
this.life = rand(4, 5);
this.alive = true;
this.c = {
h: Math.floor(rand(2, 40)),
s: 100,
l: rand(40, 100),
a: rand(0.8, 0.9)
}
this.update = update;
function update() {
this.lx = this.x;
this.ly = this.y;
this.y -= this.vy;
this.x += this.vx;
if (this.x < this.cx) {
this.vx += 0.2;
} else {
this.vx -= 0.2;
}
this.vy += 0.08;
this.life -= 0.1;
if (this.life <= 0) {
this.c.a -= 0.05;
if (this.c.a <= 0) {
this.alive = false;
}
}
}
this.draw = draw;
function draw(ctx) {
ctx.beginPath();
ctx.moveTo(this.lx, this.ly);
ctx.strokeStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + (this.c.a / 2) + ")";
ctx.lineWidth = this.r * 2;
ctx.lineCap = ''round'';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(this.lx, this.ly);
ctx.lineTo(this.x, this.y);
ctx.strokeStyle = "hsla( " + this.c.h + ", " + this.c.s + "%, " + this.c.l + "%, " + this.c.a + ")";
ctx.lineWidth = this.r;
ctx.stroke();
}
}
最后,如何实现随鼠标移动的效果。
定义一个 Fire 对象,在对象方法中实现上述 4 中元素的绘制与更新显示
//定义fire对象
function fire(x, y) {
this.x = x;
this.y = y;
this.aFires = [];
this.aSparks = [];
this.aSparks2 = [];
this.update = update;
function update(x, y) {
this.aFires.push(new flame(x, y));
this.aSparks.push(new spark(x, y));
this.aSparks2.push(new spark(x, y));
for (var i = 0; i < this.aFires.length; i++) {
if (this.aFires[i].alive) {
this.aFires[i].update();
} else {
this.aFires.splice(i, 1);
}
}
//console.log(aFires.length)
for (var i = 0; i < this.aSparks.length; i++) {
if (this.aSparks[i].alive) {
this.aSparks[i].update();
} else {
this.aSparks.splice(i, 1);
}
}
for (var i = 0; i < this.aSparks2.length; i++) {
if (this.aSparks2[i].alive) {
this.aSparks2[i].update();
} else {
this.aSparks2.splice(i, 1);
}
}
}
this.draw = draw;
function draw(ctx) {
//设置或返回如何将一个源(新的)图像绘制到目标(已有的)的图像上。
//source-over 默认。在目标图像上显示源图像。
//源图像 = 您打算放置到画布上的绘图。
//目标图像 = 您已经放置在画布上的绘图。
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "rgba(15, 5, 2, 1)";
ctx.fillRect(0, 0, window.innerWidth, window.innerHeight);
//火光阴影
var grd = ctx.createRadialGradient(this.x, this.y - 200, 200,
this.x, this.y - 100, 0);
grd.addColorStop(0, "rgb(15, 5, 2)");
grd.addColorStop(1, "rgb(30, 10, 2)");
ctx.beginPath();
ctx.arc(this.x, this.y - 100, 200, 0, 2 * Math.PI);
ctx.fillStyle = grd;
ctx.fill();
//Fire单词
ctx.font = "15em Amatic SC";
ctx.textAlign = "center";
ctx.strokeStyle = "rgb(50, 20, 0)";
ctx.fillStyle = "rgb(120, 10, 0)";
ctx.lineWidth = 2;
ctx.strokeText("Fire", w / 2, h * .72);
ctx.fillText("Fire", w / 2, h * .72);
ctx.globalCompositeOperation = "overlay";
for (var i = 0; i < this.aFires.length; i++) {
this.aFires[i].draw(ctx);
}
ctx.globalCompositeOperation = "soft-light";
for (var i = 0; i < this.aSparks.length; i++) {
if ((i % 2) === 0)
this.aSparks[i].draw(ctx);
}
ctx.globalCompositeOperation = "color-dodge";
for (var i = 0; i < this.aSparks2.length; i++) {
this.aSparks2[i].draw(ctx);
}
}
}
可以看到在 draw 方法中,实现了绘制 4 个元素。
在 update 方法中就是生成火焰和火花的实例,以及显示状态的变化。
最后实现一个 fire 的实例,引入 fire 的 draw 和 update 方法,就可以实现火焰效果了。
var current_fire = new fire(w * .5, h * .75);
function init() {
current_fire.update(current_fire.x, current_fire.y);
if (current_fire.x) {
current_fire.draw(ctx);
}
requestAnimationFrame(init);
}
而随鼠标移动就是要动态改变 current_fire 的 x, y 坐标。
window.onmousemove = function(e) {
e = e || window.event;
current_fire.x = e.clientX;
current_fire.y = e.clientY;
}
window.onmouseout = function() {
current_fire.x = w * .5;
current_fire.y = h * .75;
}
最后,源码下载地址 https://github.com/sakurayj/canvas/tree/master/test 。
Canvas入门-利用Canvas绘制好玩的电子时钟
在这之前
你需要了解一下方法的使用:
beginPath()
closePath()
moveto()
lineto()
fill()
stroke()
fillRect()
clearRect()
这些我在前面的文章介绍过,可以看:
canvas入门-利用 canvas 制作一个七巧板
画个圆
arc()
方法
arc(x,y,radius,startAngle,endAngle,anticlockwise)
= > 画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。
该方法有五个参数:x,y为绘制圆弧所在圆上的圆心坐标。radius为半径。startAngle以及endAngle参数用弧度定义了开始以及结束的弧度。这些都是以x轴为基准。参数anticlockwise 为一个布尔值。为true时,是逆时针方向,否则顺时针方向。
注意:arc()函数中的角度单位是弧度,不是度数。角度与弧度的js表达式:radians=(Math.PI/180)*degrees。
//画一个带边框的实心圆 var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); canvas.width = 600; canvas.height = 600; ctx.beginPath(); var x = 200,// x 坐标值 y = 200,// y 坐标值 radius = 50,//半径 startAngle = 0 ; //开始点 endAngle = Math.PI * 2; //结束点 anticlockwise = true; //逆时针 ctx.arc(x,anticlockwise); ctx.linewidth = 10; ctx.fill; ctx.stroke; ctx.stroke(); ctx.fill();
实现的效果图如下:
准备工作
会画个圆之后呢,就要开始绘制我们的电子时钟。开始之前,我们需要理清思路。首先,我们要创建个二维数组放置我们从0-9的点阵图形,当元素的值为1的时候,就要将其绘制出来。以下是二维数组的片段:
[ [0,1,0],[0,[1,1],0] ]//0
我们要做的就是将 0 - 9 个数字用二维数组表示出来。
绘制电子时钟的数字
首先,我们要遍历我们的二维数组,如果元素的值为 1 ,则我们就将他绘制成圆形,那如何确定每个元素的圆心呢,看下面这张图:
接着我们写这样一个绘制数字的数字:
function drawNum(x,num,ctx){ for (var i = 0; i < digit[num].length; i++){ for(var j = 0; j < digit[num][i].length; j++){ if( digit[num][i][j] == 1){ ctx.beginPath(); ctx.fill; ctx.arc(x+(RADIUS+1)*2*j+(RADIUS+1),y+(RADIUS+1)*2*i+(RADIUS+1),RADIUS,Math.PI*2); ctx.fill(); ctx.closePath(); } } } }
然后,调用该函数来绘制我们的数字:
var RADIUS = 4; // 圆的半径 drawNum(0,ctx);
绘制的效果如下:
绘制简单的电子时钟
我们需要定义一个函数draw()
来绘制我们的电子时钟。
function draw(ctx){ var curDate = new Date(); var hour = curDate.getHours(); var minute = curDate.getMinutes(); var seconds = curDate.getSeconds(); drawNum(Margin_X,Margin_Y,parseInt(hour/10),ctx); drawNum(Margin_X + (RADIUS+1)*15,parseInt(hour%10),ctx); drawNum(Margin_X + (RADIUS+1)*30,10,ctx); drawNum(Margin_X + (RADIUS+1)*45,parseInt(minute/10),ctx); drawNum(Margin_X + (RADIUS+1)*60,parseInt(minute%10),ctx); drawNum(Margin_X + (RADIUS+1)*75,ctx); drawNum(Margin_X + (RADIUS+1)*90,parseInt(seconds/10),ctx); drawNum(Margin_X + (RADIUS+1)*105,parseInt(seconds%10),ctx); }
为了让每个数字之间有些间隔,不重叠在一起,定义了两个变量 Margin_X
和Margin_Y
来控制它距画布左边和顶部的距离。初始值都是30;
var Margin_X = 30; // 离 canvas 原点的坐标值 x var Margin_Y = 30; // 离 canvas 原点的坐标值 y
让电子时钟动起来
setInterval()
方法
setInterval(function,time)
= > 该方法会循环执行一个函数,时间间隔为 time(ms)
我们利用 setInterval
方法让我们的电子时钟动起来。
setInterval(function(){ draw(ctx); },500);
相应的,我们需要在重复绘制前,清楚我们的画布,不然会导致数字重叠在一起。这里用到了clearRect()
清楚我们的画布。
ctx.clearRect(0,1024,786);
附上完整的代码:
<!DOCTYPE html> <html> <head> <Meta charset="utf-8"> <Meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>canvas</title> </head> <body> <canvas id="canvas" width = "1024" height= "786"https://www.jb51.cc/tag/dis/" target="_blank">display: block; margin: 50px auto;" > 当前浏览器不支持canvas </canvas> <script src="digit.js"></script> <script type="text/javascript"> //电子时钟 var RADIUS = 4; // 圆的半径 var Margin_X = 30; // 离 canvas 原点的坐标值 x var Margin_Y = 30; // 离 canvas 原点的坐标值 y window.onload = function(){ var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); setInterval(function(){ draw(ctx); },500); } function draw(ctx){ ctx.clearRect(0,786); var curDate = new Date(); var hour = curDate.getHours(); var minute = curDate.getMinutes(); var seconds = curDate.getSeconds(); drawNum(Margin_X,ctx); } function drawNum(x,ctx){ for (var i = 0; i < digit[num].length; i++){ for(var j = 0; j < digit[num][i].length; j++){ if( digit[num][i][j] == 1){ ctx.beginPath(); ctx.fillhttps://www.jb51.cc/tag/xiaoguo/" target="_blank">效果图如下:
甚至我们还可以这样子:
canvas制作鼠标拖动绘制图形
使用canvas 实现了用鼠标拖动绘制各种图形
其中包括 矩形,圆形,箭头,画笔
使用方法
var paint = Ypaint(canvas)
绘制圆形:
paint.chooseCircle()
圆形其他参数
圆形的粗细 paint.outerParams.circle.lineWidth
圆形的颜色 paint.outerParams.circle.color
绘制矩形:
paint.chooseRect()
矩形其他参数
矩形的粗细 paint.outerParams.rect.lineWidth
矩形的圆角 paint.outerParams.rect.radius
矩形的颜色 paint.outerParams.rect.color
绘制箭头:
paint.chooseArrow()
箭头其他参数
箭头的粗细 paint.outerParams.arrow.range
箭头的大小 paint.outerParams.arrow.color
画笔工具:
paint.chooseCircle()
画笔工具其他参数
画笔的粗细 paint.outerParams.line.lineWidth
画笔的颜色 paint.outerParams.line.color
demo实例截图:
后续有时间会添加的功能:撤销、回撤、插入图片、图片在canvas内的拖动等等 有需求的可以提一提
在这里因为代码比较长,只提供压缩版本的代码,需要完整项目及demo的朋友 可以去我的github上下载
以上就是canvas制作鼠标拖动绘制图形 的详细内容,更多请关注php中文网其它相关文章!
canvas实现按住鼠标移动绘制出轨迹的方法实例
需求
在一块canvas画布上,初始状态画布什么都没有,现在,我想给画布加一点鼠标事件,用鼠标在画布上写字。具体的效果是鼠标移动到画布上任意一点,然后按住鼠标,移动鼠标的位置,就可以开始写字啦!
本文作为学习canvas的第一篇收获,很多人初学canvas做的第一个demo是实现一个“钟”,当然,我也实现了一个,不过不讲这个,而是讲讲一个更有趣、也更简单的玩意。
鼠标按住绘制轨迹
原理
先简单分析下思路,首先我们需要一个canvas画布,然后计算鼠标在画布上的位置,给鼠标绑定onmousedown事件和onmousemove事件,在移动过程中绘制出路径,松开鼠标的时候,绘制结束。
这个思路虽然很简单,但是里面有些地方需要小技巧实现。
1、需要一个html文件,包含canvas元素。
这是一个宽度800,高度400的画布。为什么没有写px呢?哦,暂时没搞懂,canvas文档推荐的。
nbsp;html> <meta> <meta> <title>canvas学习</title> <meta> <meta> <link> <link> <link> <canvas></canvas> <script></script>
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 }
相关推荐:
CSS和JS实现唯美星空轨迹运动效果
canvas贝塞尔公式推导与物体跟随复杂曲线轨迹运动
css3动画中圆形运动轨迹的实现
以上就是canvas实现按住鼠标移动绘制出轨迹的方法实例的详细内容,更多请关注php中文网其它相关文章!
今天的关于如何利用canvas实现按住鼠标移动绘制出轨迹和canvas记录鼠标移动轨迹的分享已经结束,谢谢您的关注,如果想了解更多关于canvas 火焰动态效果 随鼠标移动、Canvas入门-利用Canvas绘制好玩的电子时钟、canvas制作鼠标拖动绘制图形、canvas实现按住鼠标移动绘制出轨迹的方法实例的相关知识,请在本站进行查询。
本文标签: