溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么在vue中利用HTML 5 拖放API

發(fā)布時(shí)間:2021-01-14 14:16:58 來(lái)源:億速云 閱讀:242 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

本篇文章給大家分享的是有關(guān)怎么在vue中利用HTML 5 拖放API,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。

拖放 API 將可拖動(dòng)元素添加到 HTML,使我們可以構(gòu)建包含可以拖動(dòng)的具有豐富 UI 元素的 Web 應(yīng)用。

在本文中我們將用 Vue.js 構(gòu)建一個(gè)簡(jiǎn)單的看板應(yīng)用。看板是一種項(xiàng)目管理工具,使用戶可以從頭到尾直觀地管理項(xiàng)目。 Trello、Pivotal Tracker 和 Jira 等工具都屬于看板應(yīng)用。

設(shè)置看板

運(yùn)行以下命令創(chuàng)建我們的看板項(xiàng)目:

vue create kanban-board

在創(chuàng)建項(xiàng)目時(shí),該選擇只包含 Babel 和 ESlint 的默認(rèn)預(yù)設(shè)。

完成后,刪除默認(rèn)組件 HelloWorld ,將 App 組件修改為空,僅包含裸組件模板:

<template> <div></div> </template>
<script>
export default {
 name: 'App',
 components: {},
};
</script>
<style></style>

接下來(lái)用 Bootstrap 進(jìn)行樣式設(shè)置,只需 Bootstrap CSS CDN 就夠了。將其添加到 public/index.html 的 head 重。

<head>
 <meta charset="utf-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width,initial-scale=1.0">
 <link rel="icon" href="<%= BASE_URL %>favicon.ico" rel="external nofollow" >
 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="external nofollow" 
 integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
 <title><%= htmlWebpackPlugin.options.title %></title>
 </head>

在看板中構(gòu)建 UI 組件

看板的樣子應(yīng)該是這樣的:

怎么在vue中利用HTML 5 拖放API

通??窗逡辛泻涂ㄆ???ㄆ且獔?zhí)行的單個(gè)項(xiàng)目或任務(wù),列用來(lái)顯示特定卡片的狀態(tài)。

所以需要?jiǎng)?chuàng)建三個(gè) Vue 組件:一個(gè)用于列,一個(gè)用于卡片,最后一個(gè)用于創(chuàng)建新卡片。

創(chuàng)建 card 組件

先來(lái)創(chuàng)建 card 組件。在 /component 目錄中創(chuàng)建一個(gè)新文件 Card.vue。

把下面的代碼添加到組件中:

<template>
 <div class="card">
 <div class="card-body">A Sample Card</div>
 </div>
</template>
<script>
export default {};
</script>
<style scoped>
div.card {
 margin-bottom: 15px;
 box-shadow: 0 0 5px #cccccc;
 transition: all ease 300ms;
 background: #fdfdfd;
}
div.card:hover {
 box-shadow: 0 0 10px #aaaaaa;
 background: #ffffff;
}
</style>

這樣就創(chuàng)建并設(shè)置了卡片組件的樣式。不過(guò)還沒(méi)有向組件添加可拖動(dòng)功能,因?yàn)檫@只是組件的框架。

創(chuàng)建 AddCard 組件

顧名思義,這個(gè)組件將負(fù)責(zé)創(chuàng)建新卡片并將其添加到列中。

在 /components 目錄中創(chuàng)建一個(gè) AddCard.vue 文件,并添加以下代碼:

<template>
 <div class="">
 <button
  class="btn btn-sm btn-info w-100"
  v-if="!inAddMode"
  @click="inAddMode = true"
 >
  Add Card
 </button>
 <form action="#" class="card p-3" ref="form" v-else>
  <div class="form-group">
  <input
   type="text"
   name="title"
   id="title"
   class="form-control"
   placeholder="Something interesting..."
   v-model="cardData"
  />
  </div>
  <div class="d-flex justify-content-center">
  <button type="submit" class="btn w-50 btn-primary mr-3">Save</button>
  <button type="reset" class="btn w-50 btn-danger">
   Cancel
  </button>
  </div>
 </form>
 </div>
</template>
<script>
export default {
 data() {
 return {
  inAddMode: false,
  cardData: '',
 };
 },
 methods: {},
};
</script>
<style></style>

具體功能將在后面進(jìn)行構(gòu)建。

創(chuàng)建 Column 組件

這是最后一個(gè)組件,它用來(lái)顯示卡列表,還會(huì)包含 AddCard 組件,以便可以將新卡片直接創(chuàng)建到列中。

