前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >17·灵魂前端工程师养成-JavaScript实现canvas画板

17·灵魂前端工程师养成-JavaScript实现canvas画板

作者头像
DriverZeng
发布2022-09-26 16:45:47
1.6K0
发布2022-09-26 16:45:47
举报
文章被收录于专栏:Linux云计算及前后端开发

-曾老湿, 江湖人称曾老大。


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


利用JS做出画图板


准备工作

在VScode中创建一个新的项目,并且初始化git仓库

新建一个 html 和一个 CSS,初始化git仓库

代码语言:javascript
复制
MacBook-Pro:canvas-demo-1 driverzeng$ git init .
MacBook-Pro:canvas-demo-1 driverzeng$ git add .
MacBook-Pro:canvas-demo-1 driverzeng$ git commit -v

有人看到上面这张图的时候,可能会秒变 黑人问号脸,What The Fuck? 这张图来的很突兀,没有错,正如你所看到的,我们今天要用代码,实现一个画板。可以让人画画的画板,你没有听错,也没有看错...

CanVas教程:MDN canvas

首先使用VScode,打开网页,方便调试

代码语言:javascript
复制
MacBook-Pro:canvas-demo-1 driverzeng$ http-server . -c-1
Starting up http-server, serving .
Available on:
  http://127.0.0.1:8080
  http://192.168.1.102:8080


鼠标点击

首先,如果我们要做个画板,我们需要知道,用户想要画线条之类的东西,那么他们的鼠标第一下的点击会是在哪里呢?

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="canvas"></div>
</body>
</html>
代码语言:javascript
复制
*{margin: 0;padding: 0;box-sizing: border-box;}

//给canvas加个样式
#canvas{
    height: 100vh;
    border: 1px solid red;
}

通过 JS 来调试获取用户第一次点击的坐标位置

代码语言:javascript
复制
    <script>
        canvas.onclick = (e)=>{
            console.log(e)
        }
    </script>

打开开发者工具,查看console,然后随便点击页面的白色位置,就会出现MouseEvent这个就是鼠标点击事件。

我们可以看见有好几个X 和 Y,到底使用哪个呢?

我们先尝试一下clientXclientY

代码语言:javascript
复制
    <script>
        canvas.onclick = (e)=>{
            console.log(e.clientX)
            console.log(e.clientY)
        }
    </script>

点击了很多次,发现这个坐标可以检测到我们鼠标点击的位置,于是接下来,我们需要,在用户点击的地方就出现一个圆点。

代码语言:javascript
复制
    <script>
        canvas.onclick = (e)=>{
            console.log(e.clientX)
            console.log(e.clientY)
           <!-- 让文档创建一个div的元素,这个是已经写好的api我们可以直接调用 -->
            let div= document.createElement('div')
           <!-- 接下来我们要让div出现在鼠标点击的位置,使用CSS的绝对定位 -->
            div.style.position = 'absolute'
            div.style.left = e.clientX + 'px'
            div.style.top = e.clientY + 'px'
            div.style.border = '1px solid red'
            div.style.width = '5px'
            div.style.height= '5px'
        }
    </script>

写完之后,我们再去点击页面,会发现.........咦?还是什么东西都没有,此时,再看一下上面的代码,我们只是创建了一个div,但是并没有说把这个div创建在哪里,所以此时这个div是在内存里,我们需要把它加入到页面当中。

代码语言:javascript
复制
canvas.appendChild(div)

如果你写完了,你点击后,你会发现,这个方框会出现在鼠标的右下角,而不是出现在鼠标的正中间,所以我们还需要把这个框修改到鼠标的正中间。

代码语言:javascript
复制
div.style.width = '6px'
div.style.height= '6px'
div.style.marginLeft = '-3px'
div.style.marginTop = '-3px'

接下来 ,我们需要把方框变成圆的,变成黑色实心的,去掉红色边框。

