JS实现Canvas画板

322 阅读5分钟

如果是首次接触JS,并没有学语法结构的情况下,怎么思考或者利用工具文档,实现写一个画板的例子:

1、通过console.log得到鼠标的位置。然后创建一个div,在上面加css样式。首先是监听pc端onclick这个事件,然后去web页面点击,来模拟onclick这个事件,看console控制台的反馈,里面可以看懂的是x,y坐标,然后就利用这个相对位置;

2、在看懂的列表里,再找一个特殊的点(最左上角),出来坐标0,0 随便找一组clientX和clientY这一组描述坐标,写到代码里试试看,是不是想要的。

3、实现onclick事件后,有轨迹记录,先用方形box-border试一下,写上canvas.appendChild(div),去web页面onclick,就会有方形的点出现,然后调整点的样式居中,最后实现onclick事件发生后,实现web页面的点出现。每onclick一次,就会走一遍这个逻辑。

4、怎么实现线出现呢?可以先用div标签实现onmousemove这个动作,鼠标移到web页面是,会有卡顿的情况,这是跨线程工作,以上步骤的代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="canvas"></div>
    <script>
        canvas.onmousemove = (e)=>{
          console.log(e)
          console.log(e.clientX)
          console.log(e.clientY)
            //console.log 调试大法
          let div = document.createElement('div')
          div.style.position = 'absolute' //让div的内联样式里面的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>
</body>
</html>
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }
  #canvas{
    height: 100vh;
    border: 1px solid red;
  }

5、还有一种直接引用canvas这个自身的属性来实现上面的过程,

<body>
   <canvas id="canvas"></canvas>
    <script>
        //看canvas怎么画线
    </script>

加入此行代码后,web页面会出现侧面滚动条,是因为canvas默认是个inline元素,即便是在canvas标签中定义高度、宽度属性,还是会被css覆盖

6、去canvas用法文档里面搜索,‘一个简单的例子’,直接抄代码【CRM_C】绘制一个长方形,用这个例子来深入探索一下它是如何工作的?

        var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");
        ctx.fillStyle = "rgb(200,0,0)";
        ctx.fillRect (10, 10, 55, 50);

7、接着就研究这行代码能有什么作用,无非就是换值来试呗,试值后的结果表明: fillRect是填充矩形,后面四个值分别代表:这个矩形框的X轴起始坐标,Y轴起始坐标,宽度,高度

ctx.fillRect (10, 10, 55, 50);  

8、目前存在一个问题,矩形框很模糊,这时去掉css里面的宽度,高度限制后,矩形框变清晰且变小了,也就是说在一开始写canvas的时候就要写好屏幕的尺寸,那怎么知道用户屏幕的宽度呢?用JS获取,Google一下:JS如何获取屏幕的宽度和高度。得到一个参数是网页可见区域宽:document.body.clientWidth.这样就可以用console.log(document.body.clientWidth)来打印一下结果呗。

得到的打印结果是958.经验证这是一个正确的参数,可以用google到的这两个参数:

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

出现了高度异常的现象,这是因为body的高度由里面的内容确定的,在css里面加个body的border边框,高度不改变,就不能满足需求了,还得找别的方法...给个捷径直接给你参数吧:

var canvas = document.getElementById("canvas"); 
canvas.width = document.documentElement.clientWidth
canvas.height = document.documentElement.clientHeight

9、经过一番调整后,实现了onmousemove出现点,实现的代码如下:

canvas.onmousemove = (e) => {
  console.log(e.clientX)
  console.log(e.clientY)
  ctx.fillRect (e.clientX -5, e.clientY -5, 10, 10);
}    

10、以上9步实现了onmousemove这个基本条件,接下来还得优化一下,mousedown不按鼠标时的情形,要引用一个指示信号来让两种情况区别逻辑实现,在此之前加一个画圆的样式修改,

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();
            //圆心x坐标,y坐标,半径,
            ctx.arc(e.clientX, e.clientY, 10, 0, 2 * Math.PI);
            ctx.stroke();
            ctx.fill();
             }else{
               console.log('什么都不做')
           }
        }
        canvas.onmouseup = () => {
            painting = false
        }

