📅 我们继续 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"><!-- 🎨 画板区域 --><canvasref="canvasRef"class="aspect-square w-[800px] border-2 border-gray-300 bg-white"@mousedown="startDrawing"@mousemove="draw"@mouseup="stopDrawing"@mouseleave="stopDrawing"@contextmenu.prevent></canvas><!-- 🛠️ 工具栏 --><divclass="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.valuecanvas.width = canvas.offsetWidthcanvas.height = canvas.offsetHeightctx = canvas.getContext('2d')ctx.lineCap = 'round'ctx.lineJoin = 'round'
})// 开始绘制
const startDrawing = (e) => {if (e.button === 0) {isDrawing.value = truelastX = e.offsetXlastY = e.offsetY}
}// 绘制中
const draw = (e) => {if (!isDrawing.value) returnctx.beginPath()ctx.moveTo(lastX, lastY)ctx.lineTo(e.offsetX, e.offsetY)ctx.strokeStyle = brushColor.valuectx.lineWidth = brushSize.valuectx.stroke()lastX = e.offsetXlastY = 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.valuectx.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
组件,一个很有意思的旋转加载动画。🚀