zukucode
主にWEB関連の情報を技術メモとして発信しています。

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.jstransition-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>

関連記事