[vue] vue3封装clip动画, 实现元素的国度效果

import { nextTick } from "vue";

// 数据类型
function getDataType() {
  return Object.prototype.toString.call(arguments[0]).slice(8, -1).toLowerCase();
}

/**
 * 
 * @param {*} els 单元素节点 或者 元素节点集合
 * @param {*} fn 数据变更的函数,通过调用函数导致外部数据变更
 * @param {*} duration 动画执行时间,默认500ms
 */
export default function (els, fn, duration = 500) {
  // 整合成数组格式
  let originList = [];
  if (getDataType(els) === "htmlcollection") {
    originList = [...els]
  } else {
    originList = [els]
  }
  // 生成原始位置
  const originPositions = [];
  originList.forEach(el => {
    const info = el.getBoundingClientRect()
    originPositions.push({
      top: info.top,
      left: info.left,
      width: info.width,
      height: info.height,
      bottom: info.bottom,
      right: info.right,
      x: info.x,
      y: info.y,
    })
  })

  // 执行函数
  fn && fn()
  nextTick(() => {
    // 页面更新后获取最新位置
    const newPositions = [];
    originList.forEach(el => {
      const info = el.getBoundingClientRect()
      newPositions.push({
        top: info.top,
        left: info.left,
        width: info.width,
        height: info.height,
        bottom: info.bottom,
        right: info.right,
        x: info.x,
        y: info.y,
      })
    })

    // 计算移动距离
    const movePositions = [];
    originPositions.forEach((origin, index) => {
      const newPosition = newPositions[index]
      const moveX = newPosition.left - origin.left
      const moveY = newPosition.top - origin.top
      const newWidth = newPosition.width
      const newHeight = newPosition.height
      movePositions.push({
        moveX,
        moveY,
        newWidth,
        newHeight,
      })
    })

    // 做动画
    originList.forEach((el, index) => {
      const moveX = movePositions[index].moveX
      const moveY = movePositions[index].moveY
      el.animate([
        {
          transform: `translate(${-moveX}px, ${-moveY}px)`,
          width: `${originPositions[index].width}px`,
          height: `${originPositions[index].height}px`,
        },
        {
          transform: `translate(0, 0)`,
          width: `${movePositions[index].newWidth}px`,
          height: `${movePositions[index].newHeight}px`,
        }
      ], duration)
    })
  })
}

 

解释: js也能做相当于css的动画, 用元素的animate方法

css的写法等同于用js操作dom的css

el.animate([
{css},
{css}
])

核心原理是: 知道元素移动的终点位置(endx,endy), 和元素的起点位置 (startx, starty)

假设:  我们现在处于元素  最终  所在的位置 (endx,endy), 然后通过transform...等手段, 将元素变更到 (startx, starty)位置  (这时候和没移动之前的位置是一样的)

然后通过animate执行动画, 位置就从 (startx, starty) `还原` 到 (endx, endy), 形成动画

 

在用户看来, 元素是从起点慢慢移动到终点的,  对于程序而言, 元素立即就移动到了终点, 通过 css 变化位置到了起点,慢慢还原

 

vue的nextTick提供了很好的 虚拟dom支持, 在界面没有渲染之前, 我么就能通过虚拟dom拿到元素变更后的位置信息, 更容易和 animate 结合使用

 

如果是原生html想做clip动画, 就需要通过其他方法计算提前计算出终点的位置, 然后运用动画

 

posted @ 2024-09-30 15:24  深海里的星星i  阅读(3)  评论(0编辑  收藏  举报