50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | DrawingApp(画板组件)
·
📅 我们继续 50 个小项目挑战!—— DrawingApp组件
仓库地址:https://github.com/SunACong/50-vue-projects
项目预览地址:https://50-vue-projects.vercel.app/

使用 Vue 3 的 Composition API(<script setup>) 和 HTML5 <canvas> 元素,结合 TailwindCSS 构建一个简单的在线画板应用。用户可以自由绘制图形、调节画笔粗细、选择颜色,并支持一键清空画布。
🎯 组件目标
- 创建一个固定尺寸的画布区域
- 支持鼠标点击拖动进行绘画
- 提供按钮控制画笔粗细(+ / -)
- 使用原生
<input type="color">选择画笔颜色 - 提供“清空”按钮重置画布内容
- 使用 TailwindCSS 快速构建现代 UI 界面
⚙️ 技术实现点
| 技术点 | 描述 |
|---|---|
Vue 3 Composition API (<script setup>) |
使用响应式变量管理画笔状态 |
ref 响应式变量 |
控制画笔大小、颜色、是否正在绘图等 |
onMounted 生命周期钩子 |
初始化 Canvas 上下文 |
canvas.getContext('2d') |
获取 2D 渲染上下文用于绘图 |
| 鼠标事件监听 | 包括 mousedown、mousemove、mouseup、mouseleave |
| TailwindCSS 布局类 | 实现居中布局、按钮样式、工具栏设计 |
🧱 组件实现
模板结构 <template>
<template>
<div class="flex min-h-screen items-center justify-center bg-gray-900">
<div class="flex flex-col items-center">
<!-- 🎨 画板区域 -->
<canvas
ref="canvasRef"
class="aspect-square w-[800px] border-2 border-gray-300 bg-white"
@mousedown="startDrawing"
@mousemove="draw"
@mouseup="stopDrawing"
@mouseleave="stopDrawing"
@contextmenu.prevent></canvas>
<!-- 🛠️ 工具栏 -->
<div
class="mt-4 flex w-[800px] items-center justify-between rounded-lg bg-gray-800 p-3">
<!-- 粗细调节 -->
<div class="flex items-center">
<button
@click="decreaseBrushSize"
class="rounded p-2 text-white hover:bg-gray-700">
-
</button>
<span class="mx-3 text-white">{{ brushSize }}</span>
<button
@click="increaseBrushSize"
class="rounded p-2 text-white hover:bg-gray-700">
+
</button>
</div>
<!-- 🎨 颜色选择 -->
<input type="color" v-model="brushColor" class="h-10 w-10 cursor-pointer" />
<!-- 清空画布 -->
<button
@click="clearCanvas"
class="rounded bg-red-600 p-2 text-white hover:bg-red-700">
清空
</button>
</div>
</div>
</div>
</template>
脚本逻辑 <script setup>
<script setup>
import { ref, onMounted } from 'vue'
// 🖼️ 画布引用
const canvasRef = ref(null)
// ✍️ 是否正在绘图
const isDrawing = ref(false)
// 画笔粗细 & 颜色
const brushSize = ref(5)
const brushColor = ref('#000000')
let lastX = 0
let lastY = 0
let ctx = null
// 初始化画布
onMounted(() => {
const canvas = canvasRef.value
canvas.width = canvas.offsetWidth
canvas.height = canvas.offsetHeight
ctx = canvas.getContext('2d')
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
})
// 开始绘制
const startDrawing = (e) => {
if (e.button === 0) {
isDrawing.value = true
lastX = e.offsetX
lastY = e.offsetY
}
}
// 绘制中
const draw = (e) => {
if (!isDrawing.value) return
ctx.beginPath()
ctx.moveTo(lastX, lastY)
ctx.lineTo(e.offsetX, e.offsetY)
ctx.strokeStyle = brushColor.value
ctx.lineWidth = brushSize.value
ctx.stroke()
lastX = e.offsetX
lastY = e.offsetY
}
// 停止绘制
const stopDrawing = () => {
isDrawing.value = false
}
// 控制画笔大小
const increaseBrushSize = () => {
if (brushSize.value < 50) brushSize.value += 1
}
const decreaseBrushSize = () => {
if (brushSize.value > 1) brushSize.value -= 1
}
// 清除画布
const clearCanvas = () => {
const canvas = canvasRef.value
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
</script>
自定义样式 <style scoped>
<style scoped>
[draggable='true'] {
transition: all 0.2s ease;
}
</style>
🔍 重点效果实现
✅ 鼠标事件绑定机制
我们通过以下事件监听器实现了完整的绘图交互:
| 事件名 | 触发时机 | 作用 |
|---|---|---|
mousedown |
鼠标按下时 | 标记为开始绘制,并记录初始坐标 |
mousemove |
鼠标移动时 | 如果处于绘制状态,则根据当前坐标绘制线条 |
mouseup |
鼠标释放时 | 结束绘制 |
mouseleave |
鼠标移出画布 | 安全结束绘制,防止悬停后继续绘制 |
contextmenu.prevent |
右键菜单阻止 | 防止右键弹出默认菜单影响操作 |
💡 Canvas 绘图基础设置
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
这两行代码设置了线条两端为圆角,使绘制效果更自然流畅。
🎨 动态画笔颜色与粗细
我们通过响应式变量 brushColor 和 brushSize 来动态更新画笔属性:
ctx.strokeStyle = brushColor.value
ctx.lineWidth = brushSize.value
这样就能实时反映用户的选择变化。
🗑️ 清空画布功能
ctx.clearRect(0, 0, canvas.width, canvas.height)
调用 clearRect 方法一次性清除整个画布,实现“清空”功能。
🎨 TailwindCSS 样式重点讲解
| 类名 | 作用 |
|---|---|
min-h-screen |
设置最小高度为视口高度 |
items-center, justify-center |
Flexbox 居中对齐布局 |
bg-gray-900 |
设置深色背景 |
aspect-square |
保持画布为正方形比例 |
w-[800px] |
固定宽度为 800px |
border-2, border-gray-300 |
边框样式 |
bg-white |
画布背景色 |
rounded-lg, p-3 |
工具栏圆角与内边距 |
hover:bg-gray-700 |
按钮悬停变色 |
text-white |
白色文字 |
cursor-pointer |
鼠标悬停变为手型 |
h-10, w-10 |
设置颜色选择器大小 |
这些 TailwindCSS 类帮助我们快速构建了一个美观、响应式的画板界面。
📁 常量定义 + 组件路由
constants/index.js 添加组件预览常量:
{
id: 22,
title: 'Drawing App',
image: 'https://50projects50days.com/img/projects-img/22-drawing-app.png',
link: 'DrawingApp',
},
router/index.js 中添加路由选项:
{
path: '/DrawingApp',
name: 'DrawingApp',
component: () => import('@/projects/DrawingApp.vue'),
},
📁 扩展建议
你可以进一步扩展此组件的功能,例如:
- ✅ 支持保存画布内容为图片(
canvas.toDataURL()) - ✅ 添加撤销/重做功能(记录历史快照)
- ✅ 支持触控设备(如 iPad 或触摸屏)
- ✅ 封装为独立组件(支持 props 传入默认颜色或大小)
🏁 总结
基于 Vue 3 和 Canvas 的画板组件不仅实现了基本的绘图功能,还提供了友好的交互体验和清晰的视觉反馈。无论是作为学习项目还是实际开发中的组件模块,都非常实用。
感谢阅读,欢迎点赞、收藏和分享 😊
👉 下一篇,我们将完成KineticLoader组件,一个很有意思的旋转加载动画。🚀
更多推荐
所有评论(0)