Vue.js で 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 なはずです。
React、Angular、Vue.js、React Nativeを使って学ぶ はじめてのフロントエンド開発
原 一浩,taisa,小松 大輔,永井 孝,池内 孝啓,新井 正貴,橋本 安司,日野 洋一郎
出版社:技術評論社
発売日:2018-05-09