效果图
效果DEMO
全部代码
HTML
<ul class="box" id="box">
<li class="rect" data-index="01">01</li>
<li class="rect" data-index="02">02</li>
<li class="rect" data-index="03">03</li>
<li class="rect" data-index="04">04</li>
<li class="rect" data-index="05">05</li>
<!-- 一共36个为了减少篇幅只写几个个 -->
</ul>
CSS
.box {
width: 336px;
height: 336px;
box-shadow: 0 0 3px pink;
list-style: none;
padding: 0;
}
.rect {
width: 50px;
height: 50px;
box-shadow: 0 0 3px orange inset;
margin: 3px;
float: left;
cursor: pointer;
line-height: 50px;
text-align: center;
}
JavaScript
var rectArr = Array.from(document.getElementsByClassName('rect'));
var box = document.getElementById('box')
var delay = 300
box.addEventListener('click', function(e){
// 如果点击的box本身就不往下执行
if(e.target === box) return
// 获取当前dom index, 当然你也可以不通过data-index 保存的方式获取,不过下面的方法就要稍微调整一下
let index = e.target.getAttribute('data-index')
// 取出要移动的DOM
var moveArr = rectArr.splice(index, rectArr.length)
// 保存要移动的DOM 未移动前的位置
moveArr.forEach(item => {
item.preLeft = item.offsetLeft
item.preTop = item.offsetTop
})
// 删除dom 删除后后续DOM 位置立即变化
box.removeChild(e.target)
// 给移动后的DOM 位移置之前的位置
moveArr.forEach(item => {
item.style.transform = `translate(${item.preLeft - item.offsetLeft}px,${item.preTop - item.offsetTop}px)`
})
// 设置一点延迟给DOM 渲染反应时间,如果不设置自行试试会出现什么问题吧
// 添加过渡,并位移回dom 新位置
setTimeout(() => {
moveArr.forEach(item => {
item.style.transition = `transform ${delay}ms ease`
item.style.transform = `translate(0, 0)`
})
}, 1)
// 位移完 删除过渡以及位移参数, 删除了下次才能正常位移
setTimeout(() => {
moveArr.forEach(item => {
item.style.transition = ``
item.style.transform = ''
})
}, delay + 1)
// 前面通过 splice 删除掉了原有数组中将要移动的 dom,现在还原。
// 有个问题就是虽然删除了DOM,但是还保存再来数组中,每次循环计算都是以36的数量计算
rectArr = [...rectArr, ...moveArr]
})
方法原理解释
删除DOM 后,后续DOM 立马出现在新的位置不会出现过渡,因此我们手动加过渡。
1 删除之前保存好未移动之前的位置
2 删除之后把位置重新赋值给移动的元素(这里通过位移的方式,简单计算一下就行)
3 赋值完成后 再 添加过渡属性 以及重新赋值 位移(因为只有这里添加了过渡属性,所以这个位移才会过渡,其他的看不出效果)
4 位移完成删除过渡属性
所以结论就是,其实这里过渡就是,从当前位置移动到目标位置(没过渡效果),再从目标位置移回来(没过渡效果),最后再移回目标位置(有过渡效果,就是页面中看到的效果)。
结语
发这篇文章的起因其实是在 Stack Overflow 上面回答别人的问题,说实话,我比较常用 Vue 。而它里面有过渡组件,也有例子,基本上不需要我考虑具体的过渡实现,看到这个同行问的问题,并且他要原生代码写的,刚开始我通过审查元素的方式查看DOM 是如果变化的,最后找了一个例子,扒了一下源码才发现具体的实现方式,也总算知道为啥我审查元素打断点看不到了,因为他前两个位移没过渡,最后一个位移才有,而打断点只能看到最后一个。基本上这种操作的过渡都是这种思路,希望大家能够很好的理解,好了就这样啦。
以上信息如有疏漏或错误,欢迎指正