GreenSock Animation Platform (GSAP) 是一个业界知名的动画库,它被1100多万个网站使用,有超过50%的获奖的网站都是用了它。不管是在原生环境中,还是任意的框架中,你可以使用GSAP去让非常多的东西动起来。不管你是想要去让UI界面产生交互动画,还是SVG图形产生动画,甚至是Threejs还是React组件,GSAP都可以轻松搞定!
创建一个动画
让我们先让一个class为'box'的HTML元素动起来
gsap.to(".box",{x:200})
动画方法 (Methods)
有四种Tween的动画方式
gsap.to() 这是一种最常用的动画,就是让元素从初始状态变化到目标状态。
gsap.from() 有点像to方法的逆向变化,就是让元素从目标状态变化到初始状态。
gsap.fromTo() 需要自己定义两个状态的数据,然后从前一个变化到后一个。
gsap.set() 直接设置成想要的状态,没有任何过度与动画效果。本质上就是duration为0的 .to 方法
目标元素 (target)
接下来我们需要去告诉GSAP去让什么元素变化。GSAP在底层实际上是使用了document.querySelector( )去选择元素,所以你可以用任何css选择器进行元素的选择。或者你也可以直接传入一个DOM元素或者一个数组
// 使用类名或者id名,其实css选择器都可以
gsap.to(".box", { x: 200 });
// 复杂一些的css选择器
gsap.to("section > .box", { x: 200 });
// 一个变量,其实是把获取到的DOM元素直接传进去
let box = document.querySelector(".box");
gsap.to(box, { x: 200 })
// 可以把dom元素放到数组里面一起传入
let square = document.querySelector(".square");
let circle = document.querySelector(".circle");
gsap.to([square, circle], { x: 200 })
对象数据 (variables)
这个对象包含着所有动画变化相关的信息。你可以设置任意的你想要发生变化的属性和值,或者一些特殊的会影响动画过程的一些属性,比如duration(动画时长),onComplete(动画完成时触发事件)或者repeat(动画重复的次数)
gsap.to(target, {
// 传入这样一个对象
// 这里面包含了需要进行变化的各种样式属性
x: 200,
rotation: 360,
// 以及变化过程的属性设置
duration: 2,
})
下面这个是Transform最常用相关的一些变化的属性和值的写法
GSAP | CSS | 作用 |
---|---|---|
x:100 | transform:translateX(100px) | 水平移动 (px或者svg单位) |
y:100 | transform:translateY(100px) | 垂直移动 (px或者svg单位) |
xPercent:-50% | transform:translateX(-50%) | 横向移动 (元素本身宽度的百分比) |
yPercent:-50% | transform:translateY(-50%) | 垂直移动 (元素本身高度的百分比) |
rotation:360 | transform:rotate(360deg) | 旋转多少度 |
scale:2 | transform:scale(2,2) | 元素整体放大或缩小 |
skewX:45 | transform:skewX(45deg) | 元素倾斜 |
transformOrigin:"0% 100%" | transform-origin: 0% 100%; | 设置旋转中心点。这里设置的旋转中心点是元素左下角 |
特别的属性,下面是常用
属性 | 作用 |
---|---|
duration | 动画变化的时长(秒)默认是0.5 |
delay | 动画变化开始前的延迟时长(秒),默认是0.5 |
repeat | 动画的重复次数 设置为-1表示无数次 |
repeatDelay | 重复延迟,重复之间的秒 |
repeatRefresh | 重复刷新 |
yoyo | 如果设置为ture,那么动画会在执行完之后再反向执行一次,就像悠悠球的效果。注意( repeat得设置大于1或者为-1才能看到效果。) 默认是false |
stagger | 是一个时间的设置(秒),如果有多个元素同时要被驱动,那么当这个属性设置了时间的值之后,元素们会被依次逐个驱动,间隔时长就是这个属性设置的时长 |
ease | 动画过渡的运动曲线的设置,默认是"power1.out" |
onComplete | 动画结束时执行的回调函数 |
onStart | 动画开始时调用 |
onUpdata | 每次动画更新时调用(在动画处于活动状态时每帧调用) |
onRepeat | 每次动画重复时调用一次。 |
onReverseComplete | 动画反转后再次到达其起点时调用 |
Timelines 时间线
时间线能让我们创建非常容易调节的、很灵活的顺序动画效果。下面就是一个简单的包含着三个tween动画的timeline实例效果。默认情况下,这些动画是依次添加的,他们在变化的时候也是依次执行,而且是一个执行完之后再下一个执行
// 创建一个Timeline类型的实例
let tl = gsap.timeline()
// 把tween动画添加到timeline实例上,注意我们在用的是tl.to 而不是gsap.to
tl.to(".green", { x: 600, duration: 2 });
tl.to(".purple", { x: 600, duration: 1 });
tl.to(".orange", { x: 600, duration: 1 });
但是,如果我们想要在动画之间加一个停顿或者说间隔改怎么办呢?
let tl = gsap.timeline()
tl.to(".green", { x: 600, duration: 2 });
tl.to(".purple", { x: 600, duration: 1, delay: 1 }); // 在这里延迟1秒执行
tl.to(".orange", { x: 600, duration: 1 });
有一个方式就是我们可以给某个tween动画在启动前提添加一个delay。但是这样的话,一点都不灵活。又或者,如果我们想要实现动画之间在时间上重叠,又或者几个动画同时启动,这些该怎么实现呢?
GSAP Keyframes
如果你发现你写了好几个Tween动画,来让某个元素变化到一个目标状态,那么可能你是需要使用关键帧(KeyFrames)的方式来做了。关键帧动画能让元素的属性变化分段进行,同时又能保持代码的清爽整洁。
比如下面这个代码,同样一个变化效果,我们先用timeline的方式来实现,然后再用关键帧的方式来实现,显然关键帧的方式看起来简洁多了
// timeline
let tl = gsap.timeline();
tl.to(".box", { x: 100 })
.to(".box", { y: 100 })
.to(".box", { x: 0 })
.to(".box", { y: 0 });
// 用数组的方式
gsap.to(".box", {
keyframes: {
x: [0, 100, 100, 0, 0],
y: [0, 0, 100, 100, 0],
ease: "power1.inOut"
},
duration: 2
});
位置参数 Position Parameter
这个参数能帮我们方便的实现执行顺序和执行时间点的精确控制
let tl = gsap.timeline();
// 绿色方块会在整个时间线开始1秒后进行移动
tl.to(".green", { x: 600, duration: 1 }, 1);
// 紫色方块会和之前一个添加的动画同时开始运动
tl.to(".purple", { x: 600, duration: 1 }, "<");
// 橘色方块会在之前所有动画都结束一秒后再开始运动
tl.to(".orange", { x: 600, duration: 1 }, "+=1");
- 绝对时间(秒)的方式,以动画的启动时间点为参考,也就是整个时间线的起始点,比如使用一个数字3
- '<'符号表示前一个添加到时间线上的动画的起始时间点。如果使用这个符号,那么动画会插入到前面一个动画的起始时间点位置
- '>'符号表示前一个添加到时间线上的动画的结束时间点。如果使用这个符号,那么动画会插入到前面一个动画的结束时间点位置
- '+=1' 表示当前时间线结束后再过1秒的时间点位置,相当于有个1秒的间隔
- '-=1' 表示当前时间线结束时间点前1秒的时间点位置,相当于有个1秒的时间重叠
- '<+=3' 表示前一个动画起始点后3秒的位置
- '❤️' 和上面一个意思('<'和'>'直接跟数字,其实就是和'<+=3'或者'>+=3'是一样的意思)
- '>-0.5' 前一个动画的结束时间点前0.5秒的时间点位置
注意,+= -= 这种是针对整个时间线动画来说的,而 >(结尾) 和 <(开头) 是针对前一个添加的动画来说的
基于百分比的复杂字符串形式。如果前缀是'+='或者'-=',那么表示的百分比是基于整个时间线已经添加的所有动画的总时长的。如果前缀是'<'或者'>',那么这个是基于前一个添加动画的时长的。注意,总时长是包含了重复或者yoyo效果的时长的
- '-=25%' 放到前面已经添加的动画总时长的末尾25%的位置
- '+=50%' 以前面所有动画总时长的50%作为时间间隔
- '<25%' 以前一个动画启动时间点为时间点,放到前一个动画时长的25%的位置。它这个写法等同于'>-75%',这个就是以前一个动画的结束点为准,往前这个前动画的75%时长的时间点位置
- '<+=25%' 以前一个动画启动时间点为时间点,向后放到全部动画总时长的25%的时间点的位置。使用百分比的时候,是否搭配'+='或者'-='是很重要的,当使用这两符号,用来计算百分比的时间长度都是整个时间线已经添加的动画的总时间长度
- 'myLabel+=30%' 以myLabel标记位置为起始点,向后挪以整个以添加到时间线上的动画总时长的30%的时长作为插入的时间点
let tl = gsap.timeline();
tl.to(element, 1, {x: 200})
// 添加到整个时间线结束时间点后1秒,相当于是有了1秒的间隔
.to(element, {duration: 1, y: 200}, "+=1")
// 添加到整个时间线结束的时间点的前0.5秒,也就是有0.5秒的时间是和时间线原本的动画重叠
.to(element, {duration: 1, rotation: 360}, "-=0.5")
// 从时间线动画开头时间点往后6秒的时间点
.to(element, {duration: 1, scale: 4}, 6);
// 在时间线2秒的时间点添加一个标记
tl.add("scene1", 2)
// 把动画提添加到 scene1 这个标记所在的时间点
.to(element, {duration: 4, x: 200}, "scene1")
// 把动画添加到 scene1 这个标记点往后3秒的时间点
.to(element, {duration: 1, opacity: 0}, "scene1+=3");
控制动画
比较常见的就是比如说我们想要点击某个按钮或有了某个交互行为之后才会让元素进行动画效果。那么控制动画的几个方法呢可以帮我们实现这个需求,在tween和timeline上都有这些方法,play,pause,reverse或者是加速变化
// 通过一个变量保存对Tween或者Timeline实例的引用
let tween = gsap.to("#logo", {duration: 1, x: 100});
tween.isActive() // 如果当前正在制作动画,则为 true
// 播放
tween.play();
// 暂停
tween.pause();
// 恢复(继续)
tween.resume();
// 反向变化
tween.reverse();
// 直接切换到整个动画变化时长的0.5秒的时间点的状态
tween.seek(0.5);
// 直接切换到整个变化过程的1/4的节点的状态
tween.progress(0.25);
// 让运动减速到0.5倍
tween.timeScale(0.5);
// 让变化加速到原来的2倍
tween.timeScale(2);
// 直接销毁tween实例,让垃圾回收机制可以处理该实例所占用的内存
tween.kill();
ScrollTrigger滚动触发器
引入方式
<!-- CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
// ES Modules
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
示例
ScrollTrigger.create({
start:"top top",
end:"+=600",
trigger:".body", //滚动到视口元素
scrub:true, //将动画进度直接链接到滚动触发器的进度
pin:true, // 固定自身元素元素
// markers:true, //开启标注功能
animation:gsap.timeline()
.to(".green", { x: 300, opacity: 1, rotation: -360, duration: 2 })
.to(".purple", { x:0, opacity: 1, rotation: -360, duration: 2 },"-=2")
.to(".blue", { x: -300, opacity: 1, rotation: -360, duration: 2 },"-=2")
})
常用属性
属性 | 类型 | 说明 |
---|---|---|
start | 数字 | 方位名词 | 滚动触发器开启滚动的位置(数字,以像素为单位)[触发器][滚动条] |
end | 数字 | 方位名词 | 滚动触发器结束滚动的位置(数字,以像素为单位)[触发器][滚动条] |
trigger | Element | undefined | 触发元素 |
animation | Tween | Timeline | undefined | 与滚动触发器实例关联的补间或时间线 |
scrub | 布尔 | 数字 | 布尔值【 scrub:true 将动画进度直接链接到滚动触发器的进度 】 数字 - 播放头“赶上”所需的时间(以秒为单位),因此0将导致动画的播放头需要0.5秒才能赶上滚动条的位置,它非常适合平滑事件 |
toggleClass | 字符串 | 对象 | 当ScrollTrigger切换活动/非活动时,向一个元素(或多个元素)添加/删除类 |
markers | true | 开启标注功能(更好看出滚动开始以及截止的地方 注意:不要在生产环境使用) |
scroller | Element | window | 与滚动触发器关联的滚动元素(或窗口)。它是滚动条连接到滚动触发器的东西。默认是窗口(视口) |
pin | Element | undefined | 固定元素。pin:true 就是自身元素,pin:'.xxx' 就是指定元素 |
ScrollToPlugin 滚动到插件
对窗口(如执行 window.scrollTo(x, y) )或 DOM 元素(如执行 myDiv.scrollTop = y; myDiv.scrollLeft = x; )的滚动位置进行动画处理。
注意:如果您想制作滚动驱动的动画,其中某些内容在某些滚动条位置被触发,请使用 ScrollTrigger 插件。
| 在 CSS 中使用 scroll-behavior: smooth 会导致冲突
引入方式
<!-- CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollToPlugin.min.js"></script>
// ES Modules
import { gsap } from "gsap";
import { ScrollToPlugin } from "gsap/ScrollToPlugin";
gsap.registerPlugin(ScrollToPlugin);
// 要将窗口滚动到特定位置,请使用窗口作为补间的目标,如下所示:
gsap.to(window, {duration: 2, scrollTo: "#someID"});