美文网首页
通过canvas画板学习PointerEvent、MouseEv

通过canvas画板学习PointerEvent、MouseEv

作者: ZZES_ZCDC | 来源:发表于2021-04-24 12:01 被阅读0次

最近想开发个草稿纸功能, 所以学习了下canvas实现简单的画板功能, 但是我们知道在PC端我们可以用MouseEvent来监听我们的鼠标点按相关操作, 移动端可以使用TouchEvent来监听我们手指触摸相关操作, 所以我们做画板的时候要想兼顾鼠标点按和手指触摸就得写两套逻辑. 但是别忘了, 还存在PointerEventer, 它可以监听鼠标, 手指触摸以及触摸笔, 支持多点触控, 它还有个特殊的参数, 即压感, 在压感屏上可以获取获取压感笔的压感值, 只要根据压感值, 我们可以控制笔画的粗细

1. PointerEvent、MouseEvent和TouchEvent相对应的事件

PointerEvent MouseEvent TouchEvent
poninterdown mousedown touchstart
pointermove mousemove touchmove
pointerup mouseup touchend
pointerleave mouseleave -
pointercancel - touchcancel

2. 做个画板

1) 共用的部分

先写出公共的画板部分代码, 后面只会写不同的事件监听部分代码

  <style>
    body {
      padding: 0;
      margin: 0;
      height: 100vh;
      overflow: hidden;
    }
    .canvas-block {
      position: relative;
      width: 100%;
      height: 200px;
    }
    #canvas {
      position: absolute;
      top: 0;
      left: 0;
    }
    #canvas {
      z-index: 1;
    }
  </style>

  <div class="canvas-block">
    <canvas id="canvas" width="400" height="200"></canvas>
  </div>


  <script>
    const canvas = document.getElementById('canvas')
    const ctx = canvas.getContext('2d')
    let width = canvas.width
    let height = canvas.height
    if (window.devicePixelRatio) {
      canvas.style.width = width + 'px'
      canvas.style.height = height + 'px'
      canvas.height = height * window.devicePixelRatio
      canvas.width = width * window.devicePixelRatio
      ctx.scale(window.devicePixelRatio, window.devicePixelRatio)
    }
    canvas.getContext('2d').imageSmoothingEnabled = true
    let lineWidth = 3
    let lineColor = '#fff'
    let painting = false
</script>

2) 使用MouseEvent监听事件

https://codepen.io/klren0312/pen/BapMBvE

// 落笔
 canvas.onmousedown = e => {
      painting = true
      const event = e || window.event
      ctx.lineCap = 'round'
      ctx.lineJoin = 'round'
      const x = event.offsetX
      const y = event.offsetY
      ctx.beginPath()
      ctx.moveTo(x, y)
      ctx.lineWidth = lineWidth
      ctx.strokeStyle = lineColor
    }
    // 移动
    canvas.onmousemove = e => {
      if (!painting) {
        return
      }
      const event = e || window.event
      const x = event.offsetX
      const y = event.offsetY
      ctx.lineTo(x, y)
      ctx.stroke()
    }
    // 抬笔
    canvas.onmouseup = () => {
      if (!painting) {
        return false
      }
      painting = false
      ctx.closePath()
    }

    // 离开画板
    canvas.onmouseleave = () => {
      if (!painting) {
        return false
      }
      painting = false
      ctx.closePath()
    }

效果

image.png

3) 使用TouchEvent

https://codepen.io/klren0312/pen/YzNBKMj

// 手指按下
canvas.ontouchstart = e => {
      console.log(e.touches)
      painting = true
      const event = e.touches[0]
      ctx.lineCap = 'round'
      ctx.lineJoin = 'round'
      const x = event.pageX
      const y = event.pageY
      ctx.beginPath()
      ctx.moveTo(x, y)
      ctx.lineWidth = lineWidth
      ctx.strokeStyle = lineColor
    }
    // 手指移动
    canvas.ontouchmove = e => {
      if (!painting) {
        return
      }
      const event = e.touches[0]
      const x = event.pageX
      const y = event.pageY
      ctx.lineTo(x, y)
      ctx.stroke()
    }
    // 手指抬起
    canvas.ontouchend = () => {
      if (!painting) {
        return false
      }
      painting = false
      ctx.closePath()
    }
    // 手指离开区域
    ontouchcancel = () => {
      if (!painting) {
        return false
      }
      painting = false
      ctx.closePath()
    }

效果

image.png

3) 使用PointerEvent

这是这次的重点, 所以新开个项目写
源码地址: klren0312/drawboard: drawboard with pressure ( 可以使用压感笔的画板 ) (github.com)
示例地址: https://klren0312.github.io/drawboard/

