Skip to content
html
<!DOCTYPE html>
<html lang="en">

<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>Document</title>
  <style>
    html,
    body {
      width: 100vw;
      height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
    }
  </style>
</head>

<body>
  <form id="form">
    Stroke Style:
    <select id="strokeStyleSelect">
      <option value="#5470c6">#5470c6</option>
      <option value="#91cc75">#91cc75</option>
      <option value="#fac858">#fac858</option>
      <option value="#ee6666">#ee6666</option>
      <option value="#73c0de">#73c0de</option>
      <option value="#3ba272">#3ba272</option>
      <option value="#fc8452">#fc8452</option>
      <option value="#9a60b4">#9a60b4</option>
      <option value="#ea7ccc">#ea7ccc</option>
    </select>
    <br />
    Guild Wires:
    <input type="checkbox" id="guildWiresCheckbox">
  </form>
  <canvas id="canvas"></canvas>

  <script>
    const canvas = document.getElementById('canvas')
    const context = canvas.getContext('2d')

    canvas.width = 600
    canvas.height = 600
    canvas.style.background = '#ddd'

    context.strokeStyle = strokeStyleSelect.value

    // 选择颜色
    strokeStyleSelect.addEventListener('change', e => {
      context.strokeStyle = e.target.value
    })

    let isDragging = false
    let isDrawguildWires = false
    let drawingSurface = null
    const rect = {
      startX: null,
      startY: null,
      endX: null,
      endY: null
    }

    guildWiresCheckbox.addEventListener('change', e => {
      isDrawguildWires = e.target.checked
    })

    // 在视口中的坐标转化为在 canvas 中的坐标
    const windowToCanvas = (x, y) => {
      const { left, top } = canvas.getBoundingClientRect()

      return {
        x: x - left,
        y: y - top
      }
    }

    // 保存绘图表面
    const saveDrawingSurface = () => {
      const { width, height } = canvas
      drawingSurface = context.getImageData(0, 0, width, height)
    }

    // 恢复绘图表面
    const restoreDrawingSurface = () => {
      context.putImageData(drawingSurface, 0, 0)
    }

    canvas.addEventListener('mousedown', e => {
      const { x, y } = windowToCanvas(e.clientX, e.clientY)
      rect.startX = rect.endX = x
      rect.startY = rect.endY = y
      isDragging = true
      // 保存绘图表面,在 mousemove 中恢复绘图表面,这样鼠标每次移动就会擦除上一次的线段。
      saveDrawingSurface()
    })

    canvas.addEventListener('mousemove', e => {
      if (isDragging) {
        restoreDrawingSurface()

        const { x, y } = windowToCanvas(e.clientX, e.clientY)
        rect.endX = x
        rect.endY = y

        drawWires()
        if (isDrawguildWires) {
          drawRect()
        }
      }
    })

    canvas.addEventListener('mouseup', e => {
      restoreDrawingSurface()
      isDragging = false
    })

    // 画对角线
    const drawWires = () => {
      context.beginPath()
      context.moveTo(rect.startX, rect.startY)
      context.lineTo(rect.endX, rect.endY)
      context.stroke()
    }

    // 画矩形边框
    const drawRect = () => {
      context.beginPath()

      // 两条横线
      context.moveTo(rect.startX, rect.startY)
      context.lineTo(rect.endX, rect.startY)
      context.moveTo(rect.startX, rect.endY)
      context.lineTo(rect.endX, rect.endY)

      // 两条竖线
      context.moveTo(rect.startX, rect.startY)
      context.lineTo(rect.startX, rect.endY)
      context.moveTo(rect.endX, rect.startY)
      context.lineTo(rect.endX, rect.endY)

      context.stroke()
    }
  </script>
</body>

</html>