手写一个签名板

Huy大约 3 分钟javascriptjavascript

签名板的实现其实很简单,直接用原生 Canvas 就行,但是想要简单实现像 signature_padopen in new window 这样的效果,需要用到 ctx.quadraticCurveTo(startX, startY, controlX, controlY) 贝塞尔曲线进行平滑过渡绘制。并且还需监听用户鼠标事件或者手指的移动事件。下面是简单实现。

签名板
签名板

HTML 和 CSS 样式

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>签名板</title>
  </head>
  <body>
    <!-- 画板 -->
    <canvas id="signature-pad" width="400" height="200"></canvas>
    <!-- 控制器 -->
    <div class="controls">
      <select id="stroke-style">
        <option value="pen">钢笔</option>
        <option value="brush">毛笔</option>
      </select>
      <button id="clear">清空</button>
    </div>
    <script src="script.js"></script>
  </body>
</html>
body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  background-color: #f0f0f0;
}

canvas {
  border: 1px solid #ccc;
  background-color: #fff;
}

.controls {
  margin-top: 20px;
}

Js 核心部分

document.addEventListener('DOMContentLoaded', function () {
  // 获取 canvas 元素和其 2D 上下文
  var canvas = document.getElementById('signature-pad')
  var ctx = canvas.getContext('2d')
  var drawing = false // 标志是否正在绘制
  var lastX = 0,
    lastY = 0 // 保存上一个点的坐标
  var strokeStyle = 'pen' // 初始笔画样式

  // 获取事件中触点的相对位置
  function getEventPosition(e) {
    // 鼠标事件或者触摸事件中的坐标
    const offsetX = e.offsetX || e.touches[0].clientX - canvas.offsetLeft
    const offsetY = e.offsetY || e.touches[0].clientY - canvas.offsetTop
    return { offsetX, offsetY }
  }

  // 开始绘制的函数
  function startDrawing(e) {
    e.preventDefault() // 阻止默认行为,避免页面滚动
    drawing = true // 设置为正在绘制
    ctx.beginPath() // 开始新路径

    // 记录初始点的位置
    const { offsetX, offsetY } = getEventPosition(e)
    lastX = offsetX
    lastY = offsetY
    ctx.moveTo(offsetX, offsetY) // 移动画笔到初始位置
  }

  // 绘制过程中的函数
  function draw(e) {
    e.preventDefault() // 阻止默认行为,避免页面滚动
    if (!drawing) return // 如果不是在绘制,直接返回

    // 获取当前触点位置
    const { offsetX, offsetY } = getEventPosition(e)

    // 使用贝塞尔曲线进行平滑过渡绘制
    ctx.quadraticCurveTo(
      lastX,
      lastY,
      (lastX + offsetX) / 2,
      (lastY + offsetY) / 2
    )
    ctx.stroke() // 实际绘制路径

    // 更新上一个点的位置
    lastX = offsetX
    lastY = offsetY
  }

  // 停止绘制的函数
  function stopDrawing(e) {
    e.preventDefault() // 阻止默认行为
    drawing = false // 结束绘制状态
  }

  // 鼠标事件绑定
  canvas.addEventListener('mousedown', startDrawing) // 鼠标按下开始绘制
  canvas.addEventListener('mousemove', draw) // 鼠标移动时绘制
  canvas.addEventListener('mouseup', stopDrawing) // 鼠标抬起停止绘制
  canvas.addEventListener('mouseout', stopDrawing) // 鼠标移出画布停止绘制

  // 触摸事件绑定
  canvas.addEventListener('touchstart', startDrawing) // 触摸开始绘制
  canvas.addEventListener('touchmove', draw) // 触摸移动时绘制
  canvas.addEventListener('touchend', stopDrawing) // 触摸结束时停止绘制
  canvas.addEventListener('touchcancel', stopDrawing) // 触摸取消时停止绘制

  // 清除画布的功能
  document.getElementById('clear').addEventListener('click', function () {
    ctx.clearRect(0, 0, canvas.width, canvas.height) // 清空整个画布
  })

  // 修改笔画样式的功能
  document
    .getElementById('stroke-style')
    .addEventListener('change', function (e) {
      strokeStyle = e.target.value // 获取选中的笔画样式
      updateStrokeStyle() // 更新样式
    })

  // 根据 strokeStyle 更新笔画样式
  function updateStrokeStyle() {
    if (strokeStyle === 'pen') {
      ctx.lineWidth = 2 // 细线条
      ctx.lineCap = 'round' // 线条末端圆角
    } else if (strokeStyle === 'brush') {
      ctx.lineWidth = 5 // 粗线条
      ctx.lineCap = 'round' // 线条末端圆角
    }
  }

  // 初始化默认的笔画样式
  updateStrokeStyle()
})
Loading...