感觉放弃csdn了,于是同样的内容搬迁到掘金上~~~也不是别的,我就是感觉csdn上放放问题就好了,有意思的小分享还是得掘金~(这绝不是舔,绝不是!!!)
这次的需求呢,听起来比较正常,指定目标区域的拖拽 + 预投影
行吧,就这么个小东西,网上绝对有很多成功的案例哈哈哈哈哈哈哈哈哈哈~
小手抄一抄,快乐又逍遥~
为什么全部都是jquery的版本 一群垃圾 市面上的博客都不与时俱进的吗???
还得自己来,唉!
先随便写个demo,把几个节点写一下,随便写点样式
startTouch方法不生效90%是因为浏览器没有切换到移动端模式
这里为什么要在body下创建一个专门用来拖拽的元素呢,是因为直接改变拖动的那个元素,可能会改变页面布局,而且可能这个拖拽元素上层有不是static定位的父元素会导致位置偏差
因为拖动过程中这个方法会触发很多很多次,而预投影会对dom进行操作,操作频繁会造成渲染压力,所以我们需要加上节流避免可能会出现的卡顿(代码注释里面也写了)
这里可以对目标节点的判断给予优化,记录每次预投影的节点,循环前去掉,循环中找到对应的目标节点之后就跳出来,不用继续循环。
这里判断的是拖拽元素的中心点是否在该目标区域的范围内,如果目标区域有重合,直接取用这里的判断逻辑会有bug,需要将if条件根据实际情况进行修改
当然啦,因为我是写demo嘛~就没有写那么多情况,上面的内容请根据个人情况自行配置哦~
代码完整版
(注释我都尽可能写的比较详细,如果觉得不够明白的就写留言~)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="drag-part">
<!-- 拖拽元素区域 -->
<div class="drag-dom drag1" ontouchstart="startTouch(event, 1)">1</div>
<div class="drag-dom drag2" ontouchstart="startTouch(event, 2)">2</div>
</div>
<div class="goal-part">
<!-- 目标位置区域 -->
<div class="goal-dom"></div>
<div class="goal-dom"></div>
</div>
<script type="text/javascript">
let drag_flag = false
let obj_height, obj_width
function startTouch(e, i) {
if (drag_flag) return
drag_flag = true
// 复制节点
const obj_div = e.target.cloneNode(false)
// 获取当前节点的位置
const { top, left, width, height } = e.target.getBoundingClientRect()
// absolute是相对于最近的且不是static定位的父元素来定位,所以一定要控制好
// 如果你有拖拽区域限制一定要注意上面一条以及设置好overflow
obj_div.style.position = 'absolute'
obj_div.style.top = top + 'px'
obj_div.style.left = left + 'px'
obj_div.innerText = e.target.innerText
document.body.appendChild(obj_div)
// 记录下块状的宽高,方便之后计算位置
obj_height = height
obj_width = width
// 隐藏原节点
e.target.classList.add('hidden')
document.ontouchmove = (e_2) => {
// 直接取鼠标位置减去一半的宽高,这样拖拽元素一直以鼠标为中心点
// 和别的处理方式不一样这点是因为我个人有强迫症,这里可以像别人一样前面计算相对位置,也不麻烦
obj_div.style.top = e_2.targetTouches[0].clientY - height / 2 + 'px'
obj_div.style.left = e_2.targetTouches[0].clientX - width / 2 + 'px'
// 因为这个是拖拽过程中是一直在触发,所以加一个节流减少dom操作
throttle(() => {
showDragShadow(obj_div, i)
}, 200)
}
document.ontouchend = (e_3) => {
// 判断是否是一次成功的拖拽
if (!checkVaildDrag(obj_div)) {
e.target.classList.remove('hidden')
}
// 删掉移动中加入的元素
obj_div.parentNode.removeChild(obj_div)
drag_flag = false
// 清掉数据监听
document.ontouchmove = null
document.ontouchend = null
}
}
// 节流
function throttle(func, delay) {
if (!this.timer) {
this.timer = setTimeout(() => {
// 只有当它处于拖拽过程中才执行方法
if(drag_flag) func()
this.timer = null
}, delay)
}
}
// 显示阴影
function showDragShadow(obj_div, i) {
// 拖拽元素的位置
const {
left = 0,
right = 0,
top = 0,
bottom = 0
} = obj_div.getBoundingClientRect()
const elements = document.getElementsByClassName('goal-dom')
Array.prototype.forEach.call(elements, (element, index) => {
// 如果已经有值
if (element.value) return
// 目标dom的位置
const position = element.getBoundingClientRect()
// 用中心点计算
// (left + right) / 2表示拖拽元素的x方向中心
// (top + bottom) / 2表示拖拽元素的y方向中心
if (
(left + right) / 2 >= position.left &&
(left + right) / 2 <= position.right &&
(top + bottom) / 2 >= position.top &&
(top + bottom) / 2 <= position.bottom
) {
// 显示阴影
element.innerText = i
element.style.opacity = 0.5
element.shadow = true
} else if (element.shadow) {
// 去掉阴影
element.innerText = ''
element.style.opacity = 1
element.shadow = false
}
})
}
function removeShadow() {
const elements = document.getElementsByClassName('goal-dom')
Array.prototype.forEach.call(elements, (element, index) => {
// 如果已经有值
if (element.value) return
if (element.shadow) {
// 投影样式,按照个人要求修改
element.innerText = ''
element.style.opacity = 1
element.shadow = false
}
})
}
function checkVaildDrag (obj_div) {
//去掉所有的阴影
removeShadow()
let drag_in_flag = false
const {
left = 0,
right = 0,
top = 0,
bottom = 0
} = obj_div.getBoundingClientRect()
const elements = document.getElementsByClassName('goal-dom')
Array.prototype.forEach.call(elements, (element, index) => {
const position = element.getBoundingClientRect()
if (
(left + right) / 2 >= position.left &&
(left + right) / 2 <= position.right &&
(top + bottom) / 2 >= position.top &&
(top + bottom) / 2 <= position.bottom
) {
// 成功拖进去的方法(因为我这里就是个demo,所以就只是单纯把数值写了进去)
drag_in_flag = true
element.innerText = obj_div.innerText
element.value = obj_div.innerText
}
})
return drag_in_flag
}
</script>
<style type="text/css">
body {
padding: 40px;
text-align: center;
}
.drag-dom {
display: inline-block;
width: 100px;
height: 100px;
line-height: 100px;
font-size: 40px;
border: 1px solid;
}
.goal-part {
margin-top: 200px;
}
.goal-dom {
display: inline-block;
width: 110px;
height: 110px;
line-height: 110px;
color: #ffffff;
font-size: 40px;
vertical-align: top;
}
.goal-dom .drag-dom {
color: #ffffff;
}
.drag-dom.drag1 {
color: darkorange;
}
.drag-dom.drag2 {
color: darkseagreen;
}
.goal-dom:nth-child(1) {
background-color: darkorange;
}
.goal-dom:nth-child(2) {
background-color: darkseagreen;
}
.hidden {
visibility: hidden;
}
</style>
</body>
</html>
很简单的demo~~可以自己手撸玩一下~~~
同样的,留一个思考题,我只做了从拖拽区到目标区(才不是因为懒),可以在上面的基础上做: 1.双向的(即也可以从目标区拽出去到拖拽区) 2.还可以做不限制返回原位的(demo目前是无效区域会回到原状) ......
可以做的还很多~~
不仅原创,每次为了写博客专门手撸demo我真的是个良心小博主了呜呜呜,说这么多就是为了要个赞,爱你们哟~~