📅 我們繼續 50 個小項目挑戰!—— DragNDrop
組件
倉庫地址:https://github.com/SunACong/50-vue-projects
項目預覽地址:https://50-vue-projects.vercel.app/
使用 Vue 3 的 Composition API 和 <script setup>
語法結合 TailwindCSS 構建一個支持拖拽交互的圖片拖放組件。該組件允許用戶將一張圖片從一個容器拖動并釋放到另一個“空位”中,并帶有視覺反饋(如懸停高亮、背景變化等)。
🎯 組件目標
- 創建多個“空位”容器
- 默認展示一張可拖動的圖片
- 支持拖拽交互并投放到任意空位
- 投放后更新對應位置的圖片狀態
- 拖拽過程中提供視覺反饋(如懸停樣式)
- 使用 TailwindCSS 快速構建現代 UI 界面
?? 技術實現點
技術點 | 描述 |
---|---|
Vue 3 Composition API (<script setup> ) | 使用響應式變量管理組件狀態 |
ref 響應式變量 | 控制當前圖片所在索引與懸停狀態 |
v-for 循環渲染 | 動態生成多個容器 |
@dragstart , @dragend , @dragover , @drop 等事件 | 實現完整的拖拽交互邏輯 |
TailwindCSS 條件類綁定 | 根據狀態動態應用樣式 |
:style 動態綁定背景圖 | 展示圖片資源 |
🧱 組件實現
模板結構 <template>
<template><div class="flex h-screen items-center justify-center overflow-hidden bg-sky-500"><!-- 多個空位容器 --><divv-for="(empty, index) in empties":key="index":class="['m-2 h-36 w-36 border-4 border-black bg-white',isHovered[index] && 'border-dashed border-black bg-gray-800',]"@dragover.prevent="dragOver"@dragenter.prevent="dragEnter(index)" @dragleave="dragLeave(index)"@drop="dragDrop(index)"><!-- 當前圖片所在的容器 --><divv-if="index === filledIndex"class="h-full w-full cursor-pointer bg-cover":style="{ backgroundImage: `url(${imageUrls[index]})` }"draggable="true"@dragstart="dragStart"@dragend="dragEnd"></div></div></div>
</template>
腳本邏輯 <script setup>
<script setup>
import { ref } from 'vue'const filledIndex = ref(0)
const isHovered = ref(Array(5).fill(false))// 用于循環生成 5 個空位容器
const empties = Array.from({ length: 5 }, (_, index) => index)// 圖片地址數組
const imageUrls = ['https://picsum.photos/id/10/150/150','https://picsum.photos/id/11/150/150','https://picsum.photos/id/12/150/150','https://picsum.photos/id/13/150/150','https://picsum.photos/id/14/150/150',
]// 拖拽開始
const dragStart = () => {// 可添加 hold 效果或數據存儲
}// 拖拽結束
const dragEnd = () => {// 可添加恢復效果
}// 鼠標懸停時觸發
const dragOver = () => {}// 進入容器時觸發
const dragEnter = (index) => {isHovered.value[index] = true
}// 離開容器時觸發
const dragLeave = (index) => {isHovered.value[index] = false
}// 投放操作
const dragDrop = (index) => {filledIndex.value = indexisHovered.value = Array(5).fill(false)
}
</script>
自定義樣式 <style scoped>
<style scoped>
[draggable='true'] {transition: all 0.2s ease;
}
</style>
🔍 重點效果實現
? 拖拽交互邏輯詳解
1. 拖拽開始:dragStart
當用戶點擊并開始拖動圖片時,會觸發 dragStart
方法。你可以在這里做一些準備操作,例如記錄當前拖動的數據。
2. 拖拽進入容器:dragEnter(index)
當鼠標帶著元素進入某個容器時,我們通過傳入 index
設置該容器的懸停狀態為 true
,從而激活其高亮樣式。
3. 拖拽離開容器:dragLeave(index)
當鼠標離開容器時,設置該容器的懸停狀態為 false
,恢復默認樣式。
4. 投放完成:dragDrop(index)
這是整個拖拽流程的核心,它負責更新 filledIndex
,將圖片移動到新的容器,并重置所有懸停狀態。
💡 視覺反饋機制
我們使用了一個布爾數組 isHovered
來保存每個容器是否被懸停:
const isHovered = ref(Array(5).fill(false))
并通過 v-if="index === filledIndex"
判斷哪個容器應該顯示圖片,其他則為空白容器。
TailwindCSS 中根據這個狀態來切換樣式:
:class="['m-2 h-36 w-36 border-4 border-black bg-white',isHovered[index] && 'border-dashed border-black bg-gray-800',
]"
🖼? 圖片動態加載
使用了 :style
來動態設置背景圖:
:style="{ backgroundImage: `url(${imageUrls[index]})` }"
每張圖片都來自 Picsum Photos 提供的隨機圖片服務,確保每次運行都能看到不同的內容。
🎨 TailwindCSS 樣式重點講解
類名 | 作用 |
---|---|
h-screen , items-center , justify-center | 全屏高度 + 內容居中布局 |
overflow-hidden | 防止內容溢出 |
bg-sky-500 | 設置背景顏色為淺藍色 |
h-36 , w-36 | 設置每個容器的寬高為 36(9rem) |
m-2 | 設置外邊距為 2(0.5rem) |
border-4 , border-black | 黑色邊框 |
bg-white / bg-gray-800 | 默認和懸停狀態下的背景顏色 |
border-dashed | 懸停時邊框變為虛線 |
cursor-pointer | 設置圖片區域為可點擊 |
bg-cover | 圖片背景自適應填充 |
transition | 添加拖拽過程中的平滑過渡動畫 |
📁 常量定義 + 組件路由
constants/index.js
添加組件預覽常量:
{id: 21,title: 'Drag N Drop',image: 'https://50projects50days.com/img/projects-img/21-drag-n-drop.png',link: 'DragNDrop',},
router/index.js
中添加路由選項:
{path: '/DragNDrop',name: 'DragNDrop',component: () => import('@/projects/DragNDrop.vue'),},
📁 擴展功能建議
進一步擴展的功能推薦:
- ? 支持多張圖片同時拖動
- ? 支持圖片預覽拖拽(不立即改變原圖位置)
- ? 拖拽時高亮目標容器邊界
- ? 支持觸摸設備拖拽交互(移動端適配)
- ? 封裝為可復用組件(支持 props 傳入圖片列表)
🏁 總結
👉 下一篇,我們將完成DrawApp
組件,創建一個畫板具有調節畫筆粗細的功能,并且能夠一鍵清除畫板上的內容。🚀