Recently, I want to develop a sketch paper function, so I learned the simple drawing board function of Canvas. However, we know that MouseEvent can be used to monitor our mouse click operations on PC, and TouchEvent can be used to monitor our finger touch operations on mobile. So we have to write two sets of logic if we want to make a drawing board with both mouse clicks and finger touches. But don’t forget, there is also a PointerEventer, it can listen for mouse, finger touch and touch pen, support multi-touch, it has a special parameter, pressure sensor, on the pressure sensor screen can get the pressure sensor pen, as long as according to the pressure sensor, we can control the thickness of the stroke

1. Events corresponding to PointerEvent, MouseEvent, and TouchEvent

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

2. Make a sketchpad

1) Shared parts

Write the common sketchpad part of the code first, and then just write the different event listener part of the code

  <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>
Copy the code

2) Use MouseEvent to listen for events

Codepen. IO/klren0312 / p…

/ / put pen to paper
 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
    }
    / / move
    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()
    }
    / / pen up
    canvas.onmouseup = () = > {
      if(! painting) {return false
      }
      painting = false
      ctx.closePath()
    }

    // Leave the artboard
    canvas.onmouseleave = () = > {
      if(! painting) {return false
      }
      painting = false
      ctx.closePath()
    }
Copy the code

The effect

3) use the TouchEvent

Codepen. IO/klren0312 / p…

// Press your finger down
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
    }
    // Move your finger
    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()
    }
    // Raise your finger
    canvas.ontouchend = () = > {
      if(! painting) {return false
      }
      painting = false
      ctx.closePath()
    }
    // Finger out of the area
    ontouchcancel = () = > {
      if(! painting) {return false
      }
      painting = false
      ctx.closePath()
    }
Copy the code

The effect

3) use PointerEvent

This is the focus of this time, so a new project to write

Source address: klren0312 / drawboard: drawboard with pressure (can use pen pressure sketchpad) (github.com) the sample address: klren0312. Making. IO/drawboard /

Note:

  1. On the mobile terminal, events such as page scrolling and browser zooming will occur when the page is touched. At this time, you need to set the canvastouch-action: none;Style to set that no action is taken when a touch event occurs on an element
  2. Because we need to live according to the pressure to set the contrast, so in every move as a start and end of the path, so of course we need to record each move eventually coordinates, when pointermove events trigger again, moving coordinates to the coordinates of the end of the last, it ensures the continuity of the strokes
/** * @param {PointerEvent} e */ function startDraw(e: PointerEvent) { painting = true pointerId = e.pointerId ctx.lineCap = 'round' ctx.lineJoin = 'round' doDraw(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 ()} /** * draw * @param {PointerEvent} e event * @param {Boolean} isStart is the starting point */ 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} line width */ 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; }}Copy the code

As you can see in the code above, we save an image to the historyList array at the end of each full stroke.

historyList.push(ctx.getImageData(0.0, canvas.width, canvas.height))
Copy the code

This is mainly to achieve the backback operation of strokes. When clicking the backback button, we can take out the picture saved in the previous step and draw it to canvas to achieve this function

The function of clearing the screen is relatively simple. You can use Canvas clearRect to clear the canvas

ctx.clearRect(0.0, canvas.width, canvas.height)
Copy the code

The effect

The resources

Developer.mozilla.org/zh-CN/docs/… Developer.mozilla.org/zh-CN/docs/… Developer.mozilla.org/zh-CN/docs/… Developer.mozilla.org/zh-CN/docs/… Useful. Javascript. The info/pointer – eve… Github.com/klren0312/d…