GSAP 之 TweenMax 源码阅读(一)
一、背景 GreenSock (绿袜子)
GreenSock 是一家做 专业级 JavaScript 动画的公司,主要产品就是其下的 GSAP (GreenSock Animation Platform),配合着 GSPA 开发了很多专业的动画插件。
GSAP (GreenSock Animation Platform)
GSAP 是一个强大的 JavaScript 工具集,可以让开发人员开发出高级动画。构建适用于所有主流浏览器的高性能动画。动画化 CSS、SVG、画布、React、Vue、WebGL、颜色、字符串、运动路径、通用对象……任何 JavaScript 可以触及的东西!
在解决超过 1100 万个站点的实际问题的同时,没有其他工具能够提供如此先进的排序、可靠性和严格控制。GSAP 解决了无数的浏览器不一致问题;你的动画效果很好。GSAP 的核心是一个高速属性操纵器,随着时间的推移以极高的准确性更新值。它比 jQuery 快 20 倍!
简而言之,GSAP 是地球上最强大的高性能 JavaScript 动画库,这可能是谷歌推荐它用于基于 JS 的动画并且每个主要广告网络都将其排除在文件大小计算之外的原因。与指示您如何构建应用程序的单一框架不同,GSAP 完全灵活;洒在任何你想要的地方。
官网地址:https://greensock.com/gsap/
二、GSAP特点(优势)
1、Crazy fast (疯狂快)
性能至关重要,尤其是在处理器速度较慢的移动设备上。丝般流畅的动画是任何值得其重量的图书馆的标志,GSAP 在压力下比旧的“行业标准”高出 1000%+。生涩和抽搐的东西现在是流动的。它是无数优化的结果,以确保您的交互式项目响应迅速、高效且流畅。查看速度测试以进行面对面的比较。
2、Freakishly robust (异常坚固)
为专业人士构建的 JavaScript 动画,GSAP 包含的功能使大多数其他引擎看起来像廉价玩具。动画颜色、贝塞尔曲线、CSS 属性、数组等等。构建令人惊叹的滚动触发效果。舍入值、动态平滑 reverse()、使用相对值、自动适应 getter/setter 函数、使用几乎任何缓动方程,并像专业人士一样管理冲突的补间。定义回调、毫不费力地构建序列(即使有重叠补间)、重复/溜溜球等等。使用简单、随意组合、自己解决诸多异常
3、Compatible(兼容性)
HTML、SVG、React、Vue、Angular、jQuery、Canvas、CSS、新浏览器、旧浏览器、移动设备等等——GSAP 与它们相处得很好。使用您最喜欢的工具,而无需跳过无休止的箍,以确保兼容性。GSAP“正常工作”。我们担心兼容性,因此您不需要担心。又一个头痛的问题解决了。
4、Animate anything(动画任何东西)
这是正确的。任何事物。没有可供选择的预定义属性列表。任何对象的任何数字属性都可以设置动画。GSAP 可以处理几乎任何格式的带有嵌套颜色的复杂字符串值。它会自动检测基于函数的值。有大量的实用方法可以解决常见问题。可能性是无止境。
5、Lightweight & expandable(轻巧且可扩展)
模块化、灵活且超高效,其插件架构使核心引擎保持紧凑,同时允许通过可选插件添加几乎任何功能。GSAP 将惊人的能量装入一个小得惊人的封装中。
6、Zero dependencies(零依赖)
GSAP 不是建立在任何第 3 方工具(如 jQuery)之上(尽管它与 jQuery 配合得很好)。这种方法最大限度地减少了加载时间并最大限度地提高了性能。不需要任何其它工具,直接使用
7、Advanced sequencing(高级排序)
您不仅限于“一个接一个”的排序;让动画尽可能多地重叠。将时间线嵌套在其他时间线内。插入间隙、回调、标签、交错补间等等。您可以获得对时间的精确控制和前所未有的灵活性,从而用最少的代码创建富有表现力的动画。
8、Dedicated support(专门支持) ??
每个人有时都需要一点帮助。我们支持你。当截止日期迫在眉睫而您无法解决问题时,请跳转到greensock.com/forums以获得与 GSAP 相关的问题的解答。有超过 100,000 个帖子!GreenSock 工具不同于大多数停滞不前或提供零支持的开源项目。有论坛帮你解决问题
三、GSAP 核心插件 TweenMax
GSAP 有很多插件,不过大部分是收费的,例如 DrawSVG , Physics2D , PhysicsProps , ScrambleText , CustomBounce , CustomWiggle,MorphSVG , Inertia 插件, SplitText , MotionPathHelper , GSDevTools , ScrollSmoother 等等 功能都很强大。TweenMax 目前免费开源,这里我们只关注 TweenMax。
TweenMax 是 GreenSock 动画平台的核心,配合其他插件就可以,对css属性、颜色、滤镜效果、颜色值、桢等等做动画。具体效果和使用可以参考中文官网地址:
https://www.tweenmax.com.cn/index.html
1、TweenMax 的使用
TweenMax 使用起来很简单
使用参考:https://www.tweenmax.com.cn/api/tweenmax/TweenMax()
效果展示:https://www.tweenmax.com.cn/demo/
2、TweenMax 原理解析
结构组成
以下代码部分都为简化的代码
1、EventDispatche 事件调度中心(分发器)
类似观察者模式的一个组件,负责收集依赖和分发事件;
var EventDispatcher = _class("events.EventDispatcher", function (target) { ... })
// 事件监听器
p.addEventListener = function (type, callback, scope, useParam, priority) { ... }
// 事件删除
p.removeEventListener = function (type, callback) {}
// 事件触发器
p.dispatchEvent = function (type) { ... }
2、Ticker 时间启动器
也可叫做动画驱动器,播放时一直自循环,实时校准当前时间,并且向外分发当前时间 time;
Ticker 根据浏览器渲染频率 16.667ms 驱动 SimpleTimeline(根时间轴),主要使用 requestAnimationFrame 方法,低端浏览器时,使用 setTimeout 来兼容;
_class("Ticker", function (fps, useRAF) {
this.funName = "Ticker";
...
_tick = function (manual) {
_self.time = (_lastUpdate - _startTime) / 1000;
if (!_fps || overlap > 0 || manual === true) {
_self.frame++;
dispatch = true;
}
_id = _req(_tick);
if (dispatch) {
_self.dispatchEvent(_tickWord);
}
};
_self.sleep = function () {...};
_self.wake = function (seamless) {...};
...
}
3、Animation 动画播放器
主要负责将动画,挂载到根时间轴,同时记录该动画的各项参数;
4、TweenMax/TweenLite 播放单元
负责播放动画,是功能最多的组件,主要功能有:
- 实例动画,记录动画状态;
- 提供插件API,处理用户传入参数(链表化参数);
- 渲染动画的每一帧播放,并判断当前阶段对应执行该阶段的回调函数;
- 播放速度曲线控制,需要借助另一个插件 Ease,默认只有几种简单速度曲线;
var TweenLite = _class( "TweenLite", function (target, duration, vars) { ... })
p = TweenLite.prototype = new Animation();
p.constructor = TweenLite;
p._init = function() { ...// 第一次渲染时处理参数 }
p._initProps = function(target, propLookup, siblings, overwrittenProps, index) { ... }
p.render = function(time, suppressEvents, force) {
...
// 动画播放程度渲染
self._totalTime = self._time = time;
self.ratio = self._ease.getRatio(time / duration);
pt = self._firstPT;
while (pt) {
if (pt.f) {
pt.t[pt.p](pt.c * self.ratio + pt.s);
} else {
pt.t[pt.p] = pt.c * self.ratio + pt.s;
}
pt = pt._next;
}
// 回调函数执行
if (callback){..}
}
p._kill = function(vars, target, overwritingTween) { ... }
...
// TweenLite 静态方法
TweenLite.to = function(target, duration, vars) { ... }
TweenLite.from = function(target, duration, vars) { ... };
TweenLite.fromTo = function(target, duration, fromVars, toVars) { ... };
TweenLite.delayedCall = function(delay, callback, params, scope, useFrames) { ... };
TweenLite.set = function(target, vars) { ... };
TweenLite.getTweensOf = function(target, onlyActive) { ... };
...
5、 SimpleTimeline/ TimelineLite 根时间轴线与普通时间轴线
时间轴主要维护一个动画队列,负责挂载动画,由 Ticker 驱动收到调用时负责循环执行时间轴上挂载的所有动画。 这里两者有区别:
SimpleTimeline 会执行所有动画;(这里是页面上的所有动画)
TimelineLite 而会按照当前时间执行对应时间区间内的动画;
并且,TimelineLite 最终也会作为一个子集挂载到 SimpleTimeline 上;
var SimpleTimeline = _class("core.SimpleTimeline", function (vars) {
Animation.call(this, 0, vars)
});
p = SimpleTimeline.prototype = new Animation();
p.constructor = SimpleTimeline;
...
p.add = p.insert = function (child, position, align, stagger) {
...
var prevTween, st;
prevTween = this._last;
if (prevTween) {
child._next = prevTween._next;
prevTween._next = child;
} else {
child._next = this._first;
this._first = child;
}
...
return this;
};
p._remove = function (tween, skipDisable) { ... };
p.render = function (time, suppressEvents, force) {
...
var tween = this._first, next;
while (tween) {
next = tween._next; // 先在此处记录它,因为渲染后值可能会更改
if (
tween._active ||
(time >= tween._startTime && !tween._paused && !tween._gc)
) {
if (!tween._reversed) {
tween.render(
(time - tween._startTime) * tween._timeScale,
suppressEvents,
force
);
} else {
tween.render(
(!tween._dirty ? tween._totalDuration : tween.totalDuration()) -
(time - tween._startTime) * tween._timeScale,
suppressEvents,
force
);
}
}
tween = next;
}
};
p.rawTime = function () { ... };
结构图:
TweenMax插件运行时
- 页面打开开始执行js 初始化;
- 创建 Ticker 继承 Event(创建Ticker后会启动,为了兼容各种情况的),一段时间后没有动画挂载会自动停止;
- 创建 SimpleTimeline 继承 Animation ,并将其渲染函数,注册到 Ticker 的观察者上,供 Ticker 驱动;
- 初始化各类参数,包括初始时间,状态等,之后加载开发者代码;
- 执行开发者写入代码;例如:
let tween = TweenLite.to(demo, 2 ,{
red:255,
score:5100,
ease: Power1.easeIn,
onUpdate: showScore
});
- 创建 TweenLite 实例继承 Animation 类,注册动画ID。这里在继承 Animation 类的时候,将自己挂载到 SimpleTimeline 的动画链表上,也是动画队列上;
- 此时进入页面不久,Ticker 还在启动状态中,6过程完成后,如果没有暂停参数,就会发现 SimpleTimeline 队列中有动画,则开始执行,SimpleTimeline 中的动画;
- SimpleTimeline 调入,TweenLite 中的render函数,渲染此时的动画,开始正式执行;
- 执行动画,第一次进入 TweenLite 中的render函数中时,会初始化动画状态,链表化初始数值,记录初始状态等等,之后会循环执行,参数数值的变化,并判断当前执行的阶段对应执行阶段回调函数;
- 动画的停止,动画本身执行完成时,会一直维持结束状态,而时间启动器 Ticker 此时还在继续执行;
- 时间轴本身有自检机制,每120桢检查一次是否还有动画在执行,没有的话,就会停止 Ticker ,减少CPU消耗,到此整个动画执行结束。
执行流程图:待补充
3、多个动画的添加,TimelineLite
1、如何添加动画的?
时间轴有个链表队列,每次添加新动画都是使用 add 方法默认往最后添加;
2、如何实现时间的提前和延后的?position
每次添加新动画时,都会记录所有动画完成的时间 _duration,下次有新动画添加时,根据参数加减时间,计算出这次动画的开始时间,并添加到时间轴;
3、时间轴的特别处理
时间轴在已经开始渲染的情况下,也可以继续添加或者删除动画,会自动矫正其位置,到了时间自动播放;
dome展示
4、使用灵活
兼容多种使用传参方式 TimelineLite
https://www.tweenmax.com.cn/api/timelinemax/
时间轴图:同上处
四、版本变化 3.0
核心的动画实现原理没有变;主要变化了编写方式和使用方面:
- 对代码编写方式,进行了升级,使用了现成工程化,大量使用了ES6语法,尤其再类和继承方便的编写,代码量减少了很多;
- 组件间的功能进行了一些调整,分出了 TweenLite 的部分功到 Animation 组件使承担更多功能,取消了 EventDispatche 组件之前只单纯添加了一个 Ticker 事件;
- 合并了TweenMax、TweenLite、TimelineLite 和 TimelineMax四个模块,更新了API的调用方式,使用起来更加简单便捷。
具体变化可参考:https://greensock.com/3-release-notes
五、GSAP 支持的一些优秀的案例网址展示
网址:https://greensock.com/showcase/
六、一些可以借鉴的动画效果
1、哈尔的移动城堡 https://www.tweenmax.com.cn/cool/howls/
2、弹性进度条 https://www.tweenmax.com.cn/cool/elastic-progress/
3、绿植 https://www.tweenmax.com.cn/cool/drop-tree/
参考网址: https://greensock.com/
https://www.tweenmax.com.cn/index.html