vue之flip动画解析

<template>
  <div>
    <ul class="container">
      <li v-for="item in base" :key="item.id" ref="lis">
        <div class="item" :style="'backgroundColor: ' + item.c"></div>
      </li>
    </ul>
    <el-button @click="sort">排序</el-button>
  </div>
</template>

<script>
function getColor() {
  const random = () => parseInt(Math.random() * 256)
  return `rgb(${random()},${random()},${random()})`
}

const base = Array.from({ length: 64 }, (_, i) => {
  return {
    id: i + 1,
    c: getColor()
  }
})

// 上面的内容应该是不需要解释的, 无非就是生成一些数据, 并且进行渲染

export default {
  data() {
    return {
      base
    }
  },
  methods: {
    // 排序
    sort() {
      const lis = this.$refs.lis
      // 排序之前, 获取当前的 64 个小盒子的 位置 top和left值
      const oldRect = this.getRect(lis)

      // 通过遍历的方式, 拷贝当前的数据
      const newBase = []
      this.base.map((i) => {
        newBase.push({
          id: i.id,
          c: i.c
        })
      })
      // 再创建一个数组, 用来装乱序后的数据
      const randomBase = []
      while (newBase.length) {
        const index = parseInt(Math.random() * newBase.length)
        randomBase.push(newBase[index])
        newBase.splice(index, 1)
      }

      // 把乱序后的数据, 赋值给base
      this.base = randomBase

      // 这里我的理解是 vNode 已经更新完了, 但是没有映射到 Dom 中的时刻
      this.$nextTick(() => {
        // 获取上面的 lis 更新后的位置
        const newRect = this.getRect(lis)

        lis.map((i, index) => {
          // 计算乱序后的每个方块的位置 相对于之前的位置的 的 top 值和 left 值的变化
          const x = oldRect[index].top - newRect[index].top
          const y = oldRect[index].left - newRect[index].left
          // javascript 设置动画帧 就是使用transform 和 translate 进行位移操作
          const step = [
            {
              transform: `translate(${y}px, ${x}px)`
            },
            {
              transform: 'translate(0)'
            }
          ]
          // 播放动画
          i.animate(step, 600)
        })
      })
    },

    // 获取矩阵位置 返回矩阵每个单元 相对于当前屏幕的 top 和 left 值
    getRect(doms) {
      return doms.map((i) => {
        const info = i.getBoundingClientRect()
        return {
          top: info.top,
          left: info.left
        }
      })
    }
  }
}
</script>

<style lang='scss' scoped>
.container {
  display: grid;
  grid-template-columns: repeat(8, 80px);
  grid-template-rows: repeat(8, 80px);

  li {
    justify-self: center;
    align-self: center;

    .item {
      width: 60px;
      height: 60px;
      border: 1px solid #333;
    }
  }
}
</style>

上面是一个 VUE 组件的 完整 案例

效果 参考 https://cn.vuejs.org/v2/guide/transitions.html#%E5%88%97%E8%A1%A8%E7%9A%84%E6%8E%92%E5%BA%8F%E8%BF%87%E6%B8%A1

<a herf="https://cn.vuejs.org/v2/guide/transitions.html#%E5%88%97%E8%A1%A8%E7%9A%84%E6%8E%92%E5%BA%8F%E8%BF%87%E6%B8%A1" ></a>

posted @ 2022-02-11 10:49  深海里的星星i  阅读(335)  评论(0)    收藏  举报