iwmlib/lib/3rdparty/greensock/src/uncompressed/plugins/ThrowPropsPlugin.js

740 lines
31 KiB
JavaScript
Executable File

/*!
* VERSION: 0.11.2
* DATE: 2019-02-07
* UPDATES AND DOCS AT: http://greensock.com
*
* @license Copyright (c) 2008-2019, GreenSock. All rights reserved.
* ThrowPropsPlugin is a Club GreenSock membership benefit; You must have a valid membership to use
* this code without violating the terms of use. Visit http://greensock.com/club/ to sign up or get more details.
* This work is subject to the software agreement that was issued with your membership.
*
* @author: Jack Doyle, jack@greensock.com
*/
/* eslint-disable */
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() {
"use strict";
_gsScope._gsDefine("plugins.ThrowPropsPlugin", ["plugins.TweenPlugin", "TweenLite", "easing.Ease", "utils.VelocityTracker"], function(TweenPlugin, TweenLite, Ease, VelocityTracker) {
var ThrowPropsPlugin = function(props, priority) {
TweenPlugin.call(this, "throwProps");
this._overwriteProps.length = 0;
},
_max = 999999999999999,
_min = 0.0000000001,
_globals = _gsScope._gsDefine.globals,
_recordEndMode = false,//in a typical throwProps css tween that has an "end" defined as a function, it grabs that value initially when the tween is rendered, then again when we calculate the necessary duration, and then a 3rd time after we invalidate() the tween, so we toggle _recordEndMode to true when we're about to begin such a tween which tells the engine to grab the end value(s) once and record them as "max" and "min" on the throwProps object, thus we can skip those extra calls. Then we set it back to false when we're done with our fancy initialization routine.
_transforms = {x:1,y:1,z:2,scale:1,scaleX:1,scaleY:1,rotation:1,rotationZ:1,rotationX:2,rotationY:2,skewX:1,skewY:1,xPercent:1,yPercent:1},
_getClosest = function(n, values, max, min, radius) {
var i = values.length,
closest = 0,
absDif = _max,
val, dif, p, dist;
if (typeof(n) === "object") {
while (--i > -1) {
val = values[i];
dif = 0;
for (p in n) {
dist = val[p] - n[p];
dif += dist * dist;
}
if (dif < absDif) {
closest = i;
absDif = dif;
}
}
if ((radius || _max) < _max && radius < Math.sqrt(absDif)) {
return n;
}
} else {
while (--i > -1) {
val = values[i];
dif = val - n;
if (dif < 0) {
dif = -dif;
}
if (dif < absDif && val >= min && val <= max) {
closest = i;
absDif = dif;
}
}
}
return values[closest];
},
_parseEnd = function(curProp, end, max, min, name, radius) {
if (curProp.end === "auto") {
return curProp;
}
var endVar = curProp.end,
adjustedEnd, p;
max = isNaN(max) ? _max : max;
min = isNaN(min) ? -_max : min;
if (typeof(end) === "object") { //for objects, like {x, y} where they're linked and we must pass an object to the function or find the closest value in an array.
adjustedEnd = end.calculated ? end : ((typeof(endVar) === "function") ? endVar(end) : _getClosest(end, endVar, max, min, radius)) || end;
if (!end.calculated) {
for (p in adjustedEnd) {
end[p] = adjustedEnd[p];
}
end.calculated = true;
}
adjustedEnd = adjustedEnd[name];
} else {
adjustedEnd = (typeof(endVar) === "function") ? endVar(end) : (endVar instanceof Array) ? _getClosest(end, endVar, max, min, radius) : Number(endVar);
}
if (adjustedEnd > max) {
adjustedEnd = max;
} else if (adjustedEnd < min) {
adjustedEnd = min;
}
return {max:adjustedEnd, min:adjustedEnd, unitFactor:curProp.unitFactor};
},
_extend = function(decoratee, extras, exclude) {
for (var p in extras) {
if (decoratee[p] === undefined && p !== exclude) {
decoratee[p] = extras[p];
}
}
return decoratee;
},
_calculateChange = ThrowPropsPlugin.calculateChange = function(velocity, ease, duration, checkpoint) {
if (checkpoint == null) {
checkpoint = 0.05;
}
var e = (ease instanceof Ease) ? ease : (!ease) ? TweenLite.defaultEase : new Ease(ease);
return (duration * checkpoint * velocity) / e.getRatio(checkpoint);
},
_calculateDuration = ThrowPropsPlugin.calculateDuration = function(start, end, velocity, ease, checkpoint) {
checkpoint = checkpoint || 0.05;
var e = (ease instanceof Ease) ? ease : (!ease) ? TweenLite.defaultEase : new Ease(ease);
return Math.abs( (end - start) * e.getRatio(checkpoint) / velocity / checkpoint );
},
_calculateTweenDuration = ThrowPropsPlugin.calculateTweenDuration = function(target, vars, maxDuration, minDuration, overshootTolerance, recordEnd) {
if (typeof(target) === "string") {
target = TweenLite.selector(target);
}
if (!target) {
return 0;
}
if (maxDuration == null) {
maxDuration = 10;
}
if (minDuration == null) {
minDuration = 0.2;
}
if (overshootTolerance == null) {
overshootTolerance = 1;
}
if (target.length) {
target = target[0] || target;
}
var duration = 0,
clippedDuration = 9999999999,
throwPropsVars = vars.throwProps || vars,
ease = (vars.ease instanceof Ease) ? vars.ease : (!vars.ease) ? TweenLite.defaultEase : new Ease(vars.ease),
checkpoint = isNaN(throwPropsVars.checkpoint) ? 0.05 : Number(throwPropsVars.checkpoint),
resistance = isNaN(throwPropsVars.resistance) ? ThrowPropsPlugin.defaultResistance : Number(throwPropsVars.resistance),
p, curProp, curDuration, curVelocity, curResistance, curVal, end, curClippedDuration, tracker, unitFactor,
linkedProps, linkedPropNames, i;
if (throwPropsVars.linkedProps) { //when there are linkedProps (typically "x,y" where snapping has to factor in multiple properties, we must first populate an object with all of those end values, then feed it to the function that make any necessary alterations. So the point of this first loop is to simply build an object (like {x:100, y:204.5}) for feeding into that function which we'll do later in the "real" loop.
linkedPropNames = throwPropsVars.linkedProps.split(",");
linkedProps = {};
for (i = 0; i < linkedPropNames.length; i++) {
p = linkedPropNames[i];
curProp = throwPropsVars[p];
if (curProp) {
if (curProp.velocity !== undefined && typeof(curProp.velocity) === "number") {
curVelocity = Number(curProp.velocity) || 0;
} else {
tracker = tracker || VelocityTracker.getByTarget(target);
curVelocity = (tracker && tracker.isTrackingProp(p)) ? tracker.getVelocity(p) : 0;
}
curResistance = isNaN(curProp.resistance) ? resistance : Number(curProp.resistance);
curDuration = (curVelocity * curResistance > 0) ? curVelocity / curResistance : curVelocity / -curResistance;
curVal = (typeof(target[p]) === "function") ? target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]() : target[p] || 0;
linkedProps[p] = curVal + _calculateChange(curVelocity, ease, curDuration, checkpoint);
}
}
}
for (p in throwPropsVars) {
if (p !== "resistance" && p !== "checkpoint" && p !== "preventOvershoot" && p !== "linkedProps" && p !== "radius") {
curProp = throwPropsVars[p];
if (typeof(curProp) !== "object") {
tracker = tracker || VelocityTracker.getByTarget(target);
if (tracker && tracker.isTrackingProp(p)) {
curProp = (typeof(curProp) === "number") ? {velocity:curProp} : {velocity:tracker.getVelocity(p)}; //if we're tracking this property, we should use the tracking velocity and then use the numeric value that was passed in as the min and max so that it tweens exactly there.
} else {
curVelocity = Number(curProp) || 0;
curDuration = (curVelocity * resistance > 0) ? curVelocity / resistance : curVelocity / -resistance;
}
}
if (typeof(curProp) === "object") {
if (curProp.velocity !== undefined && typeof(curProp.velocity) === "number") {
curVelocity = Number(curProp.velocity) || 0;
} else {
tracker = tracker || VelocityTracker.getByTarget(target);
curVelocity = (tracker && tracker.isTrackingProp(p)) ? tracker.getVelocity(p) : 0;
}
curResistance = isNaN(curProp.resistance) ? resistance : Number(curProp.resistance);
curDuration = (curVelocity * curResistance > 0) ? curVelocity / curResistance : curVelocity / -curResistance;
curVal = (typeof(target[p]) === "function") ? target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]() : target[p] || 0;
end = curVal + _calculateChange(curVelocity, ease, curDuration, checkpoint);
if (curProp.end !== undefined) {
curProp = _parseEnd(curProp, (linkedProps && p in linkedProps) ? linkedProps : end, curProp.max, curProp.min, p, throwPropsVars.radius);
if (recordEnd || _recordEndMode) {
throwPropsVars[p] = _extend(curProp, throwPropsVars[p], "end");
}
}
if (curProp.max !== undefined && end > Number(curProp.max) + _min) {
unitFactor = curProp.unitFactor || ThrowPropsPlugin.defaultUnitFactors[p] || 1; //some values are measured in special units like radians in which case our thresholds need to be adjusted accordingly.
//if the value is already exceeding the max or the velocity is too low, the duration can end up being uncomfortably long but in most situations, users want the snapping to occur relatively quickly (0.75 seconds), so we implement a cap here to make things more intuitive. If the max and min match, it means we're animating to a particular value and we don't want to shorten the time unless the velocity is really slow. Example: a rotation where the start and natural end value are less than the snapping spot, but the natural end is pretty close to the snap.
curClippedDuration = ((curVal > curProp.max && curProp.min !== curProp.max) || (curVelocity * unitFactor > -15 && curVelocity * unitFactor < 45)) ? (minDuration + (maxDuration - minDuration) * 0.1) : _calculateDuration(curVal, curProp.max, curVelocity, ease, checkpoint);
if (curClippedDuration + overshootTolerance < clippedDuration) {
clippedDuration = curClippedDuration + overshootTolerance;
}
} else if (curProp.min !== undefined && end < Number(curProp.min) - _min) {
unitFactor = curProp.unitFactor || ThrowPropsPlugin.defaultUnitFactors[p] || 1; //some values are measured in special units like radians in which case our thresholds need to be adjusted accordingly.
//if the value is already exceeding the min or if the velocity is too low, the duration can end up being uncomfortably long but in most situations, users want the snapping to occur relatively quickly (0.75 seconds), so we implement a cap here to make things more intuitive.
curClippedDuration = ((curVal < curProp.min && curProp.min !== curProp.max) || (curVelocity * unitFactor > -45 && curVelocity * unitFactor < 15)) ? (minDuration + (maxDuration - minDuration) * 0.1) : _calculateDuration(curVal, curProp.min, curVelocity, ease, checkpoint);
if (curClippedDuration + overshootTolerance < clippedDuration) {
clippedDuration = curClippedDuration + overshootTolerance;
}
}
if (curClippedDuration > duration) {
duration = curClippedDuration;
}
}
if (curDuration > duration) {
duration = curDuration;
}
}
}
if (duration > clippedDuration) {
duration = clippedDuration;
}
if (duration > maxDuration) {
return maxDuration;
} else if (duration < minDuration) {
return minDuration;
}
return duration;
},
p = ThrowPropsPlugin.prototype = new TweenPlugin("throwProps"),
_cssProxy, _cssVars, _last, _lastValue; //these serve as a cache of sorts, recording the last css-related proxy and the throwProps vars that get calculated in the _cssRegister() method. This allows us to grab them in the ThrowPropsPlugin.to() function and calculate the duration. Of course we could have structured things in a more "clean" fashion, but performance is of paramount importance.
p.constructor = ThrowPropsPlugin;
ThrowPropsPlugin.version = "0.11.2";
ThrowPropsPlugin.API = 2;
ThrowPropsPlugin._autoCSS = true; //indicates that this plugin can be inserted into the "css" object using the autoCSS feature of TweenLite
ThrowPropsPlugin.defaultResistance = 100;
ThrowPropsPlugin.defaultUnitFactors = {time:1000, totalTime:1000}; //setting the unitFactor to a higher value (default is 1) reduces the chance of the auto-accelerating behavior kicking in when determining durations when the initial velocity is adequately low - imagine dragging something past a boundary and then letting go - snapping back relatively quickly should be prioritized over matching the initial velocity (at least that's the behavior most people consider intuitive). But in some situations when the units are very low (like "time" of a timeline or rotation when using radians), it can kick in too frequently so this allows tweaking.
ThrowPropsPlugin.track = function(target, props, types) {
return VelocityTracker.track(target, props, types);
};
ThrowPropsPlugin.untrack = function(target, props) {
VelocityTracker.untrack(target, props);
};
ThrowPropsPlugin.isTracking = function(target, prop) {
return VelocityTracker.isTracking(target, prop);
};
ThrowPropsPlugin.getVelocity = function(target, prop) {
var vt = VelocityTracker.getByTarget(target);
return vt ? vt.getVelocity(prop) : NaN;
};
ThrowPropsPlugin._cssRegister = function() {
var CSSPlugin = _globals.com.greensock.plugins.CSSPlugin;
if (!CSSPlugin) {
return;
}
var _internals = CSSPlugin._internals,
_parseToProxy = _internals._parseToProxy,
_setPluginRatio = _internals._setPluginRatio,
CSSPropTween = _internals.CSSPropTween;
_internals._registerComplexSpecialProp("throwProps", {parser:function(t, e, prop, cssp, pt, plugin) {
plugin = new ThrowPropsPlugin();
var velocities = {},
min = {},
max = {},
end = {},
res = {},
preventOvershoot = {},
hasResistance, val, p, data, tracker;
_cssVars = {};
for (p in e) {
if (p !== "resistance" && p !== "preventOvershoot" && p !== "linkedProps" && p !== "radius") {
val = e[p];
if (typeof(val) === "object") {
if (val.velocity !== undefined && typeof(val.velocity) === "number") {
velocities[p] = Number(val.velocity) || 0;
} else {
tracker = tracker || VelocityTracker.getByTarget(t);
velocities[p] = (tracker && tracker.isTrackingProp(p)) ? tracker.getVelocity(p) : 0; //rotational values are actually converted to radians in CSSPlugin, but our tracking velocity is in radians already, so make it into degrees to avoid a funky conversion
}
if (val.end !== undefined) {
end[p] = val.end;
}
if (val.min !== undefined) {
min[p] = val.min;
}
if (val.max !== undefined) {
max[p] = val.max;
}
if (val.preventOvershoot) {
preventOvershoot[p] = true;
}
if (val.resistance !== undefined) {
hasResistance = true;
res[p] = val.resistance;
}
} else if (typeof(val) === "number") {
velocities[p] = val;
} else {
tracker = tracker || VelocityTracker.getByTarget(t);
if (tracker && tracker.isTrackingProp(p)) {
velocities[p] = tracker.getVelocity(p);
} else {
velocities[p] = val || 0;
}
}
if (_transforms[p]) {
cssp._enableTransforms((_transforms[p] === 2));
}
}
}
data = _parseToProxy(t, velocities, cssp, pt, plugin);
_cssProxy = data.proxy;
velocities = data.end;
for (p in _cssProxy) {
_cssVars[p] = {velocity:velocities[p], min:min[p], max:max[p], end:end[p], resistance:res[p], preventOvershoot:preventOvershoot[p]};
}
if (e.resistance != null) {
_cssVars.resistance = e.resistance;
}
if (e.linkedProps != null) {
_cssVars.linkedProps = e.linkedProps;
}
if (e.radius != null) {
_cssVars.radius = e.radius;
}
if (e.preventOvershoot) {
_cssVars.preventOvershoot = true;
}
pt = new CSSPropTween(t, "throwProps", 0, 0, data.pt, 2);
cssp._overwriteProps.pop(); //don't overwrite all other throwProps tweens. In the CSSPropTween constructor, we add the property to the _overwriteProps, so remove it here.
pt.plugin = plugin;
pt.setRatio = _setPluginRatio;
pt.data = data;
plugin._onInitTween(_cssProxy, _cssVars, cssp._tween);
return pt;
}});
};
ThrowPropsPlugin.to = function(target, vars, maxDuration, minDuration, overshootTolerance) {
if (!vars.throwProps) {
vars = {throwProps:vars};
}
if (overshootTolerance === 0) {
vars.throwProps.preventOvershoot = true;
}
_recordEndMode = true; //if we encounter a function-based "end" value, ThrowPropsPlugin will record it as "max" and "min" properties, replacing "end" (this is an optimization so that the function only gets called once)
var tween = new TweenLite(target, minDuration || 1, vars);
tween.render(0, true, true); //we force a render so that the CSSPlugin instantiates and populates the _cssProxy and _cssVars which we need in order to calculate the tween duration. Remember, we can't use the regular target for calculating the duration because the current values wouldn't be able to be grabbed like target["propertyName"], as css properties can be complex like boxShadow:"10px 10px 20px 30px red" or backgroundPosition:"25px 50px". The proxy is the result of breaking all that complex data down and finding just the numeric values and assigning them to a generic proxy object with unique names. THAT is what the _calculateTweenDuration() can look at. We also needed to do the same break down of any min or max or velocity data
if (tween.vars.css) {
tween.duration(_calculateTweenDuration(_cssProxy, {throwProps:_cssVars, ease:vars.ease}, maxDuration, minDuration, overshootTolerance));
if (tween._delay && !tween.vars.immediateRender) {
tween.invalidate(); //if there's a delay, the starting values could be off, so invalidate() to force reinstantiation when the tween actually starts.
} else {
_last._onInitTween(_cssProxy, _lastValue, tween);
}
_recordEndMode = false;
return tween;
} else {
tween.kill();
tween = new TweenLite(target, _calculateTweenDuration(target, vars, maxDuration, minDuration, overshootTolerance), vars);
_recordEndMode = false;
return tween;
}
};
p._onInitTween = function(target, value, tween, index) {
this.target = target;
this._props = [];
_last = this;
_lastValue = value;
var ease = tween._ease,
checkpoint = isNaN(value.checkpoint) ? 0.05 : Number(value.checkpoint),
duration = tween._duration,
preventOvershoot = value.preventOvershoot,
cnt = 0,
p, curProp, curVal, isFunc, velocity, change1, end, change2, tracker,
linkedProps, linkedPropNames, i;
if (value.linkedProps) { //when there are linkedProps (typically "x,y" where snapping has to factor in multiple properties, we must first populate an object with all of those end values, then feed it to the function that make any necessary alterations. So the point of this first loop is to simply build an object (like {x:100, y:204.5}) for feeding into that function which we'll do later in the "real" loop.
linkedPropNames = value.linkedProps.split(",");
linkedProps = {};
for (i = 0; i < linkedPropNames.length; i++) {
p = linkedPropNames[i];
curProp = value[p];
if (curProp) {
if (curProp.velocity !== undefined && typeof(curProp.velocity) === "number") {
velocity = Number(curProp.velocity) || 0;
} else {
tracker = tracker || VelocityTracker.getByTarget(target);
velocity = (tracker && tracker.isTrackingProp(p)) ? tracker.getVelocity(p) : 0;
}
curVal = (typeof(target[p]) === "function") ? target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]() : target[p] || 0;
linkedProps[p] = curVal + _calculateChange(velocity, ease, duration, checkpoint);
}
}
}
for (p in value) {
if (p !== "resistance" && p !== "checkpoint" && p !== "preventOvershoot" && p !== "linkedProps" && p !== "radius") {
curProp = value[p];
if (typeof(curProp) === "function") {
curProp = curProp(index, target);
}
if (typeof(curProp) === "number") {
velocity = Number(curProp) || 0;
} else if (typeof(curProp) === "object" && !isNaN(curProp.velocity)) {
velocity = Number(curProp.velocity);
} else {
tracker = tracker || VelocityTracker.getByTarget(target);
if (tracker && tracker.isTrackingProp(p)) {
velocity = tracker.getVelocity(p);
} else {
throw("ERROR: No velocity was defined in the throwProps tween of " + target + " property: " + p);
}
}
change1 = _calculateChange(velocity, ease, duration, checkpoint);
change2 = 0;
isFunc = (typeof(target[p]) === "function");
curVal = (isFunc) ? target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]() : target[p];
if (typeof(curProp) === "object") {
end = curVal + change1;
if (curProp.end !== undefined) {
curProp = _parseEnd(curProp, (linkedProps && p in linkedProps) ? linkedProps : end, curProp.max, curProp.min, p, value.radius);
if (_recordEndMode) {
value[p] = _extend(curProp, value[p], "end");
}
}
if (curProp.max !== undefined && Number(curProp.max) < end) {
if (preventOvershoot || curProp.preventOvershoot) {
change1 = curProp.max - curVal;
} else {
change2 = (curProp.max - curVal) - change1;
}
} else if (curProp.min !== undefined && Number(curProp.min) > end) {
if (preventOvershoot || curProp.preventOvershoot) {
change1 = curProp.min - curVal;
} else {
change2 = (curProp.min - curVal) - change1;
}
}
}
this._overwriteProps[cnt] = p;
this._props[cnt++] = {p:p, s:curVal, c1:change1, c2:change2, f:isFunc, r:false};
}
}
return true;
};
p._kill = function(lookup) {
var i = this._props.length;
while (--i > -1) {
if (lookup[this._props[i].p] != null) {
this._props.splice(i, 1);
}
}
return TweenPlugin.prototype._kill.call(this, lookup);
};
p._mod = function(lookup) {
var p = this._props,
i = p.length,
val;
while (--i > -1) {
val = lookup[p[i].p] || lookup.throwProps;
if (typeof(val) === "function") {
p[i].m = val;
}
}
};
p.setRatio = function(v) {
var i = this._props.length,
cp, val;
while (--i > -1) {
cp = this._props[i];
val = cp.s + cp.c1 * v + cp.c2 * v * v;
if (cp.m) {
val = cp.m(val, this.target);
} else if (v === 1) {
val = ((val * 10000 + (val < 0 ? -0.5 : 0.5)) | 0) / 10000; //if we don't round things at the very end, binary math issues can creep in and cause snapping not to be exact (like landing on 20.000000000001 instead of 20).
}
if (cp.f) {
this.target[cp.p](val);
} else {
this.target[cp.p] = val;
}
}
};
TweenPlugin.activate([ThrowPropsPlugin]);
return ThrowPropsPlugin;
}, true);
/*
* ----------------------------------------------------------------
* VelocityTracker
* ----------------------------------------------------------------
*/
_gsScope._gsDefine("utils.VelocityTracker", ["TweenLite"], function(TweenLite) {
var _first, _initted, _time1, _time2,
_capsExp = /([A-Z])/g,
_empty = {},
_doc = _gsScope.document,
_transforms = {x:1,y:1,z:2,scale:1,scaleX:1,scaleY:1,rotation:1,rotationZ:1,rotationX:2,rotationY:2,skewX:1,skewY:1,xPercent:1,yPercent:1},
_computedStyleScope = (typeof(window) !== "undefined" ? window : _doc.defaultView || {getComputedStyle:function() {}}),
_getComputedStyle = function(e) {
return _computedStyleScope.getComputedStyle(e); //to avoid errors in Microsoft Edge, we need to call getComputedStyle() from a specific scope, typically window.
},
_getStyle = function(t, p, cs) {
var rv = (t._gsTransform || _empty)[p];
if (rv || rv === 0) {
return rv;
} else if (t.style[p]) {
rv = t.style[p];
} else if ((cs = cs || _getComputedStyle(t, null))) {
rv = cs[p] || cs.getPropertyValue(p) || cs.getPropertyValue(p.replace(_capsExp, "-$1").toLowerCase());
} else if (t.currentStyle) {
rv = t.currentStyle[p];
}
return parseFloat(rv) || 0;
},
_ticker = TweenLite.ticker,
VelocityProp = function(p, isFunc, next) {
this.p = p;
this.f = isFunc;
this.v1 = this.v2 = 0;
this.t1 = this.t2 = _ticker.time;
this.css = false;
this.type = "";
this._prev = null;
if (next) {
this._next = next;
next._prev = this;
}
},
_update = function() {
var vt = _first,
t = _ticker.time,
val, vp;
//if the frame rate is too high, we won't be able to track the velocity as well, so only update the values about 33 times per second
if (t - _time1 >= 0.03) {
_time2 = _time1;
_time1 = t;
while (vt) {
vp = vt._firstVP;
while (vp) {
val = vp.css ? _getStyle(vt.target, vp.p) : vp.f ? vt.target[vp.p]() : vt.target[vp.p];
if (val !== vp.v1 || t - vp.t1 > 0.15) { //use a threshold of 0.15 seconds for zeroing-out velocity. If we only use 0.03 and things update slightly slower, like some Android devices dispatch "touchmove" events sluggishly so 2 or 3 ticks of the TweenLite.ticker may elapse inbetween, thus it may appear like the object is not moving but it actually is but it's not updating as frequently. A threshold of 0.15 seconds seems to be a good balance. We want to update things frequently (0.03 seconds) when they're moving so that we can respond to fast motions accurately, but we want to be more resistant to go back to a zero velocity.
vp.v2 = vp.v1;
vp.v1 = val;
vp.t2 = vp.t1;
vp.t1 = t;
}
vp = vp._next;
}
vt = vt._next;
}
}
},
VelocityTracker = function(target) {
this._lookup = {};
this.target = target;
this.elem = (target.style && target.nodeType) ? true : false;
if (!_initted) {
_ticker.addEventListener("tick", _update, null, false, -100);
_time1 = _time2 = _ticker.time;
_initted = true;
}
if (_first) {
this._next = _first;
_first._prev = this;
}
_first = this;
},
getByTarget = VelocityTracker.getByTarget = function(target) {
var vt = _first;
while (vt) {
if (vt.target === target) {
return vt;
}
vt = vt._next;
}
},
p = VelocityTracker.prototype;
p.addProp = function(prop, type) {
if (!this._lookup[prop]) {
var t = this.target,
isFunc = (typeof(t[prop]) === "function"),
alt = isFunc ? this._altProp(prop) : prop,
vp = this._firstVP;
this._firstVP = this._lookup[prop] = this._lookup[alt] = vp = new VelocityProp((alt !== prop && prop.indexOf("set") === 0) ? alt : prop, isFunc, vp);
vp.css = (this.elem && (this.target.style[vp.p] !== undefined || _transforms[vp.p]));
if (vp.css && _transforms[vp.p] && !t._gsTransform) {
TweenLite.set(t, {x:"+=0", overwrite:false}); //just forces CSSPlugin to create a _gsTransform for the element if it doesn't exist
}
vp.type = type || (vp.css && prop.indexOf("rotation") === 0) ? "deg" : "";
vp.v1 = vp.v2 = vp.css ? _getStyle(t, vp.p) : isFunc ? t[vp.p]() : t[vp.p];
}
};
p.removeProp = function(prop) {
var vp = this._lookup[prop];
if (vp) {
if (vp._prev) {
vp._prev._next = vp._next;
} else if (vp === this._firstVP) {
this._firstVP = vp._next;
}
if (vp._next) {
vp._next._prev = vp._prev;
}
this._lookup[prop] = 0;
if (vp.f) {
this._lookup[this._altProp(prop)] = 0; //if it's a getter/setter, we should remove the matching counterpart (if one exists)
}
}
};
p.isTrackingProp = function(prop) {
return (this._lookup[prop] instanceof VelocityProp);
};
p.getVelocity = function(prop) {
var vp = this._lookup[prop],
target = this.target,
val, dif, rotationCap;
if (!vp) {
throw "The velocity of " + prop + " is not being tracked.";
}
val = vp.css ? _getStyle(target, vp.p) : vp.f ? target[vp.p]() : target[vp.p];
dif = (val - vp.v2);
if (vp.type === "rad" || vp.type === "deg") { //rotational values need special interpretation so that if, for example, they go from 179 to -178 degrees it is interpreted as a change of 3 instead of -357.
rotationCap = (vp.type === "rad") ? Math.PI * 2 : 360;
dif = dif % rotationCap;
if (dif !== dif % (rotationCap / 2)) {
dif = (dif < 0) ? dif + rotationCap : dif - rotationCap;
}
}
return dif / (_ticker.time - vp.t2);
};
p._altProp = function(p) { //for getters/setters like getCustomProp() and setCustomProp() - we should accommodate both
var pre = p.substr(0, 3),
alt = ((pre === "get") ? "set" : (pre === "set") ? "get" : pre) + p.substr(3);
return (typeof(this.target[alt]) === "function") ? alt : p;
};
VelocityTracker.getByTarget = function(target) {
var vt = _first;
if (typeof(target) === "string") {
target = TweenLite.selector(target);
}
if (target.length && target !== window && target[0] && target[0].style && !target.nodeType) {
target = target[0];
}
while (vt) {
if (vt.target === target) {
return vt;
}
vt = vt._next;
}
};
VelocityTracker.track = function(target, props, types) {
var vt = getByTarget(target),
a = props.split(","),
i = a.length;
types = (types || "").split(",");
if (!vt) {
vt = new VelocityTracker(target);
}
while (--i > -1) {
vt.addProp(a[i], types[i] || types[0]);
}
return vt;
};
VelocityTracker.untrack = function(target, props) {
var vt = getByTarget(target),
a = (props || "").split(","),
i = a.length;
if (!vt) {
return;
}
while (--i > -1) {
vt.removeProp(a[i]);
}
if (!vt._firstVP || !props) {
if (vt._prev) {
vt._prev._next = vt._next;
} else if (vt === _first) {
_first = vt._next;
}
if (vt._next) {
vt._next._prev = vt._prev;
}
}
};
VelocityTracker.isTracking = function(target, prop) {
var vt = getByTarget(target);
return (!vt) ? false : (!prop && vt._firstVP) ? true : vt.isTrackingProp(prop);
};
return VelocityTracker;
}, true);
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); }
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
(function(name) {
"use strict";
var getGlobal = function() {
return (_gsScope.GreenSockGlobals || _gsScope)[name];
};
if (typeof(module) !== "undefined" && module.exports) { //node
require("../TweenLite.js");
module.exports = getGlobal();
} else if (typeof(define) === "function" && define.amd) { //AMD
define(["TweenLite"], getGlobal);
}
}("ThrowPropsPlugin"));