代码语言:javascript
复制
    <script>
        canvas.onclick = (e)=>{
            console.log(e.clientX)
            console.log(e.clientY)
            let div= document.createElement('div')
            div.style.position = 'absolute'
            div.style.left = e.clientX + 'px'
            div.style.top = e.clientY + 'px'
            div.style.width = '6px'
            div.style.height= '6px'
            div.style.marginLeft = '-3px'
            div.style.marginTop = '-3px'
            div.style.borderRadius = '50%'
            div.style.backgroundColor = 'black'
            canvas.appendChild(div)
        }
    </script>

提交第一版,可以获取到鼠标的位置,并且画出点.

代码语言:javascript
复制
MacBook-Pro:canvas-demo-1 driverzeng$ git add .
MacBook-Pro:canvas-demo-1 driverzeng$ git commit -v

使用canvas画线

刚才我们已经可以准确的获取到鼠标的位置,并且画出点来,现在我们需要把这些点连成线。

如图所示,我们把 onclick 更改成 onmousemove,在页面中,只要鼠标移动,就会出现黑点,慢慢移动就会变成线,但是如果移动速度很快,就会发现是断断续续的点。

所以我们需要忘记刚才的代码,有直接的方法就是 canvas ,学习官方的示例代码。

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <canvas id='canvas'></canvas>
    <script>
        // 画线
        var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");

        // 控制颜色
        ctx.fillStyle = "rgb(200,0,0)";
        // 控制 横坐标 纵坐标 宽度 高度
        ctx.fillRect (10, 10, 55, 50);
    </script>
</body>
</html>
代码语言:javascript
复制
*{margin: 0;padding: 0;box-sizing: border-box;}

#canvas{
    height: 100vh;
    width: 100vw;
    display: block;
    border: 1px solid red;
}

但是会发现,这个红色的框会很模糊,马赛克... AV画质。

就是因为我们在CSS中设置了宽度和高度。

canvas的宽 高 默认就设置了。

去掉CSS中的 宽高,使用用户的屏幕宽 高 来定义canvas的宽高。

但是,每个用户的宽和高又不一样,我们又不能把canvas的 宽高写死了,所以我们需要用到JS来获取用户屏幕的宽高

代码语言:javascript
复制
网页可见区域宽: document.body.clientWidth  
网页可见区域高: document.body.clientHeight  
网页可见区域宽: document.body.offsetWidth (包括边线的宽)  
网页可见区域高: document.body.offsetHeight (包括边线的高)  
网页正文全文宽: document.body.scrollWidth  
网页正文全文高: document.body.scrollHeight  
网页被卷去的高: document.body.scrollTop  
网页被卷去的左: document.body.scrollLeft  
网页正文部分上: window.screenTop  
网页正文部分左: window.screenLeft  
屏幕分辨率的高: window.screen.height  
屏幕分辨率的宽: window.screen.width  
屏幕可用工作区高度: window.screen.availHeight  
屏幕可用工作区宽度: window.screen.availWidth  
代码语言:javascript
复制
console.log(document.body.clientWidth)

获取一下宽度 试试

代码语言:javascript
复制
    <script>

        
        // 画线
        var canvas = document.getElementById("canvas");
        
        canvas.width = document.body.clientWidth
        canvas.height = document.body.clientHeight

        var ctx = canvas.getContext("2d");

        ctx.fillStyle = "rgb(200,0,0)";
        ctx.fillRect (10, 10, 55, 50);

    </script>

会发现,这个宽度 和 高度,是根据body的宽高来定的所以,不是这个

代码语言:javascript
复制
canvas.width = document.documentElement.clientWidth
canvas.height = document.documentElement.clientHeight

最终答案,使用documentElement,文档的高度

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <canvas id='canvas'></canvas>
    <script>

        
        // 画线
        var canvas = document.getElementById("canvas");

        canvas.width = document.documentElement.clientWidth
        canvas.height = document.documentElement.clientHeight

        var ctx = canvas.getContext("2d");

        ctx.fillStyle = "rgb(200,0,0)";
        ctx.fillRect (10, 10, 55, 50);

    </script>
