1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

備忘録:ドラックアンドドロップで並び替えをする処理を、Vue3で実装する

Posted at

はじめに

Vue3のコンポーネントを利用して、要素をドラックアンドドロップで並び替えるサンプルを作成しました。

コード

<template>
    <div>

        <h5>ドラックアンドドロップで並び替え</h5>
        <ul class="p-0 d-flex flex-column gap-1" style="list-style:none;">
            <li
            v-for="(item, index) in items"
            :key="item.id"
            class="item  card card-body bg-white"
            :class="{ dragging: index === draggedIndex || index === touchDragIndex }"
            draggable="true"
            @dragstart="onDragStart($event, index)"
            @dragover="onDragOver($event, index)"
            @drop="onDrop($event, index)"
            @touchstart="onTouchStart($event, index)"
            @touchmove="onTouchMove($event)"
            @touchend="onTouchEnd($event, index)"
            >{{ item.name }}</li>
        </ul>


    </div>
</template>
<script setup>
    import { ref, onMounted } from 'vue';

    const items = ref([
        { id: 1, name: 'Item 1' },
        { id: 2, name: 'Item 2' },
        { id: 3, name: 'Item 3' },
    ]);

    const draggedIndex = ref(null);
    const touchDragIndex = ref(null);
    const touchStartY = ref(0);
    const touchCurrentY = ref(0);


    onMounted( ()=>{} );


    /** ドラッグ操作が開始されたときに呼び出されます。ドラッグされるアイテムのインデックスを保存し、ドラッグ効果を設定します。 */
    const onDragStart = (event, index) => {
        draggedIndex.value = index;
        event.dataTransfer.effectAllowed = 'move';
    };


    /** ドラッグ中にドラッグ対象のアイテムが他のアイテム上にあるときに呼び出されます。ドロップを許可するためにデフォルトの動作を無効にします。 */
    const onDragOver = (event, index) => {
        event.preventDefault();
        event.dataTransfer.dropEffect = 'move';
    };


    /* ドロップ操作が完了したときに呼び出されます。ドラッグされたアイテムを新しい位置に移動します。 */
    const onDrop = (event, index) => {
        event.preventDefault();
        if (draggedIndex.value !== null && draggedIndex.value !== index) {
          const draggedItem = items.value[draggedIndex.value];
          items.value.splice(draggedIndex.value, 1);
          items.value.splice(index, 0, draggedItem);
          draggedIndex.value = null;
        }
    };


    /** タッチが開始されたときに呼び出されます。タッチされたアイテムのインデックスと開始位置を保存します。 */
    const onTouchStart = (event, index) => {
        touchDragIndex.value = index;
        touchStartY.value = event.touches[0].clientY;
    };


    /** タッチ移動中に呼び出されます。アイテムの位置をタッチ移動に合わせて更新します。 */
    const onTouchMove = (event) => {
        touchCurrentY.value = event.touches[0].clientY;
        const moveDistance = touchCurrentY.value - touchStartY.value;
        event.currentTarget.style.transform = `translateY(${moveDistance}px)`;
    };

    /** タッチが終了したときに呼び出されます。ドラッグされたアイテムを新しい位置に移動します。 */
    const onTouchEnd = (event, index) => {
        const moveDistance = touchCurrentY.value - touchStartY.value;
        event.currentTarget.style.transform = '';
        const targetIndex = touchDragIndex.value + Math.round(moveDistance / event.currentTarget.clientHeight);
        if (targetIndex !== touchDragIndex.value && targetIndex >= 0 && targetIndex < items.value.length) {
          const draggedItem = items.value[touchDragIndex.value];
          items.value.splice(touchDragIndex.value, 1);
          items.value.splice(targetIndex, 0, draggedItem);
        }
        touchDragIndex.value = null;
        touchStartY.value = 0;
        touchCurrentY.value = 0;
    };


    /** ドラッグ操作が終了したときに呼び出されます。draggedIndex を null にリセットして、ドラッグ中のスタイルを解除します。 */
    const onDragEnd = () => {
        draggedIndex.value = null;
    };

</script>

<style scoped>
    .item {
        cursor: grab;
        touch-action: none; /* タッチイベントのデフォルト動作を無効化 */
    }
    .dragging {
        z-index: 10;
        position: relative; /* z-index を有効にするために position を設定 */
    }
</style>

解説:PC画面上の操作

onDragStart メソッド

const onDragStart = (event, index) => {
  draggedIndex.value = index;
  event.dataTransfer.effectAllowed = 'move';
};
目的

