效果
原理详解
链接
1.beforeUpdate 获取first 变化前位置 (以id建立map映射)
2.updated 获取变化后位置 last
3.禁用transition并transform元素回初始位置
4.异步恢复transition 并取消 transform
代码
<template><div ref="container"><div style="display: flex"><div style="width:400px"><!--仅展示三级数据,若需递归自行修改--><div class="item" :id="i.value" v-for="i in locArr" :key="i.value"><div>{{i.label}}</div><div class="item" :id="j.value" v-for="j in i.children" :key="j.value"><div>{{j.label}}</div><div class="item" :id="k.value" v-for="k in j.children" :key="k.value"><div>{{k.label}}</div></div></div></div></div><div style="padding: 10px"><button @click="shuffle">重新随机数据</button><button @click="shuffleOne">打乱一级顺序</button><button @click="shuffleTwo">打乱二级顺序</button></div></div></div>
</template><script>
/*
1.beforeUpdate 获取first 变化前位置 (以id建立map映射),
2.updated 获取变化后位置 last ,
3.禁用transition并transform元素回初始位置,
4.异步恢复transition 并取消 transform
*//*** 乱序数组*/
Array.prototype.shuffle = function () {let input = this;for (let i = input.length - 1; i >= 1; i--) {let ri = ~~(Math.random() * (i + 1));input[i] = [input[ri], input[ri] = input[i]][0];}return input;
};/*** 随机多级数据* @param root* @param len* @param level*/
const treeOptions = ({ root = '0', len = 40, level = 4 }) => {let levelPoolLen = level, originPoolLen = len - levelPoolLen//创建 level 个 LevelPool ,并为每个 LevelPool 初始化一个元素let levelPool = []for (let i = 1; i <= levelPoolLen; i++) {levelPool.push([{ id: '', parentId: root, label: '', value: '' }])}//初始化指定数量的元素并随机丢入 LevelPool 中for (let i = 0; i < originPoolLen; i++) {let currLevel = ~~(Math.random() * level)levelPool[currLevel].push({ id: '', parentId: root, label: '', value: '' })}let nextId = 0//由前到后依次遍历LevelPool,遍历本级池中各项并随机从前一级池中选取一项作为当前项的父级for (let i = 0, item; (item = levelPool[0][i]) != null; i++) {item.id = ++nextIditem.parentId = rootitem.label = `label${item.id}`item.value = `value${item.id}`item.level = 0}for (let i = 1; i < levelPoolLen; i++) {let prevLevelPool = levelPool[i - 1]let prevLevelPoolLen = prevLevelPool.lengthfor (let j = 0, item; (item = levelPool[i][j]) != null; j++) {//随机父节点let parent = prevLevelPool[~~(Math.random() * prevLevelPoolLen)]if (!parent.children) {parent.children = []}item.id = `${parent.id}-${parent.children.length}`item.parentId = parent.iditem.label = `label${item.id}`item.value = `value${item.id}`item.level = iparent.children.push(item)}}//将一级levelPool合并输出return levelPool[0]
}let firstMap = [], last = []
export default {name: "FLIP",data() {return {locArr: treeOptions({ level: 3, len: 40 })}},beforeUpdate() {this.$refs.container.querySelectorAll('.item').forEach(d => firstMap[d.id] = d.getBoundingClientRect())},updated() {let lastRect, firstRect, transX, transY, $lastNodes = this.$refs.container.querySelectorAll('.item')$lastNodes.forEach(d => {lastRect = d.getBoundingClientRect()firstRect = firstMap[d.id]if (firstRect) {transX = firstRect.left - lastRect.lefttransY = firstRect.top - lastRect.topd.style.transition = 'none'd.style.transform = `translate3D(${transX}px,${transY}px,0) `}})setTimeout(_ => {$lastNodes.forEach(d => {d.style.transition = ''d.style.transform = ''})})},methods: {shuffle() {// this.locArr[1].children.push( this.locArr.shift())this.locArr = treeOptions({ level: 3, len: 40 })},shuffleOne() {this.locArr = [...this.locArr.shuffle()]},shuffleTwo() {const vm = thisthis.locArr.forEach((d, index) => {if (d.children) {d.children = [...d.children.shuffle()]}})}}
}
</script><style scoped>.item {transition: all 1s;margin-left: 20px;}.item>div:not(.item){padding:5px;border-radius:2px;background: #fff;box-shadow: #999999 2px 2px 5px 1px;}button{display: block;margin: 10px;cursor: pointer;}
</style>