11、接下来研究手机上的实现。先研究一下调整到手机屏幕大小的尺寸能否监听到鼠标事件,google到了JS手机是否支持触屏的一篇文档里介绍了打印这个动作的代码:

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

在web页面的手机和pc端切换刷新看到打印结果都是符合猜测的。

12、开始编写ontouch的逻辑实现:

先写出第一个版本,还没有在手机上实现触屏处点轨迹:

let ctx = canvas.getContext("2d");
        ctx.fillStyle = "black";
        ctx.strokeStyle = 'none' 
        let painting = false
        let isTouchDevice='ontouchstart' in document.documentElement;
        if(isTouchDevice){
            canvas.ontouchmove = (e)=>{
                console.log(e)
            }
        }else{
            canvas.onmousedown = () => {
          painting = true
        }
        canvas.onmousemove = (e) => {
             if (painting===true) {
            ctx.beginPath();
            ctx.arc(e.clientX, e.clientY, 10, 0, 2 * Math.PI);//圆心x坐标,y坐标,半径,
            ctx.stroke();
            ctx.fill();
             }
    }
        canvas.onmouseup = () => {
            painting = false
        }
        }

上例中,e这个动作拿的是单手触摸和多手指触摸的很多种情况,所以就要从所有打印的信息里,找多手指触摸的信息:

touches: TouchList {0: Touch, length: 1}
type: "touchmove"

13、最后实现了手机画图的代码如下:

 let ctx = canvas.getContext("2d");
        ctx.fillStyle = "black";
        ctx.strokeStyle = 'none'  
        let painting = false
        let isTouchDevice='ontouchstart' in document.documentElement;
        if(isTouchDevice) {
            canvas.ontouchmove = (e) => {
                let x = e.touches[0].clientX
                let y = e.touches[0].clientY
                ctx.beginPath();
                ctx.arc(x, y, 10, 0, 2 * Math.PI);//圆心x坐标,y坐标,半径,
                ctx.stroke();
                ctx.fill();
            }
        }else{
            canvas.onmousedown = () => {
          painting = true
        }
        canvas.onmousemove = (e) => {
             if (painting===true) {
            ctx.beginPath();
            ctx.arc(e.clientX, e.clientY, 10, 0, 2 * Math.PI);//圆心x坐标,y坐标,半径,
            ctx.stroke();
            ctx.fill();
             }
    }
        canvas.onmouseup = () => {
            painting = false
        }
        }

14、回头来看pc端的点怎么能更优化一下,先用的描绘三角形,简化为线段,再重新优化组成线的点,在遇到折线时的不连续,不圆润的问题,修改了一些属性:

ctx.fillStyle = "black";
ctx.strokeStyle = 'black'
ctx.lineWidth = 8;
ctx.lineCap='round';  //使线更圆润

15、再修改一下手机端的代码,重新画线,最终的完整代码为:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>画板</title>
    <link rel="stylesheet" href="style.css">  //加一个css样式的链接
</head>
<body>
   <canvas id="canvas" width="100" height="100"></canvas>   //发现代码问题善于用console.log来验证
    <script>
        let canvas = document.getElementById("canvas");   //经过尝试<div>验证后,添加的canvas属性
        canvas.width = document.documentElement.clientWidth  //定义画布的宽度,有一个中间值推导而来
        canvas.height = document.documentElement.clientHeight
        let ctx = canvas.getContext("2d");
        ctx.fillStyle = "black";
        ctx.strokeStyle = 'black'
        ctx.lineWidth = 8;
        ctx.lineCap='round';  //解决了在遇到折线时的不连续,不圆润的问题,在很多的属性里尝试       
        let painting = false
        let last
        let isTouchDevice='ontouchstart' in document.documentElement;
        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
        }
        }
        function drawLine(x1,y1,x2,y2){  
     ctx.beginPath();
     ctx.moveTo(x1, y1);
     ctx.lineTo(x2, y2);
     ctx.stroke();  //描边是stroke;填充是fill
    }         
    </script>
</body>
</html>
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }
  #canvas{
    display: block;
  }