文章目录
- ⭐前言
- ⭐vue3拖拽实现拼图
- 💖 思路分解
- 💖 布局结构
- 💖 拖拽函数
- 💖 校验函数
- 💖 inscode整体代码
- ⭐运行效果
- 💖 随机顺序
- 💖 拖拽中
- 💖 校验失败
- 💖 校验通过
- ⭐总结
- ⭐结束
⭐前言
大家好,我是yma16,本文分享关于 前端vue3——实现二次元人物拼图校验。
前端html拖拽原理
HTML 拖拽的基本原理是通过鼠标事件和 DOM 操作来实现的。
一般来说,拖拽操作包括三个阶段:
- 鼠标按下事件:当用户按下鼠标左键时,触发鼠标按下事件。在该事件处理函数中,首先需要记录下被拖拽元素的相关信息,比如该元素的位置、大小和初始鼠标点击位置等。
- 鼠标移动事件:当用户按下鼠标左键时并拖动鼠标时,触发鼠标移动事件。在该事件处理函数中,需要根据鼠标当前位置和拖拽元素的初始位置,计算出该元素要被移动的距离,并在 DOM 中更新该元素的位置。
- 鼠标松开事件:当用户松开鼠标左键时,触发鼠标松开事件。在该事件处理函数中,需要清除拖拽元素的相关信息,比如初始鼠标点击位置等。
在以上三个阶段中,需要使用的 DOM 操作包括:
- 获取 DOM 元素:可以使用 document.getElementById() 或 document.querySelector() 等方法来获取需要拖拽的元素。
- 更新元素位置:可以使用元素的 style 属性来修改元素的位置,比如设置元素的 left 和 top 属性。
- 创建新元素:可以使用 document.createElement() 方法来创建新的 DOM 元素。
通过以上鼠标事件和 DOM 操作的组合,可以实现基本的 HTML 拖拽功能。
⭐vue3拖拽实现拼图
使用vue3和原生的html drag 实现片的拖拽
💖 思路分解
- 拆分图片为4宫格样式
- 定义拖拽的样式div对应图片的4宫格
- 定义拖拽的div元素,背景图片使用图片拆分的素材
- 给元素定义序号
- 校验最终的元素序号,判断是否拼图成功
拆分图片
💖 布局结构
才有左右布局
左侧:拖拽来源区域
右侧:拖拽放入区域
template布局
<template>
<div class="container-drag">
<div style="width: 100%;text-align:center;margin: 10px;">
<a-button type="primary" class="random-button" @click="randomDragOrder">随机顺序</a-button>
</div>
<div class="container-drag-box">
<div class="container-drag-left">
<!-- <> -->
<div v-for="item in state.dragConfig.sourceArray" :key="item.id" class="drag-item-box"
:id="state.dragConfig.sourceDomPrefix + '-' + item.id">
<div class="drag-item" draggable="true" :id="state.dragConfig.dragDomPrefix + '-' + item.id">
<!-- 拖拽对象 data-order 校验排序用 -->
<img :src="item.src" width="280" height="280" :data-order="item.id" />
</div>
</div>
</div>
<div class="container-drag-right" id="target-box-id">
<!-- 目标对象 data-order 校验排序用-->
<div v-for="item in state.dragConfig.sourceArray" :key="item" class="target-item-box dropzone"
:id="state.dragConfig.targetDomPrefix + '-' + item.id" :data-order="item.id">
</div>
</div>
</div>
<div style="width: 100%;text-align:center">
<a-button type="primary" @click="confirmImg">
确定
</a-button>
</div>
</div>
</template>
css配置
<style>
.container-drag {
min-width: 800px;
}
.random-button {
margin: 5px;
cursor: pointer;
}
.container-drag-box {
display: flex;
justify-content: space-between;
}
.container-drag-left {
width: 800px;
}
.container-drag-right {
width: 600px;
height: 600px;
margin: 0;
}
.drag-item-box {
display: inline-block;
margin: 2px;
padding: 0;
width: 280px;
height: 280px;
border: 1px solid rgb(24, 144, 255);
overflow: hidden;
}
.target-item-box {
display: inline-block;
width: 280px;
height: 280px;
border: 1px solid rgb(255, 255, 255);
box-sizing: border-box;
overflow: hidden;
margin-right: 5px;
margin-bottom: 0;
margin-top: 0;
}
/* 拖拽对象 */
.drag-item {
margin: 0;
text-align: center;
cursor: pointer;
}
/* 拖拽中 */
.dragging {
opacity: .5;
}
/* 悬浮上方 */
.dragover {
background: rgba(0,255,0,.5);
}
</style>
布局效果
💖 拖拽函数
拖拽对象配置方法drag,dragstart,dragend
放入区域配置方法drop,dragover,dragleave,dragenter
// 拖拽对象
const drag = (event) => {
console.log("dragging", event);
}
const dragStart = (event) => {
// 保存被拖动元素的引用
state.dragConfig.dragTarget = event.target;
// 设置为半透明
event.target.classList.add("dragging");
}
const dragEnd = (event) => {
// 拖动结束,重置透明度
event.target.classList.remove("dragging");
}
// 目标对象
const dragOver = (event) => {
// 阻止默认行为以允许放置
event.preventDefault();
}
const dragLeave = (event) => {
// 在可拖动元素离开潜在放置目标元素时重置该目标的背景
if (event.target.classList.contains("dropzone")) {
event.target.classList.remove("dragover");
}
}
const dragEnter = (event) => {
// 在可拖动元素进入潜在的放置目标时高亮显示该目标
if (event.target.classList.contains("dropzone")) {
event.target.classList.add("dragover");
}
}
const drop = (event) => {
// 阻止默认行为(会作为某些元素的链接打开)
event.preventDefault();
// 将被拖动元素移动到选定的目标元素中
if (event.target.classList.contains("dropzone")) {
event.target.classList.remove("dragover");
// 删除自身
state.dragConfig.dragTarget.parentNode.removeChild(state.dragConfig.dragTarget);
// 添加元素
event.target.appendChild(state.dragConfig.dragTarget);
}
}
const initDragAction = (sourceElement) => {
if (!sourceElement) {
return
}
/* 在放置拖拽对象上触发的事件 */
sourceElement.addEventListener("drag", drag);
sourceElement.addEventListener("dragstart", dragStart);
sourceElement.addEventListener("dragend", dragEnd);
}
const initTargetAction = (targetElement) => {
if (!targetElement) {
return
}
/* 在放置目标对象上触发的事件 */
targetElement.addEventListener(
"dragover",
dragOver,
false,
);
targetElement.addEventListener("dragenter", dragEnter);
targetElement.addEventListener("dragleave", dragLeave);
targetElement.addEventListener("drop", drop);
}
💖 校验函数
遍历dom节点获取自定义的data-order
属性进行校验
// 校验
const confirmImg = () => {
const rightDom = document.getElementById('target-box-id')
console.log('rightDom', rightDom)
const rightDomChildNodes = rightDom.childNodes
console.log('rightDomChildNodes', rightDomChildNodes)
if (rightDomChildNodes.length) {
// childNodes会出现空文本节点
for (let i = 0, length = rightDomChildNodes.length; i < length; ++i) {
console.log('childNodes', rightDomChildNodes[i])
if (rightDomChildNodes[i].nodeType !== 1) {
// 跳过文本节点
continue
}
else if (rightDomChildNodes[i].hasChildNodes) {
console.log('childNodes attr', rightDomChildNodes[i])
const currentDataOrder = rightDomChildNodes[i].getAttribute('data-order')
const imgDataOrder = rightDomChildNodes[i].getElementsByTagName('img')[0].getAttribute('data-order')
console.log('currentDataOrder', currentDataOrder)
console.log('imgDataOrder', imgDataOrder)
if (currentDataOrder !== imgDataOrder) {
return message.warn('拼图位置错误:\t第' + currentDataOrder+'张图片')
}
}
else {
return message.warn('没有完成拼图,请拖拽图片')
}
}
}
else {
return message.warn('没有完成拼图,请拖拽图片')
}
return '恭喜你完成拼图'
}
💖 inscode整体代码
完整的vue代码
<template>
<div class="container-drag">
<div style="width: 100%;text-align:center;margin: 10px;">
<a-button type="primary" class="random-button" @click="randomDragOrder">随机顺序</a-button>
</div>
<div class="container-drag-box">
<div class="container-drag-left">
<!-- <> -->
<div v-for="item in state.dragConfig.sourceArray" :key="item.id" class="drag-item-box"
:id="state.dragConfig.sourceDomPrefix + '-' + item.id">
<div class="drag-item" draggable="true" :id="state.dragConfig.dragDomPrefix + '-' + item.id">
<!-- 拖拽对象 data-order 校验排序用 -->
<img :src="item.src" width="280" height="280" :data-order="item.id" />
</div>
</div>
</div>
<div class="container-drag-right" id="target-box-id">
<!-- 目标对象 data-order 校验排序用-->
<div v-for="item in state.dragConfig.sourceArray" :key="item" class="target-item-box dropzone"
:id="state.dragConfig.targetDomPrefix + '-' + item.id" :data-order="item.id">
</div>
</div>
</div>
<div style="width: 100%;text-align:center">
<a-button type="primary" @click="confirmImg">
确定
</a-button>
</div>
</div>
</template>
<script setup>
import { reactive, onMounted } from 'vue'
import { message } from 'ant-design-vue'
const state = reactive({
dragConfig: {
sourceDomPrefix: 'source-item',
targetDomPrefix: 'target-item',
dragDomPrefix: 'drag-item',
dragTarget: null,
sourceImg: '/img/imageSource.png',
sourceArray: [{
id: 1,
src: '/img/image1.png'
},
{
id: 2,
src: '/img/image2.png'
},
{
id: 3,
src: '/img/image3.png'
},
{
id: 4,
src: '/img/image4.png'
},
]
}
})
// 数组随机顺序
const randomDragOrder = () => {
const sourceArray = [...state.dragConfig.sourceArray]
sourceArray.sort(() => Math.random() - 0.5);
state.dragConfig.sourceArray = sourceArray
}
// 拖拽对象
const drag = (event) => {
console.log("dragging", event);
}
const dragStart = (event) => {
// 保存被拖动元素的引用
state.dragConfig.dragTarget = event.target;
// 设置为半透明
event.target.classList.add("dragging");
}
const dragEnd = (event) => {
// 拖动结束,重置透明度
event.target.classList.remove("dragging");
}
// 目标对象
const dragOver = (event) => {
// 阻止默认行为以允许放置
event.preventDefault();
}
const dragLeave = (event) => {
// 在可拖动元素离开潜在放置目标元素时重置该目标的背景
if (event.target.classList.contains("dropzone")) {
event.target.classList.remove("dragover");
}
}
const dragEnter = (event) => {
// 在可拖动元素进入潜在的放置目标时高亮显示该目标
if (event.target.classList.contains("dropzone")) {
event.target.classList.add("dragover");
}
}
const drop = (event) => {
// 阻止默认行为(会作为某些元素的链接打开)
event.preventDefault();
// 将被拖动元素移动到选定的目标元素中
if (event.target.classList.contains("dropzone")) {
event.target.classList.remove("dragover");
// 删除自身
state.dragConfig.dragTarget.parentNode.removeChild(state.dragConfig.dragTarget);
// 添加元素
event.target.appendChild(state.dragConfig.dragTarget);
}
}
const initDragAction = (sourceElement) => {
if (!sourceElement) {
return
}
/* 在放置拖拽对象上触发的事件 */
sourceElement.addEventListener("drag", drag);
sourceElement.addEventListener("dragstart", dragStart);
sourceElement.addEventListener("dragend", dragEnd);
}
const initTargetAction = (targetElement) => {
if (!targetElement) {
return
}
/* 在放置目标对象上触发的事件 */
targetElement.addEventListener(
"dragover",
dragOver,
false,
);
targetElement.addEventListener("dragenter", dragEnter);
targetElement.addEventListener("dragleave", dragLeave);
targetElement.addEventListener("drop", drop);
}
// 校验
const confirmImg = () => {
const rightDom = document.getElementById('target-box-id')
console.log('rightDom', rightDom)
const rightDomChildNodes = rightDom.childNodes
console.log('rightDomChildNodes', rightDomChildNodes)
if (rightDomChildNodes.length) {
// childNodes会出现空文本节点
for (let i = 0, length = rightDomChildNodes.length; i < length; ++i) {
console.log('childNodes', rightDomChildNodes[i])
if (rightDomChildNodes[i].nodeType !== 1) {
// 跳过文本节点
continue
}
else if (rightDomChildNodes[i].hasChildNodes) {
console.log('childNodes attr', rightDomChildNodes[i])
const currentDataOrder = rightDomChildNodes[i].getAttribute('data-order')
const imgDataOrder = rightDomChildNodes[i].getElementsByTagName('img')[0].getAttribute('data-order')
console.log('currentDataOrder', currentDataOrder)
console.log('imgDataOrder', imgDataOrder)
if (currentDataOrder !== imgDataOrder) {
return message.warn('拼图位置错误:\t第' + currentDataOrder+'张图片')
}
}
else {
return message.warn('没有完成拼图,请拖拽图片')
}
}
}
else {
return message.warn('没有完成拼图,请拖拽图片')
}
return '恭喜你完成拼图'
}
// 生命周期
onMounted(() => {
// 拖拽对象
const dragArr = state.dragConfig.sourceArray.map(item => {
return state.dragConfig.dragDomPrefix + '-' + item.id
})
dragArr.forEach(id => {
initDragAction(document.getElementById(id))
})
// 目标对象
const sourceArr = state.dragConfig.sourceArray.map(item => {
return state.dragConfig.sourceDomPrefix + '-' + item.id
})
sourceArr.forEach(id => {
initTargetAction(document.getElementById(id))
})
const targetArr = state.dragConfig.sourceArray.map(item => {
return state.dragConfig.targetDomPrefix + '-' + item.id
})
targetArr.forEach(id => {
initTargetAction(document.getElementById(id))
})
})
</script>
<style>
.container-drag {
min-width: 800px;
}
.random-button {
margin: 5px;
cursor: pointer;
}
.container-drag-box {
display: flex;
justify-content: space-between;
}
.container-drag-left {
width: 800px;
}
.container-drag-right {
width: 600px;
height: 600px;
margin: 0;
}
.drag-item-box {
display: inline-block;
margin: 2px;
padding: 0;
width: 280px;
height: 280px;
border: 1px solid rgb(255, 255, 255);
overflow: hidden;
}
.target-item-box {
display: inline-block;
width: 280px;
height: 280px;
border: 1px solid rgb(255, 255, 255);
box-sizing: border-box;
overflow: hidden;
margin-right: 5px;
margin-bottom: 0;
margin-top: 0;
}
/* 拖拽对象 */
.drag-item {
margin: 0;
text-align: center;
cursor: pointer;
}
/* 拖拽中 */
.dragging {
opacity: .5;
}
/* 悬浮上方 */
.dragover {
background: rgba(0,255,0,.5);
}
</style>
代码放在inscode vue3项目在线运行
⭐运行效果
💖 随机顺序
💖 拖拽中
💖 校验失败
💖 校验通过
拖拽过程截图
⭐总结
拼图校验总结:
1.校验的顺序可以用元素的attribute传递位置顺序进行标记
2.拖拽对象的函数使用 拖拽对象配置方法drag,dragstart,dragend
放入区域配置方法drop,dragover,dragleave,dragenter
拖拽总结:
在HTML中,拖放(drag and
drop)是一种用户界面交互的特定形式,其中用户可以拖动元素或数据并将其放置在另一个位置。以下是HTML拖放的一些总结:
- 拖放可用于各种用途,例如重新排序列表,将文本或文件拖动到另一个应用程序中,或从文件资源管理器将文件拖动到Web页面中。
- 在HTML中,拖放API由一些事件组成,包括dragstart、dragenter、dragover、dragleave、drop和dragend。
- 通过使用HTML data属性,可以将数据附加到拖动事件,并在放置事件中访问它们。
- 通过使用CSS,可以为用户拖动时显示的元素创建自定义外观。
- 在JavaScript中,可以使用dragEvent对象来访问有关拖动和放置事件的详细信息,例如拖动元素的位置和放置元素的位置。
- 可以使用HTML5的拖放API创建复杂的拖放交互,例如可拖动的图形和对象,将元素沿路径拖动等。
⭐结束
本文分享到这结束,如有错误或者不足之处欢迎指出!
👍 点赞,是我创作的动力!
⭐️ 收藏,是我努力的方向!
✏️ 评论,是我进步的财富!
💖 感谢你的阅读!