沃草這哪招 已發佈 2019-11-26

使用拖曳效果,進化寶可夢吧!

最近時不時的就可以看到 Pokemon GO 的廣告在電視、網路媒體上出現,這波行銷宣傳打的可真兇哪!

回想起小時候那個寶可夢還叫做神奇寶貝的年代(遠目...),為了每週六晚上的寶可夢卡通,客廳必定會上演的遙控器搶奪戰;不然就是在學校上課偷打寶可夢 Game Boy 的美好歲月,那些都是我一去不復返的童年時光啊~(喂喂喂,扯遠了!)

images


前言

咳咳,回歸正題,為了紀念一下那逝去的美好,於是就用寶可夢最迷人的特點「進化」為題,寫了一個互動操作效果,用滑鼠拖動進化石到伊布身上來進化牠(如圖)

這種可以用拖曳的方式來移動進化石的操作,叫做「拖曳效果」。而以下就簡單的介紹「拖曳效果」是怎麼做出來的吧!

images
也可以點此「進化吧,寶可夢!」自己操作看看唷

好啦,我知道要進化太陽精靈和月精靈不是用進化石,但這不是重點請忽略它╮(′~‵〞)╭

Ps:除了拖曳外,其他大部分的效果都是偽元素的應用,想更了解偽元素可以點此全都是假的,一起來認識偽元素!


基本認識

在以前的時候,據說要製作出這種拖曳效果是非常不容易的事,得要有一大堆的語法配合才能實現這種效果。但在現在最新的 HTML5 中,已經有直接的 Drag 與 Drop 機制,讓我們得以用更簡潔的方式來實現出拖曳的效果,真的是瞬間便利了不少啊!

在 HTML5 中提供了多種可用來觸發拖曳的監聽事件,這些事件所監聽的對象大致上可分為 Drag Source 和 Drop Target 兩種。

  • Drag Source: 指的是「拖曳對象」,意即被點擊要拖曳的物件
  • Drop Target: 指的是「放置目標」,意即拖曳的物件被放置的區域

Drag Source 和 Drop Target 兩種監聽對象的事件有不少,以下列舉這次範例使用到的幾個,也是比較常運用的幾個監聽事件:

有關「拖曳對象」的監聽事件

  • dragstart :當滑鼠按住拖曳對象不放,且拖曳時的瞬間觸發
  • drag:在拖動拖曳對象的這段期間持續觸發

有關「放置目標」的監聽事件

  • dragenter:當拖曳對象首次進入放置目標的範圍時觸發
  • dragover :當拖曳對象位在放置目標的範圍內時,會持續觸發
  • drop :當拖曳對象在放置目標的範圍內被釋放時觸發(也就是放開滑鼠的時候)

好了,關於 Drag 與 Drop 機制的基礎認識就先到這邊,接下來,我們就一步一步地來說明該怎麼運用 Drag 與 Drop 囉!


拖曳對象的設置

一般來說,除了 <a><img> 這兩個元素之外,其他的 HTML 的元素都是預設為不可被拖曳的。但在 HTML5 中只要添加上 draggable="true" 這個屬性之後,它就可以「被拖曳」囉。

因此製作拖曳效果的第一個步驟,便是先對要被拖曳的元素, 加上 draggable="true" 這個屬性,如下:

<div id="element" draggable="true">拖曳對象</div>
<div id="container">放置目標</div>

好了,現在 element 這個元素已經被設定為「可拖曳」的狀態了,那麼接下來的第二個步驟,便是要運用 JavaScript 對 element 這個元素設置監聽事件,如此一來,當 user 在使用滑鼠拖曳時,才能觸發相應的拖曳效果。

對要被拖曳的元素 element 設置 dragstart 這個監聽事件:

let el = document.querySelector('#element');
el.addEventListener('dragstart', dragStart);

當拖曳對象剛被拖動時,觸發把「拖曳對象」的內容放進「放置目標」內:

function dragStart(e) {
    e.dataTransfer.setData('text/plain', e.target.id)
};

OK ,有關於「拖曳對象」的設定先到這邊,接下來要進行的是「放置目標」部分的設置。


放置目標的設置

在正常的狀況下,元素是不能被放置拖曳物的,因此我們得要用 JavaScript 針對要放置拖曳物的元素進行一些設定,好讓這個元素變成可以放置拖曳物的「容器」。

對要放置元素的 container 容器,設置 dragenterdragoverdrop 三個監聽事件:

let container = document.querySelector('#container');
container.addEventListener('drop', dropped);
container.addEventListener('dragenter', cancelDefault);
container.addEventListener('dragover', cancelDefault);

當拖曳對象被滑鼠拖到放置目標上釋放時,就把 id 從資料中取出來,然後利用 id 把 DOM 抓過來:

function dropped(e) {
    cancelDefault(e)
    let id = e.dataTransfer.getData('text/plain');
    e.target.appendChild(document.querySelector(`#${id}`));
};

元素預設行為是不能被放置拖曳物的,因此在拖曳對象出現在放置目標上時,取消預設行為,讓放置目標可以被放置:

 function cancelDefault(e) {
    e.preventDefault();
    e.stopPropagation();
    return false
};

設定多個容器:

在放置目標的「容器」可以設置很多個,我們甚至可以改用 class 選擇器來做 JavaScript 的設定:

<div class="container"></div>
<div class="container"></div>
<div class="container"></div>
let container = document.querySelectorAll('.container');

// 用 forEach 來做多個 class 的監聽事件綁定
container.forEach(container => {
  container.addEventListener('drop', dropped);
  container.addEventListener('dragenter', cancelDefault);
  container.addEventListener('dragover', cancelDefault);
})

另外一提的是,同一個選擇器是無法設置多個「拖曳物件」的喔,這點要特別要注意!
只要運用上面的語法,再加上一點 CSS 的設定後,就可以做出如下圖的效果囉!
images

觀看完整程式碼可點擊此處


補充語法

在上面範例中有運用到了一些不是很常見的語法,所以在這邊做一個小統整,不過因為這每一個項目都可以再寫一篇文章了,所以這邊只做個簡單介紹,想知道更詳盡的內容的話可以自行 Google 一下囉。

  • dataTransfer.setData
    用來設置當拖曳對象放到放置目標時,所能取得的資料類型。
  • target
    用來找到觸發事件的 DOM 元素。
  • appendChild
    將一個 DOM 元素添加到指定的父元素內 ( 如果該父元素內已經有子元素,那麼添加的元素會排在所有子元素的最後面 )。
  • preventDefault
    阻止元素觸發預設的行為 ( 例如點擊 a 元素時會觸發開啟鏈結網址的行為 ) 。
  • stopPropagation
    阻止當前事件繼續進行捕捉 ( capturing ) 及冒泡 ( bubbling ) 階段的傳遞。

小結

一開始接觸 Drag 與 Drop 機制的時候可能會覺得有點小複雜,但是如果能弄清楚他的各種監聽事件與原理,其實這個效果要實作出來並不算太難。那麼,以上就是有關拖曳效果的簡單介紹囉!

參考資料

  1. HTML 拖放 API
  2. HTML 5 拖放
  3. 製作可拖曳的元素

關於筆者

暱稱:沃草這哪招

介紹:沒時間解釋了,快上車!

文章列表 文章列表