// 每日前端夜话 第466篇
// 正文共:3800 字
// 预计阅读时间:12 分钟
拖放 API 将可拖动元素添加到 HTML,使我们可以构建包含可以拖动的具有丰富 UI 元素的 Web 应用。
在本文中我们将用 Vue.js 构建一个简单的看板应用。看板是一种项目管理工具,使用户可以从头到尾直观地管理项目。Trello、Pivotal Tracker 和 Jira 等工具都属于看板应用。
设置看板
运行以下命令创建我们的看板项目:
vue create kanban-board
在创建项目时,该选择只包含 Babel 和 ESlint 的默认预设。
完成后,删除默认组件 HelloWorld ,将 App 组件修改为空,仅包含裸组件模板:
name: 'App',
components: {},
};
接下来用 Bootstrap 进行样式设置,只需 Bootstrap CSS CDN 就够了。将其添加到 public/index.html 的 head 重。
favicon.ico">
integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
在看板中构建 UI 组件
看板的样子应该是这样的:
通常看板要有列和卡片。卡片是要执行的单个项目或任务,列用来显示特定卡片的状态。
所以需要创建三个 Vue 组件:一个用于列,一个用于卡片,最后一个用于创建新卡片。
创建 card 组件
先来创建 card 组件。在 /component 目录中创建一个新文件 Card.vue。
把下面的代码添加到组件中:
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;
}
这样就创建并设置了卡片组件的样式。不过还没有向组件添加可拖动功能,因为这只是组件的框架。
创建 AddCard 组件
顾名思义,这个组件将负责创建新卡片并将其添加到列中。
在 /components 目录中创建一个 AddCard.vue 文件,并添加以下代码:
class="btn btn-sm btn-info w-100"
v-if="!inAddMode"
@click="inAddMode = true"
>
Add Card
type="text"
name="title"
id="title"
class="form-control"
placeholder="Something interesting..."
v-model="cardData"
/>
Save
Cancel
data() {
return {
inAddMode: false,
cardData: '',
};
},
methods: {},
};
具体功能将在后面进行构建。
创建 Column 组件
这是最后一个组件,它用来显示卡列表,还会包含 AddCard 组件,以便可以将新卡片直接创建到列中。
在 components 目录中创建一个 Column.vue 文件,并添加以下代码:
Column Name
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 h3 {
text-align: center;
}
现在项目的框架搭好了,接下来先概述一下拖放功能在浏览器中是怎样工作的。
HTML5 拖放 API 是什么?
当用户将鼠标移到可拖动元素上时,拖动操作开始,然后将元素移动到启用拖放的元素上。
再默认情况下,唯一可拖动的 HTML 元素是图像和链接。为了使其他元素可拖动,需要通过将 draggable 属性添加到元素;也可以在 JavaScript 中选择元素并将 draggable 属性设置为 true 来显式创建功能。
❝
在元素上将 draggable 属性设置为 true 之后,你会注意到 draggable 属性已添加到该元素。
❞
const div = document.querySelector('div');
div.draggable = true;
拖动元素的目的是将数据从页面的一个部分传输到另一部分。
对于图像,要传输的数据是图像 URL 或它的 base 64 表示形式。如果是链接,传输的数据是 URL。可以将链接移动到浏览器的 URL 栏中,这样使浏览器跳转到该 URL。
所以,如果没有数据传输的能力,那么拖动元素就毫无用处了。可以通过 DataTransfer API 把通过拖动操作传输的数据保存在拖动数据存储区中,这个 API 提供了在拖放操作期间存储和访问数据的方式。
DataTransfer 提供了添加要通过拖放传输的项目的位置。可以在开始拖动操作时(调用 dragstart 事件时)将数据添加到拖动数据存储中,并且只能在完成拖放操作后(调用 drop 事件时)才能接收数据。
从拖动到释放元素的这段时间中,元素被拖放后,将会在被拖动的元素上触发两个事件:dragstart 和 dragend。
现在还不能把可拖动元素拖放到任何地方。与需要显式的使元素可拖动一样,它也需要启用放置。
要启用元素拖放功能需要侦听 dragover 事件并阻止默认的浏览器操作。
section.addEventListener('dragover', (e) => {
e.preventDefault();
});
将元素拖动到启用拖放的元素上时,将会在启用拖放的元素上触发以下事件:
Dragenter:当一个元素被拖动到启用拖放的元素上时触发一次Dragover:只要元素仍然位于启用了 drop 的元素上,就会连续触发Drop:在把拖动的元素拖放到启用了拖放的元素上之后触发。
❝
需要注意的是,仅在触发放置事件时才能访问存储在 DataTransfer 对象中的数据,而不能在 dragenter 或 dragover 上访问。
❞
组合所有的组件
在向组件添加拖放功能之前,先讨论一下 app state。
这里的 app state 将存储在 App 组件中,然后可以作为 props 向下传递到 Column 组件。另一方面,列组件在渲染时会将所需的 props 传递给卡片组件。
修改 App.vue 使其能够反映状态和组件组成:
// App.vue
Vue Kanban Board
v-for="(column, index) in columns"
:column="column"
:key="index"
/>
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: [],
},
],
};
},
};
text-align: center;
}
在这里,我们导入了列组件,并在状态为 columns 的状态下循环访问数据时,将每一列的数据