</body>
</html>
代码语言:javascript
复制
*{margin: 0;padding: 0;box-sizing: border-box;}

#canvas{
    display: block;
}

现在使用canvas,开始用鼠标来填充图案,先获取鼠标点击事件看看。

代码语言:javascript
复制
    <script>    
        // 画线
        var canvas = document.getElementById("canvas");

        canvas.width = document.documentElement.clientWidth
        canvas.height = document.documentElement.clientHeight

        var ctx = canvas.getContext("2d");

        ctx.fillStyle = "black";
        ctx.fillRect (10, 10, 55, 50);
        canvas.onclick = (e) =>{
            console.log(e.clientX)
            console.log(e.clientY)
        }
    </script>

依然可以获取到,因此,我们可以按照刚才的方式,先画点

然后居中,连线

但是现在鼠标,停不下来,只要移动就会画线。

所以我们可以设置一个信号,比如开车的时候,当你看到红灯,就会停车,当你看到绿灯就会开车。

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <canvas id='canvas'></canvas>
    <script>    
        // 画线
        let canvas = document.getElementById("canvas");

        canvas.width = document.documentElement.clientWidth
        canvas.height = document.documentElement.clientHeight

        let ctx = canvas.getContext("2d");
        let painting = false

        ctx.fillStyle = "black";

        canvas.onmousedown = () => {
            painting = true
        }
        
        canvas.onmousemove = (e) =>{
            if (painting === true) {
                ctx.fillRect (e.clientX -3, e.clientY -3, 6, 6);
            }  else {
                console.log('啥都不做')
            }
        }

        canvas.onmouseup = () => {
            painting = false
        }
    </script>
</body>
</html>

现在把线条的方框,改成圆形

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <canvas id='canvas'></canvas>
    <script>    
        // 画线
        let canvas = document.getElementById("canvas");

        canvas.width = document.documentElement.clientWidth
        canvas.height = document.documentElement.clientHeight

        let ctx = canvas.getContext("2d");
        let painting = false

        ctx.fillStyle = "black";
        ctx.strokeStyle = 'none'

        canvas.onmousedown = () => {
            painting = true
        }
        
        canvas.onmousemove = (e) =>{
            if (painting === true) {
                ctx.beginPath();
                ctx.arc(e.clientX,e.clientY,5,0,2 * Math.PI);
                ctx.stroke();
                ctx.fill();
            }  else {
                console.log('啥都不做')
            }
        }

        canvas.onmouseup = () => {
            painting = false
        }
    </script>
</body>
</html>

提交git仓库,使用canvas画线

代码语言:javascript
复制
MacBook-Pro:canvas-demo-1 driverzeng$ git add .
MacBook-Pro:canvas-demo-1 driverzeng$ git commit -v

优化:手机使用

首先先要检测,是否支持触屏,鼠标点击事件,在手机上肯定是不好使了。

于是乎,经过多次辗转反侧的找文档,发现:

代码语言:javascript
复制
let isTouchDevice = 'ontouchstart' in document.documentElement;
console.log(isTouchDevice)

PC端,false

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <canvas id='canvas'></canvas>
    <script>    
        // 画线
        let canvas = document.getElementById("canvas");

        canvas.width = document.documentElement.clientWidth
        canvas.height = document.documentElement.clientHeight
        let ctx = canvas.getContext("2d");
        let painting = false
        ctx.fillStyle = "black";
        ctx.strokeStyle = 'none'

        let isTouchDevice = 'ontouchstart' in document.documentElement;
        console.log(isTouchDevice)


        if(isTouchDevice){
            canvas.ontouchmove = (e) => {
                let x = e.touches[0].clientX
                let y = e.touches[0].clientY
                ctx.beginPath();
                ctx.arc(x,y,5,0,2 * Math.PI);
                ctx.stroke();
                ctx.fill();
            }

        }else{
            canvas.onmousedown = () => {
                painting = true
            }
        
            canvas.onmousemove = (e) =>{
                if (painting === true) {
                    ctx.beginPath();
                    ctx.arc(e.clientX,e.clientY,5,0,2 * Math.PI);
                    ctx.stroke();
                    ctx.fill();
                }
             }

            canvas.onmouseup = () => {
                painting = false
            }     
        }


    </script>
