当前位置: 首页 > news >正文

Shopify Draggable + Vue 3 完整指南:打造现代化拖拽交互体验

简介

Draggable 是一个现代化的 JavaScript 拖拽库,由 Shopify 开发,提供了强大而灵活的拖拽功能。它不仅支持基本的拖拽操作,还提供了丰富的插件系统、事件处理和自定义选项,可以轻松实现复杂的拖拽交互。

主要特性

  • 🎯 模块化设计: 基于插件架构,按需加载功能
  • 📱 触摸友好: 完美支持移动设备和触摸操作
  • 🎨 高度可定制: 丰富的配置选项和样式控制
  • 🔧 插件系统: 内置多种插件扩展功能
  • 🌐 现代浏览器: 支持所有现代浏览器
  • ♿ 可访问性: 内置键盘导航和屏幕阅读器支持
  • 🚀 性能优化: 高效的事件处理和 DOM 操作

安装

# npm
npm install @shopify/draggable# yarn
yarn add @shopify/draggable# pnpm
pnpm add @shopify/draggable

Vue 3.js 基础使用示例

基本拖拽功能

<template><divclass="min-h-screen bg-gradient-to-br from-emerald-50 to-teal-100 py-12 px-4"><div class="max-w-md mx-auto"><h3 class="text-2xl font-bold text-gray-800 text-center mb-8">🎯 基本拖拽排序</h3><div ref="dragContainer" class="space-y-4"><divv-for="item in dragItems":key="item.id":data-id="item.id"class="draggable-item bg-white rounded-lg shadow-md hover:shadow-lg transition-all duration-200 p-6 cursor-move border border-gray-200 hover:border-emerald-300"><div class="flex items-center"><svgclass="w-5 h-5 text-gray-400 mr-3"fill="currentColor"viewBox="0 0 20 20"><pathd="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"></path></svg><div><h4 class="font-semibold text-gray-800">{{ item.title }}</h4><p class="text-sm text-gray-500">{{ item.description }}</p></div></div></div></div></div></div>
</template><script setup>
import { ref, onMounted, nextTick } from "vue";
import { Sortable } from "@shopify/draggable";const dragContainer = ref(null);
const dragItems = ref([{ id: 1, title: "任务 1", description: "完成项目设计" },{ id: 2, title: "任务 2", description: "开发核心功能" },{ id: 3, title: "任务 3", description: "编写测试用例" },{ id: 4, title: "任务 4", description: "部署到生产环境" },
]);let sortableInstance = null;onMounted(() => {nextTick(() => {sortableInstance = new Sortable(dragContainer.value, {draggable: ".draggable-item",mirror: {appendTo: "body",constrainDimensions: true,},});// 监听拖拽排序事件sortableInstance.on("sortable:sorted", (evt) => {const { oldIndex, newIndex } = evt;// 更新数据顺序const item = dragItems.value.splice(oldIndex, 1)[0];dragItems.value.splice(newIndex, 0, item);});// 监听拖拽开始事件sortableInstance.on("drag:start", (evt) => {console.log("开始拖拽:", evt.source.dataset.id);});// 监听拖拽结束事件sortableInstance.on("drag:stop", (evt) => {console.log("拖拽结束:", evt.source.dataset.id);});});
});
</script><style scoped>
/* Draggable 镜像样式 */
.draggable-mirror {opacity: 0.8;transform: rotate(2deg);box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);border-radius: 0.5rem;
}/* 拖拽中的原始元素样式 */
.draggable--is-dragging {opacity: 0.3;transform: scale(0.95);
}/* 拖拽悬停目标样式 */
.draggable-container--over {background-color: #f0fdf4;
}/* 拖拽项目悬停样式 */
.draggable-item:hover {transform: translateY(-2px);
}
</style>

多容器拖拽交换

<template><divclass="min-h-screen bg-gradient-to-br from-purple-50 to-pink-50 py-12 px-4"><div class="max-w-6xl mx-auto"><h3 class="text-3xl font-bold text-gray-800 text-center mb-8">🔄 多容器拖拽交换</h3><div class="grid grid-cols-1 md:grid-cols-2 gap-8"><!-- 源容器 --><div class="bg-white rounded-xl shadow-lg p-6"><h4 class="text-xl font-semibold text-gray-700 mb-4 text-center">📦 可用组件</h4><divref="sourceContainer"class="space-y-3 min-h-[400px] p-4 border-2 border-dashed border-gray-200 rounded-lg"><divv-for="item in sourceItems":key="item.id":data-id="item.id"class="swappable-item bg-gradient-to-r from-purple-100 to-pink-100 border border-purple-200 rounded-lg p-4 cursor-move hover:shadow-md transition-all duration-200"><div class="flex items-center"><div class="w-3 h-3 bg-purple-500 rounded-full mr-3"></div><div><h5 class="font-medium text-gray-800">{{ item.name }}</h5><p class="text-sm text-gray-500">{{ item.type }}</p></div></div></div></div></div><!-- 目标容器 --><div class="bg-white rounded-xl shadow-lg p-6"><h4 class="text-xl font-semibold text-gray-700 mb-4 text-center">🎯 设计画布</h4><divref="targetContainer"class="space-y-3 min-h-[400px] p-4 border-2 border-dashed border-blue-200 rounded-lg bg-blue-50"><divv-for="item in targetItems":key="item.id":data-id="item.id"class="swappable-item bg-gradient-to-r from-blue-100 to-indigo-100 border border-blue-200 rounded-lg p-4 cursor-move hover:shadow-md transition-all duration-200"><div class="flex items-center"><div class="w-3 h-3 bg-blue-500 rounded-full mr-3"></div><div><h5 class="font-medium text-gray-800">{{ item.name }}</h5><p class="text-sm text-gray-500">{{ item.type }}</p></div></div></div></div></div></div></div></div>
</template><script setup>
import { ref, onMounted, nextTick } from "vue";
import { Swappable } from "@shopify/draggable";const sourceContainer = ref(null);
const targetContainer = ref(null);const sourceItems = ref([{ id: 1, name: "按钮组件", type: "UI组件" },{ id: 2, name: "输入框", type: "UI组件" },{ id: 3, name: "图片组件", type: "媒体组件" },
]);const targetItems = ref([{ id: 4, name: "标题组件", type: "UI组件" }]);let swappableInstance = null;onMounted(() => {nextTick(() => {const containers = [sourceContainer.value, targetContainer.value];swappableInstance = new Swappable(containers, {draggable: ".swappable-item",mirror: {appendTo: "body",constrainDimensions: true,},});swappableInstance.on("swappable:stop", (evt) => {updateContainersData();});});
});function updateContainersData() {// 更新源容器数据const sourceElements =sourceContainer.value.querySelectorAll(".swappable-item");const newSourceItems = Array.from(sourceElements).map((el) => {const id = parseInt(el.dataset.id);return [...sourceItems.value, ...targetItems.value].find((item) => item.id === id);}).filter(Boolean);// 更新目标容器数据const targetElements =targetContainer.value.querySelectorAll(".swappable-item");const newTargetItems = Array.from(targetElements).map((el) => {const id = parseInt(el.dataset.id);return [...sourceItems.value, ...targetItems.value].find((item) => item.id === id);}).filter(Boolean);sourceItems.value = newSourceItems;targetItems.value = newTargetItems;
}
</script><style scoped>
.draggable-mirror {opacity: 0.8;transform: rotate(3deg) scale(1.05);box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}.swappable--is-dragging {opacity: 0.5;
}.draggable-container--over {background-color: #f0f9ff;border-color: #0ea5e9;
}
</style>

核心类和插件

主要类

类名描述用途
Draggable基础拖拽类实现基本的拖拽功能
Sortable可排序类在容器内重新排序元素
Swappable可交换类在多个容器间交换元素
Droppable可放置类实现拖放到指定区域

常用插件

插件描述功能
Mirror镜像插件显示拖拽时的镜像元素
Focusable焦点插件键盘导航支持
Announcement公告插件屏幕阅读器支持
ScrollSensitive滚动敏感插件拖拽时自动滚动

常用配置选项

选项类型默认值描述
draggableString.draggable-source--is-draggable可拖拽元素选择器
handleStringnull拖拽手柄选择器
delayNumber0拖拽延迟时间(毫秒)
distanceNumber0触发拖拽的最小距离
mirrorObject{}镜像配置选项
scrollableObject{}滚动配置选项

常用事件

事件描述触发时机
drag:start开始拖拽拖拽开始时
drag:move拖拽移动拖拽过程中
drag:stop拖拽结束拖拽结束时
sortable:start开始排序排序开始时
sortable:stop排序结束排序结束时
swappable:start开始交换交换开始时
swappable:stop交换结束交换结束时

最佳实践

  1. 性能优化: 使用事件委托,避免为每个元素绑定事件
  2. 数据同步: 及时更新 Vue 的响应式数据状态
  3. 用户体验: 提供清晰的视觉反馈和动画效果
  4. 可访问性: 启用键盘导航和屏幕阅读器支持
  5. 移动端适配: 测试触摸设备的拖拽体验
  6. 错误处理: 添加适当的错误处理和回滚机制

总结

Draggable 是一个功能强大且高度可定制的拖拽库,提供了完整的拖拽解决方案。通过其模块化的设计和丰富的插件系统,可以轻松实现各种复杂的拖拽交互需求。与 Vue 3 结合使用时,能够创建出流畅且用户友好的拖拽界面。

 Shopify Draggable + Vue 3 完整指南:打造现代化拖拽交互体验 - 高质量源码分享平台-免费下载各类网站源码与模板及前沿技术分享

http://www.lryc.cn/news/604540.html

相关文章:

  • Apache Ignite 与 Spring Data 集成
  • 人工智能与安全:智能安防的创新与伦理边界
  • 把Java程序部署到本地Docker
  • 常见CMS
  • NVIDIA Isaac平台推动医疗AI机器人发展研究
  • Hyperchain 的分级权限体系如何应对潜在的安全威胁和攻击?
  • 关于Docker【常见问题解决方案】
  • 【问题未解决-寻求帮助】VS Code 中使用 Conda 环境,运行 Python 后 PowerShell 终端输出内容立即消失
  • 随笔之TDengine基准测试示例
  • 【开源】一款开源、跨平台的.NET WPF 通用权限开发框架 (ABP) ,功能全面、界面美观
  • 基于 Flask 和 MySQL 的期货数据分析系统
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博评论IP地图可视化分析实现
  • Vue + Flask 管理系统开发方案
  • 【Flask 基础 ①】 | 路由、参数与模板渲染
  • [Agent开发平台] API网关 | 业务领域 | DTO格式 | 分页令牌
  • FPGA实现CameraLink视频解码转SRIO与DSP交互,FPGA+DSP多核异构图像处理架构,提供2套工程源码和技术支持
  • 分布式搜索和分析引擎Elasticsearch实战指南
  • 图像处理中级篇 [1]—— 彩色照相机的效果与预处理
  • RAG实战指南 Day 28:RAG系统缓存与性能优化
  • 大模型对比评测:Qwen2.5 VS Gemini 2.0谁更能打?
  • 线性代数常见的解题方法
  • Apache Ignite中分布式信号量(Distributed Semaphore)的说明和使用示例
  • GitPython03-项目setup编译
  • Directory Opus 使用优化
  • CouchDB 从入门到精通:构建高效的分布式文档数据库
  • 2025年ESWA SCI1区TOP,强化学习多目标灰狼算法MOGWO-RL+分布式混合流水车间调度,深度解析+性能实测
  • C++与AI工具(土木工程)高效编程实战
  • 从零开始,在Windows环境部署vllm
  • 如何使用 Conda 安装 Qiskit(详细教程)
  • 第七章:进入Redis的SET核心