GVKun编程网logo

如何利用canvas实现按住鼠标移动绘制出轨迹(canvas记录鼠标移动轨迹)

11

对于想了解如何利用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 火焰动态效果 随鼠标移动

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绘制好玩的电子时钟

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_XMargin_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制作鼠标拖动绘制图形

使用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的第一篇收获,很多人初学canvas做的第一个demo是实现一个“钟”,当然,我也实现了一个,不过不讲这个,而是讲讲一个更有趣、也更简单的玩意。

鼠标按住绘制轨迹

canvas实现按住鼠标移动绘制出轨迹的方法实例

原理

先简单分析下思路,首先我们需要一个canvas画布,然后计算鼠标在画布上的位置,给鼠标绑定onmousedown事件和onmousemove事件,在移动过程中绘制出路径,松开鼠标的时候,绘制结束。

这个思路虽然很简单,但是里面有些地方需要小技巧实现。

1、需要一个html文件,包含canvas元素。

这是一个宽度800,高度400的画布。为什么没有写px呢?哦,暂时没搞懂,canvas文档推荐的。

nbsp;html&gt;

    
        <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) =&gt; {
    //获取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) =&gt; {
        //移动时获取新的坐标位置,用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) =&gt; {
                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) =&gt; {
    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实现按住鼠标移动绘制出轨迹的方法实例的相关知识,请在本站进行查询。

本文标签: