用vue实现手绘功能
实现手绘功能的核心思路
在Vue中实现手绘功能主要依赖HTML5的Canvas API,通过监听鼠标或触摸事件记录轨迹并实时绘制。以下分步骤说明具体实现方法。
创建Canvas画布
在Vue组件的模板中添加Canvas元素,并设置宽高。通过ref获取DOM节点以便后续操作:
<template>
<canvas
ref="canvas"
@mousedown="startDrawing"
@mousemove="draw"
@mouseup="stopDrawing"
@touchstart="startDrawing"
@touchmove="draw"
@touchend="stopDrawing">
</canvas>
</template>
初始化画布上下文
在mounted生命周期中初始化Canvas的2D渲染上下文,并设置默认样式:

mounted() {
this.canvas = this.$refs.canvas;
this.ctx = this.canvas.getContext('2d');
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
this.ctx.strokeStyle = '#000000';
this.ctx.lineWidth = 5;
this.ctx.lineCap = 'round';
}
处理绘制逻辑
定义绘图状态变量和事件处理函数,实现以下功能:
data() {
return {
isDrawing: false,
lastX: 0,
lastY: 0
}
},
methods: {
startDrawing(e) {
this.isDrawing = true;
const pos = this.getPosition(e);
[this.lastX, this.lastY] = [pos.x, pos.y];
},
draw(e) {
if (!this.isDrawing) return;
const pos = this.getPosition(e);
this.ctx.beginPath();
this.ctx.moveTo(this.lastX, this.lastY);
this.ctx.lineTo(pos.x, pos.y);
this.ctx.stroke();
[this.lastX, this.lastY] = [pos.x, pos.y];
},
stopDrawing() {
this.isDrawing = false;
},
getPosition(e) {
const rect = this.canvas.getBoundingClientRect();
return {
x: (e.clientX || e.touches[0].clientX) - rect.left,
y: (e.clientY || e.touches[0].clientY) - rect.top
};
}
}
添加清除功能
扩展功能以支持清除画布:

methods: {
clearCanvas() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
响应式调整画布大小
监听窗口变化事件,动态调整Canvas尺寸:
mounted() {
window.addEventListener('resize', this.handleResize);
},
methods: {
handleResize() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
}
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize);
}
完整组件示例
整合上述代码的完整Vue组件示例:
<template>
<div>
<canvas
ref="canvas"
@mousedown="startDrawing"
@mousemove="draw"
@mouseup="stopDrawing"
@touchstart="startDrawing"
@touchmove="draw"
@touchend="stopDrawing">
</canvas>
<button @click="clearCanvas">Clear</button>
</div>
</template>
<script>
export default {
data() {
return {
isDrawing: false,
lastX: 0,
lastY: 0
}
},
mounted() {
this.canvas = this.$refs.canvas;
this.ctx = this.canvas.getContext('2d');
this.handleResize();
window.addEventListener('resize', this.handleResize);
},
methods: {
startDrawing(e) {
this.isDrawing = true;
const pos = this.getPosition(e);
[this.lastX, this.lastY] = [pos.x, pos.y];
},
draw(e) {
if (!this.isDrawing) return;
const pos = this.getPosition(e);
this.ctx.beginPath();
this.ctx.moveTo(this.lastX, this.lastY);
this.ctx.lineTo(pos.x, pos.y);
this.ctx.stroke();
[this.lastX, this.lastY] = [pos.x, pos.y];
},
stopDrawing() {
this.isDrawing = false;
},
clearCanvas() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
getPosition(e) {
const rect = this.canvas.getBoundingClientRect();
return {
x: (e.clientX || e.touches[0].clientX) - rect.left,
y: (e.clientY || e.touches[0].clientY) - rect.top
};
},
handleResize() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
}
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize);
}
}
</script>
扩展功能建议
- 颜色选择器:添加
<input type="color">绑定到ctx.strokeStyle - 笔刷粗细:通过滑块控制
ctx.lineWidth - 撤销功能:使用数组记录绘制路径实现多步撤销
- 保存图片:通过
canvas.toDataURL()导出绘制结果






