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组件,创建一个画板具有调节画笔粗细的功能,并且能够一键清除画板上的内容。🚀

相关推荐
deli0075 分钟前
32K 上下文塞满后,AI 最先忘掉什么?120 轮对话实测
前端
deli0071 小时前
手机拍灯为什么有一条条黑纹?PWM 240Hz 实测
前端
wangruofeng6 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng6 小时前
给前端开发者的 100 个设计参考网站
前端·设计
M--Y8 小时前
LangGraph 工作流模式
开发语言·javascript·python·langchain·大模型·llm·langgraph
星栈9 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技9 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户47949283569159 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马12 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng12 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构