50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | DragNDrop(拖拽占用组件)

📅 我们继续 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>

html 复制代码
<template>
    <div class="flex h-screen items-center justify-center overflow-hidden bg-sky-500">
        <!-- 多个空位容器 -->
        <div
            v-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)">
            
            <!-- 当前图片所在的容器 -->
            <div
                v-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>

js 复制代码
<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 = index
    isHovered.value = Array(5).fill(false)
}
</script>

自定义样式 <style scoped>

html 复制代码
<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 来保存每个容器是否被悬停:

js 复制代码
const isHovered = ref(Array(5).fill(false))

并通过 v-if="index === filledIndex" 判断哪个容器应该显示图片,其他则为空白容器。

TailwindCSS 中根据这个状态来切换样式:

css 复制代码
:class="[
    'm-2 h-36 w-36 border-4 border-black bg-white',
    isHovered[index] && 'border-dashed border-black bg-gray-800',
]"

🖼️ 图片动态加载

使用了 :style 来动态设置背景图:

css 复制代码
: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 添加组件预览常量:

js 复制代码
{
        id: 21,
        title: 'Drag N Drop',
        image: 'https://50projects50days.com/img/projects-img/21-drag-n-drop.png',
        link: 'DragNDrop',
    },

router/index.js 中添加路由选项:

js 复制代码
{
        path: '/DragNDrop',
        name: 'DragNDrop',
        component: () => import('@/projects/DragNDrop.vue'),
    },

📁 扩展功能建议

进一步扩展的功能推荐:

  • ✅ 支持多张图片同时拖动
  • ✅ 支持图片预览拖拽(不立即改变原图位置)
  • ✅ 拖拽时高亮目标容器边界
  • ✅ 支持触摸设备拖拽交互(移动端适配)
  • ✅ 封装为可复用组件(支持 props 传入图片列表)

🏁 总结

👉 下一篇,我们将完成DrawApp组件,创建一个画板具有调节画笔粗细的功能,并且能够一键清除画板上的内容。🚀

相关推荐
拓端研究室12 分钟前
视频讲解:门槛效应模型Threshold Effect分析数字金融指数与消费结构数据
前端·算法
工一木子1 小时前
URL时间戳参数深度解析:缓存破坏与前端优化的前世今生
前端·缓存
半点寒12W3 小时前
微信小程序实现路由拦截的方法
前端
某公司摸鱼前端4 小时前
uniapp socket 封装 (可拿去直接用)
前端·javascript·websocket·uni-app
要加油哦~4 小时前
vue | 插件 | 移动文件的插件 —— move-file-cli 插件 的安装与使用
前端·javascript·vue.js
小林学习编程4 小时前
Springboot + vue + uni-app小程序web端全套家具商场
前端·vue.js·spring boot
柳鲲鹏4 小时前
WINDOWS最快布署WEB服务器:apache2
服务器·前端·windows
weixin-a153003083165 小时前
【playwright篇】教程(十七)[html元素知识]
java·前端·html
ai小鬼头5 小时前
AIStarter最新版怎么卸载AI项目?一键删除操作指南(附路径设置技巧)
前端·后端·github
wen's5 小时前
React Native 0.79.4 中 [RCTView setColor:] 崩溃问题完整解决方案
javascript·react native·react.js