# Drag & Drop
{%hackmd BJrTq20hE %}
- Drag Source 指的是被點擊要拖曳的物件,意即被點擊要拖曳的物件,通常是一個 element
- Drop Target 指的是拖曳的物件被放置的區域,意即拖曳的物件被放置的區域,通常是一個 div container
## 拖放事件( Drag and Drop Events )
- 針對拖曳物件的事件 (Drag):
| 事件 | 說明 |
| --- | --- |
| dragstart | 當使用者**開始**拖曳時觸發(滑鼠開始移動時) |
| drag | 開始拖曳到結束拖曳前都會不斷觸發(約幾百毫秒觸發一次) |
| dragend | 當使用者**結束**拖曳時觸發(滑鼠按鍵放開時) |
- 針對被放置的元素事件 (Drop):
| 事件 | 說明 |
| :-: | :-: |
| dragenter | 當使用者拖曳期間**進入**元素時觸發 |
| dragover | 當使用者拖曳期間**經過**元素時觸發(約幾百毫秒觸發一次) |
| dragleave | 當使用者拖曳期間**離開**元素時觸發 |
| drop | 當使用者將拖曳的目標**放置**在元素時觸發 |
:::warning
放置資料的目標區,絕大多數的元素預設的事件都不准放置資料。
所以想要放置資料到元素上,就必須取消預設事件行為。
:::
- 數據傳輸物件( dataTransfer ):
所有的拖曳事件物件都有一個 dataTransfer 屬性,這個屬性是用來攜帶資料。
- dropEffect – 當前的拖放效果(例如移動、複製)
- effectAllowed – 指定拖放操作
- none - 不允許任何作業。
- copy - 只能複製。
- move - 只能移動。
- link - 只有連結。
- copyMove - 只能複製或移動。
- copyLink - 只能複製或連結。
- `setData(name, val)` – 允許我們向 dataTransfer 對象添加值
- `getData(name)` – 檢索我們存儲的值
- 基本拖曳範例 [codeSandbox](https://codepen.io/twoz/pen/jOGNzJq)
```htmlmixed=
<template>
<!-- list1 -->
<div
class="drop-zone bg-secondary"
style="min-height: 50px"
@drop="onDrop($event, 1)"
@dragenter.prevent
@dragover.prevent
>
<div
v-for="item in getList(1)"
:key="item.id"
class="border border-2 text-center p-3"
draggable="true"
@dragstart="startDrag($event, item)"
>
{{ item.title }}
</div>
</div>
<!-- list2 -->
<div
class="drop-zone bg-secondary mt-5"
style="min-height: 50px"
@drop="onDrop($event, 2)"
@dragenter.prevent
@dragover.prevent
>
<div
v-for="item in getList(2)"
:key="item.id"
class="border border-2 text-center p-3"
draggable="true"
@dragstart="startDrag($event, item)"
>
{{ item.title }}
</div>
</div>
</template>
```
```javascript=
<script setup>
import { ref } from "vue";
const items = ref([
{ id: 0, title: "item A", list: 1 },
{ id: 1, title: "item B", list: 1 },
{ id: 2, title: "item C", list: 2 },
]);
const getList = (list) => {
return items.value.filter((item) => item.list == list);
};
const startDrag = (event, item) => {
event.dataTransfer.setData("itemID", item.id); //紀錄正在拖曳物件的ID
};
const onDrop = (event, list) => {
const itemID = event.dataTransfer.getData("itemID"); //放開時取出物件ID(string)
const item = items.value.find((item) => {
console.log(typeof item.id, typeof itemID); //number, string
return item.id == itemID; //不能用三等號,因為不同類型
}); //在列表裡找出該物件
item.list = list; //找到物件後更改其群組
};
</script>
<style scoped></style>
```
[PJchender筆記](https://pjchender.blogspot.com/2017/08/html5-drag-and-drop-api.html)
[絲般順滑](https://blog.csdn.net/qq_45039540/article/details/112970439),[絲般順滑DEMO](https://codesandbox.io/s/kind-shirley-ukrs3?file=/src/App.vue)
# vuedraggable
[vuedraggable套件說明筆記](https://hackmd.io/@Vin27/Hk6pVPyED)、[官方example](https://sortablejs.github.io/Vue.Draggable/#/transition-example-2)
- `dragOptions`:在Sortable.js的文件中可以找到所有[options](https://github.com/SortableJS/Sortable#options)以及所有觸發事件。
```jsx=
// 拖拉設定
const dragOptions = computed(() => {
return {
animation: 200,
group: "description", // list命名
disabled: false,
ghostClass: "ghost", // 使用鬼影
};
});
```
- onSort:觸發排序設定
```jsx=
// Called by any change to the list (add / update / remove)
onSort: function (/**Event*/evt) {
// same properties as onEnd
},
```
## vue2
- 安裝套件:`npm i -S vuedraggable`
```jsx=
<template>
<div class="row">
<div class="col-6">
<h3>Transition</h3>
<draggable
class="list-group"
tag="ul"
v-model="list"
v-bind="dragOptions"
@start="drag = true"
@end="drag = false"
@sort="updateListsOrder"
>
<!-- ghost欄位 -->
<transition-group>
<li
class="list-group-item"
v-for="element in list"
:key="element.order"
>
{{ element.name }}
</li>
</transition-group>
</draggable>
</div>
{{ list }}
</div>
</template>
<script setup>
import { ref, computed } from "vue";
import draggable from "vuedraggable";
const message = [
"vue.draggable",
"draggable",
"component",
"for",
"vue.js 2.0",
"based",
"on",
"Sortablejs",
];
const list = ref(
message.map((name, index) => {
return { name, order: index + 1 };
})
);
const drag = ref(false);
// 拖拉設定
const dragOptions = computed(() => {
return {
animation: 200,
group: "description",
disabled: false,
ghostClass: "ghost",
};
});
// 更新排序號碼
async function updateListsOrder() {
list.value.map((list, index) => {
list.order = index + 1;
});
}
</script>
<style>
.ghost {
opacity: 0.5;
background: #c8ebfb;
}
.list-group-item {
cursor: move;
}
</style>
```