vendor/assets/javascripts/greensock/TweenMax.js in greensock-rails-1.11.8.0 vs vendor/assets/javascripts/greensock/TweenMax.js in greensock-rails-1.12.1.0
- old
+ new
@@ -1,8 +1,8 @@
/*!
- * VERSION: 1.11.8
- * DATE: 2014-05-13
+ * VERSION: 1.12.1
+ * DATE: 2014-06-26
* UPDATES AND DOCS AT: http://www.greensock.com
*
* Includes all of the following: TweenLite, TweenMax, TimelineLite, TimelineMax, EasePack, CSSPlugin, RoundPropsPlugin, BezierPlugin, AttrPlugin, DirectionalRotationPlugin
*
* @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
@@ -15,11 +15,11 @@
(window._gsQueue || (window._gsQueue = [])).push( function() {
"use strict";
window._gsDefine("TweenMax", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) {
-
+
var _slice = [].slice,
TweenMax = function(target, duration, vars) {
TweenLite.call(this, target, duration, vars);
this._cycle = 0;
this._yoyo = (this.vars.yoyo === true);
@@ -27,22 +27,25 @@
this._repeatDelay = this.vars.repeatDelay || 0;
this._dirty = true; //ensures that if there is any repeat, the totalDuration will get recalculated to accurately report it.
this.render = TweenMax.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method)
},
_tinyNum = 0.0000000001,
- _isSelector = TweenLite._internals.isSelector,
- _isArray = TweenLite._internals.isArray,
+ TweenLiteInternals = TweenLite._internals,
+ _isSelector = TweenLiteInternals.isSelector,
+ _isArray = TweenLiteInternals.isArray,
p = TweenMax.prototype = TweenLite.to({}, 0.1, {}),
_blankArray = [];
- TweenMax.version = "1.11.8";
+ TweenMax.version = "1.12.1";
p.constructor = TweenMax;
p.kill()._gc = false;
TweenMax.killTweensOf = TweenMax.killDelayedCallsTo = TweenLite.killTweensOf;
TweenMax.getTweensOf = TweenLite.getTweensOf;
+ TweenMax.lagSmoothing = TweenLite.lagSmoothing;
TweenMax.ticker = TweenLite.ticker;
-
+ TweenMax.render = TweenLite.render;
+
p.invalidate = function() {
this._yoyo = (this.vars.yoyo === true);
this._repeat = this.vars.repeat || 0;
this._repeatDelay = this.vars.repeatDelay || 0;
this._uncache(true);
@@ -102,11 +105,12 @@
var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
prevTime = this._time,
prevTotalTime = this._totalTime,
prevCycle = this._cycle,
duration = this._duration,
- isComplete, callback, pt, cycleDuration, r, type, pow, rawPrevTime;
+ prevRawPrevTime = this._rawPrevTime,
+ isComplete, callback, pt, cycleDuration, r, type, pow, rawPrevTime, i;
if (time >= totalDur) {
this._totalTime = totalDur;
this._cycle = this._repeat;
if (this._yoyo && (this._cycle & 1) !== 0) {
this._time = 0;
@@ -117,38 +121,37 @@
}
if (!this._reversed) {
isComplete = true;
callback = "onComplete";
}
- if (duration === 0) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
- rawPrevTime = this._rawPrevTime;
+ if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
if (this._startTime === this._timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent rounding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate.
time = 0;
}
- if (time === 0 || rawPrevTime < 0 || rawPrevTime === _tinyNum) if (rawPrevTime !== time) {
+ if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time) {
force = true;
- if (rawPrevTime > _tinyNum) {
+ if (prevRawPrevTime > _tinyNum) {
callback = "onReverseComplete";
}
}
- this._rawPrevTime = rawPrevTime = (!suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
+ this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
}
} else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
this._totalTime = this._time = this._cycle = 0;
this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
- if (prevTotalTime !== 0 || (duration === 0 && this._rawPrevTime > 0 && this._rawPrevTime !== _tinyNum)) {
+ if (prevTotalTime !== 0 || (duration === 0 && prevRawPrevTime > 0 && prevRawPrevTime !== _tinyNum)) {
callback = "onReverseComplete";
isComplete = this._reversed;
}
if (time < 0) {
this._active = false;
- if (duration === 0) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
- if (this._rawPrevTime >= 0) {
+ if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
+ if (prevRawPrevTime >= 0) {
force = true;
}
- this._rawPrevTime = rawPrevTime = (!suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
+ this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
}
} else if (!this._initted) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately.
force = true;
}
} else {
@@ -168,11 +171,11 @@
this._time = duration;
} else if (this._time < 0) {
this._time = 0;
}
}
-
+
if (this._easeType) {
r = this._time / duration;
type = this._easeType;
pow = this._easePower;
if (type === 1 || (type === 3 && r >= 0.5)) {
@@ -188,21 +191,21 @@
} else if (pow === 3) {
r *= r * r * r;
} else if (pow === 4) {
r *= r * r * r * r;
}
-
+
if (type === 1) {
this.ratio = 1 - r;
} else if (type === 2) {
this.ratio = r;
} else if (this._time / duration < 0.5) {
this.ratio = r / 2;
} else {
this.ratio = 1 - (r / 2);
}
-
+
} else {
this.ratio = this._ease.getRatio(this._time / duration);
}
}
@@ -214,23 +217,38 @@
return;
} else if (!this._initted) {
this._init();
if (!this._initted || this._gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that overwriting occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example.
return;
+ } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) { //we stick it in the queue for rendering at the very end of the tick - this is a performance optimization because browsers invalidate styles and force a recalculation if you read, write, and then read style data (so it's better to read/read/read/write/write/write than read/write/read/write/read/write). The down side, of course, is that usually you WANT things to render immediately because you may have code running right after that which depends on the change. Like imagine running TweenLite.set(...) and then immediately after that, creating a nother tween that animates the same property to another value; the starting values of that 2nd tween wouldn't be accurate if lazy is true.
+ this._time = prevTime;
+ this._totalTime = prevTotalTime;
+ this._rawPrevTime = prevRawPrevTime;
+ this._cycle = prevCycle;
+ TweenLiteInternals.lazyTweens.push(this);
+ this._lazy = time;
+ return;
}
//_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently.
if (this._time && !isComplete) {
this.ratio = this._ease.getRatio(this._time / duration);
} else if (isComplete && this._ease._calcEnd) {
this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1);
}
}
-
+ if (this._lazy !== false) {
+ this._lazy = false;
+ }
+
if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) {
this._active = true; //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done.
}
if (prevTotalTime === 0) {
+ if (this._initted === 2 && time > 0) {
+ //this.invalidate();
+ this._init(); //will just apply overwriting since _initted of (2) means it was a from() tween that had immediateRender:true
+ }
if (this._startAt) {
if (time >= 0) {
this._startAt.render(time, suppressEvents, force);
} else if (!callback) {
callback = "_dummyGS"; //if no callback is defined, use a dummy value just so that the condition at the end evaluates as true because _startAt should render AFTER the normal render loop when the time is negative. We could handle this in a more intuitive way, of course, but the render loop is the MOST important thing to optimize, so this technique allows us to avoid adding extra conditional logic in a high-frequency area.
@@ -393,11 +411,11 @@
isDC, tween, i;
for (i = 0; i < l; i++) {
tween = a[i];
if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) {
if (complete) {
- tween.totalTime(tween.totalDuration());
+ tween.totalTime(tween._reversed ? 0 : tween.totalDuration());
} else {
tween._enabled(false, false);
}
}
}
@@ -405,11 +423,11 @@
TweenMax.killChildTweensOf = function(parent, complete) {
if (parent == null) {
return;
}
- var tl = TweenLite._tweenLookup,
+ var tl = TweenLiteInternals.tweenLookup,
a, curParent, p, i, l;
if (typeof(parent) === "string") {
parent = TweenLite.selector(parent) || parent;
}
if (_isSelector(parent)) {
@@ -609,11 +627,11 @@
}
},
_slice = _blankArray.slice,
p = TimelineLite.prototype = new SimpleTimeline();
- TimelineLite.version = "1.11.8";
+ TimelineLite.version = "1.12.1";
p.constructor = TimelineLite;
p.kill()._gc = false;
p.to = function(target, duration, vars, position) {
var Engine = (vars.repeat && _globals.TweenMax) || TweenLite;
@@ -927,34 +945,30 @@
while (tween) {
next = tween._next; //record it here because the value could change after rendering...
if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
break;
} else if (tween._active || (tween._startTime <= this._time && !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;
}
} else {
tween = this._last;
while (tween) {
next = tween._prev; //record it here because the value could change after rendering...
if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
break;
} else if (tween._active || (tween._startTime <= prevTime && !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;
}
}
@@ -1011,19 +1025,27 @@
}
return a;
};
p.getTweensOf = function(target, nested) {
- var tweens = TweenLite.getTweensOf(target),
- i = tweens.length,
+ var disabled = this._gc,
a = [],
- cnt = 0;
+ cnt = 0,
+ tweens, i;
+ if (disabled) {
+ this._enabled(true, true); //getTweensOf() filters out disabled tweens, and we have to mark them as _gc = true when the timeline completes in order to allow clean garbage collection, so temporarily re-enable the timeline here.
+ }
+ tweens = TweenLite.getTweensOf(target);
+ i = tweens.length;
while (--i > -1) {
if (tweens[i].timeline === this || (nested && this._contains(tweens[i]))) {
a[cnt++] = tweens[i];
}
}
+ if (disabled) {
+ this._enabled(false, true);
+ }
return a;
};
p._contains = function(tween) {
var tl = tween.timeline;
@@ -1208,11 +1230,11 @@
_easeNone = new Ease(null, null, 1, 0),
p = TimelineMax.prototype = new TimelineLite();
p.constructor = TimelineMax;
p.kill()._gc = false;
- TimelineMax.version = "1.11.8";
+ TimelineMax.version = "1.12.1";
p.invalidate = function() {
this._yoyo = (this.vars.yoyo === true);
this._repeat = this.vars.repeat || 0;
this._repeatDelay = this.vars.repeatDelay || 0;
@@ -1453,11 +1475,10 @@
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;
}
}
@@ -2236,11 +2257,11 @@
_overwriteProps, //alias to the currently instantiating CSSPlugin's _overwriteProps array. We use this closure in order to avoid having to pass a reference around from method to method and aid in minification.
_specialProps = {},
p = CSSPlugin.prototype = new TweenPlugin("css");
p.constructor = CSSPlugin;
- CSSPlugin.version = "1.11.8";
+ CSSPlugin.version = "1.12.1";
CSSPlugin.API = 2;
CSSPlugin.defaultTransformPerspective = 0;
CSSPlugin.defaultSkewType = "compensated";
p = "px"; //we'll reuse the "p" variable to keep file size down
CSSPlugin.suffixMap = {top:p, right:p, bottom:p, left:p, width:p, height:p, fontSize:p, padding:p, margin:p, perspective:p, lineHeight:""};
@@ -2249,12 +2270,12 @@
var _numExp = /(?:\d|\-\d|\.\d|\-\.\d)+/g,
_relNumExp = /(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g,
_valuesExp = /(?:\+=|\-=|\-|\b)[\d\-\.]+[a-zA-Z0-9]*(?:%|\b)/gi, //finds all the values that begin with numbers or += or -= and then a number. Includes suffixes. We use this to split complex values apart like "1px 5px 20px rgb(255,102,51)"
_NaNExp = /[^\d\-\.]/g,
_suffixExp = /(?:\d|\-|\+|=|#|\.)*/g,
- _opacityExp = /opacity *= *([^)]*)/,
- _opacityValExp = /opacity:([^;]*)/,
+ _opacityExp = /opacity *= *([^)]*)/i,
+ _opacityValExp = /opacity:([^;]*)/i,
_alphaFilterExp = /alpha\(opacity *=.+?\)/i,
_rgbhslExp = /^(rgb|hsl)/,
_capsExp = /([A-Z])/g,
_camelExp = /-([a-z])/gi,
_urlExp = /(^(?:url\(\"|url\())|(?:(\"\))$|\)$)/gi, //for pulling out urls from url(...) or url("...") strings (some browsers wrap urls in quotes, some don't when reporting things like backgroundImage)
@@ -2343,11 +2364,11 @@
if (!_supportsOpacity) if (p === "opacity") { //several versions of IE don't use the standard "opacity" property - they use things like filter:alpha(opacity=50), so we parse that here.
return _getIEOpacity(t);
}
if (!calc && t.style[p]) {
rv = t.style[p];
- } else if ((cs = cs || _getComputedStyle(t, null))) {
+ } else if ((cs = cs || _getComputedStyle(t))) {
rv = cs[p] || cs.getPropertyValue(p) || cs.getPropertyValue(p.replace(_capsExp, "-$1").toLowerCase());
} else if (t.currentStyle) {
rv = t.currentStyle[p];
}
return (dflt != null && (!rv || rv === "none" || rv === "auto" || rv === "auto auto")) ? dflt : rv;
@@ -3527,10 +3548,14 @@
sy = t.scaleY,
sz = t.scaleZ,
perspective = t.perspective,
a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43,
zOrigin, rnd, cos, sin, t1, t2, t3, t4;
+ if (v === 1 || v === 0) if (t.force3D === "auto") if (!t.rotationY && !t.rotationX && sz === 1 && !perspective && !t.z) { //on the final render (which could be 0 for a from tween), if there are no 3D aspects, render in 2D to free up memory and improve performance especially on mobile devices
+ _set2DTransformRatio.call(this, v);
+ return;
+ }
if (_isFirefox) {
var n = 0.0001;
if (sx < n && sx > -n) { //Firefox has a bug (at least in v25) that causes it to render the transparent part of 32-bit PNG images as black when displayed inside an iframe and the 3D scale is very small and doesn't change sufficiently enough between renders (like if you use a Power4.easeInOut to scale from 0 to 1 where the beginning values only change a tiny amount to begin the tween before accelerating). In this case, we force the scale to be 0.00002 instead which is visually the same but works around the Firefox issue.
sx = sz = 0.00002;
}
@@ -3636,11 +3661,11 @@
_set2DTransformRatio = _internals.set2DTransformRatio = function(v) {
var t = this.data, //refers to the element's _gsTransform object
targ = this.t,
style = targ.style,
ang, skew, rnd, sx, sy;
- if (t.rotationX || t.rotationY || t.z || t.force3D) { //if a 3D tween begins while a 2D one is running, we need to kick the rendering over to the 3D method. For example, imagine a yoyo-ing, infinitely repeating scale tween running, and then the object gets rotated in 3D space with a different tween.
+ if (t.rotationX || t.rotationY || t.z || t.force3D === true || (t.force3D === "auto" && v !== 1 && v !== 0)) { //if a 3D tween begins while a 2D one is running, we need to kick the rendering over to the 3D method. For example, imagine a yoyo-ing, infinitely repeating scale tween running, and then the object gets rotated in 3D space with a different tween.
this.setRatio = _set3DTransformRatio;
_set3DTransformRatio.call(this, v);
return;
}
if (!t.rotation && !t.skewX) {
@@ -3664,15 +3689,17 @@
i = _transformProps.length,
v = vars,
endRotations = {},
m2, skewY, copy, orig, has3D, hasChange, dr;
if (typeof(v.transform) === "string" && _transformProp) { //for values like transform:"rotate(60deg) scale(0.5, 0.8)"
- copy = style.cssText;
- style[_transformProp] = v.transform;
- style.display = "block"; //if display is "none", the browser often refuses to report the transform properties correctly.
- m2 = _getTransform(t, null, false);
- style.cssText = copy;
+ copy = _tempDiv.style; //don't use the original target because it might be SVG in which case some browsers don't report computed style correctly.
+ copy[_transformProp] = v.transform;
+ copy.display = "block"; //if display is "none", the browser often refuses to report the transform properties correctly.
+ copy.position = "absolute";
+ _doc.body.appendChild(_tempDiv);
+ m2 = _getTransform(_tempDiv, null, false);
+ _doc.body.removeChild(_tempDiv);
} else if (typeof(v) === "object") { //for values like scaleX, scaleY, rotation, x, y, skewX, and skewY or transform:{...} (object)
m2 = {scaleX:_parseVal((v.scaleX != null) ? v.scaleX : v.scale, m1.scaleX),
scaleY:_parseVal((v.scaleY != null) ? v.scaleY : v.scale, m1.scaleY),
scaleZ:_parseVal(v.scaleZ, m1.scaleZ),
x:_parseVal(v.x, m1.x),
@@ -3742,16 +3769,16 @@
pt.plugin = plugin;
if (_supports3D) {
copy = m1.zOrigin;
orig = orig.split(" ");
m1.zOrigin = ((orig.length > 2 && !(copy !== 0 && orig[2] === "0px")) ? parseFloat(orig[2]) : copy) || 0; //Safari doesn't handle the z part of transformOrigin correctly, so we'll manually handle it in the _set3DTransformRatio() method.
- pt.xs0 = pt.e = style[p] = orig[0] + " " + (orig[1] || "50%") + " 0px"; //we must define a z value of 0px specifically otherwise iOS 5 Safari will stick with the old one (if one was defined)!
+ pt.xs0 = pt.e = orig[0] + " " + (orig[1] || "50%") + " 0px"; //we must define a z value of 0px specifically otherwise iOS 5 Safari will stick with the old one (if one was defined)!
pt = new CSSPropTween(m1, "zOrigin", 0, 0, pt, -1, pt.n); //we must create a CSSPropTween for the _gsTransform.zOrigin so that it gets reset properly at the beginning if the tween runs backward (as opposed to just setting m1.zOrigin here)
pt.b = copy;
pt.xs0 = pt.e = m1.zOrigin;
} else {
- pt.xs0 = pt.e = style[p] = orig;
+ pt.xs0 = pt.e = orig;
}
//for older versions of IE (6-8), we need to manually calculate things inside the setRatio() function. We record origin x and y (ox and oy) and whether or not the values are percentages (oxp and oyp).
} else {
_parsePosition(orig + "", m1);
@@ -3902,11 +3929,11 @@
}
if (!skip) {
if (this.xn1) {
t.filter = filters = filters || ("alpha(opacity=" + val + ")"); //works around bug in IE7/8 that prevents changes to "visibility" from being applied properly if the filter is changed to a different alpha on the same frame.
}
- if (filters.indexOf("opacity") === -1) { //only used if browser doesn't support the standard opacity style property (IE 7 and 8)
+ if (filters.indexOf("pacity") === -1) { //only used if browser doesn't support the standard opacity style property (IE 7 and 8). We omit the "O" to avoid case-sensitivity issues
if (val !== 0 || !this.xn1) { //bugs in IE7/8 won't render the filter properly if opacity is ADDED on the same frame/render as "visibility" changes (this.xn1 is 1 if this tween is an "autoAlpha" tween)
t.filter = filters + " alpha(opacity=" + val + ")"; //we round the value because otherwise, bugs in IE7/8 can prevent "visibility" changes from being applied properly.
}
} else {
t.filter = filters.replace(_opacityExp, "opacity=" + val);
@@ -3959,11 +3986,11 @@
}
},
_setClassNameRatio = function(v) {
this.t._gsClassPT = this;
if (v === 1 || v === 0) {
- this.t.className = (v === 0) ? this.b : this.e;
+ this.t.setAttribute("class", (v === 0) ? this.b : this.e);
var mpt = this.data, //first MiniPropTween
s = this.t.style;
while (mpt) {
if (!mpt.v) {
_removeProp(s, mpt.p);
@@ -3973,16 +4000,16 @@
mpt = mpt._next;
}
if (v === 1 && this.t._gsClassPT === this) {
this.t._gsClassPT = null;
}
- } else if (this.t.className !== this.e) {
- this.t.className = this.e;
+ } else if (this.t.getAttribute("class") !== this.e) {
+ this.t.setAttribute("class", this.e);
}
};
_registerComplexSpecialProp("className", {parser:function(t, e, p, cssp, pt, plugin, vars) {
- var b = t.className,
+ var b = t.getAttribute("class") || "", //don't use t.className because it doesn't work consistently on SVG elements; getAttribute("class") and setAttribute("class", value") is more reliable.
cssText = t.style.cssText,
difData, bs, cnpt, cnptLookup, mpt;
pt = cssp._classNamePT = new CSSPropTween(t, p, 0, 0, pt, 2);
pt.setRatio = _setClassNameRatio;
pt.pr = -11;
@@ -4001,13 +4028,13 @@
cnpt.setRatio(1);
}
t._gsClassPT = pt;
pt.e = (e.charAt(1) !== "=") ? e : b.replace(new RegExp("\\s*\\b" + e.substr(2) + "\\b"), "") + ((e.charAt(0) === "+") ? " " + e.substr(2) : "");
if (cssp._tween._duration) { //if it's a zero-duration tween, there's no need to tween anything or parse the data. In fact, if we switch classes temporarily (which we must do for proper parsing) and the class has a transition applied, it could cause a quick flash to the end state and back again initially in some browsers.
- t.className = pt.e;
+ t.setAttribute("class", pt.e);
difData = _cssDif(t, bs, _getAllStyles(t), vars, cnptLookup);
- t.className = b;
+ t.setAttribute("class", b);
pt.data = difData.firstMPT;
t.style.cssText = cssText; //we recorded cssText before we swapped classes and ran _getAllStyles() because in cases when a className tween is overwritten, we remove all the related tweening properties from that class change (otherwise class-specific stuff can't override properties we've directly set on the target's style object due to specificity).
pt = pt.xfirst = cssp.parse(t, difData.difs, pt, plugin); //we record the CSSPropTween as the xfirst so that we can handle overwriting propertly (if "className" gets overwritten, we must kill all the properties associated with the className part of the tween, so we can loop through from xfirst to the pt itself)
}
return pt;
@@ -4089,11 +4116,11 @@
v, pt, pt2, first, last, next, zIndex, tpt, threeD;
if (_reqSafariFix) if (style.zIndex === "") {
v = _getStyle(target, "zIndex", _cs);
if (v === "auto" || v === "") {
//corrects a bug in [non-Android] Safari that prevents it from repainting elements in their new positions if they don't have a zIndex set. We also can't just apply this inside _parseTransform() because anything that's moved in any way (like using "left" or "top" instead of transforms like "x" and "y") can be affected, so it is best to ensure that anything that's tweening has a z-index. Setting "WebkitPerspective" to a non-zero value worked too except that on iOS Safari things would flicker randomly. Plus zIndex is less memory-intensive.
- style.zIndex = 0;
+ this._addLazySet(style, "zIndex", 0);
}
}
if (typeof(vars) === "string") {
first = style.cssText;
@@ -4116,20 +4143,20 @@
_reqSafariFix = true;
//if zIndex isn't set, iOS Safari doesn't repaint things correctly sometimes (seemingly at random).
if (style.zIndex === "") {
zIndex = _getStyle(target, "zIndex", _cs);
if (zIndex === "auto" || zIndex === "") {
- style.zIndex = 0;
+ this._addLazySet(style, "zIndex", 0);
}
}
//Setting WebkitBackfaceVisibility corrects 3 bugs:
// 1) [non-Android] Safari skips rendering changes to "top" and "left" that are made on the same frame/render as a transform update.
// 2) iOS Safari sometimes neglects to repaint elements in their new positions. Setting "WebkitPerspective" to a non-zero value worked too except that on iOS Safari things would flicker randomly.
// 3) Safari sometimes displayed odd artifacts when tweening the transform (or WebkitTransform) property, like ghosts of the edges of the element remained. Definitely a browser bug.
//Note: we allow the user to override the auto-setting by defining WebkitBackfaceVisibility in the vars of the tween.
if (_isSafariLT6) {
- style.WebkitBackfaceVisibility = this._vars.WebkitBackfaceVisibility || (threeD ? "visible" : "hidden");
+ this._addLazySet(style, "WebkitBackfaceVisibility", this._vars.WebkitBackfaceVisibility || (threeD ? "visible" : "hidden"));
}
}
pt2 = pt;
while (pt2 && pt2._next) {
pt2 = pt2._next;
@@ -4351,10 +4378,22 @@
p._enableTransforms = function(threeD) {
this._transformType = (threeD || this._transformType === 3) ? 3 : 2;
this._transform = this._transform || _getTransform(this._target, _cs, true); //ensures that the element has a _gsTransform property with the appropriate values.
};
+ var lazySet = function(v) {
+ this.t[this.p] = this.e;
+ this.data._linkCSSP(this, this._next, null, true); //we purposefully keep this._next even though it'd make sense to null it, but this is a performance optimization, as this happens during the while (pt) {} loop in setRatio() at the bottom of which it sets pt = pt._next, so if we null it, the linked list will be broken in that loop.
+ };
+ /** @private Gives us a way to set a value on the first render (and only the first render). **/
+ p._addLazySet = function(t, p, v) {
+ var pt = this._firstPT = new CSSPropTween(t, p, 0, 0, this._firstPT, 2);
+ pt.e = v;
+ pt.setRatio = lazySet;
+ pt.data = this;
+ };
+
/** @private **/
p._linkCSSP = function(pt, next, prev, remove) {
if (pt) {
if (next) {
next._prev = pt;
@@ -4581,11 +4620,11 @@
* ----------------------------------------------------------------
*/
window._gsDefine.plugin({
propName: "attr",
API: 2,
- version: "0.3.0",
+ version: "0.3.2",
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
init: function(target, value, tween) {
var p, start, end;
if (typeof(target.setAttribute) !== "function") {
@@ -4593,34 +4632,33 @@
}
this._target = target;
this._proxy = {};
this._start = {}; // we record start and end values exactly as they are in case they're strings (not numbers) - we need to be able to revert to them cleanly.
this._end = {};
- this._endRatio = tween.vars.runBackwards ? 0 : 1;
for (p in value) {
this._start[p] = this._proxy[p] = start = target.getAttribute(p);
- this._end[p] = end = value[p];
- this._addTween(this._proxy, p, parseFloat(start), end, p);
+ end = this._addTween(this._proxy, p, parseFloat(start), value[p], p);
+ this._end[p] = end ? end.s + end.c : value[p];
this._overwriteProps.push(p);
}
return true;
},
//called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
set: function(ratio) {
this._super.setRatio.call(this, ratio);
var props = this._overwriteProps,
i = props.length,
- lookup = (ratio !== 0 && ratio !== 1) ? this._proxy : (ratio === this._endRatio) ? this._end : this._start,
+ lookup = (ratio === 1) ? this._end : ratio ? this._proxy : this._start,
p;
while (--i > -1) {
p = props[i];
this._target.setAttribute(p, lookup[p] + "");
}
}
- })
+ });
@@ -5326,16 +5364,22 @@
_class("Ticker", function(fps, useRAF) {
var _self = this,
_startTime = _getTime(),
_useRAF = (useRAF !== false && _reqAnimFrame),
+ _lagThreshold = 500,
+ _adjustedLag = 33,
_fps, _req, _id, _gap, _nextTime,
_tick = function(manual) {
- _lastUpdate = _getTime();
+ var elapsed = _getTime() - _lastUpdate,
+ overlap, dispatch;
+ if (elapsed > _lagThreshold) {
+ _startTime += elapsed - _adjustedLag;
+ }
+ _lastUpdate += elapsed;
_self.time = (_lastUpdate - _startTime) / 1000;
- var overlap = _self.time - _nextTime,
- dispatch;
+ overlap = _self.time - _nextTime;
if (!_fps || overlap > 0 || manual === true) {
_self.frame++;
_nextTime += overlap + (overlap >= _gap ? 0.004 : _gap - overlap);
dispatch = true;
}
@@ -5351,10 +5395,15 @@
_self.time = _self.frame = 0;
_self.tick = function() {
_tick(true);
};
+ _self.lagSmoothing = function(threshold, adjustedLag) {
+ _lagThreshold = threshold || (1 / _tinyNum); //zero should be interpreted as basically unlimited
+ _adjustedLag = Math.min(adjustedLag, _lagThreshold, 0);
+ };
+
_self.sleep = function() {
if (_id == null) {
return;
}
if (!_useRAF || !_cancelAnimFrame) {
@@ -5370,10 +5419,12 @@
};
_self.wake = function() {
if (_id !== null) {
_self.sleep();
+ } else if (_self.frame > 10) { //don't trigger lagSmoothing if we're just waking up, and make sure that at least 10 frames have elapsed because of the iOS bug that we work around below with the 1.5-second setTimout().
+ _lastUpdate = _getTime() - _lagThreshold + 5;
}
_req = (_fps === 0) ? _emptyFunc : (!_useRAF || !_reqAnimFrame) ? function(f) { return setTimeout(f, ((_nextTime - _self.time) * 1000 + 1) | 0); } : _reqAnimFrame;
if (_self === _ticker) {
_tickerActive = true;
}
@@ -5655,10 +5706,13 @@
if (this._gc) {
this._enabled(true, false);
}
if (this._totalTime !== time || this._duration === 0) {
this.render(time, suppressEvents, false);
+ if (_lazyTweens.length) { //in case rendering caused any tweens to lazy-init, we should render them because typically when someone calls seek() or time() or progress(), they expect an immediate render.
+ _lazyRender();
+ }
}
}
return this;
};
@@ -5835,11 +5889,10 @@
_ticker.wake();
}
return this._totalTime;
};
-
/*
* ----------------------------------------------------------------
* TweenLite
* ----------------------------------------------------------------
*/
@@ -5891,21 +5944,22 @@
if (overwrite === 1) if (this._siblings.length > 1) {
_applyOverwrite(target, this, null, 1, this._siblings);
}
}
if (this.vars.immediateRender || (duration === 0 && this._delay === 0 && this.vars.immediateRender !== false)) {
- this.render(-this._delay, false, true);
+ this._time = -_tinyNum; //forces a render without having to set the render() "force" parameter to true because we want to allow lazying by default (using the "force" parameter always forces an immediate full render)
+ this.render(-this._delay);
}
}, true),
_isSelector = function(v) {
return (v.length && v !== window && v[0] && (v[0] === window || (v[0].nodeType && v[0].style && !v.nodeType))); //we cannot check "nodeType" if the target is window from within an iframe, otherwise it will trigger a security error in some browsers like Firefox.
},
_autoCSS = function(vars, target) {
var css = {},
p;
for (p in vars) {
- if (!_reservedProps[p] && (!(p in target) || p === "x" || p === "y" || p === "width" || p === "height" || p === "className" || p === "border") && (!_plugins[p] || (_plugins[p] && _plugins[p]._autoCSS))) { //note: <img> elements contain read-only "x" and "y" properties. We should also prioritize editing css width/height rather than the element's properties.
+ if (!_reservedProps[p] && (!(p in target) || p === "transform" || p === "x" || p === "y" || p === "width" || p === "height" || p === "className" || p === "border") && (!_plugins[p] || (_plugins[p] && _plugins[p]._autoCSS))) { //note: <img> elements contain read-only "x" and "y" properties. We should also prioritize editing css width/height rather than the element's properties.
css[p] = vars[p];
delete vars[p];
}
}
vars.css = css;
@@ -5917,37 +5971,61 @@
//----TweenLite defaults, overwrite management, and root updates ----------------------------------------------------
p.ratio = 0;
p._firstPT = p._targets = p._overwrittenProps = p._startAt = null;
- p._notifyPluginsOfEnabled = false;
+ p._notifyPluginsOfEnabled = p._lazy = false;
- TweenLite.version = "1.11.8";
+ TweenLite.version = "1.12.1";
TweenLite.defaultEase = p._ease = new Ease(null, null, 1, 1);
TweenLite.defaultOverwrite = "auto";
TweenLite.ticker = _ticker;
TweenLite.autoSleep = true;
+ TweenLite.lagSmoothing = function(threshold, adjustedLag) {
+ _ticker.lagSmoothing(threshold, adjustedLag);
+ };
TweenLite.selector = window.$ || window.jQuery || function(e) { if (window.$) { TweenLite.selector = window.$; return window.$(e); } return window.document ? window.document.getElementById((e.charAt(0) === "#") ? e.substr(1) : e) : e; };
- var _internals = TweenLite._internals = {isArray:_isArray, isSelector:_isSelector}, //gives us a way to expose certain private values to other GreenSock classes without contaminating tha main TweenLite object.
+ var _lazyTweens = [],
+ _lazyLookup = {},
+ _internals = TweenLite._internals = {isArray:_isArray, isSelector:_isSelector, lazyTweens:_lazyTweens}, //gives us a way to expose certain private values to other GreenSock classes without contaminating tha main TweenLite object.
_plugins = TweenLite._plugins = {},
- _tweenLookup = TweenLite._tweenLookup = {},
+ _tweenLookup = _internals.tweenLookup = {},
_tweenLookupNum = 0,
- _reservedProps = _internals.reservedProps = {ease:1, delay:1, overwrite:1, onComplete:1, onCompleteParams:1, onCompleteScope:1, useFrames:1, runBackwards:1, startAt:1, onUpdate:1, onUpdateParams:1, onUpdateScope:1, onStart:1, onStartParams:1, onStartScope:1, onReverseComplete:1, onReverseCompleteParams:1, onReverseCompleteScope:1, onRepeat:1, onRepeatParams:1, onRepeatScope:1, easeParams:1, yoyo:1, immediateRender:1, repeat:1, repeatDelay:1, data:1, paused:1, reversed:1, autoCSS:1},
+ _reservedProps = _internals.reservedProps = {ease:1, delay:1, overwrite:1, onComplete:1, onCompleteParams:1, onCompleteScope:1, useFrames:1, runBackwards:1, startAt:1, onUpdate:1, onUpdateParams:1, onUpdateScope:1, onStart:1, onStartParams:1, onStartScope:1, onReverseComplete:1, onReverseCompleteParams:1, onReverseCompleteScope:1, onRepeat:1, onRepeatParams:1, onRepeatScope:1, easeParams:1, yoyo:1, immediateRender:1, repeat:1, repeatDelay:1, data:1, paused:1, reversed:1, autoCSS:1, lazy:1},
_overwriteLookup = {none:0, all:1, auto:2, concurrent:3, allOnStart:4, preexisting:5, "true":1, "false":0},
_rootFramesTimeline = Animation._rootFramesTimeline = new SimpleTimeline(),
- _rootTimeline = Animation._rootTimeline = new SimpleTimeline();
+ _rootTimeline = Animation._rootTimeline = new SimpleTimeline(),
+ _lazyRender = function() {
+ var i = _lazyTweens.length;
+ _lazyLookup = {};
+ while (--i > -1) {
+ a = _lazyTweens[i];
+ if (a && a._lazy !== false) {
+ a.render(a._lazy, false, true);
+ a._lazy = false;
+ }
+ }
+ _lazyTweens.length = 0;
+ };
_rootTimeline._startTime = _ticker.time;
_rootFramesTimeline._startTime = _ticker.frame;
_rootTimeline._active = _rootFramesTimeline._active = true;
+ setTimeout(_lazyRender, 1); //on some mobile devices, there isn't a "tick" before code runs which means any lazy renders wouldn't run before the next official "tick".
- Animation._updateRoot = function() {
+ Animation._updateRoot = TweenLite.render = function() {
+ var i, a, p;
+ if (_lazyTweens.length) { //if code is run outside of the requestAnimationFrame loop, there may be tweens queued AFTER the engine refreshed, so we need to ensure any pending renders occur before we refresh again.
+ _lazyRender();
+ }
_rootTimeline.render((_ticker.time - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false);
_rootFramesTimeline.render((_ticker.frame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false);
+ if (_lazyTweens.length) {
+ _lazyRender();
+ }
if (!(_ticker.frame % 120)) { //dump garbage every 120 frames...
- var i, a, p;
for (p in _tweenLookup) {
a = _tweenLookup[p].tweens;
i = a.length;
while (--i > -1) {
if (a[i]._gc) {
@@ -6063,20 +6141,27 @@
p._init = function() {
var v = this.vars,
op = this._overwrittenProps,
dur = this._duration,
- immediate = v.immediateRender,
+ immediate = !!v.immediateRender,
ease = v.ease,
- i, initPlugins, pt, p;
+ i, initPlugins, pt, p, startVars;
if (v.startAt) {
if (this._startAt) {
this._startAt.render(-1, true); //if we've run a startAt previously (when the tween instantiated), we should revert it so that the values re-instantiate correctly particularly for relative tweens. Without this, a TweenLite.fromTo(obj, 1, {x:"+=100"}, {x:"-=100"}), for example, would actually jump to +=200 because the startAt would run twice, doubling the relative change.
+ this._startAt.kill();
}
- v.startAt.overwrite = 0;
- v.startAt.immediateRender = true;
- this._startAt = TweenLite.to(this.target, 0, v.startAt);
+ startVars = {};
+ for (p in v.startAt) { //copy the properties/values into a new object to avoid collisions, like var to = {x:0}, from = {x:500}; timeline.fromTo(e, 1, from, to).fromTo(e, 1, to, from);
+ startVars[p] = v.startAt[p];
+ }
+ startVars.overwrite = false;
+ startVars.immediateRender = true;
+ startVars.lazy = (immediate && v.lazy !== false);
+ startVars.startAt = startVars.delay = null; //no nesting of startAt objects allowed (otherwise it could cause an infinite loop).
+ this._startAt = TweenLite.to(this.target, 0, startVars);
if (immediate) {
if (this._time > 0) {
this._startAt = null; //tweens that render immediately (like most from() and fromTo() tweens) shouldn't revert when their parent timeline's playhead goes backward past the startTime because the initial render could have happened anytime and it shouldn't be directly correlated to this tween's startTime. Imagine setting up a complex animation where the beginning states of various objects are rendered immediately but the tween doesn't happen for quite some time - if we revert to the starting values as soon as the playhead goes backward past the tween's startTime, it will throw things off visually. Reversion should only happen in TimelineLite/Max instances where immediateRender was false (which is the default in the convenience methods like from()).
} else if (dur !== 0) {
return; //we skip initialization here so that overwriting doesn't occur until the tween actually begins. Otherwise, if you create several immediateRender:true tweens of the same target/properties to drop into a TimelineLite or TimelineMax, the last one created would overwrite the first ones because they didn't get placed into the timeline yet before the first render occurs and kicks in overwriting.
@@ -6084,23 +6169,27 @@
}
} else if (v.runBackwards && dur !== 0) {
//from() tweens must be handled uniquely: their beginning values must be rendered but we don't want overwriting to occur yet (when time is still 0). Wait until the tween actually begins before doing all the routines like overwriting. At that time, we should render at the END of the tween to ensure that things initialize correctly (remember, from() tweens go backwards)
if (this._startAt) {
this._startAt.render(-1, true);
+ this._startAt.kill();
this._startAt = null;
} else {
pt = {};
for (p in v) { //copy props into a new object and skip any reserved props, otherwise onComplete or onUpdate or onStart could fire. We should, however, permit autoCSS to go through.
if (!_reservedProps[p] || p === "autoCSS") {
pt[p] = v[p];
}
}
pt.overwrite = 0;
pt.data = "isFromStart"; //we tag the tween with as "isFromStart" so that if [inside a plugin] we need to only do something at the very END of a tween, we have a way of identifying this tween as merely the one that's setting the beginning values for a "from()" tween. For example, clearProps in CSSPlugin should only get applied at the very END of a tween and without this tag, from(...{height:100, clearProps:"height", delay:1}) would wipe the height at the beginning of the tween and after 1 second, it'd kick back in.
+ pt.lazy = (immediate && v.lazy !== false);
+ pt.immediateRender = immediate; //zero-duration tweens render immediately by default, but if we're not specifically instructed to render this tween immediately, we should skip this and merely _init() to record the starting values (rendering them immediately would push them to completion which is wasteful in that case - we'd have to render(-1) immediately after)
this._startAt = TweenLite.to(this.target, 0, pt);
- if (!v.immediateRender) {
- this._startAt.render(-1, true); //for tweens that aren't rendered immediately, we still need to use the _startAt to record the starting values so that we can revert to them if the parent timeline's playhead goes backward beyond the beginning, but we immediately revert the tween back otherwise the parent tween that's currently instantiating wouldn't see the wrong starting values (since they were changed by the _startAt tween)
+ if (!immediate) {
+ this._startAt._init(); //ensures that the initial values are recorded
+ this._startAt._enabled(false); //no need to have the tween render on the next cycle. Disable it because we'll always manually control the renders of the _startAt tween.
} else if (this._time === 0) {
return;
}
}
}
@@ -6147,10 +6236,15 @@
p._initProps = function(target, propLookup, siblings, overwrittenProps) {
var p, i, initPlugins, plugin, pt, v;
if (target == null) {
return false;
}
+
+ if (_lazyLookup[target._gsTweenID]) {
+ _lazyRender(); //if other tweens of the same target have recently initted but haven't rendered yet, we've got to force the render so that the starting values are correct (imagine populating a timeline with a bunch of sequential tweens and then jumping to the end)
+ }
+
if (!this.vars.css) if (target.style) if (target !== window && target.nodeType) if (_plugins.css) if (this.vars.autoCSS !== false) { //it's so common to use TweenLite/Max to animate the css of DOM elements, we assume that if the target is a DOM element, that's what is intended (a convenience so that users don't have to wrap things in css:{}, although we still recommend it for a slight performance boost and better specificity). Note: we cannot check "nodeType" on the window inside an iframe.
_autoCSS(this.vars, target);
}
for (p in this.vars) {
v = this.vars[p];
@@ -6196,52 +6290,55 @@
}
if (this._overwrite > 1) if (this._firstPT) if (siblings.length > 1) if (_applyOverwrite(target, this, propLookup, this._overwrite, siblings)) {
this._kill(propLookup, target);
return this._initProps(target, propLookup, siblings, overwrittenProps);
}
+ if (this._firstPT) if ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration)) { //zero duration tweens don't lazy render by default; everything else does.
+ _lazyLookup[target._gsTweenID] = true;
+ }
return initPlugins;
};
p.render = function(time, suppressEvents, force) {
var prevTime = this._time,
duration = this._duration,
+ prevRawPrevTime = this._rawPrevTime,
isComplete, callback, pt, rawPrevTime;
if (time >= duration) {
this._totalTime = this._time = duration;
this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1;
- if (!this._reversed) {
+ if (!this._reversed ) {
isComplete = true;
callback = "onComplete";
}
- if (duration === 0) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
- rawPrevTime = this._rawPrevTime;
+ if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
if (this._startTime === this._timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent rounding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate.
time = 0;
}
- if (time === 0 || rawPrevTime < 0 || rawPrevTime === _tinyNum) if (rawPrevTime !== time) {
+ if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time) {
force = true;
- if (rawPrevTime > _tinyNum) {
+ if (prevRawPrevTime > _tinyNum) {
callback = "onReverseComplete";
}
}
- this._rawPrevTime = rawPrevTime = (!suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
+ this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
}
} else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
this._totalTime = this._time = 0;
this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
- if (prevTime !== 0 || (duration === 0 && this._rawPrevTime > 0 && this._rawPrevTime !== _tinyNum)) {
+ if (prevTime !== 0 || (duration === 0 && prevRawPrevTime > 0 && prevRawPrevTime !== _tinyNum)) {
callback = "onReverseComplete";
isComplete = this._reversed;
}
if (time < 0) {
this._active = false;
- if (duration === 0) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
- if (this._rawPrevTime >= 0) {
+ if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
+ if (prevRawPrevTime >= 0) {
force = true;
}
- this._rawPrevTime = rawPrevTime = (!suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
+ this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
}
} else if (!this._initted) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately.
force = true;
}
} else {
@@ -6276,28 +6373,35 @@
}
} else {
this.ratio = this._ease.getRatio(time / duration);
}
-
}
if (this._time === prevTime && !force) {
return;
} else if (!this._initted) {
this._init();
if (!this._initted || this._gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that overwriting occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example.
return;
+ } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) {
+ this._time = this._totalTime = prevTime;
+ this._rawPrevTime = prevRawPrevTime;
+ _lazyTweens.push(this);
+ this._lazy = time;
+ return;
}
//_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently.
if (this._time && !isComplete) {
this.ratio = this._ease.getRatio(this._time / duration);
} else if (isComplete && this._ease._calcEnd) {
this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1);
}
}
-
+ if (this._lazy !== false) { //in case a lazy render is pending, we should flush it because the new render is occuring now (imagine a lazy tween instantiating and then immediately the user calls tween.seek(tween.duration()), skipping to the end - the end render would be forced, and then if we didn't flush the lazy render, it'd fire AFTER the seek(), rendering it at the wrong time.
+ this._lazy = false;
+ }
if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) {
this._active = true; //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done.
}
if (prevTime === 0) {
if (this._startAt) {
@@ -6354,10 +6458,11 @@
p._kill = function(vars, target) {
if (vars === "all") {
vars = null;
}
if (vars == null) if (target == null || target === this.target) {
+ this._lazy = false;
return this._enabled(false, false);
}
target = (typeof(target) !== "string") ? (target || this._targets || this.target) : TweenLite.selector(target) || target;
var i, overwrittenProps, p, pt, propLookup, changed, killProps, record;
if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") {
@@ -6424,10 +6529,10 @@
}
this._firstPT = null;
this._overwrittenProps = null;
this._onUpdate = null;
this._startAt = null;
- this._initted = this._active = this._notifyPluginsOfEnabled = false;
+ this._initted = this._active = this._notifyPluginsOfEnabled = this._lazy = false;
this._propLookup = (this._targets) ? {} : [];
return this;
};
p._enabled = function(enabled, ignoreTimeline) {
\ No newline at end of file