当前位置:首页 > VUE

vue实现手写功能

2026-02-20 11:53:20VUE

实现手写功能的基本思路

在Vue中实现手写功能通常需要结合HTML5的Canvas API,通过监听鼠标或触摸事件来捕获用户的绘制路径,并将其渲染到画布上。核心步骤包括初始化画布、记录绘制路径、处理触摸事件兼容性等。

初始化Canvas画布

在Vue组件的模板中添加Canvas元素,并设置宽度和高度。建议通过ref获取DOM引用以便后续操作:

<template>
  <canvas
    ref="canvas"
    @mousedown="startDrawing"
    @mousemove="draw"
    @mouseup="stopDrawing"
    @touchstart="handleTouchStart"
    @touchmove="handleTouchMove"
    @touchend="stopDrawing"
  ></canvas>
</template>

mounted生命周期中初始化画布上下文:

mounted() {
  const canvas = this.$refs.canvas;
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;
  this.ctx = canvas.getContext('2d');
  this.ctx.lineWidth = 5;
  this.ctx.lineCap = 'round';
  this.ctx.strokeStyle = '#000000';
}

处理绘制逻辑

定义绘制状态变量和事件处理函数。使用isDrawing标记是否处于绘制状态,并记录路径点:

vue实现手写功能

data() {
  return {
    isDrawing: false,
    lastX: 0,
    lastY: 0
  };
},
methods: {
  startDrawing(e) {
    this.isDrawing = true;
    const { offsetX, offsetY } = this.getPosition(e);
    [this.lastX, this.lastY] = [offsetX, offsetY];
  },
  draw(e) {
    if (!this.isDrawing) return;
    const { offsetX, offsetY } = this.getPosition(e);
    this.ctx.beginPath();
    this.ctx.moveTo(this.lastX, this.lastY);
    this.ctx.lineTo(offsetX, offsetY);
    this.ctx.stroke();
    [this.lastX, this.lastY] = [offsetX, offsetY];
  },
  stopDrawing() {
    this.isDrawing = false;
  }
}

处理触摸事件兼容性

添加触摸事件的支持需要转换触摸坐标为画布坐标:

methods: {
  getPosition(e) {
    const canvas = this.$refs.canvas;
    if (e.type.includes('touch')) {
      const rect = canvas.getBoundingClientRect();
      return {
        offsetX: e.touches[0].clientX - rect.left,
        offsetY: e.touches[0].clientY - rect.top
      };
    }
    return { offsetX: e.offsetX, offsetY: e.offsetY };
  },
  handleTouchStart(e) {
    e.preventDefault();
    this.startDrawing(e);
  },
  handleTouchMove(e) {
    e.preventDefault();
    this.draw(e);
  }
}

添加清除和保存功能

扩展功能按钮,实现清除画布和保存图像:

vue实现手写功能

<button @click="clearCanvas">清除</button>
<button @click="saveAsImage">保存</button>

对应方法实现:

methods: {
  clearCanvas() {
    const canvas = this.$refs.canvas;
    this.ctx.clearRect(0, 0, canvas.width, canvas.height);
  },
  saveAsImage() {
    const canvas = this.$refs.canvas;
    const link = document.createElement('a');
    link.download = 'signature.png';
    link.href = canvas.toDataURL('image/png');
    link.click();
  }
}

优化绘制性能

对于频繁触发的mousemove事件,可以使用requestAnimationFrame进行节流:

draw(e) {
  if (!this.isDrawing) return;
  window.requestAnimationFrame(() => {
    const { offsetX, offsetY } = this.getPosition(e);
    this.ctx.beginPath();
    this.ctx.moveTo(this.lastX, this.lastY);
    this.ctx.lineTo(offsetX, offsetY);
    this.ctx.stroke();
    [this.lastX, this.lastY] = [offsetX, offsetY];
  });
}

完整组件示例

整合后的完整Vue组件代码:

