簡介:
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,
- }]
- };
- }
- }