FLIP是什么

2022/1/22 CSSJavaScript

# 概念

FLIP是一种可以高效为元素位置、尺寸做动画的技术。

F:First 元素的初始状态 L:Last 元素最终的形态 I:Invert 反转,意思是动画的过程本来是从A到B,现在理解为当前位置是B,上个位置是A P:Play 播放

Vue中的动画的实现原理就是这个 传送门 (opens new window)

# 实现

.square{height: 120px;width: 120px; position: fixed; top: 100px; left: 100px;background-color: silver;}
.position1{height: 180px;width: 90px;top: 200px; left: 170px;}
1
2
function move(dom){
  dom.classList.add('position1')
}
const square = document.querySelector('.square')
const first = square.getBoundingClientRect();
move(square); // 执行位置变换的脚本
const last = square.getBoundingClientRect();
console.log(first, last); // 方法返回元素的大小及其相对于视口的位置。 
const deltaX = first.left - last.left;
const deltaY = first.top - last.top;
const deltaW = first.width / last.width;
const deltaH = first.height / last.height;
console.log(deltaX, deltaY, deltaW, deltaH);
const frames = {
  transformOrigin: ['top left', 'top left'],
  transform: [`translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH})`, 'none'] // 计算初始帧和结束帧
}
const options = {
  duration: 300,
  easing: 'ease-in-out',
  fill: 'both'
}
// Element API: animate(关键帧集合, 动画选项)
// square.animate(frames, options); // 兼容很差: IE、Edge<79、Chrome<59、safari<8、移动端UC、QQ、Baidu不支持
// Element API: animate polyfill
square.animate(frames, options);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 拓展例子

点击页面,方块进入,再次点击,方块位置大小来回切换。

实际生产中 可以在弹窗中使用。

<!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>FLIP</title>
    <style>
        .square{position: fixed;background-color: silver;} /* 初始帧 */
        .frameIn{height: 180px;width: 90px;top: 200px; left: 170px;}
        .frameOut{height: 280px;width: 190px;top: 80px; left: 320px;}
    </style>
</head>
<body>
    <div class="square"></div>
    <script>
        function slibingsFrame(dom, classIn, classOut){
            const options = {
                duration: 300,
                easing: 'ease-in-out',
                fill: 'both'
            }
            const first = square.getBoundingClientRect();
            let isIn = false
            if(dom.classList.value.includes(classIn)){
                dom.classList.remove(classIn)
                dom.classList.add(classOut)
                isIn = false
            }else{
                dom.classList.add(classIn)
                dom.classList.remove(classOut)
                isIn = true
            }
            const last = square.getBoundingClientRect();
            const deltaX = first.left - last.left;
            const deltaY = first.top - last.top;
            const deltaW = first.width / last.width;
            const deltaH = first.height / last.height;
            const framesTo = {
                transformOrigin: ['top left', 'top left'],
                transform: [`translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH})`, 'none']
            }
            dom.animate(framesTo, options)
        }
        const square = document.querySelector('.square')
        document.onclick = function(){
            slibingsFrame(square, 'frameIn', 'frameOut')
        }
    </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 总结

推荐阅读

FLIP作者的 FLIP your animations (opens new window)

使用 FLIP 技术动画布局 (opens new window)

animation polyfill (opens new window)