学接上一回,基础API看完了,想实现一个排序功能,再有功能也能实现,就是从box1 拿到 box2 里,然后按照想要的顺序再拿回来。
想了想如果只有一个 box 咋办?琢磨出以下思路:
实现效果,代码在最后
加上拖拽时的效果,就是在 dragenter
和 dragleave
上添加和删除类名。
这里有个问题记录下,dragenter
和 dragleave
读不到 dataTransfer
在 dragstart
中设置的数据。
HTML5 拖放规范 规定了一个
drag data store mode
。这可能会导致预期外的结果,即DataTransfer.getData()
没有返回预期值。
实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
body,
html {
user-select: none;
height: 100%;
margin: 0;
}
#draggable {
text-align: center;
background: white;
}
#app {
display: flex;
height: 100%;
}
.box {
width: 200px;
border: 1px solid blueviolet;
margin: 10px;
padding: 10px;
transition: all 0.5s;
}
.box.dragover {
box-shadow: 0 0 5px blueviolet;
}
.block-item {
margin: 5px;
}
.block-item-content {
text-align: center;
background: blueviolet;
height: 25px;
line-height: 25px;
pointer-events: none;
}
.block-item-over::before {
content: '';
position: relative;
display: block;
width: 100%;
height: 100%;
}
.shadow-child::before {
content: '';
display: block;
height: 20px;
border: 1px dashed blueviolet;
background-color: #fff;
margin-bottom: 5px;
pointer-events: none;
}
</style>
</head>
<body>
<div id="app">
<!-- 放置目标 -->
<div v-for="(v, k) in boxList" class="box" :id="k" :key="k" @dragenter="boxDragEnter($event, k)"
@dragover="boxDrapOver($event, k)" @dragleave="boxDragLeave($event, k)" @drop="boxDop($event, k)">
<span>{{ k }}</span>
<!-- 拖拽元素 -->
<div v-for="(block, index) in boxList[k]" class="block-item" draggable="true" :key="block.name"
:data-index="index" @dragstart="blockDrapStart($event, block, k)">
<div class="block-item-content">
{{block.name}}
</div>
</div>
</div>
</div>
<script>
const { createApp, reactive, onMounted } = Vue
createApp({
setup() {
// 俩个box,分别包含一个水果
const boxList = reactive({
box1: [{ name: '🍎1' }, { name: '🍎2' }, { name: '🍎3' }],
box2: [{ name: '🍐1' }, { name: '🍐2' }, { name: '🍐3' }]
});
// 该事件在放置目标上触发
// 拖动的元素进入一个有效的放置目标时触发
function boxDragEnter(event, box) {
console.log("【box】DragEnter:", box, event.target)
const isBox = event.target.classList.contains("box")
if (isBox && !event.target.classList.contains("dragover")) {
event.target.classList.add("dragover");
}
// 放置虚线框
if (!isBox) {
event.target.classList.add("shadow-child");
}
}
// 该事件在放置目标上触发
// 拖动的元素离开一个有效的放置目标时触发
function boxDragLeave(event, box) {
console.log("【box】DragLeave:", box, event.target)
const isBox = event.target.classList.contains("box")
if (isBox && event.target.classList.contains("dragover")) {
event.target.classList.remove("dragover");
}
// 放置虚线框
if (!isBox) {
event.target.classList.remove("shadow-child");
}
}
// 该事件在放置目标上触发
function boxDrapOver(event, box) {
console.log("【box】DrapOver:", box)
// event.preventDefault(),使目标容器能够接收 drop 事件。
event.preventDefault()
}
// 元素被放置到目标元素上时触发.
// 为确保 drop 事件始终按预期触发,应当在处理 dragover 事件的代码部分始终包含 preventDefault() 调用。
function boxDop(event, box) {
console.log("【box】Dop:", box, event.target)
const isBox = event.target.classList.contains("box")
// 读取数据
const { sourceBox, data } = JSON.parse(event.dataTransfer.getData('text/plain'));
const sourceIndex = boxList[sourceBox].findIndex(item => item.name === data.name);
// 放到了box上,直接放到最后面
if (isBox && sourceBox !== box) {
boxList[box].push(data);
boxList[sourceBox].splice(sourceIndex, 1)
}
// 放到了水果上
if (!isBox) {
// 放到哪个水果上了
const { index } = event.target.dataset;
// 不是放到自己原来的位置 || 不是同一个box
if (sourceIndex != index || sourceBox !== box) {
// 先放进去
boxList[box].splice(index, 0, data);
// 删掉原来位置上的
if (sourceBox === box) {
boxList[box].splice(sourceIndex > index ? sourceIndex + 1 : sourceIndex, 1)
} else {
boxList[sourceBox].splice(sourceIndex, 1)
}
}
event.target.classList.remove("shadow-child");
}
if (event.target.classList.contains("dragover")) {
event.target.classList.remove("dragover");
}
}
// 开始拖动元素时调用,在拖拽元素上触发
function blockDrapStart(event, data, sourceBox) {
console.log("【block】DrapStart:", data, sourceBox, event)
// 拖拽的内容存一下
event.dataTransfer.setData("text/plain", JSON.stringify({ sourceBox, data }))
}
return {
boxList,
boxDragEnter,
boxDragLeave,
boxDrapOver,
boxDop,
blockDrapStart
}
}
}).mount('#app')
</script>
</body>
</html>