Vue.js ドラッグ&ドロップで表示順を入れ替える
Vue.js
で、ドラッグ&ドロップで表示順を入れ替える方法を紹介します。
データの準備
まずは以下のように、v-for
でリスト形式で一覧表示します。
以下ではフルーツ(fruits
)の一覧を表示しています。
data
をそのまま表示するのではなく、data
の表示順(order
)でソートした内容を表示するようにします。
<template>
<table>
<tr
v-for="fruit in fruitsDisp"
:key="fruit.id"
>
<td>{{ fruit.name }}</td>
<td>{{ fruit.price }}</td>
</tr>
</table>
</template>
<script>
export default {
data() {
return {
fruits: [
{ id: 1, name: 'りんご', price: 200, order: 1 },
{ id: 2, name: 'みかん', price: 50, order: 2 },
{ id: 3, name: 'ばなな', price: 100, order: 3 },
],
};
},
computed: {
fruitsDisp() {
return this.fruits.sort((a, b) => a.order - b.order);
},
},
};
</script>
ドラッグ&ドロップの実装
各行の要素をドラッグできるようにdraggable
の属性を設定します。また、ドラッグ&ドロップの各イベントを定義します。
<template>
<table>
<tr
v-for="fruit in fruitsDisp"
:key="fruit.id"
draggable="true"
@dragstart="dragstart(fruit, $event)"
@dragenter="dragenter(fruit)"
@dragover.stop.prevent="dragover"
@dragend.stop.prevent="dragend"
>
<td>{{ fruit.name }}</td>
<td>{{ fruit.price }}</td>
</tr>
</table>
</template>
次にドラッグ&ドロップの各イベントの中身を実装します。
<script>
export default {
data() {
return {
fruits: [
{ id: 1, name: 'りんご', price: 200, order: 1 },
{ id: 2, name: 'みかん', price: 50, order: 2 },
{ id: 3, name: 'ばなな', price: 100, order: 3 },
],
draggingItem: null, // ドラッグ中の要素を保持するための変数
};
},
computed: {
fruitsDisp() {
return this.fruits.sort((a, b) => a.order - b.order);
},
},
methods: {
dragstart(item, e) {
this.draggingItem = item; // ドラッグ中の要素を保持
e.dataTransfer.effectAllowed = 'move'; // 移動モードに設定
e.target.style.opacity = 0.5; // ドラッグ中要素のスタイルを変更
},
dragenter(item) {
// ドラッグ中の要素とドラッグ先の要素の表示順を入れ替える
[item.order, this.draggingItem.order] = [this.draggingItem.order, item.order];
},
dragover(e) {
e.dataTransfer.dropEffect = 'move'; // 移動モードに設定
},
dragend(e) {
e.target.style.opacity = 1; // ドラッグ中要素のスタイルを変更(元に戻す)
this.draggingItem = null; // ドラッグ中の要素をクリア
},
L }
};
</script>
アニメーションの設定
要素を入れ替えるときにアニメーションで要素が移動するように表現すると、要素を入れ替えているのが視覚的にわかりやすくなります。
Vue.js
のtransition-group
を設定すれば簡単に実装ができます。
<template>
<table>
<transition-group name="fruits-list" tag="tbody">
<tr
v-for="fruit in fruitsDisp"
:key="fruit.id"
draggable="true"
@dragstart="dragstart(fruit, $event)"
@dragenter="dragenter(fruit)"
@dragover.stop.prevent="dragover"
@dragend.stop.prevent="dragend"
>
<td>{{ fruit.name }}</td>
<td>{{ fruit.price }}</td>
</tr>
</transition-group>
</table>
</template>
<style scoped>
.fruits-list-move {
transition: transform 0.3s;
}
</style>