注意点:

  1. 在移动端, 会出现触摸页面时, 产生页面滚动, 浏览器缩放等事件, 这时候需要给画布设置touch-action: none;样式, 来设置当触控事件发生在元素上时,不进行任何操作
  2. 因为我们需要实时根据压感来设置笔画粗细, 所以在每一次移动都作为一个路径的起始和结束, 当然这样的话我们需要记录每次移动的最终坐标, 在pointermove事件再次触发的时候, 将坐标移动到上一次结束的坐标处, 这样保证了笔画的连续性
 /**
     * @description 开始
     * @param {PointerEvent} e 事件
     */
    function startDraw(e: PointerEvent) {
      painting = true
      pointerId = e.pointerId
      ctx.lineCap = 'round'
      ctx.lineJoin = 'round'
      doDraw(e, true)
    }
    /**
     * @description 移动
     * @param {PointerEvent} e 事件
     */
    function moveDraw(e: PointerEvent) {
      if (!painting && e.pointerId !== pointerId) {
        return
      } else {
        doDraw(e)
      }
    }
    function cancelDraw(e: PointerEvent) {
      console.log('cancel')
      pointerId = -1
      if (!painting) {
        return false
      }
      painting = false
      historyList.push(ctx.getImageData(0, 0, canvas.width, canvas.height))
      ctx.closePath()
    }
    /**
     * 绘画
     * @param {PointerEvent} e 事件
     * @param {Boolean} isStart 是否是起始点
     */
    function doDraw(e, isStart = false) {
      if (isPen.value && e.pointerType !== 'pen') {
        return
      }
      const event: PointerEvent = e || window.event
      const x: number = event.offsetX
      const y: number = event.offsetY
      ctx.lineWidth = getLineWidth(event)
      ctx.strokeStyle = lineColor.value
      ctx.beginPath()
      if (!isStart) {
        ctx.moveTo(startXY.x, startXY.y)
        ctx.lineTo(x, y)
      } else {
        ctx.moveTo(x, y)
      }
      ctx.closePath()
      ctx.stroke()
      startXY = {
        x: x,
        y: y
      }
    }
    /**
     * @description 计算线宽
     * @param {PointerEvent} e 事件
     * @return {number} 线宽
     */
    function getLineWidth(e: PointerEvent): number {
      switch (e.pointerType) {
        case 'touch': {
          if (e.width < 10 && e.height < 10) {
            return (e.width + e.height) * 2 + 10;
          } else {
            return (e.width + e.height - 40) / 2;
          }
        }
        case 'pen': return e.pressure * 8;
        default: return (e.pressure) ? e.pressure * 8 : 4;
      }
    }

可以看到上面的代码 , 我们在每次完整的笔画绘制结束, 会向historyList数组里保存一个图片数据.

historyList.push(ctx.getImageData(0, 0, canvas.width, canvas.height))

这么做主要是为了实现笔画的回退操作, 我们可以在点击回退按钮时, 取出上一步保存的图片绘制到canvas中, 达到此功能

清屏的功能就相对简单, 可以使用canvas的clearRect来将画布清空

ctx.clearRect(0, 0, canvas.width, canvas.height)

效果

image.png

参考资料

https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent
https://developer.mozilla.org/zh-CN/docs/Web/API/TouchEvent
https://developer.mozilla.org/zh-CN/docs/Web/API/Pointer_events
https://developer.mozilla.org/zh-CN/docs/Web/CSS/touch-action
https://zh.javascript.info/pointer-events
https://github.com/klren0312/daliy_knowledge/issues/372

相关文章

  • 通过canvas画板学习PointerEvent、MouseEv

    最近想开发个草稿纸功能, 所以学习了下canvas实现简单的画板功能, 但是我们知道在PC端我们可以用MouseE...

  • canvas2

    canvas简易画板

  • Canvas画板---手机上也可以用的画板

    学习制作画板之前,我们先来了解一下canvas标签一.canvas标签1.canvas标签与img标签相似,但是c...

  • 微信小程序制作简易画板

    微信小程序制作简易画板 效果图 原理介绍   利用官方组件canvas来实现画板的制作,通过不断获取手指触摸的位置...

  • Canvas画板

    一、实现思路 (非触屏设备) 监听鼠标事件①按下鼠标:onmousedown;滑动鼠标:onmousemove;松...

  • canvas2-text

    canvas画板结合JS事件实现写字效果

  • JavaScript画板-canvas

    1.创建画布节点 2.获得画布节点 3.获得绘画对象 4.绘制画布底色 5.绘制直线 6.绘制圆 7.绘制实心文本...

  • canvas画板所学

    1.学到的新API1.1 鼠标监听document.onmousedown = function (xxx) {}...

  • canvas-画板

    闲来无趣写了个网页版的画图 Title *{padding:0; margin:0...

  • canvas画板工具

    canvas画板工具 好久不见,先来一个美美的么么哒~~~ 需要注意的几点我们之前说的,不能在css中固定canv...

网友评论

      本文标题:通过canvas画板学习PointerEvent、MouseEv

      本文链接:https://www.haomeiwen.com/subject/twtycltx.html