在 components 目錄中創(chuàng)建一個(gè) Column.vue 文件,并添加以下代碼:

<template>
 <div class="col-md-3 card column" ref="column">
 <header class="card-header">
  <h4 class="col">Column Name</h4>
 </header>
 <div class="card-list"></div>
 </div>
</template>
<script>
export default {};
</script>
<style scoped>
div.column {
 padding: 0;
 padding-bottom: 15px;
 margin: 0 15px;
 box-shadow: 0 0 10px #cccccc;
}
div.card-list {
 padding: 0 15px;
}
header {
 margin-bottom: 10px;
}
header h4 {
 text-align: center;
}
</style>

現(xiàn)在項(xiàng)目的框架搭好了,接下來(lái)先概述一下拖放功能在瀏覽器中是怎樣工作的。

HTML5 拖放 API 是什么?

當(dāng)用戶將鼠標(biāo)移到可拖動(dòng)元素上時(shí),拖動(dòng)操作開(kāi)始,然后將元素移動(dòng)到啟用拖放的元素上。

再默認(rèn)情況下,唯一可拖動(dòng)的 HTML 元素是圖像和鏈接。為了使其他元素可拖動(dòng),需要通過(guò)將 draggable 屬性添加到元素;也可以在 JavaScript 中選擇元素并將 draggable 屬性設(shè)置為 true 來(lái)顯式創(chuàng)建功能。

在元素上將 draggable 屬性設(shè)置為 true 之后,你會(huì)注意到 draggable 屬性已添加到該元素。

<!-- Making an element draggable in HTML -->
<div draggable="true">This is a draggable div in HTML</div>

<script>
// Making an element draggable in javascript
const div = document.querySelector('div');
div.draggable = true;
</script>

拖動(dòng)元素的目的是將數(shù)據(jù)從頁(yè)面的一個(gè)部分傳輸?shù)搅硪徊糠帧?/p>

對(duì)于圖像,要傳輸?shù)臄?shù)據(jù)是圖像 URL 或它的 base 64 表示形式。如果是鏈接,傳輸?shù)臄?shù)據(jù)是 URL??梢詫㈡溄右苿?dòng)到瀏覽器的 URL 欄中,這樣使瀏覽器跳轉(zhuǎn)到該 URL。

所以,如果沒(méi)有數(shù)據(jù)傳輸?shù)哪芰Γ敲赐蟿?dòng)元素就毫無(wú)用處了??梢酝ㄟ^(guò) DataTransfer API 把通過(guò)拖動(dòng)操作傳輸?shù)臄?shù)據(jù)保存在拖動(dòng)數(shù)據(jù)存儲(chǔ)區(qū)中,這個(gè) API 提供了在拖放操作期間存儲(chǔ)和訪問(wèn)數(shù)據(jù)的方式。

DataTransfer 提供了添加要通過(guò)拖放傳輸?shù)捻?xiàng)目的位置??梢栽陂_(kāi)始拖動(dòng)操作時(shí)(調(diào)用 dragstart 事件時(shí))將數(shù)據(jù)添加到拖動(dòng)數(shù)據(jù)存儲(chǔ)中,并且只能在完成拖放操作后(調(diào)用 drop 事件時(shí))才能接收數(shù)據(jù)。

從拖動(dòng)到釋放元素的這段時(shí)間中,元素被拖放后,將會(huì)在被拖動(dòng)的元素上觸發(fā)兩個(gè)事件:dragstart 和 dragend。

現(xiàn)在還不能把可拖動(dòng)元素拖放到任何地方。與需要顯式的使元素可拖動(dòng)一樣,它也需要啟用放置。

要啟用元素拖放功能需要偵聽(tīng) dragover 事件并阻止默認(rèn)的瀏覽器操作。

<!-- Make a section drop-enabled -->
<section class="section"></section>
<script>
const section = document.querySelector('.section');
section.addEventListener('dragover', (e) => {
 e.preventDefault();
});
</script>

將元素拖動(dòng)到啟用拖放的元素上時(shí),將會(huì)在啟用拖放的元素上觸發(fā)以下事件:

Dragenter:當(dāng)一個(gè)元素被拖動(dòng)到啟用拖放的元素上時(shí)觸發(fā)一次
Dragover:只要元素仍然位于啟用了 drop 的元素上,就會(huì)連續(xù)觸發(fā)
Drop:在把拖動(dòng)的元素拖放到啟用了拖放的元素上之后觸發(fā)。

需要注意的是,僅在觸發(fā)放置事件時(shí)才能訪問(wèn)存儲(chǔ)在 DataTransfer 對(duì)象中的數(shù)據(jù),而不能在 dragenter 或 dragover 上訪問(wèn)。