</body>
</html>

优化,在点和点之间,连成一条线。

我们先要知道,如何画一条线

代码语言:javascript
复制
let ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.strokeStyle = 'none'
ctx.beginPath();
ctx.moveTo(125,125);
ctx.lineTo(125,45);
ctx.lineTo(45,125);
ctx.closePath();
ctx.stroke();

这个三角形是怎么做到的呢?

我们可以通过修改代码来看

beginPath: 是开始 moveTo:起点 lineTo:从起点画一根线 stroke:描边 fill:填充颜色

如果不要后面的,只要前面的起点和第一条线。

这样就出来了一条线,这样 我们可以写个画线的函数

通过传参 的方式

代码语言:javascript
复制
        function drawLine(x1,y1,x2,y2){
            ctx.beginPath();
            ctx.moveTo(x1,y1);
            ctx.lineTo(x2,y2);
            ctx.stroke();
        }

        drawLine(0,0,500,500)

使用drawLine函数

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <canvas id='canvas'></canvas>
    <script>    
        // 画线
        let canvas = document.getElementById("canvas");

        canvas.width = document.documentElement.clientWidth
        canvas.height = document.documentElement.clientHeight
        let ctx = canvas.getContext("2d");
        let painting = false
        ctx.fillStyle = "black";
        ctx.strokeStyle = 'none'

        let isTouchDevice = 'ontouchstart' in document.documentElement;

        function drawLine(x1,y1,x2,y2){
            ctx.beginPath();
            ctx.moveTo(x1,y1);
            ctx.lineTo(x2,y2);
            ctx.stroke();
        }


        if(isTouchDevice){
            canvas.ontouchmove = (e) => {
                console.log(e.touches[0].clientX)

                let x = e.touches[0].clientX
                let y = e.touches[0].clientY
                ctx.beginPath();
                ctx.arc(x,y,5,0,2 * Math.PI);
                ctx.stroke();
                ctx.fill();
            }

        }else{
            canvas.onmousedown = () => {
                painting = true
            }
        
            canvas.onmousemove = (e) =>{
                if (painting === true) {
                    drawLine(0,0,e.clientX,e.clientY)
                }
             }

            canvas.onmouseup = () => {
                painting = false
            }     
        }
    </script>
</body>
</html>

每画一个点,都会从(0,0)这个坐标点,开始,所以变成这样了。

所以,我们需要修改一下起点,改成上一个点

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <canvas id='canvas'></canvas>
    <script>    
        // 画线
        let canvas = document.getElementById("canvas");

        canvas.width = document.documentElement.clientWidth
        canvas.height = document.documentElement.clientHeight
        let ctx = canvas.getContext("2d");
        let painting = false
        ctx.fillStyle = "black";
        ctx.strokeStyle = 'none'

        let isTouchDevice = 'ontouchstart' in document.documentElement;
        let last

        function drawLine(x1,y1,x2,y2){
            ctx.beginPath();
            ctx.moveTo(x1,y1);
            ctx.lineTo(x2,y2);
            ctx.stroke();
        }


        if(isTouchDevice){
            canvas.ontouchmove = (e) => {
                console.log(e.touches[0].clientX)

                let x = e.touches[0].clientX
                let y = e.touches[0].clientY
                ctx.beginPath();
                ctx.arc(x,y,5,0,2 * Math.PI);
                ctx.stroke();
                ctx.fill();
            }

        }else{
            canvas.onmousedown = () => {
                painting = true
                last  = [e.clientX, e.clientY]
            }
        
            canvas.onmousemove = (e) =>{
                if (painting === true) {
                    drawLine(last[0],last[1],e.clientX,e.clientY)
                }
             }

            canvas.onmouseup = () => {
                painting = false
            }     
        }
    </script>
