Skip to content

Instantly share code, notes, and snippets.

@uinz
Last active March 29, 2018 13:22
Show Gist options
  • Select an option

  • Save uinz/39a42ee438032ee2ecb82c540d24452a to your computer and use it in GitHub Desktop.

Select an option

Save uinz/39a42ee438032ee2ecb82c540d24452a to your computer and use it in GitHub Desktop.
所谓FLIP

首先我是被这个动画效果吸引到的, 效果好棒

说用到 FLIP (First, Last, Invert, Play) 不要被英文单词吓到, 原理可能比你想象的简单

虽然它很简单, 但也是为了解决一些问题才提出来的.

这个FLIP 要做的事情呢, 就是提高动画的帧率 但是呢, 浏览器这个东西吧, 就是比较矫情, 主要有一下2个特点

  1. 浏览器 transition 对 opacity transform 的动画会采用 GPU 加速
  2. top left width height 等 属性没有GPU加速, 也就是容易卡.

所以 FLIP 能就是想用有 GPU加持的属性 来模拟那些没有GPU加持的属性

分为以下几部

F: 获取初始位置数据

感谢getBoundingClientRect 这个API 可以轻松获取到 x y left top left bottom right

L: 设置终点位置

 直接加class等一些列方法,把元素直接变成你想要的样子吧. 但是这个时候不要有 animation 然后还是使用 getBoundingClientRect 获取 数据 x y left top left bottom right

I: 翻转

重点就是这里了, 通过 F 和 L 拿到 两个状态的 x y left top left bottom right

使用这2份数据 就可以使用 transilate scale 等一系列支持GPU加速的属性来描述这个动画过程了

不过呢, 和正常的动画过程不一样, 这个动画过程是要翻过来写的.

举个例子:

  • F: A 元素本来 left = 0px
  • L: 然后忽然设置成了 left = 100px (A 元素被瞬间移动了100px)
  • I: 我不能让你这么突兀的直接过来呀, 我要使用 transform 属性把你 translateX(-100px),    虽然你 left = 100px 但是 你的 translateX(-100px) 抵消掉了你还是你的位置.

 - P: 好了, 动起来吧, 给A加上 transition: 300ms, 再把 transform 清空, 执行动画吧(translateX(-100px) -> translateX(0px)). 这个时候 A元素 就会从 视觉 left = 0 以 300ms 的时间移动到 left = 100px 的位置.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>DEMO</title>
</head>

<body>
  <style>
    #demo {
      width: 100px;
      height: 100px;
      background-color: #ccc;
      position: absolute;
      top: 0;
      left: 0;
    }

    #demo.big {
      width: 300px;
      height: 500px;
    }
    #demo.move {
      left: 300px;
      top: 100px;
    }

    .on-transition {
      transition: .3s;
    }
  </style>

  <div id="demo">
  </div>

  <script>
    document.onclick = () => {
      const el = document.getElementById('demo')
      const first = el.getBoundingClientRect();
      el.classList.add('big');
      el.classList.add('move');
      const last = el.getBoundingClientRect();
      const dy = first.top - last.top
      const dx = first.left - last.left
      const rx = first.width / last.width
      const ry = first.height / last.height

      el.style.transform = `translate(${dx}px, ${dy}px) scaleX(${rx}) scaleY(${ry})`;
      el.style.transformOrigin = `${rx}% ${ry}%`;

      // 等到下一帧,也就是其他所有的样式都已经被应用
      requestAnimationFrame(function () {
        el.classList.add('on-transition')

        el.style.transform = '';
      });
      // 结束时清理
      el.addEventListener('transitionend', () => console.log(1));
    }

  </script>
</body>

</html>

所以其实很简单, 总结起来一句话, 拿到2个状态的数据, 然后通过支持GPU 的属性让移动后的元素保持为移动的状态. 最后再释放, 让他动起来

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment