Draggable学习总结
简介:
Draggable为基于Sortable.js的vue组件,用以实现拖拽功能。
特性
- 支持触摸设备
- 支持拖拽和选择文本
- 支持智能滚动
- 支持不同列表之间的拖拽
- 不以jQuery为基础
- 和视图模型同步刷新
- 和vue2的国度动画兼容
- 支持撤销操作
- 当需要完全控制时,可以抛出所有变化
- 可以和现有的UI组件兼容
操作步骤:
1. 安装
npm install vuedraggable2.引入
import draggable from 'vuedraggable'3.基础用法
定义一个json串
list
,实现它的拖拽排序。<template> <div> <!-- 调用组件 --> <draggable element="ul" v-model="list"> <li v-for="item in list">{{item.name}}</li> </draggable> <!-- 输出list数据 --> {{list}} </div> </template> <script> // 引入拖拽组件 import draggable from 'vuedraggable' export default { name: 'demo', components: { //调用组件 draggable, }, data () { return { list:[ {id: 1,name: 'a'}, {id: 2,name: 'b'}, {id: 3,name: 'c'}, {id: 4,name: 'd'}, {id: 5,name: 'e'}, { id: 6,name: 'f'}, ] } }, } </script>
数据原扩展
value和list都可以给一个draggable注入数据源(基本用法中使用的是list的方法)
/*value注入数据源*/ <draggable v-model="list" ></draggable> export default { data() { return { list:[{ name:'aaa',id:1, },{ name:'bbb',id:2, }] }; } } /*list注入数据源头*/ <draggable :list="list" ></draggable> export default { data() { return { list:[{ name:'aaa',id:1, },{ name:'bbb',id:2, }] }; } }区别:
1. 代码 :就是 v-model (value注入)和 :list = "list" (list注入)
2. 效果 :value注入的,如果发生了拖拽,value的数据并不会跟着变化,
list注入的,数据则会发生变化
也就是说,value注入的,后续有无变化都和数据体没关系,它用于只需要展示拖拽效果的地方;
list注入的,数据体和当前页面上的属性保持一致,页面上的顺序变了,内部数组对应的结构体数组的顺序也会重新排列,和显示的一致
注意:他们不能同时出现,只能二选一
常用配置:
ghost-class
<draggable ghost-class="ghost" > </draggable> <style scoped> .ghost { opacity: 0.5; background: #c8ebfb; } </style>handle
正常情况下拖拽元素的整体都是可拖拽的,加了handle之后,只能指定的地方可以拖拽了,其他地方不能进行拖拽
<draggable element="ul" v-model="list" handle=".handle"> <li v-for="(item,index) in list" :key="index"> <i class="el-icon-share handle"></i> {{item.name}} </li> </draggable>tag和componentData
tag
对于一些特定的原生组合标签,例如ul,li,table,tr,td之类的,可以指定一个tag,让draggable替换成指定的标签
<draggable v-model="list" tag="tbody"> <tr v-for="item in list" :key="item.name"> <td scope="row">{{ item.id }}</td> <td>{{ item.name }}</td> <td>{{ item.sport }}</td> </tr> </draggable>上面的draggable就会被替换成table
componentData
componentData和tag类似,但是是对于组合组件的,例如在elementui的折叠面板,el-collapse和el-collapse-item就是这种组合关系:
<el-collapse v-model="activeNames" @change="handleChange"> <el-collapse-item title="一致性 Consistency" name="1"> <div>与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;</div> <div>在界面中一致:所有的元素和结构需保持一致,</div> </el-collapse-item> <el-collapse-item title="反馈 Feedback" name="2"> <div>控制反馈:通过界面样式和交互动效让用户可以清晰的感知自己的操作;</div> <div>页面反馈:操作后,通过页面元素的变化清晰地展现当前状态。</div> </el-collapse-item> </el-collapse>对于上面那种情况,如果要转成vuedraggable的话,首先要设置他跟,但是el-sollapse上的数据怎么办呢?就要通过component-data配置了:
<draggable tag="el-collapse" :list="list" :component-data="collapseComponentData" > <el-collapse-item v-for="item in list" :key="item.id" :title="item.title" :name="item.id" > <div v-for="(lign, idx) in item.text" :key="idx">{{ lign }}</div> </el-collapse-item> </draggable> <script> export default { name: "third-party", display: "Third party", order: 10, components: { draggable }, data() { return { list: [ { title: "Consistency", id: 1, text: [ "Consistent with real life: in line with the process and logic of real life, ", "Consistent within interface: all elements should be consistent, " ] }, { title: "Feedback", id: 2, text: [ "Operation feedback: enable the users to clearly perceive their operations by style updates and interactive effects;", "Visual feedback: reflect current state by updating or rearranging elements of the page." ] } ], activeNames: [1], collapseComponentData: { on: { change: this.inputChanged }, props: { value: this.activeNames } } }; }, methods: { inputChanged(val) { this.activeNames = val; } } }; </script>上面代码中的collapseComponentDate就是将原来el-collapse上传递数据的部分抽离出来了,包括事件(on),属性(prop,attr)的设置。
group和clone
group一般的用法就是用来去扽拖拽组的,group名称相同的拖拽组可以互相拖放
<draggable class="list-group" :list="list1" group="people" > <div class="list-group-item" v-for="(element, index) in list1" :key="element.name" > {{ element.name }} {{ index }} </div> </draggable> <draggable class="list-group" :list="list2" group="people" > <div class="list-group-item" v-for="(element, index) in list2" :key="element.name" > {{ element.name }} {{ index }} </div> </draggable>互相拖放的一搬效果如下:
group属性更详细配置如下
例如:group = "{ name:'abc', pull:'clone', put:false}"
参数:
put: 控制别的地方的内容是否可以拖拽到自己这边来。如果设置为false,那么就表示别的地方的内容无法拖拽到自己这边来。
pull: 控制从当前拽走,放在另外一个地方的行为。默认情况下(设置为true)是你拽到另外一个地方去,当前列表中就会少一个,对方别表多一个。如果设置为clone,那么当前列表不会减少,同时对方列表多了一个。
当然你甚至可以配置一个:clone="func",用来控制对方别表的内容,下面是一个复杂一点的例子:
<draggable class="list-group" :list="list1" :group="{name:'people',pull:pullFunction,put:false}" :clone='clone'> <div class="list-group-item" v-for="(element, index) in list1" :key="element.name" > {{ element.name }} {{ index }} </div> </draggable> <draggable class="list-group" :list="list2" group="people" > <div class="list-group-item" v-for="(element, index) in list2" :key="element.name" > {{ element.name }} {{ index }} </div> </draggable> export default { methods: { clone: function(el) { return { name: el.name + " cloned" }; }, pullFunction() { return Math.random()*10%2 ? "clone" : true; }, } };上面的代码中,pull设置为true还是clone是随机的(pullFunction)。如果设置为true,那么就是当前少一个,对方多一个;如果设置为clone,同时:clone = "func",那么就会调用你自定义的clone方法,当前不少,对方多一个。
animation 和transition-group
animation: 交换过程的动画
上图我们可以看到,拖拽元素每经过一个内容想,它就会发生动画移动的效果,这个主要是通过animation的设置:
<draggable class="list-group" :list="list1" :animation='200'> <transition-group> <div class="list-group-item" v-for="(element, index) in list1" :key="element.name" > {{ element.name }} {{ index }} </div> </transition-group> </draggable>transition-group:只有交换的双方才有的动画
这种和上面的第一种不一样,这种交换过程中没有动画,但是最后真正交换的时候才有动画,这种需要加个class就好了:
<draggable class="list-group" :list="list1" > <transition-group name='flip-list'> <div class="list-group-item" v-for="(element, index) in list1" :key="element.name" > {{ element.name }} {{ index }} </div> </transition-group> </draggable> <style> .flip-list-move { transition: transform 0.5s; } </style>给transition-group添加一个name 属性ABC,然后增加一个ABC-move的样式类就好了。
属性
value
Array,非必须,默认为null
- 用于实现拖拽的list,通常和内部v-for循环的数组为同一数组。
- 最好使用vuex来实现传入。
- 不是直接使用,而是通过
v-model
引入。<draggable v-model="myArray">list
Array,非必须,默认为null
- 就是
value
的替代品。- 和
v-model
不能共用- 从表现上没有看出不同
element
String,默认div
- 就是
<draggable>
标签在渲染后展现出来的标签类型- 也是包含拖动列表和插槽的外部标签
- 可以用来兼容UI组件
options
Object
- 配置项
- group: string or array 分组用的,同一组的不同list可以相互拖动
- sort: boolean 定义是否可以拖拽
- delay:number 定义鼠标选中列表单元可以开始拖动的延迟时间
- touchStartThreshold:number (不清楚)
- disabled: boolean 定义是否此sortable对象是否可用,为true时sortable对象不能拖放排序等功能
- store:
- animation: umber 单位:ms 动画时间
- handle: selector 格式为简单css选择器的字符串,使列表单元中符合选择器的元素成为拖动的手柄,只有按住拖动手柄才能使列表单元进行拖动
- filter: selector 格式为简单css选择器的字符串,定义哪些列表单元不能进行拖放,可设置为多个选择器,中间用“,”分隔
- preventOnFilter: 当拖动
filter
时是否触发event.preventDefault()
默认触发- draggable: selector 格式为简单css选择器的字符串,定义哪些列表单元可以进行拖放
- ghostClass: selector 格式为简单css选择器的字符串,当拖动列表单元时会生成一个副本作为影子单元来模拟被拖动单元排序的情况,此配置项就是来给这个影子单元添加一个class,我们可以通过这种方式来给影子元素进行编辑样式
- chosenClass: selector 格式为简单css选择器的字符串,目标被选中时添加
- dragClass:selector 格式为简单css选择器的字符串,目标拖动过程中添加
- forceFallback: boolean 如果设置为true时,将不使用原生的html5的拖放,可以修改一些拖放中元素的样式等
- fallbackClass: string 当forceFallback设置为true时,拖放过程中鼠标附着单元的样式
- dataIdAttr:
data-id
- scroll:boolean当排序的容器是个可滚动的区域,拖放可以引起区域滚动
- scrollFn:function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { … } 用于自定义滚动条的适配
- scrollSensitivity: number 就是鼠标靠近边缘多远开始滚动默认30
- scrollSpeed: number 滚动速度
函数配置
- setData: 设置值时的回调函数
- onChoose: 选择单元时的回调函数
- onStart: 开始拖动时的回调函数
- onEnd: 拖动结束时的回调函数
- onAdd: 添加单元时的回调函数
- onUpdate: 排序发生变化时的回调函数
- onRemove: 单元被移动到另一个列表时的回调函数
- onFilter: 尝试选择一个被filter过滤的单元的回调函数
- onMove: 移动单元时的回调函数
- onClone: clone时的回调函数
- 以上函数对象的属性:
- to: 移动到的列表的容器
- from:来源列表容器
- item: 被移动的单元
- clone: 副本的单元
- oldIndex:移动前的序号
- newIndex:移动后的序号
clone
function,默认值: 无处理
- 这一项要配合着
options
的group
项的pull
项处理,当pull:'clone
时的拖拽的回调函数’- 就是克隆的意思。
- 可以理解为正常的拖拽变成了复制。
- 当为
true
时克隆move
function,默认值:null
- 就是拖拽项时调用的函数
- 用来确定拖拽是否生效
- 返回null时可以生效
- 可以通过函数判断
- 有一个参数:
evt
evt
为object- draggedContext: 被拖拽元素的上下文
- index:拖拽元素的指针
- element: 拖拽数据本身
- futureIndex: 拖动后的index
- relatedContext: 拖入区域的上下文
- index: 目标元素的index
- element:目标数据本身
- list: 拖入的列表
- component:目标组件
<draggable element="ul" v-model="list" :move='allow'> ... methods: { allow(evt) { console.log(evt.draggedContext.index) console.log(evt.draggedContext.element) console.log(evt.draggedContext.futureIndex) console.log(evt.relatedContext.index) console.log(evt.relatedContext.element) console.log(evt.relatedContext.list) console.log(evt.relatedContext.component) return (evt.draggedContext.element.name!== 'b') } }componentData
Object,默认值:null
- 用来结合UI组件的,可以理解为代理了UI组件的定制信息
- 包含两项:
props
和on
props
用来代理UI组件需要绑定的属性(:)on
用来代理UI组件需要绑定的事件(@) <draggable element="el-collapse" :list="list" :component-data="getComponentData()"> <el-collapse-item v-for="e in list" :title="e.title" :name="e.name" :key="e.name"> <div>{{e.description}}</div> </el-collapse-item> </draggable>methods: { handleChange() { console.log('changed'); }, inputChanged(value) { this.activeNames = value; }, getComponentData() { return { on: { change: this.handleChange, input: this.inputChanged }, props: { value: this.activeNames } }; } }
事件
有以下几种start, add, remove, update, end, choose, sort, filter, clone
参数带有如下属性:
- add: 包含被添加到列表的元素
- newIndex: 添加后的新索引
- element: 被添加的元素
- removed: 从列表中移除的元素
- oldIndex: 移除前的索引
- element: 被移除的元素
- moved:内部移动的
- newIndex: 改变后的索引
- oldIndex: 改变前的索引
- element: 被移动的元素
插槽
提供一个footer插槽,在排序列表之下。
永远位于最下方。<draggable v-model="myArray" :options="{draggable:'.item'}"> <div v-for="element in myArray" :key="element.id" class="item"> {{element.name}} </div> <button slot="footer" @click="addPeople">Add</button> </draggable>
- /*value注入数据源*/
- <draggable v-model="list" ></draggable>
- export default {
- data() {
- return {
- list:[{
- name:'aaa',id:1,
- },{
- name:'bbb',id:2,
- }]
- };
- }
- }
- /*list注入数据源头*/
- <draggable :list="list" ></draggable>
- export default {
- data() {
- return {
- list:[{
- name:'aaa',id:1,
- },{
- name:'bbb',id:2,
- }]
- };
- }
- }