Vue.js で Drag & Drop を実験してみましたので、ログします。

完成イメージ

Drag & Drop イメージ 開始前
Drag & Drop イメージ 開始前

「デッキ組み換え」のイメージです。
デッキには今セット中のカードが表示されます。

Drag & Drop イメージ ドラッグ中
Drag & Drop イメージ ドラッグ中

カードを一つドラッグします。

Drag & Drop イメージ ドロップ
Drag & Drop イメージ ドロップ

移動後のデッキにドロップすると、カードが移動します。

用意したデータ

 1{
 2  decks: [
 3    {id: 1, name: "Deck 1", cards: [
 4      {id: 1, name: "C 1"},
 5      {id: 2, name: "C 2"},
 6      {id: 3, name: "C 3"},
 7      {id: 4, name: "C 4"},
 8      {id: 5, name: "C 5"}
 9    ]},
10    {id: 2, name: "Deck 2", cards: []},
11  ],
12}
13

デッキと、デッキ内のカードをまとめたデータです。
具体的には「 Laravel のリレーションシップを hasMany で定義し、それをまとめて SELECT 」したイメージです。

サンプルコード

環境は次です。

  • Vue.js 2.4.x
  • npm 5.6.0

HTML

 1<!-- デッキ枠 全体をドロップエリアとする -->
 2<div class="deck"
 3  v-for="deck in decks" :key="deck.id"
 4  @dragover="dragOver(deck)"
 5>
 6  <div class="heading">
 7    {{ deck.name }}
 8  </div>
 9  <div class="card-wrap">
10    <!-- カード draggableを定義 -->
11    <div class="card"
12      draggable="true"
13      v-for="card in relationMap[deck.id]" :key="card.id"
14      @dragstart="dragStart(card, $event)"
15      @dragend="dragEnd"
16    >{{ card.name }}</div>
17  </div>
18</div>

データの加工

デッキとカードのデータは次のように、デッキ ID をキーとした配列にコンバートします。

1let map = {}
2for (let index in this.decks) {
3  let deck = this.decks[index];
4  map[deck.id] = deck.cards
5}
6
7this.relationMap = map
8

JavaScript

 1export default {
 2  data() {
 3    return {
 4      relationMap   : {},
 5      draggingCardId: null,
 6      enterDeckId   : null
 7    }
 8  },
 9  methods: {
10    dragStart(card, e) {
11      // ドラッグ中のカードIDを保持し、ドラッグ要素を半透明にする
12      this.draggingCardId    = card.id
13      e.target.style.opacity = 0.5
14    },
15    dragEnd(e) {
16      // ドラッグ中の要素情報、関係データを書き換える
17      this.relationMap = this.moveDeck(
18        this.relationMap,
19        this.draggingCardId,
20        this.enterDeckId
21      )
22
23      // 一時編集を初期化し、ドラッグ中の要素を半透明から戻す
24      this.enterDeckId       = null
25      this.draggingCardId    = null
26      e.target.style.opacity = 1
27    },
28    dragOver(deck) {
29      // ドラッグ中要素がいま重なっているデッキのIDを保持
30      this.enterDeckId = deck.id
31    },
32    moveDeck(map, targetCardId, afterDeckId) {
33      // targetCardId を今入っているデッキから削除し、新しいデッキに追加する
34      // あまり美しくなくてごめんね
35      for (let deckId in map) {
36        for (let ci=0; ci<map[deckId].length; ci++) {
37          let card = map[deckId][ci]
38          if (card.id == targetCardId) {
39            if (deckId != afterDeckId) {
40              map[deckId].splice(ci, 1)
41              map[afterDeckId].push(card)
42            }
43            break
44          }
45        }
46      }
47      return map
48    }
49  }
50}
51

解説

カードはドラッグ操作できる要素のため、draggable 属性をいれています。

1draggable="true"

カードのドラッグ開始時、dragstart を発火し、カードのキーを覚えておきます。

1dragStart(card, e) {
2  // ドラッグ中のカードIDを保存し、ドラッグ要素を半透明にする
3  this.draggingCardId    = card.id
4  e.target.style.opacity = 0.5
5},
6

ドラッグ中のカードがデッキに重なった際に dragover を発火し、デッキのキーを覚えておきます。

1dragOver(deck) {
2  this.enterDeckId = deck.id
3},
4

カードをドロップすると dragend が発火、覚えていたカードとデッキのキーを使って relationMap を書き換えます。

すると、リアクティブされ、カードのいちが移動します。

おわりに

Drag & Drop は直感的に操作できある程度需要はありそうです。
なにより動かしてみて面白いですよね。

実装にはとっつきにくさがありますが、一度手を動かしてみると次は OK なはずです。

Vue.js入門 基礎から実践アプリケーション開発まで

川口 和也,喜多 啓介,野田 陽平,手島 拓也,片山 真也
出版社:技術評論社  発売日:2018-09-22

Amazonで詳細を見る

基礎から学ぶ Vue.js

mio
出版社:シーアンドアール研究所  発売日:2018-05-29

Amazonで詳細を見る

React、Angular、Vue.js、React Nativeを使って学ぶ はじめてのフロントエンド開発

原 一浩,taisa,小松 大輔,永井 孝,池内 孝啓,新井 正貴,橋本 安司,日野 洋一郎
出版社:技術評論社  発売日:2018-05-09

Amazonで詳細を見る

この記事の著者 Webrow (うぇぶろう)
Web アプリ開発、 Web 顧問 エンジニア、WordPress サポートいたします。