Vue 可拖拽组件 Vue Smooth DnD 详解和应用演示
本文发布自 https://www.cnblogs.com/wenruo/p/15061907.html 转载请注明出处。
简介和 Demo 展示
最近需要有个拖拽列表的需求,发现一个简单好用的 Vue 可拖拽组件。安利一下~
Vue Smooth DnD 是一个快速、轻量级的拖放、可排序的 Vue.js 库,封装了 smooth-dnd 库。
Vue Smooth DnD 主要包含了两个组件,Container 和 Draggable,Container 包含可拖动的元素或组件,它的每一个子元素都应该被 Draggable 包裹。每一个要被设置为可拖动的元素都需要被 Draggable 包裹。
安装: npm i vue-smooth-dnd
一个简单的 Demo ,展示组件的基础用法,实现了可以拖拽的列表。
<template>
<div>
<div class="simple-page">
<Container @drop="onDrop">
<Draggable v-for="item in items" :key="item.id">
<div class="draggable-item">
{{item.data}}
</div>
</Draggable>
</Container>
</div>
</div>
</template>
<script>
import { Container, Draggable } from "vue-smooth-dnd";
const applyDrag = (arr, dragResult) => {
const { removedIndex, addedIndex, payload } = dragResult
console.log(removedIndex, addedIndex, payload)
if (removedIndex === null && addedIndex === null) return arr
const result = [...arr]
let itemToAdd = payload
if (removedIndex !== null) {
itemToAdd = result.splice(removedIndex, 1)[0]
}
if (addedIndex !== null) {
result.splice(addedIndex, 0, itemToAdd)
}
return result
}
const generateItems = (count, creator) => {
const result = []
for (let i = 0; i < count; i++) {
result.push(creator(i))
}
return result
}
export default {
name: "Simple",
components: { Container, Draggable },
data() {
return {
items: generateItems(50, i => ({ id: i, data: "Draggable " + i }))
};
},
methods: {
onDrop(dropResult) {
this.items = applyDrag(this.items, dropResult);
}
}
};
</script>
<style>
.draggable-item {
height: 50px;
line-height: 50px;
text-align: center;
display: block;
background-color: #fff;
outline: 0;
border: 1px solid rgba(0, 0, 0, .125);
margin-bottom: 2px;
margin-top: 2px;
cursor: default;
user-select: none;
}
</style>
效果
API: Container
属性
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| :orientation | string | vertical |
容器的方向,可以为 horizontal 或 vertical |
| :behaviour | string | move |
描述被拖动的元素被移动或复制到目标容器。 可以为 move 或 copy 或 drop-zone 或 contain 。move 可以在容器间互相移动,copy 是可以将元素复制到其他容器,但本容器内元素不可变,drop-zone 可以在容器间移动,但是容器内元素的顺序是固定的。contain 只能在容器内移动。 |
| :tag | string, NodeDescription | div |
容器的元素标签,默认是 div ,可以是字符串如 tag="table" 也可以是包含 value和 props 属性的对象 :tag="{value: 'table', props: {class: 'my-table'}}" |
| :group-name | string | undefined |
可拖动元素可以在具有相同组名的容器之间移动。如果未设置组名容器将不接受来自外部的元素。 这种行为可以被 shouldAcceptDrop 函数覆盖。 见下文。 |
| :lock-axis | string | undefined |
锁定拖动的移动轴。可用值 x, y 或 undefined。 |
| :drag-handle-selector | string | undefined |
用于指定可以开启拖拽的 CSS 选择器,如果不指定的话则元素内部任意位置都可抓取。 |
| :non-drag-area-selector | string | undefined |
禁止拖动的 CSS 选择器,优先于 dragHandleSelector. |
| :drag-begin-delay | number | 0(触控设备为 200) |
单位毫秒。表示点击元素持续多久后可以开始拖动。在此之前移动光标超过 5px 将取消拖动。 |
| :animation-duration | number | 250 |
单位毫秒。表示放置元素和重新排序的动画持续时间。 |
| :auto-scroll-enabled | boolean | true |
如果拖动项目接近边界,第一个可滚动父项将自动滚动。(这个属性没看懂= =) |
| :drag-class | string | undefined |
元素被拖动中的添加的类(不会影响拖拽结束后元素的显示)。 |
| :drop-class | string | undefined |
从拖拽元素被放置到被添加到页面过程中添加的类。 |
| :remove-on-drop-out | boolean | undefined |
如果设置为 true,在被拖拽元素没有被放置到任何相关容器时,使用元素索引作为 removedIndex 调用 onDrop() |
| :drop-placeholder | boolean,object | undefined |
占位符的选项。包含 className, animationDuration, showOnTop |
关于 drag-class,drop-class 和 drop-placeholder.className 的效果演示
<Container # 省略其它属性...
:animation-duration="1000" # 放置元素后动画延时
drag-class="card-ghost"
drop-class="card-ghost-drop"
:drop-placeholder="{
className: 'drop-preview', # 占位符的样式
animationDuration: '1000', # 占位符的动画延迟
showOnTop: true # 是否在其它元素的上面显示 设置为false会被其他的拖拽元素覆盖
}"
>
<!-- 一些可拖拽元素 -->
<Draggable>....</Draggable>
</Container>
类对应样式
.card-ghost {
transition: transform 0.18s ease;
transform: rotateZ(35deg);
background: red !important;
}
.card-ghost-drop {
transition: transform 1s cubic-bezier(0,1.43,.62,1.56);
transform: rotateZ(0deg);
background: green !important;
}
.drop-preview {
border: 1px dashed #abc;
margin: 5px;
background: yellow !important;
}
实际效果(我这优秀的配色啊)
生命周期
一次拖动的生命周期通过一系列回调和事件进行描述和控制,下面以包含 3 个容器的示例为例进行说明
(直接复制了文档没有翻译,API 详细解释可以看后面介绍。):
Mouse Calls Callback / Event Parameters Notes
down o Initial click
move o Initial drag
|
| get-child-payload() index Function should return payload
|
| 3 x should-accept-drop() srcOptions, payload Fired for all containers
|
| 3 x drag-start dragResult Fired for all containers
|
| drag-enter
v
move o Drag over containers
|
| n x drag-leave Fired as draggable leaves container
| n x drag-enter Fired as draggable enters container
v
up o Finish drag
should-animate-drop() srcOptions, payload Fires once for dropped container
3 x drag-end dragResult Fired for all containers
n x drop dropResult Fired only for droppable containers
请注意,应在每次 drag-start 之前和每次 drag-end 之前触发 should-accept-drop,但为了清晰起见,此处已省略。
其中 dragResult 参数的格式:
dragResult: {
payload, # 负载 可以理解为用来记录被拖动的对象
isSource, # 是否是被拖动的容器本身
willAcceptDrop, # 是否可以被放置
}
其中 dropResult 参数的格式:
dropResult: {
addedIndex, # 被放置的新添加元素的下标,没有则为 null
removedIndex, # 将被移除的元素下标,没有则为 null
payload, # 拖动的元素对象,可通过 getChildPayload 指定
droppedElement, # 放置的 DOM 元素
}
回调
回调在用户交互之前和期间提供了额外的逻辑和检查。
get-child-payload(index)自定义传给onDrop()的payload对象。should-accept-drop(sourceContainerOptions, payload)用来确定容器是否可被放置,会覆盖group-name属性。should-animate-drop(sourceContainerOptions, payload)返回false则阻止放置动画。get-ghost-parent()返回幽灵元素(拖动时显示的元素)应该添加到的元素,默认是父元素,某些情况定位会出现问题,则可以选择自定义,如返回document.body。
事件
@drag-start在拖动开始时由所有容器发出的事件。参数dragResult。@drag-end所有容器在拖动结束时调用的函数。 在@drop事件之前调用。参数dragResult。@drag-enter每当拖动的项目在拖动时进入其边界时,相关容器要发出的事件。@drag-leave每当拖动的项目在拖动时离开其边界时,相关容器要发出的事件。@drop-ready当容器中可能放置位置的索引发生变化时,被拖动的容器将调用的函数。基本上,每次容器中的可拖动对象滑动以打开拖动项目的空间时都会调用它。参数dropResult。@drop放置结束时所有相关容器会发出的事件(放置动画结束后)。源容器和任何可以接受放置的容器都被认为是相关的。参数dropResult。
API: Draggable
tag
同容器的 tag 指定可拖拽元素的 DOM 元素标签。
实战
实现一个简单的团队协作任务管理器。
<template>
<div class="card-scene">
<Container
orientation="horizontal"
@drop="onColumnDrop($event)"
drag-handle-selector=".column-drag-handle"
>
<Draggable v-for="column in taskColumnList" :key="column.name">
<div class="card-container">
<div class="card-column-header">
<span class="column-drag-handle">☰</span>
{{ column.name }}
</div>
<Container
group-name="col"
@drop="(e) => onCardDrop(column.id, e)"
:get-child-payload="getCardPayload(column.id)"
drag-class="card-ghost"
drop-class="card-ghost-drop"
:drop-placeholder="dropPlaceholderOptions"
class="draggable-container"
>
<Draggable v-for="task in column.list" :key="task.id">
<div class="task-card">
<div class="task-title">{{ task.name }}</div>
<div class="task-priority" :style="{ background: priorityMap[task.priority].color }">
{{ priorityMap[task.priority].label }}
</div>
</div>
</Draggable>
</Container>
</div>
</Draggable>
</Container>
</div>
</template>
<script>
import { Container, Draggable } from "vue-smooth-dnd";
const applyDrag = (arr, dragResult) => {
const { removedIndex, addedIndex, payload } = dragResult
console.log(removedIndex, addedIndex, payload)
if (removedIndex === null && addedIndex === null) return arr
const result = [...arr]
let itemToAdd = payload
if (removedIndex !== null) {
itemToAdd = result.splice(removedIndex, 1)[0]
}
if (addedIndex !== null) {
result.splice(addedIndex, 0, itemToAdd)
}
return result
}
const taskList = [
{
name: '首页',
priority: 'P1',
status: '待开发',
id: 1,
},
{
name: '流程图开发',
priority: 'P3',
status: '待评审',
id: 2,
},
{
name: '统计图展示',
priority: 'P0',
status: '开发中',
id: 3,
},
{
name: '文件管理',
priority: 'P1',
status: '开发中',
id: 4,
}
]
const statusList = ['待评审', '待开发', '开发中', '已完成']
const taskColumnList = statusList.map((status, index) => {
return {
name: status,
list: taskList.filter(item => item.status === status),
id: index
}
})
const priorityMap = {
'P0': {
label: '最高优',
color: '#ff5454',
},
'P1': {
label: '高优',
color: '#ff9a00',
},
'P2': {
label: '中等',
color: '#ffd139',
},
'P3': {
label: '较低',
color: '#1ac7b5',
},
}
export default {
name: 'Cards',
components: {Container, Draggable},
data () {
return {
taskColumnList,
priorityMap,
dropPlaceholderOptions: {
className: 'drop-preview',
animationDuration: '150',
showOnTop: true
}
}
},
methods: {
onColumnDrop (dropResult) {
this.taskColumnList = applyDrag(this.taskColumnList, dropResult)
},
onCardDrop (columnId, dropResult) {
let { removedIndex, addedIndex, payload } = dropResult
if (removedIndex !== null || addedIndex !== null) {
const column = taskColumnList.find(p => p.id === columnId)
if (addedIndex !== null && payload) { // 更新任务状态
dropResult.payload = {
...payload,
status: column.name,
}
}
column.list = applyDrag(column.list, dropResult)
}
},
getCardPayload (columnId) {
return index =>
this.taskColumnList.find(p => p.id === columnId).list[index]
},
}
}
</script>
<style>
* {
margin: 0;
padding: 0;
font-family: 'Microsoft YaHei','PingFang SC','Helvetica Neue',Helvetica,sans-serif;
line-height: 1.45;
color: rgba(0,0,0,.65);
}
.card-scene {
user-select: none;
display: flex;
height: 100%;
margin: 20px;
}
.card-container {
display: flex;
flex-direction: column;
width: 260px;
min-width: 260px;
border-radius: 12px;
background-color: #edeff2;
margin-right: 16px;
height: calc(100vh - 40px);
}
.card-column-header {
display: flex;
height: 50px;
margin: 0 16px;
align-items: center;
flex-shrink: 0;
font-weight: 500;
font-size: 16px;
}
.draggable-container {
flex-grow: 1;
overflow: auto;
}
.column-drag-handle {
cursor: move;
padding: 5px;
}
.task-card {
margin: 10px;
background-color: white;
padding: 15px 10px;
border-radius: 8px;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.12);
cursor: pointer;
display: flex;
justify-content: space-between;
}
.task-title {
color: #333333;
font-size: 14px;
}
.task-priority {
width: 60px;
line-height: 20px;
border-radius: 12px;
text-align: center;
color: #fff;
font-size: 12px;
}
.card-ghost {
transition: transform 0.18s ease;
transform: rotateZ(5deg)
}
.card-ghost-drop {
transition: transform 0.18s ease-in-out;
transform: rotateZ(0deg)
}
.drop-preview {
background-color: rgba(150, 150, 200, 0.1);
border: 1px dashed #abc;
margin: 5px;
}
</style>
效果
Vue 可拖拽组件 Vue Smooth DnD 详解和应用演示的更多相关文章
- 强大的拖拽组件:React DnD 的使用
强大的拖拽组件:React DnD 的使用 react.js 10.6k 次阅读 · 读完需要 25 分钟 17 文章首发我的个人blog : 原文链接 学习 React DnD 的最初原因是阅读 ...
- vue实现拖拽组件
可以拖拽,靠边停靠,效果图如下 代码如下: 注意:代码中使用的图片未上传 DragAndDrop组件: <template> <div class="drag" ...
- HTML5拖拽功能中 dataTransfer对象详解
有了HTML5,老板再也不用担心我们的上传了,再加上有拖拽上传是不是很酷.百度一下,有关HTML5拖拽上传的文章和实例不少,都缺不了一个至关重要的东东DataTransfer.但是详细介绍的不多,尤其 ...
- vue列表拖拽组件 vue-dragging
安装 $ npm install awe-dnd --save 应用 在main.js中,通过Vue.use导入插件 import VueDND from 'awe-dnd' Vue.use(VueD ...
- Vue.Draggable:基于 Sortable.js 的 Vue 拖拽组件使用中遇到的问题
Sortable.js 介绍 https://segmentfault.com/a/1190000008209715 项目中遇到的问题: A - 我需要在项目的拖拽组件中,使用背景 1 - 想到的第一 ...
- vue拖拽组件开发
vue拖拽组件开发 创建临时vue项目 先查看node和npm版本,怎么安装就不多多bb了 再安装vue-cli npm install vue-cli -g //全局安装 vue-cli 检测是否安 ...
- vue自由拖拽、缩放组件
github地址:https://github.com/kirillmurashov/vue-drag-resize 安装: npm i -s vue-drag-resize 使用: <temp ...
- Vue实现拖拽穿梭框功能四种方式
一.使用原生js实现拖拽 点击打开视频讲解更加详细 <html lang="en"> <head> <meta charset="UTF-8 ...
- 原生js拖拽、jQuery拖拽、vue自定义指令拖拽
原生js拖拽: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...
随机推荐
- Ryzen 4000'Vermeer' CPU和Radeon RX'Big Navi'图形卡
Ryzen 4000'Vermeer' CPU和Radeon RX'Big Navi'图形卡 来自中国媒体的多篇报道表明,AMD都准备在2020年第四季度初推出其下一代Ryzen 4000'Zen 3 ...
- NOIP模拟测试「简单的区间·简单的玄学·简单的填数·简单的序列」
简单的区间 $update$ 终于$AC$了 找到$(sum[r]+sum[l](sum表示以中间点为基准的sum)-mx)\%k==0$的点 注意这里$sum$表示是以$mid$为基准点,(即$su ...
- OO unit4 summary
Unit4 一.第四单元作业的架构设计 第四单元个人认为主要是考察对于层次结构的理解,即如何理解并处理好UML图的树状结构组织,在理好层次之间以及层次内部的相互关系之后,就只剩下代码实现的问题了.但是 ...
- Android集合中对象排序
如果将集合中的对象进行排序,最近使用了一个简单的方法解决了,随笔记下来. 主要思路: 首先,新建类实现Comparator<?>,这个类是做比较的关键类,一般做比较的类型 int 或 St ...
- Linux 安装及管理程序
Linux 安装及管理程序 目录 一.Linux应用程序基础 1.1.应用程序与系统命令的关系 1.2.典型应用程序的目录结构 1.2.常见的软件包封装类型 二.RPM包管理工具 2.1.RPM软件包 ...
- 使用 TypeScript,React,ANTLR 和 Monaco Editor 创建一个自定义 Web 编辑器(二)
译文来源 欢迎阅读如何使用 TypeScript, React, ANTLR4, Monaco Editor 创建一个自定义 Web 编辑器系列的第二章节, 在这之前建议您阅读使用 TypeScrip ...
- Redis的数据类型以及应用场景
1. Redis的作用 1.1 Redis可以做什么 1.缓存:缓存机制几乎在所有的大型网站都有使用,合理地使用缓存不仅可以加快数据的访问速度,而且能够有效地降低后端数据源的压力.Redis提供了键值 ...
- Docker安装运行Portainer
基本简介 Portainer是一个轻量级的docker环境管理UI,可以用来管理docker宿主机和docker swarm集群.他的轻量级,轻量到只要个不到100M的docker镜像容器就可以完整的 ...
- Gerrit+replication 同步Gitlab
配置环境:gerrit 192.168.1.100gitlab 192.168.1.1011.创建秘钥 [root@gerrit ~]# ssh-keygen -m PEM -t rsa 2.添加ho ...
- 7.4、Horizon部署
1.horizon介绍: horizon只需要连上keystone即可: 这里在控制节点controller进行操作: 提示:horizon和keystone运行时使用的都是apache软件,如果都装 ...