# 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> ```