列表排序应用FLIP动画(vue)

效果

在这里插入图片描述

原理详解

链接

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 = ++nextId
    item.parentId = root
    item.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.length
    for (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.id
      item.label = `label${
      
      item.id}`
      item.value = `value${
      
      item.id}`
      item.level = i
      parent.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.left
        transY = firstRect.top - lastRect.top
        d.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 = this
      this.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>

猜你喜欢

转载自blog.csdn.net/weixin_43954962/article/details/124189484