前端必学的flip动画思想
前言
相信大家在用Vue的时候,一定用过他的transition-group组件。在该组件下方可以看到这么一句话
这个看起来很神奇,内部的实现,Vue 使用了一个叫 FLIP 简单的动画队列,使用 transforms 将元素从之前的位置平滑过渡新的位置。,我们将之前实现的例子和这个技术结合,使我们列表的一切变动都会有动画过渡。
和一个特别特别酷炫的动画效果
下面,跟我一起走进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
具体过程如下
在代码实现上,可以遵循以下结构实现动画效果
// ① 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非常有用,可以实现在任何需要动画的地方
链接:https://juejin.cn/post/7016912165789515783