所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

Vue3拖拽新体验!drag-and-drop让列表排序变得如此丝滑

还在为复杂的拖拽功能而头疼?今天给大家介绍一个超轻量级的拖拽库,让你的 Vue3 项目瞬间拥有丝滑的拖拽体验!

为什么选择 drag-and-drop?

在开始之前,先说说为什么推荐这个库:

  • 超轻量:只有 ~4KB gzipped,不会让你的项目变重
  • 框架无关:虽然我们主要讲 Vue3,但它支持任何框架
  • 数据优先:专注于数据操作,而不是 DOM 操作
  • 简单易用:API 设计简洁,学习成本低
  • TypeScript 支持:完整的类型定义,开发体验极佳

安装与基础配置

安装依赖

npm install @formkit/drag-and-drop
# 或者
yarn add @formkit/drag-and-drop

在 Vue3 项目中使用

<template>
  <div class="custom-drag-container">
    <div
      v-for="item in items"
      :key="item.id"
      :data-id="item.id"
      class="custom-draggable-item"
      :class="{ dragging: draggingId === item.id }"
    >
      <div class="drag-handle">⋮⋮</div>
      <div class="item-content">
        <h4>{{ item.title }}</h4>
        <p>{{ item.description }}</p>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from "vue";
import { dragAndDrop } from "@formkit/drag-and-drop";

const items = ref([
  { id: 1, title: "重要任务", description: "这个任务很重要" },
  { id: 2, title: "普通任务", description: "这是一个普通任务" },
  { id: 3, title: "简单任务", description: "这个任务很简单" },
]);

const draggingId = ref<number | null>(null);

onMounted(() => {
  dragAndDrop({
    parent: document.querySelector(".custom-drag-container"),
    getValues: () => items.value.map((item) => item.id),
    setValues: (newOrder) => {
      const newItems = newOrder
        .map((id) => items.value.find((item) => item.id === id))
        .filter(Boolean);
      items.value = newItems;
    },
    config: {
      // 自定义拖拽手柄
      handle: ".drag-handle",
      // 拖拽开始时的回调
      onDragStart: (data) => {
        draggingId.value = data.value;
        console.log("开始拖拽:", data.value);
      },
      // 拖拽结束时的回调
      onDragEnd: () => {
        draggingId.value = null;
        console.log("拖拽结束");
      },
    },
  });
});
</script>

<style scoped>
.custom-drag-container {
  display: flex;
  flex-direction: column;
  gap: 15px;
  padding: 20px;
}

.custom-draggable-item {
  display: flex;
  align-items: center;
  background: white;
  border-radius: 12px;
  padding: 20px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
  cursor: move;
}

.custom-draggable-item:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}

.custom-draggable-item.dragging {
  opacity: 0.5;
  transform: rotate(5deg);
}

.drag-handle {
  color: #999;
  font-size: 18px;
  margin-right: 15px;
  cursor: grab;
}

.drag-handle:active {
  cursor: grabbing;
}

.item-content h4 {
  margin: 0 0 5px 0;
  color: #333;
}

.item-content p {
  margin: 0;
  color: #666;
  font-size: 14px;
}
</style>

进阶用法:多列表拖拽

实际项目中,我们经常需要在不同的列表之间拖拽元素。比如看板应用:

<template>
  <div class="kanban-board">
    <div class="column">
      <h3>待办</h3>
      <ul ref="todoList" class="task-list">
        <li v-for="task in todos" :key="task.id" class="task-item">
          {{ task.title }}
        </li>
      </ul>
    </div>

    <div class="column">
      <h3>进行中</h3>
      <ul ref="doingList" class="task-list">
        <li v-for="task in doings" :key="task.id" class="task-item">
          {{ task.title }}
        </li>
      </ul>
    </div>

    <div class="column">
      <h3>已完成</h3>
      <ul ref="doneList" class="task-list">
        <li v-for="task in dones" :key="task.id" class="task-item">
          {{ task.title }}
        </li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import { useDragAndDrop } from "@formkit/drag-and-drop/vue";

// 定义任务类型
interface Task {
  id: number;
  title: string;
}

// 初始任务数据
const todoItems = ref<Task[]>([
  { id: 1, title: "设计新功能" },
  { id: 2, title: "编写文档" },
]);

const doingItems = ref<Task[]>([{ id: 3, title: "开发API" }]);

const doneItems = ref<Task[]>([{ id: 4, title: "修复Bug" }]);