<template>
  <div>
    <canvas
      ref="canvas"
      @mousedown="startDrawing"
      @mousemove="draw"
      @mouseup="stopDrawing"
      @touchstart="handleTouchStart"
      @touchmove="handleTouchMove"
      @touchend="stopDrawing"
    ></canvas>
    <div>
      <button @click="clearCanvas">清除</button>
      <button @click="saveAsImage">保存</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isDrawing: false,
      lastX: 0,
      lastY: 0,
      ctx: null
    };
  },
  mounted() {
    const canvas = this.$refs.canvas;
    canvas.width = canvas.offsetWidth;
    canvas.height = canvas.offsetHeight;
    this.ctx = canvas.getContext('2d');
    this.ctx.lineWidth = 5;
    this.ctx.lineCap = 'round';
    this.ctx.strokeStyle = '#000000';
  },
  methods: {
    getPosition(e) {
      const canvas = this.$refs.canvas;
      if (e.type.includes('touch')) {
        const rect = canvas.getBoundingClientRect();
        return {
          offsetX: e.touches[0].clientX - rect.left,
          offsetY: e.touches[0].clientY - rect.top
        };
      }
      return { offsetX: e.offsetX, offsetY: e.offsetY };
    },
    startDrawing(e) {
      this.isDrawing = true;
      const { offsetX, offsetY } = this.getPosition(e);
      [this.lastX, this.lastY] = [offsetX, offsetY];
    },
    draw(e) {
      if (!this.isDrawing) return;
      window.requestAnimationFrame(() => {
        const { offsetX, offsetY } = this.getPosition(e);
        this.ctx.beginPath();
        this.ctx.moveTo(this.lastX, this.lastY);
        this.ctx.lineTo(offsetX, offsetY);
        this.ctx.stroke();
        [this.lastX, this.lastY] = [offsetX, offsetY];
      });
    },
    stopDrawing() {
      this.isDrawing = false;
    },
    handleTouchStart(e) {
      e.preventDefault();
      this.startDrawing(e);
    },
    handleTouchMove(e) {
      e.preventDefault();
      this.draw(e);
    },
    clearCanvas() {
      const canvas = this.$refs.canvas;
      this.ctx.clearRect(0, 0, canvas.width, canvas.height);
    },
    saveAsImage() {
      const canvas = this.$refs.canvas;
      const link = document.createElement('a');
      link.download = 'signature.png';
      link.href = canvas.toDataURL('image/png');
      link.click();
    }
  }
};
</script>

<style scoped>
canvas {
  border: 1px solid #000;
  background-color: white;
  touch-action: none;
}
</style>

标签: 功能vue
分享给朋友:

相关文章

vue懒加载实现难吗

vue懒加载实现难吗

vue懒加载的实现难度 Vue懒加载的实现并不复杂,核心逻辑是通过动态导入(Dynamic Imports)和路由配置或组件异步加载完成。以下是具体实现方法: 路由懒加载实现 在Vue Router…

vue实现下拉框删除

vue实现下拉框删除

Vue 实现下拉框删除功能 在Vue中实现下拉框的删除功能通常涉及以下步骤: 数据绑定与渲染 在Vue组件中定义数据数组用于存储下拉框选项: data() { return {…

vue实现视窗

vue实现视窗

Vue 实现视窗功能 在 Vue 中实现视窗功能通常涉及监听浏览器窗口大小变化、响应式布局或创建自定义弹窗组件。以下是几种常见实现方式: 监听浏览器窗口大小变化 使用 Vue 的 mounted 和…

vue element实现

vue element实现

Vue Element UI 实现 Vue Element UI 是基于 Vue.js 的组件库,提供丰富的 UI 组件,适合快速开发企业级中后台产品。以下是一些关键实现方法和步骤: 安装 Vue…

vue vnode实现

vue vnode实现

Vue VNode 实现原理 VNode(Virtual Node)是 Vue 的核心概念之一,用于描述真实 DOM 的轻量级虚拟表示。VNode 的实现涉及以下关键点: VNode 的基本结构…

vue拖拽实现

vue拖拽实现

Vue 拖拽实现方法 使用 HTML5 原生拖拽 API HTML5 提供了原生拖拽 API,可以通过 draggable 属性、dragstart、dragend、dragover 和 drop 事…