本篇文章是GSAP系列的一篇文章,关于web动画,目前我已经发布了以下文章:
web动画篇:
- 用CSS做出漂亮的字体动画
- HTML动画的那些事-车轮旋转篇
GSAP篇:
- GSAP(GreenSock):最健全的web动画库之一
- GSAP动画插件-ScrollTrigger(一)
其中web动画也可能会用到一定的GSAP动画库的知识。
GSAP是一个非常优秀的动画库,但是不知道为什么中文资料非常少,几乎很少有人会提到GSAP,并且它拥有非常宽裕的使用协议,几乎大部分商业用途都可以免费使用,它的英文文档也非常健全,可以说只要是你见过的web动画,都可以使用GSAP进行实现。
今天的主角是GSAP中的Flip Plugin插件,该插件主要用途是使一个DOM能够平滑的从一个状态转变成另一个状态,具体是什么意思呢?看下面的动图就可以明白:
这不是一个视频,就是官方给出的一个使用Flip Plugin插件做的一个案例。
我们尝试去掉Flip Plugin插件所生成的效果,看看会有什么样的展示:
是不是黯然失色,完全没有过渡动画。
其实说白了Flip这个插件就是实现过渡动画,和CSS属性中的transition
有几分相似之处,不过transition
要实现过渡有着许多的限制,比如必须要指定需要过渡的属性值。
1. Flip.getState()
Flip.getState( targets:String | Element | Array, vars:Object ) : Object
捕获需要操作的目标尺寸、倾斜角度、旋转程度、不透明度、目标在视口中的位置这一系列的信息,后面可以接一个vars
对象,表示额外需要捕捉的信息。
作用就是让Flip知道应该操作哪个目标,以及记录它们的信息,在后面自动填充相关的过渡动画。
2. 几个属性
在Flip插件中,有几个非常重要的属性,接下来一一进行讲解。
2.1 scale
通常情况下,是通过设置元素的宽高来实现元素的缩放,如果你想要更好的动画效果或者更好的性能,可以设置scale: true
,它可以通过元素的scale属性来控制元素的大小。
2.2 absolute
在Flex和Grid布局中,如果过渡动画看起来比较生硬,可以设置absolute: true
。
设置前:
设置后:
2.3 nested
类型:Boolean
。
如果需要过渡的元素上有嵌套的子级元素,设置该属性为true
则可以保证嵌套的子级元素的过渡动画也会正常进行显示。
未设置该属性:
设置该属性:
2.4 spin
值类型:Boolean | Number | Function
。
如果该值为true
则在元素的过渡中会额外旋转360°,也可以用数字来表示旋转方向-1
会反向旋转一次,而1
为正向旋转。
当然也可以通过一个函数来控制旋转方向,比如:
Flip.from(state, {
spin: (index, target) => {
if (target.classList.contains("clockwise")) {
return 1;
} else if (target.classList.contains("counter-clockwise")) {
return -1;
} else {
return 0;
}
}
});
2.5 zIndex
该属性是用来设置你需要过渡元素的z-index
属性,因为有时候因为各种原因,导致过渡的时候元素没有表现在最顶层,被其它元素遮挡了一部分,这个时候你就可以通过设置该属性来将元素调整到最顶层。在过渡开始时会立即设置z-index
,而过渡结束后z-index
会被复原。
3. 官方示例的完整代码
这是文章开始时的,那个动画的完整代码,因为动画这玩意使用文字描述起来非常的不容易,所以放出完整的代码可能比较直观,重要的地方我都做了注释。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<meta content="ie=edge" http-equiv="X-UA-Compatible" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/gsap.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/Flip.min.js"></script>
<title>Flip Plugin</title>
<style>
* {
box-sizing: border-box;
}
body {
background: black;
padding: 0;
margin: 0;
font-family: "Signika Negative", sans-serif, Arial;
font-weight: 300;
height: 100vh;
overflow: hidden;
}
.container {
display: flex;
height: 100%;
width: 100%;
justify-content: center;
align-items: center;
overflow: hidden;
}
.container.grid,
.container.columns {
align-content: stretch;
align-items: stretch;
flex-wrap: wrap;
}
.letter {
text-align: center;
color: black;
font-size: 10vmax;
font-weight: 400;
display: flex;
align-items: center;
justify-content: center;
padding: 2px 6px;
}
.container.grid .letter {
flex-basis: 50%;
}
.container.columns .letter {
flex-basis: 25%;
}
.for,
.gsap {
font-size: 5vmax;
color: white;
}
.for {
padding: 2px 1.6vmax;
font-weight: 300;
display: none;
}
.gsap {
padding: 2px 0;
font-weight: 600;
display: none;
}
.container.final .for,
.container.final .gsap {
display: block;
}
.F {
background: rgba(0, 188, 212, 0.7);
}
.l {
background: rgba(40, 150, 255, 0.7);
}
.i {
background: rgba(153, 80, 220, 0.7);
}
.p {
background: rgba(90, 108, 225, 0.7);
}
.container.plain .letter {
background: transparent;
color: white;
padding: 0;
}
</style>
</head>
<body>
<div class="container final">
<div class="letter F">F</div>
<div class="letter l">l</div>
<div class="letter i">i</div>
<div class="letter p">p</div>
<div class="for">for</div>
<div class="gsap">GSAP</div>
</div>
<script>
gsap.registerPlugin(Flip);
let layouts = ["final", "plain", "columns", "grid"],
container = document.querySelector(".container"),
curLayout = 0; // 现在动画布局
function nextState() {
// 获取元素信息
const state = Flip.getState(".letter, .for, .gsap", {
props: "color,backgroundColor",
simple: true,
});
container.classList.remove(layouts[curLayout]); // 移除旧的类名
curLayout = (curLayout + 1) % layouts.length; // 增量,如果在末尾,则回到起点,即实现动画循环
container.classList.add(layouts[curLayout]); // 增加新的类名,使元素动起来
Flip.from(state, {
absolute: true, // 在过渡动画运行的过程中,将元素的position设置为true
stagger: 0.07, // 错开属性,让一个元素的动画相对于前一个元素有延迟
duration: 0.7, // 持续时间
ease: "power2.inOut", // 运动轨迹
spin: curLayout === 0, // 控制元素是否旋转
simple: true, // 设置为true时,就不再监听 缩放、旋转、倾斜属性,一般情况下不建议开启,因为性能差别不大
// 对其它元素设置单独动画
onEnter: (elements, animation) =>
gsap.fromTo(
elements,
{ opacity: 0 },
{ opacity: 1, delay: animation.duration() - 0.1 }
),
// 对其它元素设置单独动画
onLeave: (elements) => gsap.to(elements, { opacity: 0 }),
});
// 为0的时候则表示播放完成
gsap.delayedCall(curLayout === 0 ? 3.5 : 1.5, nextState);
}
// gsap.delayedCall相当于专门针对动画的setTimeout。
gsap.delayedCall(1, nextState);
</script>
</body>
</html>
4. 最后
至于该插件更多的用法可以参考:Flip插件文档。
Flip Plugin插件使用起来并不难,就那么几个api,但是将这几个api正确使用,创造出炫酷的动画就是一个比较困难的事情。
所以HTML中动画的制作是一件非常耗时的过程,同时也非常考验一个开发者对于动画的积累程度,因为大多数关于动画的CSS属性在我们平时编写网页的过程中都不会用到。
所以平时积累HTML动画相关的知识就显得尤为重要,万一哪一天你要开发的某个网页就会用到这些知识呢。