</body>
</html>

现在又变成每次从第一个点,开始画线,我们需要让上一次实时更新

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <canvas id='canvas'></canvas>
    <script>    
        // 画线
        let canvas = document.getElementById("canvas");

        canvas.width = document.documentElement.clientWidth
        canvas.height = document.documentElement.clientHeight
        let ctx = canvas.getContext("2d");
        let painting = false
        ctx.fillStyle = "black";
        ctx.strokeStyle = 'none'

        let isTouchDevice = 'ontouchstart' in document.documentElement;
        let last

        function drawLine(x1,y1,x2,y2){
            ctx.beginPath();
            ctx.moveTo(x1,y1);
            ctx.lineTo(x2,y2);
            ctx.stroke();
        }


        if(isTouchDevice){
            canvas.ontouchmove = (e) => {
                console.log(e.touches[0].clientX)

                let x = e.touches[0].clientX
                let y = e.touches[0].clientY
                ctx.beginPath();
                ctx.arc(x,y,5,0,2 * Math.PI);
                ctx.stroke();
                ctx.fill();
            }

        }else{
            canvas.onmousedown = (e) => {
                painting = true
                last  = [e.clientX, e.clientY]
            }
        
            canvas.onmousemove = (e) =>{
                if (painting === true) {
                    drawLine(last[0],last[1],e.clientX,e.clientY)
                    last = [e.clientX,e.clientY]
                }
             }

            canvas.onmouseup = () => {
                painting = false
            }     
        }
    </script>
</body>
</html>

加上线的宽度,太细了...

变粗了之后,会发现,画线中间会有缝隙。所以我们还需要调整一个参数

手机适配:

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <canvas id='canvas'></canvas>
    <script>    
        // 画线
        let canvas = document.getElementById("canvas");

        canvas.width = document.documentElement.clientWidth
        canvas.height = document.documentElement.clientHeight
        let ctx = canvas.getContext("2d");
        let painting = false

        ctx.fillStyle = "black";
        ctx.strokeStyle = 'none'
        ctx.lineWidth = 20;
        ctx.lineCap = 'round'

        let isTouchDevice = 'ontouchstart' in document.documentElement;
        let last

        function drawLine(x1,y1,x2,y2){
            ctx.beginPath();
            ctx.moveTo(x1,y1);
            ctx.lineTo(x2,y2);
            ctx.stroke();
        }


        if(isTouchDevice){
            canvas.ontouchstart = (e) => {
                let x = e.touches[0].clientX
                let y = e.touches[0].clientY
                last = [x,y]
            }

            canvas.ontouchmove = (e) => {
                let x = e.touches[0].clientX
                let y = e.touches[0].clientY
                drawLine(last[0],last[1],x,y)
                last = [x,y]
            }

        } else {
            canvas.onmousedown = (e) => {
                painting = true
                last  = [e.clientX, e.clientY]
            }
        
            canvas.onmousemove = (e) =>{
                if (painting === true) {
                    drawLine(last[0],last[1],e.clientX,e.clientY)
                    last = [e.clientX,e.clientY]
                }
             }

            canvas.onmouseup = () => {
                painting = false
            }     
        }
    </script>
</body>
</html>

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-05-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 利用JS做出画图板
相关产品与服务
运维安全中心(堡垒机)
腾讯云运维安全中心(堡垒机)(Operation and Maintenance Security Center (Bastion Host))可为您的 IT 资产提供代理访问以及智能操作审计服务,为客户构建一套完善的事前预防、事中监控、事后审计安全管理体系,助力企业顺利通过等保测评。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档