ドラッグ操作が開始されたときに呼び出されます。ドラッグされるアイテムのインデックスを保存し、ドラッグ効果を設定します。

引数

event: ドラッグイベントオブジェクト。
index: ドラッグされたアイテムのインデックス。

処理内容

draggedIndex.value にドラッグされたアイテムのインデックスを保存します。これは、後でアイテムを移動する際に使用します。
event.dataTransfer.effectAllowed に 'move' を設定して、ドラッグ操作が移動(move)であることを示します。

onDragOver メソッド

const onDragOver = (event, index) => {
  event.preventDefault();
  event.dataTransfer.dropEffect = 'move';
};
目的

ドラッグ中にドラッグ対象のアイテムが他のアイテム上にあるときに呼び出されます。ドロップを許可するためにデフォルトの動作を無効にします。

引数

event: ドラッグイベントオブジェクト。
index: ドラッグオーバーされたアイテムのインデックス。

処理内容

event.preventDefault() を呼び出してデフォルトの動作(例えば、ブラウザがデフォルトで提供するドラッグアンドドロップ動作)を無効にします。これにより、ドロップが許可されます。
event.dataTransfer.dropEffect に 'move' を設定して、ドロップ効果を移動に設定します。

onDrop メソッド

const onDrop = (event, index) => {
  event.preventDefault();
  if (draggedIndex.value !== null && draggedIndex.value !== index) {
    const draggedItem = items.value[draggedIndex.value];
    items.value.splice(draggedIndex.value, 1);
    items.value.splice(index, 0, draggedItem);
    draggedIndex.value = null;
  }
};
目的

ドロップ操作が完了したときに呼び出されます。ドラッグされたアイテムを新しい位置に移動します。

引数

event: ドロップイベントオブジェクト。
index: ドロップされたアイテムのインデックス。

処理内容

event.preventDefault() を呼び出してデフォルトの動作を無効にします。これにより、ドロップイベントが正常に処理されることを保証します。
ドラッグされたアイテムのインデックス (draggedIndex.value) が存在し、かつドロップされた位置のインデックスと異なる場合に処理を行います。
draggedItem にドラッグされたアイテムを取得します。
items.value.splice(draggedIndex.value, 1) でドラッグされたアイテムを元の位置から削除します。
items.value.splice(index, 0, draggedItem) でドロップされた位置にアイテムを挿入します。
draggedIndex.value を null に設定して、ドラッグ操作が完了したことを示します。

解説:モバイル画面上の操作

onTouchStart メソッド

const onTouchStart = (event, index) => {
  touchDragIndex.value = index;
  touchStartY.value = event.touches[0].clientY;
};
目的

タッチが開始されたときに呼び出されます。タッチされたアイテムのインデックスと開始位置を保存します。

引数

event: タッチイベントオブジェクト。
index: タッチされたアイテムのインデックス。

onTouchMove メソッド

const onTouchMove = (event) => {
  touchCurrentY.value = event.touches[0].clientY;
  const moveDistance = touchCurrentY.value - touchStartY.value;
  event.currentTarget.style.transform = `translateY(${moveDistance}px)`;
};
目的

タッチ移動中に呼び出されます。アイテムの位置をタッチ移動に合わせて更新します。

引数

event: タッチイベントオブジェクト。

onTouchEnd メソッド

const onTouchEnd = (event, index) => {
  const moveDistance = touchCurrentY.value - touchStartY.value;
  event.currentTarget.style.transform = '';
  const targetIndex = touchDragIndex.value + Math.round(moveDistance / event.currentTarget.clientHeight);
  if (targetIndex !== touchDragIndex.value && targetIndex >= 0 && targetIndex < items.value.length) {
    const draggedItem = items.value[touchDragIndex.value];
    items.value.splice(touchDragIndex.value, 1);
    items.value.splice(targetIndex, 0, draggedItem);
  }
  touchDragIndex.value = null;
  touchStartY.value = 0;
  touchCurrentY.value = 0;
};
目的

タッチが終了したときに呼び出されます。ドラッグされたアイテムを新しい位置に移動します。

引数

event: タッチイベントオブジェクト。
index: ドロップされたアイテムのインデックス。

onDragEnd メソッド

const onDragEnd = () => {
  draggedIndex.value = null;
};
目的

ドラッグ操作が終了したときに呼び出されます。draggedIndex を null にリセットして、ドラッグ中のスタイルを解除します。

引数

なし。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?