iwmlib/lib/3rdparty/greensock/src/esm/BezierPlugin.js

604 lines
19 KiB
JavaScript

/*!
* VERSION: 1.3.8
* DATE: 2018-05-30
* UPDATES AND DOCS AT: http://greensock.com
*
* @license Copyright (c) 2008-2019, GreenSock. All rights reserved.
* This work is subject to the terms at http://greensock.com/standard-license or for
* Club GreenSock members, the software agreement that was issued with your membership.
*
* @author: Jack Doyle, jack@greensock.com
**/
/* eslint-disable */
import { _gsScope } from "./TweenLite.js";
var _RAD2DEG = 180 / Math.PI,
_r1 = [],
_r2 = [],
_r3 = [],
_corProps = {},
_globals = _gsScope._gsDefine.globals,
Segment = function(a, b, c, d) {
if (c === d) { //if c and d match, the final autoRotate value could lock at -90 degrees, so differentiate them slightly.
c = d - (d - b) / 1000000;
}
if (a === b) { //if a and b match, the starting autoRotate value could lock at -90 degrees, so differentiate them slightly.
b = a + (c - a) / 1000000;
}
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.da = d - a;
this.ca = c - a;
this.ba = b - a;
},
_correlate = ",x,y,z,left,top,right,bottom,marginTop,marginLeft,marginRight,marginBottom,paddingLeft,paddingTop,paddingRight,paddingBottom,backgroundPosition,backgroundPosition_y,",
cubicToQuadratic = function(a, b, c, d) {
var q1 = {a:a},
q2 = {},
q3 = {},
q4 = {c:d},
mab = (a + b) / 2,
mbc = (b + c) / 2,
mcd = (c + d) / 2,
mabc = (mab + mbc) / 2,
mbcd = (mbc + mcd) / 2,
m8 = (mbcd - mabc) / 8;
q1.b = mab + (a - mab) / 4;
q2.b = mabc + m8;
q1.c = q2.a = (q1.b + q2.b) / 2;
q2.c = q3.a = (mabc + mbcd) / 2;
q3.b = mbcd - m8;
q4.b = mcd + (d - mcd) / 4;
q3.c = q4.a = (q3.b + q4.b) / 2;
return [q1, q2, q3, q4];
},
_calculateControlPoints = function(a, curviness, quad, basic, correlate) {
var l = a.length - 1,
ii = 0,
cp1 = a[0].a,
i, p1, p2, p3, seg, m1, m2, mm, cp2, qb, r1, r2, tl;
for (i = 0; i < l; i++) {
seg = a[ii];
p1 = seg.a;
p2 = seg.d;
p3 = a[ii+1].d;
if (correlate) {
r1 = _r1[i];
r2 = _r2[i];
tl = ((r2 + r1) * curviness * 0.25) / (basic ? 0.5 : _r3[i] || 0.5);
m1 = p2 - (p2 - p1) * (basic ? curviness * 0.5 : (r1 !== 0 ? tl / r1 : 0));
m2 = p2 + (p3 - p2) * (basic ? curviness * 0.5 : (r2 !== 0 ? tl / r2 : 0));
mm = p2 - (m1 + (((m2 - m1) * ((r1 * 3 / (r1 + r2)) + 0.5) / 4) || 0));
} else {
m1 = p2 - (p2 - p1) * curviness * 0.5;
m2 = p2 + (p3 - p2) * curviness * 0.5;
mm = p2 - (m1 + m2) / 2;
}
m1 += mm;
m2 += mm;
seg.c = cp2 = m1;
if (i !== 0) {
seg.b = cp1;
} else {
seg.b = cp1 = seg.a + (seg.c - seg.a) * 0.6; //instead of placing b on a exactly, we move it inline with c so that if the user specifies an ease like Back.easeIn or Elastic.easeIn which goes BEYOND the beginning, it will do so smoothly.
}
seg.da = p2 - p1;
seg.ca = cp2 - p1;
seg.ba = cp1 - p1;
if (quad) {
qb = cubicToQuadratic(p1, cp1, cp2, p2);
a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]);
ii += 4;
} else {
ii++;
}
cp1 = m2;
}
seg = a[ii];
seg.b = cp1;
seg.c = cp1 + (seg.d - cp1) * 0.4; //instead of placing c on d exactly, we move it inline with b so that if the user specifies an ease like Back.easeOut or Elastic.easeOut which goes BEYOND the end, it will do so smoothly.
seg.da = seg.d - seg.a;
seg.ca = seg.c - seg.a;
seg.ba = cp1 - seg.a;
if (quad) {
qb = cubicToQuadratic(seg.a, cp1, seg.c, seg.d);
a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]);
}
},
_parseAnchors = function(values, p, correlate, prepend) {
var a = [],
l, i, p1, p2, p3, tmp;
if (prepend) {
values = [prepend].concat(values);
i = values.length;
while (--i > -1) {
if (typeof( (tmp = values[i][p]) ) === "string") if (tmp.charAt(1) === "=") {
values[i][p] = prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)); //accommodate relative values. Do it inline instead of breaking it out into a function for speed reasons
}
}
}
l = values.length - 2;
if (l < 0) {
a[0] = new Segment(values[0][p], 0, 0, values[0][p]);
return a;
}
for (i = 0; i < l; i++) {
p1 = values[i][p];
p2 = values[i+1][p];
a[i] = new Segment(p1, 0, 0, p2);
if (correlate) {
p3 = values[i+2][p];
_r1[i] = (_r1[i] || 0) + (p2 - p1) * (p2 - p1);
_r2[i] = (_r2[i] || 0) + (p3 - p2) * (p3 - p2);
}
}
a[i] = new Segment(values[i][p], 0, 0, values[i+1][p]);
return a;
},
bezierThrough = function(values, curviness, quadratic, basic, correlate, prepend) {
var obj = {},
props = [],
first = prepend || values[0],
i, p, a, j, r, l, seamless, last;
correlate = (typeof(correlate) === "string") ? ","+correlate+"," : _correlate;
if (curviness == null) {
curviness = 1;
}
for (p in values[0]) {
props.push(p);
}
//check to see if the last and first values are identical (well, within 0.05). If so, make seamless by appending the second element to the very end of the values array and the 2nd-to-last element to the very beginning (we'll remove those segments later)
if (values.length > 1) {
last = values[values.length - 1];
seamless = true;
i = props.length;
while (--i > -1) {
p = props[i];
if (Math.abs(first[p] - last[p]) > 0.05) { //build in a tolerance of +/-0.05 to accommodate rounding errors.
seamless = false;
break;
}
}
if (seamless) {
values = values.concat(); //duplicate the array to avoid contaminating the original which the user may be reusing for other tweens
if (prepend) {
values.unshift(prepend);
}
values.push(values[1]);
prepend = values[values.length - 3];
}
}
_r1.length = _r2.length = _r3.length = 0;
i = props.length;
while (--i > -1) {
p = props[i];
_corProps[p] = (correlate.indexOf(","+p+",") !== -1);
obj[p] = _parseAnchors(values, p, _corProps[p], prepend);
}
i = _r1.length;
while (--i > -1) {
_r1[i] = Math.sqrt(_r1[i]);
_r2[i] = Math.sqrt(_r2[i]);
}
if (!basic) {
i = props.length;
while (--i > -1) {
if (_corProps[p]) {
a = obj[props[i]];
l = a.length - 1;
for (j = 0; j < l; j++) {
r = (a[j+1].da / _r2[j] + a[j].da / _r1[j]) || 0;
_r3[j] = (_r3[j] || 0) + r * r;
}
}
}
i = _r3.length;
while (--i > -1) {
_r3[i] = Math.sqrt(_r3[i]);
}
}
i = props.length;
j = quadratic ? 4 : 1;
while (--i > -1) {
p = props[i];
a = obj[p];
_calculateControlPoints(a, curviness, quadratic, basic, _corProps[p]); //this method requires that _parseAnchors() and _setSegmentRatios() ran first so that _r1, _r2, and _r3 values are populated for all properties
if (seamless) {
a.splice(0, j);
a.splice(a.length - j, j);
}
}
return obj;
},
_parseBezierData = function(values, type, prepend) {
type = type || "soft";
var obj = {},
inc = (type === "cubic") ? 3 : 2,
soft = (type === "soft"),
props = [],
a, b, c, d, cur, i, j, l, p, cnt, tmp;
if (soft && prepend) {
values = [prepend].concat(values);
}
if (values == null || values.length < inc + 1) { throw "invalid Bezier data"; }
for (p in values[0]) {
props.push(p);
}
i = props.length;
while (--i > -1) {
p = props[i];
obj[p] = cur = [];
cnt = 0;
l = values.length;
for (j = 0; j < l; j++) {
a = (prepend == null) ? values[j][p] : (typeof( (tmp = values[j][p]) ) === "string" && tmp.charAt(1) === "=") ? prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)) : Number(tmp);
if (soft) if (j > 1) if (j < l - 1) {
cur[cnt++] = (a + cur[cnt-2]) / 2;
}
cur[cnt++] = a;
}
l = cnt - inc + 1;
cnt = 0;
for (j = 0; j < l; j += inc) {
a = cur[j];
b = cur[j+1];
c = cur[j+2];
d = (inc === 2) ? 0 : cur[j+3];
cur[cnt++] = tmp = (inc === 3) ? new Segment(a, b, c, d) : new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c);
}
cur.length = cnt;
}
return obj;
},
_addCubicLengths = function(a, steps, resolution) {
var inc = 1 / resolution,
j = a.length,
d, d1, s, da, ca, ba, p, i, inv, bez, index;
while (--j > -1) {
bez = a[j];
s = bez.a;
da = bez.d - s;
ca = bez.c - s;
ba = bez.b - s;
d = d1 = 0;
for (i = 1; i <= resolution; i++) {
p = inc * i;
inv = 1 - p;
d = d1 - (d1 = (p * p * da + 3 * inv * (p * ca + inv * ba)) * p);
index = j * resolution + i - 1;
steps[index] = (steps[index] || 0) + d * d;
}
}
},
_parseLengthData = function(obj, resolution) {
resolution = resolution >> 0 || 6;
var a = [],
lengths = [],
d = 0,
total = 0,
threshold = resolution - 1,
segments = [],
curLS = [], //current length segments array
p, i, l, index;
for (p in obj) {
_addCubicLengths(obj[p], a, resolution);
}
l = a.length;
for (i = 0; i < l; i++) {
d += Math.sqrt(a[i]);
index = i % resolution;
curLS[index] = d;
if (index === threshold) {
total += d;
index = (i / resolution) >> 0;
segments[index] = curLS;
lengths[index] = total;
d = 0;
curLS = [];
}
}
return {length:total, lengths:lengths, segments:segments};
},
BezierPlugin = _gsScope._gsDefine.plugin({
propName: "bezier",
priority: -1,
version: "1.3.8",
API: 2,
global:true,
//gets 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, vars, tween) {
this._target = target;
if (vars instanceof Array) {
vars = {values:vars};
}
this._func = {};
this._mod = {};
this._props = [];
this._timeRes = (vars.timeResolution == null) ? 6 : parseInt(vars.timeResolution, 10);
var values = vars.values || [],
first = {},
second = values[0],
autoRotate = vars.autoRotate || tween.vars.orientToBezier,
p, isFunc, i, j, prepend;
this._autoRotate = autoRotate ? (autoRotate instanceof Array) ? autoRotate : [["x","y","rotation",((autoRotate === true) ? 0 : Number(autoRotate) || 0)]] : null;
for (p in second) {
this._props.push(p);
}
i = this._props.length;
while (--i > -1) {
p = this._props[i];
this._overwriteProps.push(p);
isFunc = this._func[p] = (typeof(target[p]) === "function");
first[p] = (!isFunc) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]();
if (!prepend) if (first[p] !== values[0][p]) {
prepend = first;
}
}
this._beziers = (vars.type !== "cubic" && vars.type !== "quadratic" && vars.type !== "soft") ? bezierThrough(values, isNaN(vars.curviness) ? 1 : vars.curviness, false, (vars.type === "thruBasic"), vars.correlate, prepend) : _parseBezierData(values, vars.type, first);
this._segCount = this._beziers[p].length;
if (this._timeRes) {
var ld = _parseLengthData(this._beziers, this._timeRes);
this._length = ld.length;
this._lengths = ld.lengths;
this._segments = ld.segments;
this._l1 = this._li = this._s1 = this._si = 0;
this._l2 = this._lengths[0];
this._curSeg = this._segments[0];
this._s2 = this._curSeg[0];
this._prec = 1 / this._curSeg.length;
}
if ((autoRotate = this._autoRotate)) {
this._initialRotations = [];
if (!(autoRotate[0] instanceof Array)) {
this._autoRotate = autoRotate = [autoRotate];
}
i = autoRotate.length;
while (--i > -1) {
for (j = 0; j < 3; j++) {
p = autoRotate[i][j];
this._func[p] = (typeof(target[p]) === "function") ? target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ] : false;
}
p = autoRotate[i][2];
this._initialRotations[i] = (this._func[p] ? this._func[p].call(this._target) : this._target[p]) || 0;
this._overwriteProps.push(p);
}
}
this._startRatio = tween.vars.runBackwards ? 1 : 0; //we determine the starting ratio when the tween inits which is always 0 unless the tween has runBackwards:true (indicating it's a from() tween) in which case it's 1.
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(v) {
var segments = this._segCount,
func = this._func,
target = this._target,
notStart = (v !== this._startRatio),
curIndex, inv, i, p, b, t, val, l, lengths, curSeg;
if (!this._timeRes) {
curIndex = (v < 0) ? 0 : (v >= 1) ? segments - 1 : (segments * v) >> 0;
t = (v - (curIndex * (1 / segments))) * segments;
} else {
lengths = this._lengths;
curSeg = this._curSeg;
v *= this._length;
i = this._li;
//find the appropriate segment (if the currently cached one isn't correct)
if (v > this._l2 && i < segments - 1) {
l = segments - 1;
while (i < l && (this._l2 = lengths[++i]) <= v) { }
this._l1 = lengths[i-1];
this._li = i;
this._curSeg = curSeg = this._segments[i];
this._s2 = curSeg[(this._s1 = this._si = 0)];
} else if (v < this._l1 && i > 0) {
while (i > 0 && (this._l1 = lengths[--i]) >= v) { }
if (i === 0 && v < this._l1) {
this._l1 = 0;
} else {
i++;
}
this._l2 = lengths[i];
this._li = i;
this._curSeg = curSeg = this._segments[i];
this._s1 = curSeg[(this._si = curSeg.length - 1) - 1] || 0;
this._s2 = curSeg[this._si];
}
curIndex = i;
//now find the appropriate sub-segment (we split it into the number of pieces that was defined by "precision" and measured each one)
v -= this._l1;
i = this._si;
if (v > this._s2 && i < curSeg.length - 1) {
l = curSeg.length - 1;
while (i < l && (this._s2 = curSeg[++i]) <= v) { }
this._s1 = curSeg[i-1];
this._si = i;
} else if (v < this._s1 && i > 0) {
while (i > 0 && (this._s1 = curSeg[--i]) >= v) { }
if (i === 0 && v < this._s1) {
this._s1 = 0;
} else {
i++;
}
this._s2 = curSeg[i];
this._si = i;
}
t = ((i + (v - this._s1) / (this._s2 - this._s1)) * this._prec) || 0;
}
inv = 1 - t;
i = this._props.length;
while (--i > -1) {
p = this._props[i];
b = this._beziers[p][curIndex];
val = (t * t * b.da + 3 * inv * (t * b.ca + inv * b.ba)) * t + b.a;
if (this._mod[p]) {
val = this._mod[p](val, target);
}
if (func[p]) {
target[p](val);
} else {
target[p] = val;
}
}
if (this._autoRotate) {
var ar = this._autoRotate,
b2, x1, y1, x2, y2, add, conv;
i = ar.length;
while (--i > -1) {
p = ar[i][2];
add = ar[i][3] || 0;
conv = (ar[i][4] === true) ? 1 : _RAD2DEG;
b = this._beziers[ar[i][0]];
b2 = this._beziers[ar[i][1]];
if (b && b2) { //in case one of the properties got overwritten.
b = b[curIndex];
b2 = b2[curIndex];
x1 = b.a + (b.b - b.a) * t;
x2 = b.b + (b.c - b.b) * t;
x1 += (x2 - x1) * t;
x2 += ((b.c + (b.d - b.c) * t) - x2) * t;
y1 = b2.a + (b2.b - b2.a) * t;
y2 = b2.b + (b2.c - b2.b) * t;
y1 += (y2 - y1) * t;
y2 += ((b2.c + (b2.d - b2.c) * t) - y2) * t;
val = notStart ? Math.atan2(y2 - y1, x2 - x1) * conv + add : this._initialRotations[i];
if (this._mod[p]) {
val = this._mod[p](val, target); //for modProps
}
if (func[p]) {
target[p](val);
} else {
target[p] = val;
}
}
}
}
}
}),
p = BezierPlugin.prototype;
BezierPlugin.bezierThrough = bezierThrough;
BezierPlugin.cubicToQuadratic = cubicToQuadratic;
BezierPlugin._autoCSS = true; //indicates that this plugin can be inserted into the "css" object using the autoCSS feature of TweenLite
BezierPlugin.quadraticToCubic = function(a, b, c) {
return new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c);
};
BezierPlugin._cssRegister = function() {
var CSSPlugin = _globals.CSSPlugin;
if (!CSSPlugin) {
return;
}
var _internals = CSSPlugin._internals,
_parseToProxy = _internals._parseToProxy,
_setPluginRatio = _internals._setPluginRatio,
CSSPropTween = _internals.CSSPropTween;
_internals._registerComplexSpecialProp("bezier", {parser:function(t, e, prop, cssp, pt, plugin) {
if (e instanceof Array) {
e = {values:e};
}
plugin = new BezierPlugin();
var values = e.values,
l = values.length - 1,
pluginValues = [],
v = {},
i, p, data;
if (l < 0) {
return pt;
}
for (i = 0; i <= l; i++) {
data = _parseToProxy(t, values[i], cssp, pt, plugin, (l !== i));
pluginValues[i] = data.end;
}
for (p in e) {
v[p] = e[p]; //duplicate the vars object because we need to alter some things which would cause problems if the user plans to reuse the same vars object for another tween.
}
v.values = pluginValues;
pt = new CSSPropTween(t, "bezier", 0, 0, data.pt, 2);
pt.data = data;
pt.plugin = plugin;
pt.setRatio = _setPluginRatio;
if (v.autoRotate === 0) {
v.autoRotate = true;
}
if (v.autoRotate && !(v.autoRotate instanceof Array)) {
i = (v.autoRotate === true) ? 0 : Number(v.autoRotate);
v.autoRotate = (data.end.left != null) ? [["left","top","rotation",i,false]] : (data.end.x != null) ? [["x","y","rotation",i,false]] : false;
}
if (v.autoRotate) {
if (!cssp._transform) {
cssp._enableTransforms(false);
}
data.autoRotate = cssp._target._gsTransform;
data.proxy.rotation = data.autoRotate.rotation || 0;
cssp._overwriteProps.push("rotation");
}
plugin._onInitTween(data.proxy, v, cssp._tween);
return pt;
}});
};
p._mod = function(lookup) {
var op = this._overwriteProps,
i = op.length,
val;
while (--i > -1) {
val = lookup[op[i]];
if (val && typeof(val) === "function") {
this._mod[op[i]] = val;
}
}
};
p._kill = function(lookup) {
var a = this._props,
p, i;
for (p in this._beziers) {
if (p in lookup) {
delete this._beziers[p];
delete this._func[p];
i = a.length;
while (--i > -1) {
if (a[i] === p) {
a.splice(i, 1);
}
}
}
}
a = this._autoRotate;
if (a) {
i = a.length;
while (--i > -1) {
if (lookup[a[i][2]]) {
a.splice(i, 1);
}
}
}
return this._super._kill.call(this, lookup);
};
export { BezierPlugin, BezierPlugin as default };