📅 我們繼續 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
組件,一個很有意思的旋轉加載動畫。🚀