組合所有的組件

在向組件添加拖放功能之前,先討論一下 app state。

這里的 app state 將存儲(chǔ)在 App 組件中,然后可以作為 props 向下傳遞到 Column 組件。另一方面,列組件在渲染時(shí)會(huì)將所需的 props 傳遞給卡片組件。

修改 App.vue 使其能夠反映狀態(tài)和組件組成:

// App.vue
<template>
 <div class="container-fluid">
 <h3 class="m-5">
  Vue Kanban Board
 </h3>
 <div class="row justify-content-center">
  <Column
  v-for="(column, index) in columns"
  :column="column"
  :key="index"
  />
 </div>
 </div>
</template>
<script>
import Column from './components/Column';
export default {
 name: 'App',
 components: {
 Column,
 },
 data() {
 return {
  columns: [
  {
   name: 'TO-DO',
   cards: [
   {
    value: 'Prepare breakfast',
   },
   {
    value: 'Go to the market',
   },
   {
    value: 'Do the laundry',
   },
   ],
  },
  {
   name: 'In Progress',
   cards: [],
  },
  {
   name: 'Done',
   cards: [],
  },
  ],
 };
 },
};
</script>
<style>
h3 {
 text-align: center;
}
</style>

在這里,我們導(dǎo)入了列組件,并在狀態(tài)為 columns 的狀態(tài)下循環(huán)訪問(wèn)數(shù)據(jù)時(shí),將每一列的數(shù)據(jù)傳遞給 column 組件。在這種情況下,只有 “To-Do”,“In Progress” 和 “Done” 三列,每列都有一個(gè)卡片數(shù)組。

接下來(lái),更新 Column 組件來(lái)接收 props 并顯示它:

// Column.vue
<template>
 <div class="col-md-3 card column" ref="column">
 <header class="card-header">
  <h4 class="col">{{ column.name }}</h4>
  <AddCard />
 </header>
 <div class="card-list">
  <Card v-for="(card, index) in column.cards" :key="index" :card="card" />
 </div>
 </div>
</template>
<script>
import Card from './Card';
import AddCard from './AddCard';
export default {
 name: 'Column',
 components: {
 Card,
 AddCard,
 },
 props: {
 column: {
  type: Object,
  required: true,
 },
 },
};
</script>

...

Column 組件從 App 組件接收 props,并用 props 渲染 Card 組件列表。在這里還會(huì)使用 AddCard 組件,因?yàn)閼?yīng)該可以將新卡直接添加到列中。

最后更新 Card 組件顯示從 Column 接收的數(shù)據(jù)。

// Card.vue
<template>
 <div class="card" ref="card">
 <div class="card-body">{{ card.value }}</div>
 </div>
</template>
<script>
export default {
 name: 'Card',
 props: {
 card: {
  type: Object,
  required: true,
 },
 },
};
</script>

Card 組件僅從 Column 接收它需要的所有數(shù)據(jù)并顯示出來(lái)。我們還在此處添加了對(duì) card 元素的引用,這樣在用 JavaScript 訪問(wèn) card 元素時(shí)非常有用。

完成上述操作后,你的應(yīng)用應(yīng)該是下面這樣了:

怎么在vue中利用HTML 5 拖放API

添加拖放功能

添加拖放功能的第一步是識(shí)別可拖動(dòng)組件和放置目標(biāo)。

用戶應(yīng)該能夠按照卡片中的活動(dòng)進(jìn)度將卡片從一列拖到另一列。所以可拖動(dòng)組件應(yīng)該是 Card 組件,而放置目標(biāo)是 Column 組件。

使卡片可拖動(dòng)

需要執(zhí)行以下操作才能使卡組件可拖動(dòng):

  1. 將 draggable 屬性設(shè)置為 true

  2. 用 DataTransfer 對(duì)象設(shè)置要傳輸?shù)臄?shù)據(jù)

應(yīng)該先把 draggable 設(shè)置為 true,根據(jù) Vue 生命周期 hook,安全的位置應(yīng)該是已安裝的 hook。把以下內(nèi)容添加到 Card 組件的已安裝 hook 中:

// Card.vue
<script>
export default {
 name: 'Card',
 props: {...},

 mounted() {
 this.setDraggable();
 },

 methods: {
 setDraggable() {
  // Get Card element.
  const card = this.$refs.card;
  card.draggable = true;
  // Setup event listeners.
  card.addEventListener('dragstart', this.handleDragStart);
  card.addEventListener('dragend', this.handleDragEnd);
 },
 },
</script>

在上面,我們創(chuàng)建了一個(gè) setDraggable 方法來(lái)使卡片組件可拖動(dòng)。

在 setDraggable 中,從上一節(jié)中添加的引用中得到卡片,并將 draggable 屬性設(shè)置為 true 。

同時(shí)還需要設(shè)置事件監(jiān)聽(tīng)器:

// Card.vue
<script>
export const CardDataType = 'text/x-kanban-card';

export default {
...
 methods: {
 setDraggable() {...},
 handleDragStart(event) {
  const dataTransfer = event.dataTransfer;
  // Set the data to the value of the card which is gotten from props.
  dataTransfer.setData(CardDataType, this.card.value);
  dataTransfer.effectAllowed = 'move';
  // Add visual cues to show that the card is no longer in it's position.
  event.target.style.opacity = 0.2;
 },
 handleDragEnd(event) {
  // Return the opacity to normal when the card is dropped.
  event.target.style.opacity = 1;
 }
 }
}
</script>

在前面提到,只有在 dragstart 事件被調(diào)用時(shí),數(shù)據(jù)才可以被添加到拖動(dòng)數(shù)據(jù)存儲(chǔ)中。所以需要在 handleDragStart 方法中添加數(shù)據(jù)。

設(shè)置數(shù)據(jù)時(shí)要用到的重要信息是格式,可以是字符串。在我們的例子中,它被設(shè)置為 text/x-kanban-card。存儲(chǔ)這個(gè)數(shù)據(jù)格式并導(dǎo)出它,因?yàn)樵趧h除卡后獲取數(shù)據(jù)時(shí),Column 組件將會(huì)用到它。

最后,將 card 的透明度降低到 0.2 ,以便向用戶提供一些反饋,表明該卡實(shí)際上已被拉出其原始位置。拖動(dòng)完成后,再把透明度恢復(fù)為 1。

現(xiàn)在可以拖動(dòng)卡片了。接下來(lái)添加放置目標(biāo)。

把 dragover 設(shè)置為 drop-enabled

將卡片拖到列組件上時(shí),會(huì)立即觸發(fā) dragover 事件,將卡放入列中后會(huì)觸發(fā) drop 事件。

要使卡片掉落到列中,需要偵聽(tīng)這些事件。

// Column.vue
<template>...</template>
<script>
import Card { CardDataType } from './Card';
import AddCard from './AddCard';
export default {
 name: 'Column',
 components: {...},
 props: {...},
 mounted() {
 this.enableDrop();
 },
 methods: {
 enableDrop() {
  const column = this.$refs.column;
  column.addEventListener('dragenter', this.handleDragEnter);
  column.addEventListener('dragover', this.handleDragOver);
  column.addEventListener('drop', this.handleDrop);
 },
 /**
  * @param {DragEvent} event
  */
 handleDragEnter(event) {
  if (event.dataTransfer.types.includes[CardDataType]) {
  // Only handle cards.
  event.preventDefault();
  }
 },
 handleDragOver(event) {
  // Create a move effect.
  event.dataTransfer.dropEffect = 'move';
  event.preventDefault();
 },
 /**
  * @param {DragEvent} event
  */
 handleDrop(event) {
  const data = event.dataTransfer.getData(CardDataType);
  // Emit a card moved event.
  this.$emit('cardMoved', data);
 },
 },
};
</script>

在這里將設(shè)置在掛載 Column 組件之后啟用 drop 所需的所有事件偵聽(tīng)器。

在這三個(gè)事件中,第一個(gè)被觸發(fā)的是 dragenter ,當(dāng)可拖動(dòng)元素被拖到列中時(shí)會(huì)立即被觸發(fā)。對(duì)于我們的程序,只希望將卡片放入一列中,所以在 dragenter 事件中,只阻止數(shù)據(jù)類(lèi)型的默認(rèn)值,數(shù)據(jù)類(lèi)型包括在 card 組件中所定義的 card 數(shù)據(jù)類(lèi)型。

在 dragover 事件中,把放置效果設(shè)置為 move。

在 drop 事件中獲得從 dataTransfer 對(duì)象傳輸?shù)臄?shù)據(jù)。

接下來(lái),需要更新?tīng)顟B(tài)并將卡片移動(dòng)到當(dāng)前列。因?yàn)槲覀兊某绦驙顟B(tài)位于 App 組件中,所以在 drop 偵聽(tīng)器中發(fā)出 cardMoved 事件,傳遞已傳輸?shù)臄?shù)據(jù),并在 App 組件中偵聽(tīng) cardMoved 事件。