// 使用 useDragAndDrop 为每个列创建拖拽功能
const [todoList, todos] = useDragAndDrop(todoItems, {
  group: "kanban-tasks",
});

const [doingList, doings] = useDragAndDrop(doingItems, {
  group: "kanban-tasks",
});

const [doneList, dones] = useDragAndDrop(doneItems, {
  group: "kanban-tasks",
});
</script>

<style scoped>
.kanban-board {
  display: flex;
  gap: 20px;
  padding: 20px;
}

.column {
  flex: 1;
  background: #f8f9fa;
  border-radius: 12px;
  padding: 20px;
  min-height: 400px;
}

.column h3 {
  margin: 0 0 20px 0;
  color: #333;
  font-size: 18px;
}

.task-list {
  min-height: 300px;
  list-style: none;
  padding: 0;
  margin: 0;
}

.task-item {
  background: white;
  padding: 15px;
  margin-bottom: 10px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  cursor: move;
  transition: all 0.3s ease;
}

.task-item:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
</style>

高级功能

添加拖拽限制, 例如限制拖拽方向、限制拖拽区域等。

// 只允许在特定区域内拖拽
dragAndDrop({
  parent: document.querySelector(".drag-container"),
  getValues: () => items.value.map((item) => item.id),
  setValues: (newOrder) => {
    // 更新逻辑
  },
  config: {
    // 限制拖拽方向
    direction: "vertical", // 'horizontal' | 'vertical' | 'both'
    // 添加拖拽阈值
    threshold: 10, // 像素
    // 自定义拖拽类名
    draggingClass: "is-dragging",
    // 禁用拖拽的条件
    disabled: false,
  },
});

样式优化技巧

1. 拖拽时的视觉反馈

/* 拖拽中的元素样式 */
.is-dragging {
  opacity: 0.6;
  transform: rotate(3deg) scale(1.05);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
  z-index: 1000;
}

/* 拖拽目标区域样式 */
.drag-over {
  background: linear-gradient(45deg, #f0f8ff, #e6f3ff);
  border: 2px dashed #4a90e2;
  border-radius: 8px;
}

/* 拖拽手柄样式 */
.drag-handle {
  background: #f5f5f5;
  border-radius: 4px;
  padding: 4px 8px;
  cursor: grab;
  user-select: none;
}

.drag-handle:active {
  cursor: grabbing;
  background: #e0e0e0;
}

2. 动画效果

/* 平滑的拖拽动画 */
.draggable-item {
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

/* 拖拽时的特殊动画 */
.draggable-item:hover {
  transform: translateY(-2px) scale(1.02);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}

/* 放置时的动画 */
.draggable-item.drag-end {
  animation: dropBounce 0.5s ease-out;
}

@keyframes dropBounce {
  0% {
    transform: scale(1.1);
  }
  50% {
    transform: scale(0.95);
  }
  100% {
    transform: scale(1);
  }
}

常见问题与解决方案

1. 拖拽不生效

问题:拖拽功能没有响应

解决方案

// 确保在DOM完全渲染后再初始化
onMounted(() => {
  nextTick(() => {
    // 初始化拖拽
    dragAndDrop({...})
  })
})

2. 跨列表拖拽失败

问题:无法在不同列表间拖拽

解决方案

// 确保所有列表使用相同的group配置
dragAndDrop({
  config: {
    group: "shared-group", // 所有列表使用相同的group
  },
});

3. 移动端触摸问题

问题:在移动设备上拖拽不流畅

解决方案

dragAndDrop({
  config: {
    // 启用触摸支持
    touch: true,
    // 调整触摸阈值
    threshold: 5,
  },
});

总结

@formkit/drag-and-drop 是一个功能强大且轻量级的拖拽库,特别适合 Vue3 项目。通过本文的介绍,你应该能够:

  1. ✅ 快速集成拖拽功能
  2. ✅ 实现复杂的多列表拖拽
  3. ✅ 自定义拖拽行为和样式
  4. ✅ 优化性能和用户体验
  5. ✅ 解决常见问题

记住,好的拖拽体验不仅仅是技术实现,更重要的是用户交互的流畅性和视觉反馈。多测试,多优化,让你的应用更加出色!


** 小贴士**:如果你觉得这篇文章对你有帮助,别忘了点赞和分享哦!有问题欢迎在评论区讨论~

** 相关链接**:

原文链接:https://code.ifrontend.net/archives/1349,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?