前端必学的flip动画思想

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

相信大家在用Vue的时候,一定用过他的transition-group组件。在该组件下方可以看到这么一句话

这个看起来很神奇,内部的实现,Vue 使用了一个叫 FLIP 简单的动画队列,使用 transforms 将元素从之前的位置平滑过渡新的位置。,我们将之前实现的例子和这个技术结合,使我们列表的一切变动都会有动画过渡。

和一个特别特别酷炫的动画效果

dfx1w-f5cnr.gif

下面,跟我一起走进Flip动画的奇妙世界

前置知识

getBoundingClientRect

通过dom.getBoundingClientRect(),可以得到某个元素在屏幕上的矩形区域

const rect = dom.getBoundingClientRect(); // 获取矩形区域
rect.left; // 获取矩形区域的left值
rect.top; // 获取矩形区域的top值
复制代码

transform

transform是css3提供的属性,含义为变形或变换

css3提供了多种变换方式,包括平移、旋转、倾斜、缩放,还包括更加具有通用性的矩阵变换

所有变换,均不会影响真实布局位置,只是影响最终的视觉效果

animate api

Element 接口的animate()方法是一个创建新Animation的便捷方法,将它应用于元素,然后运行动画。它将返回一个新建的 Animation 对象实例

使用animate api实现动画非常简单,仅需要通过下面的代码即可实现

dom.animate(
  [
    { /* 起始css属性 */ },
    { /* 结束css属性 */ },
  ],
  {
    duration: 800, // 完成动画的时间
  }
);
复制代码

其他API请看MDN文档

Flip思想

Flip是一种动画思路,专门针对上述场景

它由四个单词组成,分别是:

  • First
  • Last
  • Invert
  • Play

具体过程如下

image.png

在代码实现上,可以遵循以下结构实现动画效果

// ① Frist
record(container); // 记录容器中每个子元素的起始坐标
// 改变元素顺序
change();
// ② Last + ③ Invert + ④ Play
move(container); // 让元素真正实现移动
复制代码

实现

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    * {
        margin: 0;
        padding: 0;
    }

    .btns {
        text-align: center;
    }

    .btns button {
        margin: 0 1em;
        outline: none;
        border: none;
        background: #579ef8;
        color: #fff;
        padding: 7px 10px;
        border-radius: 5px;
        cursor: pointer;
    }

    .btns button:hover {
        opacity: 0.8;
    }

    .container {
        width: 500px;
        overflow: hidden;
        margin: 20px auto;
        display: flex;
        flex-wrap: wrap;
    }

    .item {
        width: 50px;
        height: 50px;
        box-sizing: border-box;
        text-align: center;
        background: #eef5fe;
        border: 1px solid #ddebfd;
        line-height: 50px;
        margin: 5px;
    }
</style>

<body>
    <div class="btns">
        <button id="sort">随机排序</button>
    </div>
    <div class="container">
        <div class="item">1</div>
        <div class="item">2</div>
        <div class="item">3</div>
        <div class="item">4</div>
        <div class="item">5</div>
        <div class="item">6</div>
        <div class="item">7</div>
        <div class="item">8</div>
        <div class="item">9</div>
        <div class="item">10</div>
        <div class="item">11</div>
        <div class="item">12</div>
        <div class="item">13</div>
        <div class="item">14</div>
        <div class="item">15</div>
        <div class="item">16</div>
        <div class="item">17</div>
        <div class="item">18</div>
        <div class="item">19</div>
        <div class="item">20</div>
        <div class="item">21</div>
        <div class="item">22</div>
        <div class="item">23</div>
        <div class="item">24</div>
        <div class="item">25</div>
        <div class="item">26</div>
        <div class="item">27</div>
        <div class="item">28</div>
        <div class="item">29</div>
        <div class="item">30</div>
        <div class="item">31</div>
        <div class="item">32</div>
        <div class="item">33</div>
        <div class="item">34</div>
        <div class="item">35</div>
        <div class="item">36</div>
        <div class="item">37</div>
        <div class="item">38</div>
        <div class="item">39</div>
        <div class="item">40</div>
        <div class="item">41</div>
        <div class="item">42</div>
        <div class="item">43</div>
        <div class="item">44</div>
        <div class="item">45</div>
        <div class="item">46</div>
        <div class="item">47</div>
        <div class="item">48</div>
        <div class="item">49</div>
        <div class="item">50</div>
    </div>

<script>
    const container = document.querySelector('.container')
    function change() {
        const childrens = [...container.children]
        for(let i = 0, l = childrens.length; i < l; i ++) {
            const children = childrens[i]
            const j = Math.floor(Math.random() * l)
            if (i !== j) {
                // 获取当前dom的下一个元素
                const inextDom = children.nextElementSibling
                // 把i插入j之前
                container.insertBefore(children, childrens[j])
                // 把下标j的元素插入到i元素之前
                container.insertBefore(childrens[j], inextDom)
            }
        }
    }
    sort.onclick = () => {
        record(container)
        change()
        move(container)
    }

    function record(container) {
        for(let i = 0, len = container.children.length; i < len; i ++) {
            const dom = container.children[i]
            const rect = dom.getBoundingClientRect()
            dom.startX = rect.left
            dom.startY = rect.top
        }
    }

    function move(container) {
        for(let i = 0, len = container.children.length; i < len; i ++) {
            const dom = container.children[i]
            const rect = dom.getBoundingClientRect()
            const curX = rect.left, curY = rect.top
            dom.animate([
                { transform: `translate(${dom.startX - curX}px, ${dom.startY - curY}px)` },
                { transform: `translate(0px, 0px)` }
            ], { duration: 600 })
        }
    }
</script>
</body>

</html>
复制代码

以上就是所有代码了,可以在控制台看到,不会出现style标签,非常的神奇。

结语

FLIP 不光可以做位置变化的动画,对于透明度,大小等等都可以很轻松的实现。

Flip非常有用,可以实现在任何需要动画的地方

希望这篇文章对你们有一定的收获,感谢!

猜你喜欢

转载自juejin.im/post/7016912165789515783