GSAP动画学习——来自GSAP中文文档
如何高效的制作动画
1.使用一个动画库
使用GSAP动画库可以解决的问题:
- 浏览器错误、不一致性和兼容性
- 不仅仅是DOM的动画:Canvas、WebGL、通用对象和复杂字符串不能使用原生技术进行动画处理。对于所有动画使用一个一致的工具更加简洁。
- 运行时候控制:可以在运行的时候独立的控制每个变化的组件,简单来说就是这几个API
- 缓动选项(弹跳,弹性等)
- 延迟平滑:如果CPU变慢,GSAP可以优先考虑绝对时间或即时调整以避免动画卡顿跳跃。
- 高级功能:使用GSAP,很容易就可以变形SVG、添加物理/惯性、直接在浏览器中编辑运动路径、使用位置感知的交错动画等等。
2.时间线的使用
在GSAP中,实现动画的顺序执行可以使用到时间线,最后一个参数中是动画的执行时机
加数字
,例如1,则是在时间线的1秒开始执行<
, 在上一个动画开始时插入,相当于把动画变成指向上一个动画开头的指针>
, 在上一个动画结束后插入,相当于把动画变成指向上一个动画结束的指针+=1
,在上一个动画结束后的1秒后播放这个动画
const tl = gsap.timeline();
tl.to(".box", { duration: 1, x: 100 })
.to(".box", { duration: 1, backgroundColor: "#f38630" }, "+=0.5")
.to(".box", { duration: 1, x: 0, rotation: -360 }, "+=0.5")
3.相对值的使用
GSAP识别+= 和 -= 前缀
x:"+=200”将在当前x的基础上添加200个单位(通常是像素)。而x:“-=200”将从当前值中减去200。
当值需要响应视口大小更改时,请使用相对单位(如vw,vh和在某些情况下为%)
尽可能使用.to()和.from()方法(而不是.fromTo())
4.关键帧的使用
如果你的动画效果需要循环往复,这个时候就用关键帧吧
gsap.to(".box", { keyframes: [
{ duration: 1, x: 100 },
{ duration: 1, backgroundColor: "#f38630", delay: 0.5 },
{ duration: 1, x: 0, rotation: -360, delay: 0.5 }
]});
这样可以不用使用时间线动画,通过delay的设置让每一个动画效果之间产生间隔(设置负数就会重叠)
关键帧的使用看起来会比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
});
关键帧中的参数
gsap.to(".elem", {
keyframes: [ // 注意这里是数组
{x: 100, duration: 1, ease: 'sine.out'}, // 定义这个分段动画自己的ease曲线
{y: 200, duration: 1, delay: 0.5}, // 产生和前个分段动画0.5秒的间隔
{rotation: 360, duration: 2, delay: -0.25} // 和前一个分段动画产生0.25秒的重叠
],
ease: 'expo.inOut' // 设置整个关键帧动画的曲线
});
关键帧动画的默认动画曲线是线性的,也就是线性匀速的动画,你可以在每个动画中都重新定义ease的设置,你也可以给整个关键帧动画添加一个统一的的动画曲线设置。
使用Percentage
gsap.to(".elem", {
keyframes: { // 注意这里是对象
"0%": { x: 100, y: 100},
"75%": { x: 0, y: 0, ease: 'sine.out'}, // 指定这个分段的动画曲线
"100%": { x: 50, y: 50 },
easeEach: 'expo.inOut' // 每个分段的动画曲线
},
ease: 'none' // 整个关键帧动画的动画曲线
duration: 2,
})
这样的写法,默认的ease曲线是power1.inout,这个曲线看起来的整个变化效果是不错的,但是如果你想要指定每个分段变化的动画曲线,你可以通过每个分段动画内部的ease曲线,或者也可以通过easeEach属性来设置。
这个写法和CSS中的关键帧动画的写法是很类似的。 不需要给每一段动画单独设置时长和延时,只要给整体设置一个时长,然后确定每个分段动画的时间比例。
使用数组的方式编写关键帧
gsap.to(".elem", {
keyframes: { // 注意这里是对象
x: [100, 0, 50], // 数组分段,也就是两段动画,100-0,,0-50
y: [100, 0, 50]
easeEach: 'sine.inOut' // 每个分段都设置成这个动画曲线
ease: 'expo.out' // 关键帧整体的动画曲线设置
},
duration: 2, // 总时长2秒,这里有两段,平均分就是每段的时长是1秒
})
通过数组的方式,各个分段自动平均分总时长。
这样的写法,默认的ease曲线是power1.inout,或者也可以通过easeEach属性来设置所有分段的ease曲线。
5.在使用动画之前提前设置好默认值
GSAP有默认值,例如ease(“power1.out”)和duration(0.5秒)等属性。
GSAP可以改变全局的默认设置,使用gsap.default()进行设置
// 使用线性缓动曲线 和 1秒的动画时长
gsap.defaults({ ease: "none", duration: 1 });
6.同时驱动多个元素
可以使用更复杂的选择器字符串选择具有不同选择器的多个元素:
gsap.to(".box, .circle", { ... });
或者,只要是相同类型(选择器字符串,变量引用,通用对象等),您就可以传递使用数组进行一起传递:
var box = document.querySelector(".box");
var circle = document.querySelector(".circle");
// some time later…
gsap.to([box, circle], { ... });
7.使用基于函数的值、stagger、loop
基于函数的值:
可以根据函数的任何内容作为属性值
var yMove = 50;
gsap.to(".box", {
delay: 0.5,
duration: 1,
// 第一个参数是当前节点索引 第二个是当前节点 第三个是节点列表
y: function(index, elem, boxes) {
return index % 2 === 1 ? -yMove : yMove;
}
});
或者可以从数组中选择元素:
var colors = ["#3498db", "#27ae60", "#f1c40f"];
gsap.to(".box", {
delay: 0.5,
duration: 1,
backgroundColor: function(i, elem, boxes) {
return colors[i % 3];
}
});
Staggers
通过使用stagger来偏移动画的开始时间,使其看起来更加动态和有趣。对于单个动画中的简单间隔偏移,只需使用stagger:0.2即可在每个动画的开始时间之间添加0.2秒
var yMove = 50;
gsap.to(".box", {
delay: 0.5,
duration: 1,
stagger: 0.2,
y: function(index, target, list) {
// 第一个参数是索引 第二个是当前元素 第三个是元素列表
return index % 2 === 1 ? -yMove : yMove;
}
});
同时在stagger的值可以是一个对象,给每一个动画设置一定的效果
gsap.to(".box", {
y: 100,
stagger: { // 将高级选项包裹在对象中
each: 0.1,
from: "center",
grid: "auto",
ease: "power2.inOut",
repeat: -1 ,// 立即重复播放,不需要等待其他的动画结束
yoyo:'true',
}
});
还可以传递一个对象获得更复杂的stagger效果,下面是stagger文档里面的demo,关于GSAP的更多信息可以查看stagger文档
LOOP循环
要循环遍历元素,最简单的方法是使用.forEach()。但是由于IE不支持在使用.querySelectorAll()选择的元素上使用.forEach(),因此可以改用GSAP的utils.toArray()函数。
// toArray:将几乎任何类似数组的对象转换为数组,包括选择器文本!
gsap.utils.toArray(".container").forEach((container, i) => {
const header = container.querySelector("h1");
const content = container.querySelector(".content");
gsap.to([header, content], {autoAlpha: 1, duration: 0.5, stagger: 1, delay: i * 2});
});
8.模块化你的动画
Function函数
将动画效果提前封装好调用,将它们组合成更大的程序,同时保持代码整洁、可重用和易于修改,增加代码的可重用性。
function doAnimation() {
// 可以进行一些处理,比如计算,使用传入该函数的参数数据
// r返回一个tween,可以使用上面计算后的数据
return gsap.to(".myElem", { duration: 1, color: "red"});
}
tl.add( doAnimation() );使用嵌套的时间线方式可以改变动画组织逻辑。能让你轻松的实现各种复杂的逻辑动画。
function doAnimation() {
const tl = gsap.timeline();
tl.to(...);
tl.to(...);
// 可以添加任意多的动画效果
// 结束之后返回时间线实例
return tl;
}
const master = gsap.timeline();
master.add( doAnimation() );
master.add( doAnotherAnimation() );
// 可以添加更多的时间线
将动画构建例程包装在函数中,还可以使重新创建动画,下面是在调整窗口时候重新创建动画
var tl; // 设置一个引用,可以访问到时间线实例
function buildAnimation() {
var time = tl ? tl.time() : 0; // 储存实例动画已经进行了多长的时间
// 如果实例已经存在,就销毁它
if (tl) {
tl.kill();
}
// 创建一个新的时间线实例
tl = gsap.timeline();
tl.to(...)
.to(...); // 开始动画
tl.time(time); // 把动画时间线设置到它已经进行到的位置
}
buildAnimation(); // 启动
window.addEventListener("resize", buildAnimation); // 处理浏览器窗口变化
Effects效果
使用效果可以自定义动画转换为一个命名效果,可以随时使用新的目标的配置调用它。你对动画有标准或者如果你将从不同的上下文中调用相同的动画时,这将非常有帮助。
下面是一个简单的“淡入淡出的效果”
// 在GSAP上注册effect:
gsap.registerEffect({
name: "fade",
defaults: {duration: 2}, // 在这个defaults设置的数据可以通过下方的config参数获取到进行设置
effect: (targets, config) => {
return gsap.to(targets, {duration: config.duration, opacity:0});
}
});
// 然后我们可以这样使用了:
gsap.effects.fade(".box");
//或者还可以把默认的设置覆盖掉:
gsap.effects.fade(".box", {duration: 1});
9.使用control方法
GSAP 提供了许多控制补间或时间线状态的方法。它们包括 .play()、.pause()、.reverse()、.progress()、.seek()、.restart()、.timeScale() 等等。
使用控制方法可以使动画之间的过渡更加流畅(例如能够在部分过程中反转)并且更高效(通过重用相同的补间/时间线而不是每次创建新实例)。
let nav = document.querySelector('.nav')
let tween = gsap.to(".purple", {
duration: 4,
x: () => nav.offsetWidth, // animate by the px width of the nav
xPercent: -100, // offset by the width of the box
rotation: 360,
ease: "none",
paused: true
});
// click handlers for controlling the tween instance...
document.querySelector("#play").onclick = () => tween.play();
document.querySelector("#pause").onclick = () => tween.pause();
document.querySelector("#resume").onclick = () => tween.resume();
document.querySelector("#reverse").onclick = () => tween.reverse();
document.querySelector("#restart").onclick = () => tween.restart();
下面是一个控制timeScale来控制时间线动画来实现的效果:在鼠标悬浮在节点的时候会减缓速度,timeScale的效果可以理解为会使得原来的duration = duration/timScale。
const tl = gsap.timeline(),
atom = document.querySelector(".atom"),
dur = 2,
del = 0.5;
const e = tl.fromTo('.electron', {rotationX: 90}, {
rotationZ: -360,
rotationX: 90,
ease: 'none',
duration: dur,
stagger: {
each: -del,
repeat: -1
}
}, 0);
const p = tl.to('.path', {
rotationZ: 360,
ease: 'none',
duration: dur,
stagger: {
each: -del,
repeat: -1
}
}, 0);
// Add a rotation to the whole atom
gsap.set(atom, {transformOrigin: "center center"});
gsap.to(atom, {rotation: 360, ease: "none", repeat: -1, duration: 300})
// Skip the loading
tl.progress(0.9999);
const timeScaleTween = gsap.to(tl, {
duration: 0.75,
timeScale: 0.1,
paused: true
});
// Slow the animation on mouse enter
atom.addEventListener("mouseenter", function() {
timeScaleTween.play();
});
// Set the animation back to normal on mouse leave
atom.addEventListener("mouseleave", function() {
timeScaleTween.reverse();
});
案例:交互事件触发动画
在下面的示例中,我们为每个元素创建一个时间线动画(以便不会在所有实例上触发相同的动画),将该时间轴的引用附加到元素本身,然后在悬停元素时播放相关的时间轴,在鼠标离开时反转它。
gsap.set(".information", {yPercent: 100});
gsap.utils.toArray(".container").forEach(container => {
let info = container.querySelector(".information"),
silhouette = container.querySelector(".silhouette .cover"),
tl = gsap.timeline({ paused: true });
tl.to(info, { yPercent: 0 })
.to(silhouette, { opacity: 0 }, 0);
container.addEventListener("mouseenter", () => tl.play() );
container.addEventListener("mouseleave", () => tl.reverse() );
});
案例:在时间线上多个状态之间实现变化
下面的案例中,一个元素中有两种动画效果,鼠标悬浮在元素上,在点击元素之前是三条线变成六边形,在点击后是透明度的变化,再次点击再变回三条线。
// 初始化数据
const menu_dur = 0.3,
menu_btn_anim = gsap.timeline({paused: true, defaults: {duration: menu_dur}}),
menu_btn = document.querySelector(".menu_button");
// 第一个动画,使用svg画三条线
menu_btn_anim
.to("#x_left", {drawSVG:"0%"}, 0)
.fromTo("#top_right,#middle_left, #middle_right, #bottom_left", {autoAlpha:1, drawSVG:"0%"}, {drawSVG:"100%"}, 0)
// 第二个动画,一个叫hover的动画 在下面会调用,作用是将三条线变为一个叉
.addLabel("hover")
.to("#x_right, #x_left", {autoAlpha:1, drawSVG:"100%"}, "hover")
.to(["#top_right,#middle_left, #middle_right, #bottom_left", "#top_left", "#bottom_right"], {drawSVG:"0%"}, "hover")
menu_btn.addEventListener("mouseenter", function() {
if(!isClicked) {
// tweenTo:创建一个线性补间,本质上将播放头拖动到特定时间或标签,然后停止。
menu_btn_anim.tweenTo("hover");
// 在本例中tweenTo的作用是 将hover的动画播放头拖回到开始
} else {
gsap.to(menu_btn, {duration: menu_dur, autoAlpha: 0.4});
}
});
menu_btn.addEventListener("mouseleave", function() {
if(!isClicked) {
menu_btn_anim.reverse();
} else {
gsap.to(menu_btn, {duration: menu_dur, autoAlpha: 1});
}
});
var isClicked = false;
menu_btn.addEventListener("click", function() {
if(!isClicked) {
// 点击之前 btn的动画是menu_btn_anim
menu_btn_anim.play();
gsap.to(menu_btn, {duration: menu_dur, autoAlpha: 0.4});
} else {
// 将hover的动画播放头拖回到开始
menu_btn_anim.tweenTo("hover");
gsap.to(menu_btn, {duration: menu_dur, autoAlpha: 1});
}
isClicked = !isClicked;
});
案例:基于滚动位置的动画
此演示在到达滚动位置后播放完整动画:
const tl = gsap.timeline({paused: true});
tl.from("h1", {scale: 0.7, autoAlpha: 0, duration: 1});
function animateAtScrollPos() {
if(scrollY > 100) {
tl.play();
document.removeEventListener("scroll", animateAtScrollPos);
}
}
document.addEventListener("scroll", animateAtScrollPos);
稍微高级一点的效果
const tl = gsap.timeline({paused: true});
tl.from("h1", {scale: 0.7, autoAlpha: 0});
const startY = innerHeight / 10;
const finishDistance = innerHeight / 5;
function animateAtScrollPos() {
// progress获取或设置时间线的进度,该进度是 0 到 1 之间的值,指示虚拟播放头的位置(不包括重复),其中 0 表示开始,0.5 表示完成一半,1 表示完成。
tl.progress((scrollY - startY) / finishDistance);
}
document.addEventListener("scroll", animateAtScrollPos);
以上就是在中文文档中GSAP的案例的动画效果,此外,你还可以使用GSAP的插件,制作更加优美的动画效果,例如ScrollTrigger、MorphSVG、GSDevTools等等。
更多信息可以点击GSAP中文文档
10.GSAP中的部分API
10.1 gsap.killTweensOf()
终止特定对象的所有动画,你可以传入一个节点,这样可以销毁节点上的所有gsap动画
如果你想终止特定的动画,也可以在后面写上需要终止动画的参数,例如
gsap.killTweensOf(qc.Node,"opacity,x")
如果想要杀死所有延迟调用(例如使用gsap.delayedCall(5.myFunction)
,创建的调用),可以直接调用gsap.killTweensOf(myFunction)
,需要注意的是,即使尚未启动的动画,使用killTweensOf
任然会销毁动画。(设定了一个5秒后延迟的动画,如果在两秒之后删除它,那么动画将不会执行)。
10.2 gsap.globalTimeline
gsap.globalTimeline
是驱动 GSAP 中所有内容的根时间轴实例,使其成为同时影响所有动画的强大方式。有如下有用的方法
gsap.globalTimeline.pause()
暂停影响所用动画的全局时间线 返回自身
.play()
恢复所有动画 返回自身
.paused()
如果全局时间线暂停 返回 true 否则返回 false
.timeScale()
设置全局时间刻度,会影响根时间轴的播放速率,这个时间轴包含所有其他的动画,这是一个全局加速或减慢所有动画的好方法。
gsap.globalTimeline.timeScale(0.5); // 用0.5倍速播放所有动画
gsap.globalTimeline.timeScale(2); //用 2倍速播放所有动画
var currentTimeScale = gsap.globalTimeline.timeScale(); // 返回当前全局timeScale的值
10.3 gsap.ticker
Gap.ticker 是个整个引擎的心跳, 它在每一个requestAnimationFrame 事件上更新 globalTimeline ,因此 它和浏览器的渲染周期完美同步。我们可以添加自己的侦听器,用于在每次跟新之后运行自定义逻辑。(非常适合游戏开发人员)
//添加一个侦听器
gsap.ticker.add(myFunction,once,prioritize);
// myFunction:执行逻辑函数
// 下面两个参数在GSAP 3.10.0 中才存在
// once: boolean 执行逻辑函数只会触发一次,然后自动删除
// prioritize:布尔值 - 回调将被添加到队列的顶部而不是底部,这意味着它将在队列中当前的任何侦听器之前触发。如果希望在 GSAP 的全局时间线之前触发回调,这是非常完美的选择。
function myFunction(time, deltaTime, frame) {
// 在引擎的每一个tick跟新之后执行
// time:第一个tick启动后的总时间
// deltaTime:从上一个tick 到当前tick的时间
// frame:帧变好 在每一个刻度递增
}
//移除侦听器
gsap.ticker.remove(myFunction);
gsap.ticker.fps(60)
设置动画执行的帧率
10.4 gsap.util.toArray()
将几乎任何类似数组的对象转换为数组,包括选择器文本!(例如: toArray(".class")
--> [element1, element2]
)。
10.5 gsap.version
属性值 当前正在使用的gsap 版本
10.6 gsap.config()
配置非补间的GSAP设置 比如 autoSleep、force3D、units
10.7 gsap.context()
收集在提供函数中创建的所有GSAP动画 以便轻松的一次性完成所有的动画和滚动触发器的 反转 删除 revert() kill()
let ctx = gsap.context(() => {// 收集所用的动画
gsap.to(...);
gsap.from(...);
gsap.timeline().to(...).to(...);
...
});
ctx.revert(); // 现在每一个动画都反转了
10.8 gsap.defaults()
设置动画的默认值 比如ease duration
gsap.defaults({
ease: "power2.in",
duration: 1
});
10.9 gsap.delayedCall()
一种在设定时间之后调用函数的简单方法。
//calls myFunction() after 1 second and passes 2 parameters:
gsap.delayedCall(1, myFunction, ["param1", "param2"]);
function myFunction(param1, param2) {
//do stuff
}
10.10 gasp.exportRoot()
将所有补间、时间线和 [可选] 延迟调用从根时间线无缝传输到新时间线,以便您可以在看似全局的基础上执行高级任务,而不会影响导出后创建的补间/时间线。
例如,假设一个游戏使用GSAP进行其所有动画,并且在游戏过程中的某个时刻,您希望将所有内容减慢到停止(动画化 timeScale
),同时将新的弹出窗口动画化到位:
var tl = gsap.exportRoot();
gsap.to(tl, {duration: 0.5, timeScale: 0});
//this tween isn't affected because it's created after the export.
gsap.fromTo(myWindow, {scaleX: 0, scaleY: 0}, {duration: 1, scaleX: 1, scaleY: 1});
10.11 gsap.getById()
创建补间或时间线的时候,会分配一个id 可以引用它。对使用框架和构建工具如(react)很有帮助
gsap.to(obj, {id: "myTween", duration: 1, x: 100});
//later
let tween = gsap.getById("myTween"); //returns the tween
tween.pause();
注意: gsap会在动画完成后布局自动发布用于垃圾回收的动画,硬刺getById() 只会查找 处于活动状态 或尚未开始的动画。
10.12 gasp.getProperty()
通过id值 返回所请求属性的值
gsap.getProperty("#id", "x"); // 20
gsap.getProperty("#id", "x", "px") // "20px"
gsap.getProperty("#id", "backgroundColor") // "rgb(255, 128, 0)"
10.13 gsap.isTweening()
判断对象是否在进行动画处理 如果 动画已经暂、已经完成 或者尚未开始,则不会将其视为活动状态
if (!gsap.isTweening("#id")) {
// do stuff
}
传入的参数可以是一个id 或者一个 对象
10.14 gsap.matchMedia()
一个媒体查询方法,用于在不同屏幕,实现不同的动画,每次屏幕有变化的时候都会执行这个函数,如果用户多次调整浏览器大小,则该函数会被多次调用。
基本用法:
let mm = gsap.matchMedia();
// 添加一个媒体查询,当匹配的的时候 里面的函数就会执行
mm.add("(min-width: 800px)", () => {
// 下面的动画只有当屏幕宽度小于800px 的时候会执行
gsap.to(...);
gsap.from(...);
ScrollTrigger.create({...});
return () => { // 可选的
// 在这里定义清除动画代码 内存回收
};
});
//=============简单的桌面/移动 示例
let mm = gsap.matchMedia();
mm.add("(min-width: 800px)", () => {
// desktop setup code here...
});
mm.add("(max-width: 799px)", () => {
// mobile setup code here...
});
10.15 gsap.set()
立即设置目标的属性,不会以动画的方法执行,相当于修改属性
11.gsap.timeLine()
时间轴是我们常用的实现动画的方式,在构建动画的时候,我们经常会先创建一个时间轴let tl = gsap.timeline()
以下是一些时间轴的API
11.1 tl.addLable()
给时间线添加标签,以便轻松标记重要位置/时间 传入参数为字符串
//案例额
var tl = gsap.timeline();
tl.to("#green", {x:750, duration:1})
// 我们在 时间线1s之后添加了一个标签 "blueGreenSpin"
.addLabel("blueGreenSpin", "+=1")
// 在标签处添加了 一个动画
.to("#blue", {x:750, rotation:360, duration:2}, "blueGreenSpin")
// 在标签后的0.5s处添加了一个动画
.to("#orange", {x:750, rotation:360, duration:2}, "blueGreenSpin+=0.5");
于是动画效果变成了
标签的定位 和动画的定位一样,同样可以使用 “<”,“>”, “+=1”, “-=1”, 也可以直接用标签定位标签
// 在someLable标签处插入了myLable标签
tl.addLabel("myLabel", "someLabel");
11.2 tl.addPause()
用于在特定时间或者标签暂停时间轴的播放
// 在时间轴2s处暂停播放
timeline.addPause(2);
// 在标签"yourLabel"处 暂停播放
timeline.addPause("yourLabel");
// 在标签"yourLabel"后三秒暂停播放
timeline.addPause("yourLabel+=3", yourFunction);
// 在4s 处暂停播放 并执行函数 yourFunction 传入两个参数 "param1" "param2"
timeline.addPause(4, yourFunction, ["param1", "param2"]);
暂停只是一个0持续时间的补间,硬刺可以通过timeline().removePause() 删除
11.3 tl.call()
将回调添加到时间线的末尾(或使用 position
参数在其他地方),这与在时间轴本身上使用 onComplete
特殊属性不同,因为一旦附加回调,它就会保留在原位,而 an onComplete
总是在时间线的最后调用。
function myFunction(param1, param2) {
//...
}
tl.add( gsap.delayedCall(0, myFunction, ["param1", "param2"]) );
tl.call(myFunction, ["param1", "param2"]);
11.4 tl.delay()
获取或设置动画的初始 delay
值,即动画应开始之前的时间长度(以秒为单位)。
11.5 tl.endTime()
根据父时间轴的本地时间返回动画完成的时间。会考虑timeScale对时间线的影响
var tl = gsap.timeline();
// 创建一个 1秒的动画
var tween = gsap.to(e, {duration: 1, x: 100});
// 在时间线0.5秒之后插入动画
tl.add(tween, 0.5);
console.log(tween.endTime()); //1.5
// 让刚刚创建的动画 以2倍速播放 即持续时间变为0.5s
tween.timeScale(2);
console.log(tween.endTime()); //1
11.6 tl.startTime()
获取或设置动画在其父时间轴上开始的时间(在定义的任何延迟之后)。
11.7 tl.time()
获取或设置播放头的本地位置(实质上是当前时间),不包括任何重复或重复延迟。
11.8 tl.totalTime()
根据 获取或设置播放头 totalDuration
的位置,其中包括任何重复和重复延迟。