更新 App.vue 來(lái)監(jiān)聽(tīng) cardMoved 事件:

// App.vue

<template>
 <div class="container-fluid">
 ...
 <div class="row justify-content-center">
  <Column
  v-for="(column, index) in columns"
  :column="column"
  :key="index"
  @cardMoved="moveCardToColumn($event, column)"
  />
 </div>
 </div>
</template>

<script>
import Column from './components/Column';
export default {
 name: 'App',
 components: {...},
 data() {
 return {...}
 },
 methods: {
 moveCardToColumn(data, newColumn) {
  const formerColumn = this.columns.find(column => {
  // Get all the card values in a column.
  const cardValues = column.cards.map((card) => card.value);
  return cardValues.includes(data);
  })
  // Remove card from former column.
  formerColumn.cards = formerColumn.cards.filter(
  (card) => card.value !== data
  );
  // Add card to the new column.
  newColumn.cards.push({ value: data });
 },
 },
}
</script>

在這里通過(guò) @cardMoved 偵聽(tīng) cardMoved 事件,并調(diào)用 moveCardToColumn 方法。 cardMoved 事件發(fā)出一個(gè)值(卡片數(shù)據(jù)),可以通過(guò) $event 訪問(wèn)這個(gè)值,另外還傳遞了放置卡的當(dāng)前列(這是調(diào)度事件的位置)。

moveCardToColumn 函數(shù)做了三件事:找到卡偏先前所在的列,從該列中取出卡片,最后把卡片加到新列中。

完成看板

現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了拖放功能,最后只剩下添加卡片的功能了。

在 AddCard.vue 中添加以下代碼:

<template>
 <div class="">
 <button
  class="btn btn-sm btn-info w-100"
  v-if="!inAddMode"
  @click="inAddMode = true"
 >
  Add Card
 </button>
 <form
  action="#"
  class="card p-3"
  @submit.prevent="handleSubmit"
  @reset="handleReset"
  ref="form"
  v-else
 >
  ...
 </form>
 </div>
</template>
<script>
export default {
 data() {
 return {...};
 },
 methods: {
 handleSubmit() {
  if (this.cardData.trim()) {
  this.cardData = '';
  this.inAddMode = false;
  this.$emit('newcard', this.cardData.trim());
  }
 },
 handleReset() {
  this.cardData = '';
  this.inAddMode = false;
 },
 },
};
</script>

上面的代碼是在提交“add card”表單或重置時(shí)運(yùn)行的函數(shù)。

重置后清除 cardData,并將 inAddMode 設(shè)置為 false。

在提交表單后還要清除 cardData ,以便在添加新項(xiàng)目時(shí)不會(huì)顯示以前的數(shù)據(jù),并且還要將 inAddMode 設(shè)置為 false 并發(fā)出 newcard 事件。

Column組件中使用了AddCard組件,所以需要在 Column 組件中監(jiān)聽(tīng) newcard 事件。在 Column 組件中添加偵聽(tīng) newcard 事件的代碼:

<template>
 <div class="col-md-3 card column" ref="column">
 <header class="card-header">
  <h4 class="col">{{ column.name }}</h4>
  <AddCard @newcard="$emit('newcard', $event)"></AddCard>
 </header>
 ...
</template>
...

在這里重新發(fā)出 newcard 事件,這樣可以使它到達(dá) App 組件,實(shí)際的動(dòng)作將在該組件上發(fā)生。

自定義 Vue 事件不會(huì)冒泡,因此 App 組件無(wú)法偵聽(tīng) AddCard 組件中發(fā)出的 newcard 事件,因?yàn)樗皇侵苯幼咏M件。
更新 App 組件處理 newcard 事件的代碼:

// App.vue

<template>
 <div class="container-fluid">
 ...
 <div class="row justify-content-center">
  <Column
  v-for="(column, index) in columns"
  :column="column"
  :key="index"
  @cardMoved="moveCardToColumn($event, column)"
  @newcard="handleNewCard($event, column)"
  />
 </div>
 </div>
</template>

<script>
import Column from './components/Column';
export default {
 name: 'App',
 components: {...},
 data() {
 return {...}
 },
 methods: {
 moveCardToColumn(data, newColumn) {...},
 handleNewCard(data, column) {
  // Add new card to column.
  column.cards.unshift({ value: data });
 },
 },
};
</script>

在這里偵聽(tīng)從 Column 組件調(diào)用的 newcard 事件,在獲取數(shù)據(jù)后,創(chuàng)建一個(gè)新卡片并將其添加到創(chuàng)建該卡的列中。

以上就是怎么在vue中利用HTML 5 拖放API,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI