* replaced some path processing with SVGPathCommander
* minor improvements
This commit is contained in:
thednp 2020-08-16 20:20:36 +00:00
parent b152aa8fa6
commit b0a6495748
16 changed files with 1177 additions and 1770 deletions

View file

@ -240,6 +240,19 @@
return {name:ComponentName} return {name:ComponentName}
}; };
function queueStart(){
for (var obj in onStart) {
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj);
} else {
for (var prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
linkInterpolation.call(this);
}
var TweenBase = function TweenBase(targetElement, startObject, endObject, options){ var TweenBase = function TweenBase(targetElement, startObject, endObject, options){
this.element = targetElement; this.element = targetElement;
this.playing = false; this.playing = false;
@ -273,16 +286,7 @@
if (this._onStart) { if (this._onStart) {
this._onStart.call(this); this._onStart.call(this);
} }
for (var obj in onStart) { queueStart.call(this);
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj);
} else {
for (var prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
linkInterpolation.call(this);
this._startFired = true; this._startFired = true;
} }
!Tick && Ticker(); !Tick && Ticker();

File diff suppressed because one or more lines are too long

View file

@ -21,7 +21,7 @@
var Interpolate = {}; var Interpolate = {};
var onStart$1 = {}; var onStart = {};
var Time = {}; var Time = {};
Time.now = self.performance.now.bind(self.performance); Time.now = self.performance.now.bind(self.performance);
@ -42,11 +42,11 @@
if (!Tweens.length && Tick) { if (!Tweens.length && Tick) {
cancelAnimationFrame(Tick); cancelAnimationFrame(Tick);
Tick = null; Tick = null;
for (var obj in onStart$1) { for (var obj in onStart) {
if (typeof (onStart$1[obj]) === 'function') { if (typeof (onStart[obj]) === 'function') {
KUTE[obj] && (delete KUTE[obj]); KUTE[obj] && (delete KUTE[obj]);
} else { } else {
for (var prop in onStart$1[obj]) { for (var prop in onStart[obj]) {
KUTE[prop] && (delete KUTE[prop]); KUTE[prop] && (delete KUTE[prop]);
} }
} }
@ -92,7 +92,7 @@
prepareProperty: prepareProperty, prepareProperty: prepareProperty,
prepareStart: prepareStart, prepareStart: prepareStart,
crossCheck: crossCheck, crossCheck: crossCheck,
onStart: onStart$1, onStart: onStart,
onComplete: onComplete, onComplete: onComplete,
linkProperty: linkProperty linkProperty: linkProperty
}; };
@ -112,7 +112,7 @@
function removeAll () { Tweens.length = 0; } function removeAll () { Tweens.length = 0; }
function linkInterpolation$1() { function linkInterpolation() {
var this$1 = this; var this$1 = this;
var loop = function ( component ) { var loop = function ( component ) {
var componentLink = linkProperty[component]; var componentLink = linkProperty[component];
@ -149,7 +149,7 @@
getAll: getAll, getAll: getAll,
removeAll: removeAll, removeAll: removeAll,
stop: stop, stop: stop,
linkInterpolation: linkInterpolation$1 linkInterpolation: linkInterpolation
}; };
function getInlineStyle(el) { function getInlineStyle(el) {
@ -352,6 +352,19 @@
} }
} }
function queueStart(){
for (var obj in onStart) {
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj);
} else {
for (var prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
linkInterpolation.call(this);
}
var TweenBase = function TweenBase(targetElement, startObject, endObject, options){ var TweenBase = function TweenBase(targetElement, startObject, endObject, options){
this.element = targetElement; this.element = targetElement;
this.playing = false; this.playing = false;
@ -369,8 +382,8 @@
if( !(internalOption in this ) ) { this[internalOption] = options[op]; } if( !(internalOption in this ) ) { this[internalOption] = options[op]; }
} }
var easingFnName = this._easing.name; var easingFnName = this._easing.name;
if (!onStart$1[easingFnName]) { if (!onStart[easingFnName]) {
onStart$1[easingFnName] = function(prop){ onStart[easingFnName] = function(prop){
!KUTE[prop] && prop === this._easing.name && (KUTE[prop] = this._easing); !KUTE[prop] && prop === this._easing.name && (KUTE[prop] = this._easing);
}; };
} }
@ -385,16 +398,7 @@
if (this._onStart) { if (this._onStart) {
this._onStart.call(this); this._onStart.call(this);
} }
for (var obj in onStart$1) { queueStart.call(this);
if (typeof (onStart$1[obj]) === 'function') {
onStart$1[obj].call(this,obj);
} else {
for (var prop in onStart$1[obj]) {
onStart$1[obj][prop].call(this,prop);
}
}
}
linkInterpolation$1.call(this);
this._startFired = true; this._startFired = true;
} }
!Tick && Ticker(); !Tick && Ticker();
@ -540,16 +544,7 @@
if (this._onResume !== undefined) { if (this._onResume !== undefined) {
this._onResume.call(this); this._onResume.call(this);
} }
for (var obj in onStart) { queueStart.call(this);
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj);
} else {
for (var prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
linkInterpolation.call(this);
this._startTime += KUTE.Time() - this._pauseTime; this._startTime += KUTE.Time() - this._pauseTime;
add(this); add(this);
!Tick && Ticker(); !Tick && Ticker();
@ -799,7 +794,7 @@
Animation.prototype.setComponent = function setComponent (Component){ Animation.prototype.setComponent = function setComponent (Component){
var propertyInfo = this; var propertyInfo = this;
var ComponentName = Component.component; var ComponentName = Component.component;
var Functions = { prepareProperty: prepareProperty, prepareStart: prepareStart, onStart: onStart$1, onComplete: onComplete, crossCheck: crossCheck }; var Functions = { prepareProperty: prepareProperty, prepareStart: prepareStart, onStart: onStart, onComplete: onComplete, crossCheck: crossCheck };
var Category = Component.category; var Category = Component.category;
var Property = Component.property; var Property = Component.property;
var Length = Component.properties && Component.properties.length || Component.subProperties && Component.subProperties.length; var Length = Component.properties && Component.properties.length || Component.subProperties && Component.subProperties.length;
@ -868,7 +863,7 @@
AnimationDevelopment.prototype.setComponent = function setComponent (Component){ AnimationDevelopment.prototype.setComponent = function setComponent (Component){
Animation.prototype.setComponent.call(this, Component); Animation.prototype.setComponent.call(this, Component);
var propertyInfo = this; var propertyInfo = this;
var Functions = { prepareProperty: prepareProperty,prepareStart: prepareStart,onStart: onStart$1,onComplete: onComplete,crossCheck: crossCheck }; var Functions = { prepareProperty: prepareProperty,prepareStart: prepareStart,onStart: onStart,onComplete: onComplete,crossCheck: crossCheck };
var Category = Component.category; var Category = Component.category;
var Property = Component.property; var Property = Component.property;
var Length = Component.properties && Component.properties.length || Component.subProperties && Component.subProperties.length; var Length = Component.properties && Component.properties.length || Component.subProperties && Component.subProperties.length;
@ -1232,7 +1227,7 @@
if ( currentValue !== null && regex.test(currentValue) ) { if ( currentValue !== null && regex.test(currentValue) ) {
var unit = trueDimension(currentValue).u || trueDimension(attrObj[p]).u; var unit = trueDimension(currentValue).u || trueDimension(attrObj[p]).u;
var suffix = /%/.test(unit) ? '_percent' : ("_" + unit); var suffix = /%/.test(unit) ? '_percent' : ("_" + unit);
onStart$1[ComponentName][prop+suffix] = function(tp) { onStart[ComponentName][prop+suffix] = function(tp) {
if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) { if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) {
attributes[tp] = function (elem, p, a, b, v) { attributes[tp] = function (elem, p, a, b, v) {
var _p = p.replace(suffix,''); var _p = p.replace(suffix,'');
@ -1242,7 +1237,7 @@
}; };
attributesObject[prop+suffix] = trueDimension(attrObj[p]); attributesObject[prop+suffix] = trueDimension(attrObj[p]);
} else if ( !regex.test(attrObj[p]) || currentValue === null || currentValue !== null && !regex.test(currentValue) ) { } else if ( !regex.test(attrObj[p]) || currentValue === null || currentValue !== null && !regex.test(currentValue) ) {
onStart$1[ComponentName][prop] = function(tp) { onStart[ComponentName][prop] = function(tp) {
if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) { if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) {
attributes[tp] = function (elem, oneAttr, a, b, v) { attributes[tp] = function (elem, oneAttr, a, b, v) {
elem.setAttribute(oneAttr, (numbers(a,b,v) * 1000 >> 0) / 1000 ); elem.setAttribute(oneAttr, (numbers(a,b,v) * 1000 >> 0) / 1000 );
@ -1252,7 +1247,7 @@
attributesObject[prop] = parseFloat(attrObj[p]); attributesObject[prop] = parseFloat(attrObj[p]);
} }
} else { } else {
onStart$1[ComponentName][prop] = function(tp) { onStart[ComponentName][prop] = function(tp) {
if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) { if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) {
attributes[tp] = function (elem, oneAttr, a, b, v) { attributes[tp] = function (elem, oneAttr, a, b, v) {
elem.setAttribute(oneAttr, colors(a,b,v)); elem.setAttribute(oneAttr, colors(a,b,v));
@ -1560,17 +1555,16 @@
}; };
Components.SVGDraw = svgDraw; Components.SVGDraw = svgDraw;
function toPathString(pathArray) { function pathToString(pathArray) {
var newPath = pathArray.map( function (c) { return pathArray.map( function (c) {
if (typeof(c) === 'string') { if (typeof c === 'string') {
return c return c
} else { } else {
var c0 = c.shift(); return c.shift() + c.join(',')
return c0 + c.join(',')
} }
}); }).join(' ')
return newPath.join('');
} }
function onStartCubicMorph(tweenProp){ function onStartCubicMorph(tweenProp){
if (!KUTE[tweenProp] && this.valuesEnd[tweenProp]) { if (!KUTE[tweenProp] && this.valuesEnd[tweenProp]) {
KUTE[tweenProp] = function(elem,a,b,v){ KUTE[tweenProp] = function(elem,a,b,v){
@ -1581,12 +1575,256 @@
curve[i].push( (numbers(path1[i][j], path2[i][j], v) * 1000 >>0)/1000 ); curve[i].push( (numbers(path1[i][j], path2[i][j], v) * 1000 >>0)/1000 );
} }
} }
elem.setAttribute("d", v === 1 ? b.original : toPathString(curve) ); elem.setAttribute("d", v === 1 ? b.original : pathToString(curve) );
}; };
} }
} }
var INVALID_INPUT = 'Invalid path value'; function clonePath(pathArray){
return pathArray.map(function (x) { return Array.isArray(x) ? clonePath(x) : !isNaN(+x) ? +x : x; } )
}
var SVGPCOps = {
decimals:3,
round:1
};
function roundPath(pathArray) {
return pathArray.map( function (seg) { return seg.map(function (c,i) {
var nr = +c, dc = Math.pow(10,SVGPCOps.decimals);
return i ? (nr % 1 === 0 ? nr : (nr*dc>>0)/dc) : c
}
); })
}
function SVGPathArray(pathString){
this.segments = [];
this.isClosed = 0;
this.isAbsolute = 0;
this.pathValue = pathString;
this.max = pathString.length;
this.index = 0;
this.param = 0.0;
this.segmentStart = 0;
this.data = [];
this.err = '';
return this
}
var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 };
function isSpace(ch) {
var specialSpaces = [
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF ];
return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) ||
(ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
(ch >= 0x1680 && specialSpaces.indexOf(ch) >= 0);
}
function isCommand(code) {
switch (code | 0x20) {
case 0x6D:
case 0x7A:
case 0x6C:
case 0x68:
case 0x76:
case 0x63:
case 0x73:
case 0x71:
case 0x74:
case 0x61:
case 0x72:
return true;
}
return false;
}
function isArc(code) {
return (code | 0x20) === 0x61;
}
function isDigit(code) {
return (code >= 48 && code <= 57);
}
function isDigitStart(code) {
return (code >= 48 && code <= 57) ||
code === 0x2B ||
code === 0x2D ||
code === 0x2E;
}
function skipSpaces(state) {
while (state.index < state.max && isSpace(state.pathValue.charCodeAt(state.index))) {
state.index++;
}
}
function scanFlag(state) {
var ch = state.pathValue.charCodeAt(state.index);
if (ch === 0x30) {
state.param = 0;
state.index++;
return;
}
if (ch === 0x31) {
state.param = 1;
state.index++;
return;
}
state.err = 'SvgPath: arc flag can be 0 or 1 only (at pos ' + state.index + ')';
}
function scanParam(state) {
var start = state.index,
index = start,
max = state.max,
zeroFirst = false,
hasCeiling = false,
hasDecimal = false,
hasDot = false,
ch;
if (index >= max) {
state.err = 'SvgPath: missed param (at pos ' + index + ')';
return;
}
ch = state.pathValue.charCodeAt(index);
if (ch === 0x2B || ch === 0x2D) {
index++;
ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
}
if (!isDigit(ch) && ch !== 0x2E) {
state.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
return;
}
if (ch !== 0x2E) {
zeroFirst = (ch === 0x30);
index++;
ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
if (zeroFirst && index < max) {
if (ch && isDigit(ch)) {
state.err = 'SvgPath: numbers started with `0` such as `09` are illegal (at pos ' + start + ')';
return;
}
}
while (index < max && isDigit(state.pathValue.charCodeAt(index))) {
index++;
hasCeiling = true;
}
ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
}
if (ch === 0x2E) {
hasDot = true;
index++;
while (isDigit(state.pathValue.charCodeAt(index))) {
index++;
hasDecimal = true;
}
ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
}
if (ch === 0x65 || ch === 0x45) {
if (hasDot && !hasCeiling && !hasDecimal) {
state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
return;
}
index++;
ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
if (ch === 0x2B || ch === 0x2D) {
index++;
}
if (index < max && isDigit(state.pathValue.charCodeAt(index))) {
while (index < max && isDigit(state.pathValue.charCodeAt(index))) {
index++;
}
} else {
state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
return;
}
}
state.index = index;
state.param = parseFloat(state.pathValue.slice(start, index)) + 0.0;
}
function finalizeSegment(state) {
var cmd = state.pathValue[state.segmentStart], cmdLC = cmd.toLowerCase(), params = state.data;
if (cmdLC === 'm' && params.length > 2) {
state.segments.push([ cmd, params[0], params[1] ]);
params = params.slice(2);
cmdLC = 'l';
cmd = (cmd === 'm') ? 'l' : 'L';
}
if (cmdLC === 'r') {
state.segments.push([ cmd ].concat(params));
} else {
while (params.length >= paramCounts[cmdLC]) {
state.segments.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC])));
if (!paramCounts[cmdLC]) {
break;
}
}
}
}
function scanSegment(state) {
var max = state.max, cmdCode, is_arc, comma_found, need_params, i;
state.segmentStart = state.index;
cmdCode = state.pathValue.charCodeAt(state.index);
is_arc = isArc(cmdCode);
if (!isCommand(cmdCode)) {
state.err = 'SvgPath: bad command ' + state.pathValue[state.index] + ' (at pos ' + state.index + ')';
return;
}
need_params = paramCounts[state.pathValue[state.index].toLowerCase()];
state.index++;
skipSpaces(state);
state.data = [];
if (!need_params) {
state.isClosed = 1;
finalizeSegment(state);
return;
}
comma_found = false;
for (;;) {
for (i = need_params; i > 0; i--) {
if (is_arc && (i === 3 || i === 4)) { scanFlag(state); }
else { scanParam(state); }
if (state.err.length) {
return;
}
state.data.push(state.param);
skipSpaces(state);
comma_found = false;
if (state.index < max && state.pathValue.charCodeAt(state.index) === 0x2C) {
state.index++;
skipSpaces(state);
comma_found = true;
}
}
if (comma_found) {
continue;
}
if (state.index >= state.max) {
break;
}
if (!isDigitStart(state.pathValue.charCodeAt(state.index))) {
break;
}
}
finalizeSegment(state);
}
function parsePathString(pathString) {
if ( Array.isArray(pathString) ) {
return clonePath(pathString)
}
var state = new SVGPathArray(pathString), max = state.max;
skipSpaces(state);
while (state.index < max && !state.err.length) {
scanSegment(state);
}
if (state.err.length) {
state.segments = [];
} else if (state.segments.length) {
if ('mM'.indexOf(state.segments[0][0]) < 0) {
state.err = 'SvgPath: string should start with `M` or `m`';
state.segments = [];
} else {
state.segments[0][0] = 'M';
}
}
return roundPath(state.segments)
}
function catmullRom2bezier(crp, z) { function catmullRom2bezier(crp, z) {
var d = []; var d = [];
for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) { for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
@ -1612,17 +1850,19 @@
p[0] = {x: +crp[i], y: +crp[i + 1]}; p[0] = {x: +crp[i], y: +crp[i + 1]};
} }
} }
d.push(["C", d.push([
(-p[0].x + 6 * p[1].x + p[2].x) / 6, "C",
(-p[0].y + 6 * p[1].y + p[2].y) / 6, (-p[0].x + 6 * p[1].x + p[2].x) / 6,
(p[1].x + 6 * p[2].x - p[3].x) / 6, (-p[0].y + 6 * p[1].y + p[2].y) / 6,
(p[1].y + 6*p[2].y - p[3].y) / 6, (p[1].x + 6 * p[2].x - p[3].x) / 6,
p[2].x, (p[1].y + 6*p[2].y - p[3].y) / 6,
p[2].y p[2].x,
p[2].y
]); ]);
} }
return d return d
} }
function ellipsePath(x, y, rx, ry, a) { function ellipsePath(x, y, rx, ry, a) {
if (a == null && ry == null) { if (a == null && ry == null) {
ry = rx; ry = rx;
@ -1634,10 +1874,10 @@
var res; var res;
if (a != null) { if (a != null) {
var rad = Math.PI / 180, var rad = Math.PI / 180,
x1 = x + rx * Math.cos(-ry * rad), x1 = x + rx * Math.cos(-ry * rad),
x2 = x + rx * Math.cos(-a * rad), x2 = x + rx * Math.cos(-a * rad),
y1 = y + rx * Math.sin(-ry * rad), y1 = y + rx * Math.sin(-ry * rad),
y2 = y + rx * Math.sin(-a * rad); y2 = y + rx * Math.sin(-a * rad);
res = [["M", x1, y1], ["A", rx, rx, 0, +(a - ry > 180), 0, x2, y2]]; res = [["M", x1, y1], ["A", rx, rx, 0, +(a - ry > 180), 0, x2, y2]];
} else { } else {
res = [ res = [
@ -1650,49 +1890,18 @@
} }
return res; return res;
} }
function parsePathString(pathString) {
if (!pathString) {
return null;
}
if( pathString instanceof Array ) {
return pathString;
} else {
var spaces = "\\" + (("x09|x0a|x0b|x0c|x0d|x20|xa0|u1680|u180e|u2000|u2001|u2002|u2003|u2004|u2005|u2006|u2007|u2008|u2009|u200a|u202f|u205f|u3000|u2028|u2029").split('|').join('\\')),
pathCommand = new RegExp(("([a-z])[" + spaces + ",]*((-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?[" + spaces + "]*,?[" + spaces + "]*)+)"), "ig"),
pathValues = new RegExp(("(-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?)[" + spaces + "]*,?[" + spaces + "]*"), "ig"),
paramCounts = {a: 7, c: 6, o: 2, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, u: 3, z: 0},
data = [];
pathString.replace(pathCommand, function (a, b, c) {
var params = [], name = b.toLowerCase();
c.replace(pathValues, function (a, b) {
b && params.push(+b);
});
if (name == "m" && params.length > 2) {
data.push([b].concat(params.splice(0, 2)));
name = "l";
b = b == "m" ? "l" : "L";
}
if (name == "o" && params.length == 1) {
data.push([b, params[0]]);
}
if (name == "r") {
data.push([b].concat(params));
} else { while (params.length >= paramCounts[name]) {
data.push([b].concat(params.splice(0, paramCounts[name])));
if (!paramCounts[name]) {
break;
}
} }
});
return data;
}
}
function pathToAbsolute(pathArray) { function pathToAbsolute(pathArray) {
pathArray = parsePathString(pathArray); pathArray = parsePathString(pathArray);
if (!pathArray || !pathArray.length) { if (!pathArray || !pathArray.length) {
return [["M", 0, 0]]; return [["M", 0, 0]];
} }
var res = [], x = 0, y = 0, mx = 0, my = 0, start = 0, pa0; var res = [], x = 0, y = 0, mx = 0, my = 0, start = 0,
ii = pathArray.length,
crz = pathArray.length === 3 &&
pathArray[0][0] === "M" &&
pathArray[1][0].toUpperCase() === "R" &&
pathArray[2][0].toUpperCase() === "Z";
if (pathArray[0][0] === "M") { if (pathArray[0][0] === "M") {
x = +pathArray[0][1]; x = +pathArray[0][1];
y = +pathArray[0][2]; y = +pathArray[0][2];
@ -1701,14 +1910,9 @@
start++; start++;
res[0] = ["M", x, y]; res[0] = ["M", x, y];
} }
var crz = pathArray.length === 3 && var loop = function ( i ) {
pathArray[0][0] === "M" && var r = (void 0), pa = pathArray[i], pa0 = pa[0];
pathArray[1][0].toUpperCase() === "R" &&
pathArray[2][0].toUpperCase() === "Z";
for (var r = (void 0), pa = (void 0), i = start, ii = pathArray.length; i < ii; i++) {
res.push(r = []); res.push(r = []);
pa = pathArray[i];
pa0 = pa[0];
if (pa0 !== pa0.toUpperCase()) { if (pa0 !== pa0.toUpperCase()) {
r[0] = pa0.toUpperCase(); r[0] = pa0.toUpperCase();
switch (r[0]) { switch (r[0]) {
@ -1728,19 +1932,19 @@
r[1] = +pa[1] + x; r[1] = +pa[1] + x;
break; break;
case "R": case "R":
var dots = [x, y].concat(pa.slice(1)); var dots$1 = [x, y].concat(pa.slice(1));
for (var j = 2, jj = dots.length; j < jj; j++) { for (var j = 2, jj = dots$1.length; j < jj; j++) {
dots[j] = +dots[j] + x; dots$1[j] = +dots$1[j] + x;
dots[++j] = +dots[j] + y; dots$1[++j] = +dots$1[j] + y;
} }
res.pop(); res.pop();
res = res.concat(catmullRom2bezier(dots, crz)); res = res.concat(catmullRom2bezier(dots$1, crz));
break; break;
case "O": case "O":
res.pop(); res.pop();
dots = ellipsePath(x, y, pa[1], pa[2]); dots$1 = ellipsePath(x, y, +pa[1], +pa[2]);
dots.push(dots[0]); dots$1.push(dots$1[0]);
res = res.concat(dots); res = res.concat(dots$1);
break; break;
case "U": case "U":
res.pop(); res.pop();
@ -1751,84 +1955,71 @@
mx = +pa[1] + x; mx = +pa[1] + x;
my = +pa[2] + y; my = +pa[2] + y;
default: default:
for (j = 1, jj = pa.length; j < jj; j++) { for (var j$1 = 1, jj$1 = pa.length; j$1 < jj$1; j$1++) {
r[j] = +pa[j] + ((j % 2) ? x : y); r[j$1] = +pa[j$1] + ((j$1 % 2) ? x : y);
} }
} }
} else if (pa0 == "R") { } else if (pa0 === "R") {
dots = [x, y].concat(pa.slice(1)); dots = [x, y].concat(pa.slice(1));
res.pop(); res.pop();
res = res.concat(catmullRom2bezier(dots, crz)); res = res.concat(catmullRom2bezier(dots, crz));
r = ["R"].concat(pa.slice(-2)); r = ["R"].concat(pa.slice(-2));
} else if (pa0 == "O") { } else if (pa0 === "O") {
res.pop(); res.pop();
dots = ellipsePath(x, y, pa[1], pa[2]); dots = ellipsePath(x, y, +pa[1], +pa[2]);
dots.push(dots[0]); dots.push(dots[0]);
res = res.concat(dots); res = res.concat(dots);
} else if (pa0 == "U") { } else if (pa0 === "U") {
res.pop(); res.pop();
res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3])); res = res.concat(ellipsePath(x, y, +pa[1], +pa[2], +pa[3]));
r = ["U"].concat(res[res.length - 1].slice(-2)); r = ["U"].concat(res[res.length - 1].slice(-2));
} else { } else {
for (var k = 0, kk = pa.length; k < kk; k++) { pa.map(function (k){ return r.push(k); });
r[k] = pa[k];
}
} }
pa0 = pa0.toUpperCase(); pa0 = pa0.toUpperCase();
if (pa0 != "O") { if (pa0 !== "O") {
switch (r[0]) { switch (r[0]) {
case "Z": case "Z":
x = +mx; x = mx;
y = +my; y = my;
break; break;
case "H": case "H":
x = r[1]; x = +r[1];
break; break;
case "V": case "V":
y = r[1]; y = +r[1];
break; break;
case "M": case "M":
mx = r[r.length - 2]; mx = +r[r.length - 2];
my = r[r.length - 1]; my = +r[r.length - 1];
default: default:
x = r[r.length - 2]; x = +r[r.length - 2];
y = r[r.length - 1]; y = +r[r.length - 1];
} }
} }
} };
return res for (var i = start; i < ii; i++) loop( i );
return roundPath(res)
} }
function l2c(x1, y1, x2, y2) {
return [x1, y1, x2, y2, x2, y2]; function rotateVector(x, y, rad) {
} var X = x * Math.cos(rad) - y * Math.sin(rad),
function q2c(x1, y1, ax, ay, x2, y2) { Y = x * Math.sin(rad) + y * Math.cos(rad);
var _13 = 1 / 3; return {x: X, y: Y}
var _23 = 2 / 3;
return [
_13 * x1 + _23 * ax,
_13 * y1 + _23 * ay,
_13 * x2 + _23 * ax,
_13 * y2 + _23 * ay,
x2,
y2
]
} }
function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
var _120 = Math.PI * 120 / 180, rad = Math.PI / 180 * (+angle || 0); var _120 = Math.PI * 120 / 180,
var res = [], xy, f1, f2, cx, cy; rad = Math.PI / 180 * (angle || 0),
function rotateVector(x, y, rad) { res = [], xy, f1, f2, cx, cy;
var X = x * Math.cos(rad) - y * Math.sin(rad),
Y = x * Math.sin(rad) + y * Math.cos(rad);
return {x: X, y: Y};
}
if (!recursive) { if (!recursive) {
xy = rotateVector(x1, y1, -rad); xy = rotateVector(x1, y1, -rad);
x1 = xy.x; x1 = xy.x; y1 = xy.y;
y1 = xy.y;
xy = rotateVector(x2, y2, -rad); xy = rotateVector(x2, y2, -rad);
x2 = xy.x; x2 = xy.x; y2 = xy.y;
y2 = xy.y; var x = (x1 - x2) / 2,
var x = (x1 - x2) / 2, y = (y1 - y2) / 2, h = (x * x) / (rx * rx) + (y * y) / (ry * ry); y = (y1 - y2) / 2,
h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
if (h > 1) { if (h > 1) {
h = Math.sqrt(h); h = Math.sqrt(h);
rx = h * rx; rx = h * rx;
@ -1841,8 +2032,8 @@
/ (rx2 * y * y + ry2 * x * x))); / (rx2 * y * y + ry2 * x * x)));
cx = k * rx * y / ry + (x1 + x2) / 2, cx = k * rx * y / ry + (x1 + x2) / 2,
cy = k * -ry * x / rx + (y1 + y2) / 2; cy = k * -ry * x / rx + (y1 + y2) / 2;
f1 = Math.asin(((y1 - cy) / ry).toFixed(9)), f1 = Math.asin(((y1 - cy) / ry)),
f2 = Math.asin(((y2 - cy) / ry).toFixed(9)); f2 = Math.asin(((y2 - cy) / ry));
f1 = x1 < cx ? Math.PI - f1 : f1; f1 = x1 < cx ? Math.PI - f1 : f1;
f2 = x2 < cx ? Math.PI - f2 : f2; f2 = x2 < cx ? Math.PI - f2 : f2;
f1 < 0 && (f1 = Math.PI * 2 + f1); f1 < 0 && (f1 = Math.PI * 2 + f1);
@ -1869,30 +2060,43 @@
} }
df = f2 - f1; df = f2 - f1;
var c1 = Math.cos(f1), var c1 = Math.cos(f1),
s1 = Math.sin(f1), s1 = Math.sin(f1),
c2 = Math.cos(f2), c2 = Math.cos(f2),
s2 = Math.sin(f2), s2 = Math.sin(f2),
t = Math.tan(df / 4), t = Math.tan(df / 4),
hx = 4 / 3 * rx * t, hx = 4 / 3 * rx * t,
hy = 4 / 3 * ry * t, hy = 4 / 3 * ry * t,
m1 = [x1, y1], m1 = [x1, y1],
m2 = [x1 + hx * s1, y1 - hy * c1], m2 = [x1 + hx * s1, y1 - hy * c1],
m3 = [x2 + hx * s2, y2 - hy * c2], m3 = [x2 + hx * s2, y2 - hy * c2],
m4 = [x2, y2]; m4 = [x2, y2];
m2[0] = 2 * m1[0] - m2[0]; m2[0] = 2 * m1[0] - m2[0];
m2[1] = 2 * m1[1] - m2[1]; m2[1] = 2 * m1[1] - m2[1];
if (recursive) { if (recursive) {
return [m2, m3, m4].concat(res); return [m2, m3, m4].concat(res);
} else { } else {
res = [m2, m3, m4].concat(res).join().split(","); res = [m2, m3, m4].concat(res).join().split(",");
var newres = []; return res.map(function (rz,i){ return i % 2 ? rotateVector(res[i - 1], rz, rad).y : rotateVector(rz, res[i + 1], rad).x; });
for (var i = 0, ii = res.length; i < ii; i++) {
newres[i] = i % 2 ? rotateVector(res[i - 1], res[i], rad).y : rotateVector(res[i], res[i + 1], rad).x;
}
return newres;
} }
} }
function processPath (path, d, pcom) {
function q2c (x1, y1, ax, ay, x2, y2) {
var _13 = 1 / 3, _23 = 2 / 3;
return [
_13 * x1 + _23 * ax,
_13 * y1 + _23 * ay,
_13 * x2 + _23 * ax,
_13 * y2 + _23 * ay,
x2,
y2
]
}
function l2c(x1, y1, x2, y2) {
return [x1, y1, x2, y2, x2, y2];
}
function processPath(path, d, pcom) {
var nx, ny; var nx, ny;
if (!path) { if (!path) {
return ["C", d.x, d.y, d.x, d.y, d.x, d.y]; return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
@ -1946,10 +2150,10 @@
path = ["C"].concat(l2c(d.x, d.y, d.X, d.Y)); path = ["C"].concat(l2c(d.x, d.y, d.X, d.Y));
break; break;
} }
path.map(function (x,i){ return i?x.toFixed(3):x; });
return path; return path;
} }
function fixM (path1, path2, a1, a2, i) {
function fixM(path1, path2, a1, a2, i) {
if (path1 && path2 && path1[i][0] === "M" && path2[i][0] !== "M") { if (path1 && path2 && path1[i][0] === "M" && path2[i][0] !== "M") {
path2.splice(i, 0, ["M", a2.x, a2.y]); path2.splice(i, 0, ["M", a2.x, a2.y]);
a1.bx = 0; a1.bx = 0;
@ -1958,25 +2162,29 @@
a1.y = path1[i][2]; a1.y = path1[i][2];
} }
} }
function fixArc (p, p2, pcoms1, pcoms2, i) {
function fixArc(p, p2, pc1, pc2, i) {
if (p[i].length > 7) { if (p[i].length > 7) {
p[i].shift(); p[i].shift();
var pi = p[i]; var pi = p[i];
while (pi.length) { while (pi.length) {
pcoms1[i] = "A"; pc1[i] = "A";
p2 && (pcoms2[i] = "A"); p2 && (pc2[i] = "A");
p.splice(i++, 0, ["C"].concat(pi.splice(0, 6))); p.splice(i++, 0, ["C"].concat(pi.splice(0, 6)));
} }
p.splice(i, 1); p.splice(i, 1);
} }
} }
function path2curve(path, path2) {
function pathToCurve(path, path2) {
var p = pathToAbsolute(path), var p = pathToAbsolute(path),
p2 = path2 && pathToAbsolute(path2), p2 = path2 && pathToAbsolute(path2),
attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}; attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null};
var pcoms1 = [], pcoms2 = [], pfirst = "", pcom = ""; var pcoms1 = [], pcoms2 = [],
for (var i = 0, ii = Math.max(p.length, p2 && p2.length || 0); i < ii; i++) { pfirst = "", pcom = "",
ii = Math.max(p.length, p2 && p2.length || 0);
for (var i = 0; i < ii; i++) {
p[i] && (pfirst = p[i][0]); p[i] && (pfirst = p[i][0]);
if (pfirst !== "C") { if (pfirst !== "C") {
pcoms1[i] = pfirst; pcoms1[i] = pfirst;
@ -2015,73 +2223,68 @@
attrs2.x = p2 && seg2[seg2len - 2]; attrs2.x = p2 && seg2[seg2len - 2];
attrs2.y = p2 && seg2[seg2len - 1]; attrs2.y = p2 && seg2[seg2len - 1];
} }
return p2 ? [p, p2] : p; return p2 ? [roundPath(p), roundPath(p2)] : roundPath(p);
} }
function reverseCurve(pathCurveArray){
var curveSegments = clonePath(pathCurveArray),
curveCount = curveSegments.length - 2,
ci = 0, ni = 0,
currentSeg = [],
nextSeg = [],
x1, y1, x2, y2, x, y,
curveOnly = curveSegments.slice(1),
rotatedCurve = curveOnly.map(function (p,i){
ci = curveCount - i;
ni = ci - 1 < 0 ? curveCount : ci - 1;
currentSeg = curveOnly[ci];
nextSeg = curveOnly[ni];
x = nextSeg[nextSeg.length - 2];
y = nextSeg[nextSeg.length - 1];
x1 = currentSeg[3]; y1 = currentSeg[4];
x2 = currentSeg[1]; y2 = currentSeg[2];
return [p[0],x1,y1,x2,y2,x,y]
});
return [['M',rotatedCurve[curveCount][5],rotatedCurve[curveCount][6]]].concat(rotatedCurve)
}
function createPath (path) { function createPath (path) {
var np = document.createElementNS('http://www.w3.org/2000/svg','path'), var np = document.createElementNS('http://www.w3.org/2000/svg','path'),
d = path instanceof SVGElement ? path.getAttribute('d') : path; d = path instanceof SVGElement ? path.getAttribute('d') : path;
np.setAttribute('d',d); np.setAttribute('d',d);
return np return np
} }
function getSegments(curveArray) { function getRotationSegments(s,idx) {
return curveArray.map(function (seg) { var segsCount = s.length, pointCount = segsCount - 1;
return { return s.map(function (p,i){
x: seg[seg[0] === 'M' ? 1 : 5], var oldSegIdx = idx + i, seg;
y: seg[seg[0] === 'M' ? 2 : 6], if (i===0 || s[oldSegIdx] && s[oldSegIdx][0] === 'M') {
seg: seg seg = s[oldSegIdx];
return ['M',seg[seg.length-2],seg[seg.length-1]]
} else {
if (oldSegIdx >= segsCount) { oldSegIdx -= pointCount; }
return s[oldSegIdx]
} }
}) })
} }
function reverseCurve(path){
var newSegments = [],
oldSegments = getSegments(path),
segsCount = oldSegments.length,
pointCount = segsCount - 1,
oldSegIdx = pointCount,
oldSegs = [];
oldSegments.map(function (p,i){
if (i === 0||oldSegments[oldSegIdx].seg[0] === 'M') {
newSegments[i] = ['M',oldSegments[oldSegIdx].x,oldSegments[oldSegIdx].y];
} else {
oldSegIdx = pointCount - i > 0 ? pointCount - i : pointCount;
oldSegs = oldSegments[oldSegIdx].seg;
newSegments[i] = [oldSegs[0], oldSegs[5],oldSegs[6],oldSegs[3],oldSegs[4], oldSegs[1], oldSegs[2]];
}
});
return newSegments
}
function getRotationSegments(s,idx) {
var newSegments = [], segsCount = s.length, pointCount = segsCount - 1;
s.map(function (p,i){
var oldSegIdx = idx + i;
if (i===0 || s[oldSegIdx] && s[oldSegIdx].seg[0] === 'M') {
newSegments[i] = ['M',s[oldSegIdx].x,s[oldSegIdx].y];
} else {
if (oldSegIdx >= segsCount) { oldSegIdx -= pointCount; }
newSegments[i] = s[oldSegIdx].seg;
}
});
return newSegments
}
function getRotations(a) { function getRotations(a) {
var startSegments = getSegments(a), rotations = []; return a.map(function (s,i) { return getRotationSegments(a,i); })
startSegments.map(function (s,i){rotations[i] = getRotationSegments(startSegments,i);});
return rotations
} }
function getRotatedCurve(a,b) { function getRotatedCurve(a,b) {
var startSegments = getSegments(a), var segCount = a.length - 1,
endSegments = getSegments(b),
segsCount = startSegments.length,
pointCount = segsCount - 1,
linePaths = [], linePaths = [],
lineLengths = [], lineLengths = [],
rotations = getRotations(a); rotations = getRotations(a);
rotations.map(function (r,i){ rotations.map(function (r,i){
var sumLensSqrd = 0, linePath = createPath('M0,0L0,0'); var sumLensSqrd = 0,
for (var j = 0; j < pointCount; j++) { linePath = createPath('M0,0L0,0'),
var linePt1 = startSegments[(i + j) % pointCount]; linePt1, ll1,
var linePt2 = endSegments[ j % pointCount]; linePt2, ll2,
var linePathStr = "M" + (linePt1.x) + "," + (linePt1.y) + "L" + (linePt2.x) + "," + (linePt2.y); linePathStr;
for (var j = 0; j < segCount; j++) {
linePt1 = a[(i + j) % segCount]; ll1 = linePt1.length;
linePt2 = b[ j % segCount]; ll2 = linePt2.length;
linePathStr = "M" + (linePt1[ll1-2]) + "," + (linePt1[ll1-1]) + "L" + (linePt2[ll2-2]) + "," + (linePt2[ll2-1]);
linePath.setAttribute('d',linePathStr); linePath.setAttribute('d',linePathStr);
sumLensSqrd += Math.pow(linePath.getTotalLength(),2); sumLensSqrd += Math.pow(linePath.getTotalLength(),2);
linePaths[j] = linePath; linePaths[j] = linePath;
@ -2089,9 +2292,8 @@
lineLengths[i] = sumLensSqrd; lineLengths[i] = sumLensSqrd;
sumLensSqrd = 0; sumLensSqrd = 0;
}); });
var computedIndex = lineLengths.indexOf(Math.min.apply(null,lineLengths)), var computedIndex = lineLengths.indexOf(Math.min.apply(null,lineLengths));
newPath = rotations[computedIndex]; return rotations[computedIndex]
return newPath
} }
function getCubicMorph(tweenProp){ function getCubicMorph(tweenProp){
return this.element.getAttribute('d'); return this.element.getAttribute('d');
@ -2121,9 +2323,9 @@
if ( !pathCurve1 || !pathCurve2 || ( pathCurve1 && pathCurve2 && pathCurve1[0][0] === 'M' && pathCurve1.length !== pathCurve2.length) ) { if ( !pathCurve1 || !pathCurve2 || ( pathCurve1 && pathCurve2 && pathCurve1[0][0] === 'M' && pathCurve1.length !== pathCurve2.length) ) {
var path1 = this.valuesStart[tweenProp].original, var path1 = this.valuesStart[tweenProp].original,
path2 = this.valuesEnd[tweenProp].original, path2 = this.valuesEnd[tweenProp].original,
curves = path2curve(path1,path2); curves = pathToCurve(path1,path2);
var curve0 = this._reverseFirstPath ? reverseCurve.call(this,curves[0]) : curves[0], var curve0 = this._reverseFirstPath ? reverseCurve(curves[0]) : curves[0],
curve1 = this._reverseSecondPath ? reverseCurve.call(this,curves[1]) : curves[1]; curve1 = this._reverseSecondPath ? reverseCurve(curves[1]) : curves[1];
curve0 = getRotatedCurve.call(this,curve0,curve1); curve0 = getRotatedCurve.call(this,curve0,curve1);
this.valuesStart[tweenProp].curve = curve0; this.valuesStart[tweenProp].curve = curve0;
this.valuesEnd[tweenProp].curve = curve1; this.valuesEnd[tweenProp].curve = curve1;
@ -2140,13 +2342,12 @@
component: 'svgCubicMorph', component: 'svgCubicMorph',
property: 'path', property: 'path',
defaultValue: [], defaultValue: [],
Interpolate: {numbers: numbers,toPathString: toPathString}, Interpolate: {numbers: numbers,pathToString: pathToString},
functions: svgCubicMorphFunctions, functions: svgCubicMorphFunctions,
Util: { Util: {
l2c: l2c, q2c: q2c, a2c: a2c, catmullRom2bezier: catmullRom2bezier, ellipsePath: ellipsePath, pathToCurve: pathToCurve, pathToAbsolute: pathToAbsolute, pathToString: pathToString, parsePathString: parsePathString,
path2curve: path2curve, pathToAbsolute: pathToAbsolute, toPathString: toPathString, parsePathString: parsePathString,
getRotatedCurve: getRotatedCurve, getRotations: getRotations, getRotatedCurve: getRotatedCurve, getRotations: getRotations,
getRotationSegments: getRotationSegments, reverseCurve: reverseCurve, getSegments: getSegments, createPath: createPath getRotationSegments: getRotationSegments, reverseCurve: reverseCurve, createPath: createPath
} }
}; };
Components.SVGCubicMorph = svgCubicMorph; Components.SVGCubicMorph = svgCubicMorph;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

533
dist/kute.esm.js vendored
View file

@ -15,7 +15,7 @@ var globalObject = typeof (global) !== 'undefined' ? global
var Interpolate = {}; var Interpolate = {};
var onStart$1 = {}; var onStart = {};
var Time = {}; var Time = {};
Time.now = self.performance.now.bind(self.performance); Time.now = self.performance.now.bind(self.performance);
@ -36,11 +36,11 @@ function stop() {
if (!Tweens.length && Tick) { if (!Tweens.length && Tick) {
cancelAnimationFrame(Tick); cancelAnimationFrame(Tick);
Tick = null; Tick = null;
for (var obj in onStart$1) { for (var obj in onStart) {
if (typeof (onStart$1[obj]) === 'function') { if (typeof (onStart[obj]) === 'function') {
KUTE[obj] && (delete KUTE[obj]); KUTE[obj] && (delete KUTE[obj]);
} else { } else {
for (var prop in onStart$1[obj]) { for (var prop in onStart[obj]) {
KUTE[prop] && (delete KUTE[prop]); KUTE[prop] && (delete KUTE[prop]);
} }
} }
@ -86,7 +86,7 @@ var Objects = {
prepareProperty: prepareProperty, prepareProperty: prepareProperty,
prepareStart: prepareStart, prepareStart: prepareStart,
crossCheck: crossCheck, crossCheck: crossCheck,
onStart: onStart$1, onStart: onStart,
onComplete: onComplete, onComplete: onComplete,
linkProperty: linkProperty linkProperty: linkProperty
}; };
@ -106,7 +106,7 @@ function getAll () { return Tweens; }
function removeAll () { Tweens.length = 0; } function removeAll () { Tweens.length = 0; }
function linkInterpolation$1() { function linkInterpolation() {
var this$1 = this; var this$1 = this;
var loop = function ( component ) { var loop = function ( component ) {
var componentLink = linkProperty[component]; var componentLink = linkProperty[component];
@ -143,7 +143,7 @@ var Internals = {
getAll: getAll, getAll: getAll,
removeAll: removeAll, removeAll: removeAll,
stop: stop, stop: stop,
linkInterpolation: linkInterpolation$1 linkInterpolation: linkInterpolation
}; };
function getInlineStyle(el) { function getInlineStyle(el) {
@ -346,6 +346,19 @@ function selector(el, multi) {
} }
} }
function queueStart(){
for (var obj in onStart) {
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj);
} else {
for (var prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
linkInterpolation.call(this);
}
var TweenBase = function TweenBase(targetElement, startObject, endObject, options){ var TweenBase = function TweenBase(targetElement, startObject, endObject, options){
this.element = targetElement; this.element = targetElement;
this.playing = false; this.playing = false;
@ -363,8 +376,8 @@ var TweenBase = function TweenBase(targetElement, startObject, endObject, option
if( !(internalOption in this ) ) { this[internalOption] = options[op]; } if( !(internalOption in this ) ) { this[internalOption] = options[op]; }
} }
var easingFnName = this._easing.name; var easingFnName = this._easing.name;
if (!onStart$1[easingFnName]) { if (!onStart[easingFnName]) {
onStart$1[easingFnName] = function(prop){ onStart[easingFnName] = function(prop){
!KUTE[prop] && prop === this._easing.name && (KUTE[prop] = this._easing); !KUTE[prop] && prop === this._easing.name && (KUTE[prop] = this._easing);
}; };
} }
@ -379,16 +392,7 @@ TweenBase.prototype.start = function start (time) {
if (this._onStart) { if (this._onStart) {
this._onStart.call(this); this._onStart.call(this);
} }
for (var obj in onStart$1) { queueStart.call(this);
if (typeof (onStart$1[obj]) === 'function') {
onStart$1[obj].call(this,obj);
} else {
for (var prop in onStart$1[obj]) {
onStart$1[obj][prop].call(this,prop);
}
}
}
linkInterpolation$1.call(this);
this._startFired = true; this._startFired = true;
} }
!Tick && Ticker(); !Tick && Ticker();
@ -534,16 +538,7 @@ var Tween = (function (TweenBase) {
if (this._onResume !== undefined) { if (this._onResume !== undefined) {
this._onResume.call(this); this._onResume.call(this);
} }
for (var obj in onStart) { queueStart.call(this);
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj);
} else {
for (var prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
linkInterpolation.call(this);
this._startTime += KUTE.Time() - this._pauseTime; this._startTime += KUTE.Time() - this._pauseTime;
add(this); add(this);
!Tick && Ticker(); !Tick && Ticker();
@ -708,7 +703,7 @@ var Animation = function Animation(Component){
Animation.prototype.setComponent = function setComponent (Component){ Animation.prototype.setComponent = function setComponent (Component){
var propertyInfo = this; var propertyInfo = this;
var ComponentName = Component.component; var ComponentName = Component.component;
var Functions = { prepareProperty: prepareProperty, prepareStart: prepareStart, onStart: onStart$1, onComplete: onComplete, crossCheck: crossCheck }; var Functions = { prepareProperty: prepareProperty, prepareStart: prepareStart, onStart: onStart, onComplete: onComplete, crossCheck: crossCheck };
var Category = Component.category; var Category = Component.category;
var Property = Component.property; var Property = Component.property;
var Length = Component.properties && Component.properties.length || Component.subProperties && Component.subProperties.length; var Length = Component.properties && Component.properties.length || Component.subProperties && Component.subProperties.length;
@ -936,7 +931,7 @@ function prepareAttr(tweenProp,attrObj){
if ( currentValue !== null && regex.test(currentValue) ) { if ( currentValue !== null && regex.test(currentValue) ) {
var unit = trueDimension(currentValue).u || trueDimension(attrObj[p]).u; var unit = trueDimension(currentValue).u || trueDimension(attrObj[p]).u;
var suffix = /%/.test(unit) ? '_percent' : ("_" + unit); var suffix = /%/.test(unit) ? '_percent' : ("_" + unit);
onStart$1[ComponentName][prop+suffix] = function(tp) { onStart[ComponentName][prop+suffix] = function(tp) {
if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) { if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) {
attributes[tp] = function (elem, p, a, b, v) { attributes[tp] = function (elem, p, a, b, v) {
var _p = p.replace(suffix,''); var _p = p.replace(suffix,'');
@ -946,7 +941,7 @@ function prepareAttr(tweenProp,attrObj){
}; };
attributesObject[prop+suffix] = trueDimension(attrObj[p]); attributesObject[prop+suffix] = trueDimension(attrObj[p]);
} else if ( !regex.test(attrObj[p]) || currentValue === null || currentValue !== null && !regex.test(currentValue) ) { } else if ( !regex.test(attrObj[p]) || currentValue === null || currentValue !== null && !regex.test(currentValue) ) {
onStart$1[ComponentName][prop] = function(tp) { onStart[ComponentName][prop] = function(tp) {
if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) { if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) {
attributes[tp] = function (elem, oneAttr, a, b, v) { attributes[tp] = function (elem, oneAttr, a, b, v) {
elem.setAttribute(oneAttr, (numbers(a,b,v) * 1000 >> 0) / 1000 ); elem.setAttribute(oneAttr, (numbers(a,b,v) * 1000 >> 0) / 1000 );
@ -956,7 +951,7 @@ function prepareAttr(tweenProp,attrObj){
attributesObject[prop] = parseFloat(attrObj[p]); attributesObject[prop] = parseFloat(attrObj[p]);
} }
} else { } else {
onStart$1[ComponentName][prop] = function(tp) { onStart[ComponentName][prop] = function(tp) {
if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) { if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) {
attributes[tp] = function (elem, oneAttr, a, b, v) { attributes[tp] = function (elem, oneAttr, a, b, v) {
elem.setAttribute(oneAttr, colors(a,b,v)); elem.setAttribute(oneAttr, colors(a,b,v));
@ -1462,30 +1457,45 @@ function onStartSVGMorph(tweenProp){
} }
} }
var INVALID_INPUT = 'Invalid path value'; function clonePath(pathArray){
function isFiniteNumber(number) { return pathArray.map(function (x) { return Array.isArray(x) ? clonePath(x) : !isNaN(+x) ? +x : x; } )
return typeof number === "number" && isFinite(number);
} }
function distance(a, b) {
return Math.sqrt( var SVGPCOps = {
(a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) decimals:3,
); round:1
};
function roundPath(pathArray) {
return pathArray.map( function (seg) { return seg.map(function (c,i) {
var nr = +c, dc = Math.pow(10,SVGPCOps.decimals);
return i ? (nr % 1 === 0 ? nr : (nr*dc>>0)/dc) : c
}
); })
} }
function pointAlong(a, b, pct) {
return [a[0] + (b[0] - a[0]) * pct, a[1] + (b[1] - a[1]) * pct]; function SVGPathArray(pathString){
} this.segments = [];
function samePoint(a, b) { this.isClosed = 0;
return distance(a, b) < 1e-9; this.isAbsolute = 0;
this.pathValue = pathString;
this.max = pathString.length;
this.index = 0;
this.param = 0.0;
this.segmentStart = 0;
this.data = [];
this.err = '';
return this
} }
var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 }; var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 };
var SPECIAL_SPACES = [
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF
];
function isSpace(ch) { function isSpace(ch) {
var specialSpaces = [
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF ];
return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) || return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) ||
(ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) || (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
(ch >= 0x1680 && SPECIAL_SPACES.indexOf(ch) >= 0); (ch >= 0x1680 && specialSpaces.indexOf(ch) >= 0);
} }
function isCommand(code) { function isCommand(code) {
switch (code | 0x20) { switch (code | 0x20) {
@ -1516,23 +1526,13 @@ function isDigitStart(code) {
code === 0x2D || code === 0x2D ||
code === 0x2E; code === 0x2E;
} }
function State(path) {
this.index = 0;
this.path = path;
this.max = path.length;
this.result = [];
this.param = 0.0;
this.err = '';
this.segmentStart = 0;
this.data = [];
}
function skipSpaces(state) { function skipSpaces(state) {
while (state.index < state.max && isSpace(state.path.charCodeAt(state.index))) { while (state.index < state.max && isSpace(state.pathValue.charCodeAt(state.index))) {
state.index++; state.index++;
} }
} }
function scanFlag(state) { function scanFlag(state) {
var ch = state.path.charCodeAt(state.index); var ch = state.pathValue.charCodeAt(state.index);
if (ch === 0x30) { if (ch === 0x30) {
state.param = 0; state.param = 0;
state.index++; state.index++;
@ -1543,7 +1543,7 @@ function scanFlag(state) {
state.index++; state.index++;
return; return;
} }
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: arc flag can be 0 or 1 only (at pos ' + state.index + ')';
} }
function scanParam(state) { function scanParam(state) {
var start = state.index, var start = state.index,
@ -1555,81 +1555,78 @@ function scanParam(state) {
hasDot = false, hasDot = false,
ch; ch;
if (index >= max) { if (index >= max) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: missed param (at pos ' + index + ')';
return; return;
} }
ch = state.path.charCodeAt(index); ch = state.pathValue.charCodeAt(index);
if (ch === 0x2B || ch === 0x2D) { if (ch === 0x2B || ch === 0x2D) {
index++; index++;
ch = (index < max) ? state.path.charCodeAt(index) : 0; ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
} }
if (!isDigit(ch) && ch !== 0x2E) { if (!isDigit(ch) && ch !== 0x2E) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
return; return;
} }
if (ch !== 0x2E) { if (ch !== 0x2E) {
zeroFirst = (ch === 0x30); zeroFirst = (ch === 0x30);
index++; index++;
ch = (index < max) ? state.path.charCodeAt(index) : 0; ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
if (zeroFirst && index < max) { if (zeroFirst && index < max) {
if (ch && isDigit(ch)) { if (ch && isDigit(ch)) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: numbers started with `0` such as `09` are illegal (at pos ' + start + ')';
return; return;
} }
} }
while (index < max && isDigit(state.path.charCodeAt(index))) { while (index < max && isDigit(state.pathValue.charCodeAt(index))) {
index++; index++;
hasCeiling = true; hasCeiling = true;
} }
ch = (index < max) ? state.path.charCodeAt(index) : 0; ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
} }
if (ch === 0x2E) { if (ch === 0x2E) {
hasDot = true; hasDot = true;
index++; index++;
while (isDigit(state.path.charCodeAt(index))) { while (isDigit(state.pathValue.charCodeAt(index))) {
index++; index++;
hasDecimal = true; hasDecimal = true;
} }
ch = (index < max) ? state.path.charCodeAt(index) : 0; ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
} }
if (ch === 0x65 || ch === 0x45) { if (ch === 0x65 || ch === 0x45) {
if (hasDot && !hasCeiling && !hasDecimal) { if (hasDot && !hasCeiling && !hasDecimal) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
return; return;
} }
index++; index++;
ch = (index < max) ? state.path.charCodeAt(index) : 0; ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
if (ch === 0x2B || ch === 0x2D) { if (ch === 0x2B || ch === 0x2D) {
index++; index++;
} }
if (index < max && isDigit(state.path.charCodeAt(index))) { if (index < max && isDigit(state.pathValue.charCodeAt(index))) {
while (index < max && isDigit(state.path.charCodeAt(index))) { while (index < max && isDigit(state.pathValue.charCodeAt(index))) {
index++; index++;
} }
} else { } else {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
return; return;
} }
} }
state.index = index; state.index = index;
state.param = parseFloat(state.path.slice(start, index)) + 0.0; state.param = parseFloat(state.pathValue.slice(start, index)) + 0.0;
} }
function finalizeSegment(state) { function finalizeSegment(state) {
var cmd, cmdLC; var cmd = state.pathValue[state.segmentStart], cmdLC = cmd.toLowerCase(), params = state.data;
cmd = state.path[state.segmentStart];
cmdLC = cmd.toLowerCase();
var params = state.data;
if (cmdLC === 'm' && params.length > 2) { if (cmdLC === 'm' && params.length > 2) {
state.result.push([ cmd, params[0], params[1] ]); state.segments.push([ cmd, params[0], params[1] ]);
params = params.slice(2); params = params.slice(2);
cmdLC = 'l'; cmdLC = 'l';
cmd = (cmd === 'm') ? 'l' : 'L'; cmd = (cmd === 'm') ? 'l' : 'L';
} }
if (cmdLC === 'r') { if (cmdLC === 'r') {
state.result.push([ cmd ].concat(params)); state.segments.push([ cmd ].concat(params));
} else { } else {
while (params.length >= paramCounts[cmdLC]) { while (params.length >= paramCounts[cmdLC]) {
state.result.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC]))); state.segments.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC])));
if (!paramCounts[cmdLC]) { if (!paramCounts[cmdLC]) {
break; break;
} }
@ -1639,17 +1636,18 @@ function finalizeSegment(state) {
function scanSegment(state) { function scanSegment(state) {
var max = state.max, cmdCode, is_arc, comma_found, need_params, i; var max = state.max, cmdCode, is_arc, comma_found, need_params, i;
state.segmentStart = state.index; state.segmentStart = state.index;
cmdCode = state.path.charCodeAt(state.index); cmdCode = state.pathValue.charCodeAt(state.index);
is_arc = isArc(cmdCode); is_arc = isArc(cmdCode);
if (!isCommand(cmdCode)) { if (!isCommand(cmdCode)) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: bad command ' + state.pathValue[state.index] + ' (at pos ' + state.index + ')';
return; return;
} }
need_params = paramCounts[state.path[state.index].toLowerCase()]; need_params = paramCounts[state.pathValue[state.index].toLowerCase()];
state.index++; state.index++;
skipSpaces(state); skipSpaces(state);
state.data = []; state.data = [];
if (!need_params) { if (!need_params) {
state.isClosed = 1;
finalizeSegment(state); finalizeSegment(state);
return; return;
} }
@ -1664,7 +1662,7 @@ function scanSegment(state) {
state.data.push(state.param); state.data.push(state.param);
skipSpaces(state); skipSpaces(state);
comma_found = false; comma_found = false;
if (state.index < max && state.path.charCodeAt(state.index) === 0x2C) { if (state.index < max && state.pathValue.charCodeAt(state.index) === 0x2C) {
state.index++; state.index++;
skipSpaces(state); skipSpaces(state);
comma_found = true; comma_found = true;
@ -1676,148 +1674,250 @@ function scanSegment(state) {
if (state.index >= state.max) { if (state.index >= state.max) {
break; break;
} }
if (!isDigitStart(state.path.charCodeAt(state.index))) { if (!isDigitStart(state.pathValue.charCodeAt(state.index))) {
break; break;
} }
} }
finalizeSegment(state); finalizeSegment(state);
} }
function pathParse(svgPath) { function parsePathString(pathString) {
var state = new State(svgPath), max = state.max; if ( Array.isArray(pathString) ) {
return clonePath(pathString)
}
var state = new SVGPathArray(pathString), max = state.max;
skipSpaces(state); skipSpaces(state);
while (state.index < max && !state.err.length) { while (state.index < max && !state.err.length) {
scanSegment(state); scanSegment(state);
} }
if (state.err.length) { if (state.err.length) {
state.result = []; state.segments = [];
} else if (state.result.length) { } else if (state.segments.length) {
if ('mM'.indexOf(state.result[0][0]) < 0) { if ('mM'.indexOf(state.segments[0][0]) < 0) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: string should start with `M` or `m`';
state.result = []; state.segments = [];
} else { } else {
state.result[0][0] = 'M'; state.segments[0][0] = 'M';
} }
} }
return { return roundPath(state.segments)
err: state.err,
segments: state.result
};
} }
var SvgPath = function SvgPath(path){
if (!(this instanceof SvgPath)) { return new SvgPath(path); } function catmullRom2bezier(crp, z) {
var pstate = pathParse(path); var d = [];
this.segments = pstate.segments; for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
this.err = pstate.err; var p = [
}; {x: +crp[i - 2], y: +crp[i - 1]},
SvgPath.prototype.iterate = function iterate (iterator) { {x: +crp[i], y: +crp[i + 1]},
var segments = this.segments, {x: +crp[i + 2], y: +crp[i + 3]},
replacements = {}, {x: +crp[i + 4], y: +crp[i + 5]}
needReplace = false, ];
lastX = 0, if (z) {
lastY = 0, if (!i) {
countourStartX = 0, p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]};
countourStartY = 0, } else if (iLen - 4 == i) {
newSegments; p[3] = {x: +crp[0], y: +crp[1]};
segments.map( function (s,index) { } else if (iLen - 2 == i) {
var res = iterator(s, index, lastX, lastY); p[2] = {x: +crp[0], y: +crp[1]};
if (Array.isArray(res)) { p[3] = {x: +crp[2], y: +crp[3]};
replacements[index] = res; }
needReplace = true;
}
var isRelative = (s[0] === s[0].toLowerCase());
switch (s[0]) {
case 'm':
case 'M':
lastX = s[1] + (isRelative ? lastX : 0);
lastY = s[2] + (isRelative ? lastY : 0);
countourStartX = lastX;
countourStartY = lastY;
return;
case 'h':
case 'H':
lastX = s[1] + (isRelative ? lastX : 0);
return;
case 'v':
case 'V':
lastY = s[1] + (isRelative ? lastY : 0);
return;
case 'z':
case 'Z':
lastX = countourStartX;
lastY = countourStartY;
return;
default:
lastX = s[s.length - 2] + (isRelative ? lastX : 0);
lastY = s[s.length - 1] + (isRelative ? lastY : 0);
}
});
if (!needReplace) { return this; }
newSegments = [];
segments.map(function (s,i){
if (typeof replacements[i] !== 'undefined') {
replacements[i].map(function (r){
newSegments.push(r);
});
} else { } else {
newSegments.push(s); if (iLen - 4 == i) {
p[3] = p[2];
} else if (!i) {
p[0] = {x: +crp[i], y: +crp[i + 1]};
}
} }
}); d.push([
this.segments = newSegments; "C",
return this; (-p[0].x + 6 * p[1].x + p[2].x) / 6,
}; (-p[0].y + 6 * p[1].y + p[2].y) / 6,
SvgPath.prototype.abs = function abs () { (p[1].x + 6 * p[2].x - p[3].x) / 6,
this.iterate(function (s, index, x, y) { (p[1].y + 6*p[2].y - p[3].y) / 6,
var name = s[0], p[2].x,
nameUC = name.toUpperCase(), p[2].y
i; ]);
if (name === nameUC) { return; } }
s[0] = nameUC; return d
switch (name) { }
case 'v':
s[1] += y; function ellipsePath(x, y, rx, ry, a) {
return; if (a == null && ry == null) {
case 'a': ry = rx;
s[6] += x; }
s[7] += y; x = +x;
return; y = +y;
default: rx = +rx;
for (i = 1; i < s.length; i++) { ry = +ry;
s[i] += i % 2 ? x : y; var res;
} if (a != null) {
var rad = Math.PI / 180,
x1 = x + rx * Math.cos(-ry * rad),
x2 = x + rx * Math.cos(-a * rad),
y1 = y + rx * Math.sin(-ry * rad),
y2 = y + rx * Math.sin(-a * rad);
res = [["M", x1, y1], ["A", rx, rx, 0, +(a - ry > 180), 0, x2, y2]];
} else {
res = [
["M", x, y],
["m", 0, -ry],
["a", rx, ry, 0, 1, 1, 0, 2 * ry],
["a", rx, ry, 0, 1, 1, 0, -2 * ry],
["z"]
];
}
return res;
}
function pathToAbsolute(pathArray) {
pathArray = parsePathString(pathArray);
if (!pathArray || !pathArray.length) {
return [["M", 0, 0]];
}
var res = [], x = 0, y = 0, mx = 0, my = 0, start = 0,
ii = pathArray.length,
crz = pathArray.length === 3 &&
pathArray[0][0] === "M" &&
pathArray[1][0].toUpperCase() === "R" &&
pathArray[2][0].toUpperCase() === "Z";
if (pathArray[0][0] === "M") {
x = +pathArray[0][1];
y = +pathArray[0][2];
mx = x;
my = y;
start++;
res[0] = ["M", x, y];
}
var loop = function ( i ) {
var r = (void 0), pa = pathArray[i], pa0 = pa[0];
res.push(r = []);
if (pa0 !== pa0.toUpperCase()) {
r[0] = pa0.toUpperCase();
switch (r[0]) {
case "A":
r[1] = pa[1];
r[2] = pa[2];
r[3] = pa[3];
r[4] = pa[4];
r[5] = pa[5];
r[6] = +pa[6] + x;
r[7] = +pa[7] + y;
break;
case "V":
r[1] = +pa[1] + y;
break;
case "H":
r[1] = +pa[1] + x;
break;
case "R":
var dots$1 = [x, y].concat(pa.slice(1));
for (var j = 2, jj = dots$1.length; j < jj; j++) {
dots$1[j] = +dots$1[j] + x;
dots$1[++j] = +dots$1[j] + y;
}
res.pop();
res = res.concat(catmullRom2bezier(dots$1, crz));
break;
case "O":
res.pop();
dots$1 = ellipsePath(x, y, +pa[1], +pa[2]);
dots$1.push(dots$1[0]);
res = res.concat(dots$1);
break;
case "U":
res.pop();
res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3]));
r = ["U"].concat(res[res.length - 1].slice(-2));
break;
case "M":
mx = +pa[1] + x;
my = +pa[2] + y;
default:
for (var j$1 = 1, jj$1 = pa.length; j$1 < jj$1; j$1++) {
r[j$1] = +pa[j$1] + ((j$1 % 2) ? x : y);
}
}
} else if (pa0 === "R") {
dots = [x, y].concat(pa.slice(1));
res.pop();
res = res.concat(catmullRom2bezier(dots, crz));
r = ["R"].concat(pa.slice(-2));
} else if (pa0 === "O") {
res.pop();
dots = ellipsePath(x, y, +pa[1], +pa[2]);
dots.push(dots[0]);
res = res.concat(dots);
} else if (pa0 === "U") {
res.pop();
res = res.concat(ellipsePath(x, y, +pa[1], +pa[2], +pa[3]));
r = ["U"].concat(res[res.length - 1].slice(-2));
} else {
pa.map(function (k){ return r.push(k); });
} }
}, true); pa0 = pa0.toUpperCase();
return this; if (pa0 !== "O") {
}; switch (r[0]) {
SvgPath.prototype.toString = function toString () { case "Z":
var this$1 = this; x = mx;
var elements = [], skipCmd, cmd; y = my;
this.segments.map( function (s,i) { break;
cmd = s[0]; case "H":
skipCmd = i > 0 && cmd !== 'm' && cmd !== 'M' && cmd === this$1.segments[i - 1][0]; x = +r[1];
elements = elements.concat(skipCmd ? s.slice(1) : s); break;
}); case "V":
return elements.join(' ') y = +r[1];
.replace(/ ?([achlmqrstvz]) ?/gi, '$1') break;
.replace(/ \-/g, '-') case "M":
.replace(/zm/g, 'z m'); mx = +r[r.length - 2];
}; my = +r[r.length - 1];
function split(parsed) { default:
return parsed x = +r[r.length - 2];
.toString() y = +r[r.length - 1];
.split("M") }
.map(function (d, i) { }
d = d.trim(); };
return i && d ? "M" + d : d; for (var i = start; i < ii; i++) loop( i );
}) return roundPath(res)
.filter(function (d) { return d; }); }
function pathToString(pathArray) {
return pathArray.map( function (c) {
if (typeof c === 'string') {
return c
} else {
return c.shift() + c.join(',')
}
}).join(' ')
}
function splitPath(pathString) {
return pathString
.replace( /(m|M)/g, "|$1")
.split('|')
.map(function (s){ return s.trim(); })
.filter(function (s){ return s; })
}
var INVALID_INPUT = 'Invalid path value';
function isFiniteNumber(number) {
return typeof number === "number" && isFinite(number);
}
function distance(a, b) {
return Math.sqrt(
(a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1])
);
}
function pointAlong(a, b, pct) {
return [a[0] + (b[0] - a[0]) * pct, a[1] + (b[1] - a[1]) * pct];
}
function samePoint(a, b) {
return distance(a, b) < 1e-9;
} }
function pathStringToRing(str, maxSegmentLength) { function pathStringToRing(str, maxSegmentLength) {
var parsed = new SvgPath(str).abs(); var parsed = pathToAbsolute(str);
return exactRing(parsed) || approximateRing(parsed, maxSegmentLength); return exactRing(parsed) || approximateRing(parsed, maxSegmentLength);
} }
function exactRing(parsed) { function exactRing(segments) {
var segments = parsed.segments || [], var ring = [];
ring = [];
if (!segments.length || segments[0][0] !== "M") { if (!segments.length || segments[0][0] !== "M") {
return false; return false;
} }
@ -1841,7 +1941,8 @@ function exactRing(parsed) {
return ring.length ? { ring: ring } : false; return ring.length ? { ring: ring } : false;
} }
function approximateRing(parsed, maxSegmentLength) { function approximateRing(parsed, maxSegmentLength) {
var ringPath = split(parsed)[0], ring = [], len, testPath, numPoints = 3; var ringPath = splitPath(pathToString(parsed))[0],
ring = [], len, testPath, numPoints = 3;
if (!ringPath) { if (!ringPath) {
throw new TypeError(INVALID_INPUT); throw new TypeError(INVALID_INPUT);
} }
@ -2032,9 +2133,11 @@ var svgMorph = {
defaultOptions: {morphPrecision : 10, morphIndex:0}, defaultOptions: {morphPrecision : 10, morphIndex:0},
functions: svgMorphFunctions, functions: svgMorphFunctions,
Util: { Util: {
INVALID_INPUT: INVALID_INPUT,isFiniteNumber: isFiniteNumber,distance: distance,pointAlong: pointAlong,samePoint: samePoint,paramCounts: paramCounts,SPECIAL_SPACES: SPECIAL_SPACES,isSpace: isSpace,isCommand: isCommand,isArc: isArc,isDigit: isDigit, INVALID_INPUT: INVALID_INPUT,isFiniteNumber: isFiniteNumber,distance: distance,pointAlong: pointAlong,samePoint: samePoint,
isDigitStart: isDigitStart,State: State,skipSpaces: skipSpaces,scanFlag: scanFlag,scanParam: scanParam,finalizeSegment: finalizeSegment,scanSegment: scanSegment,pathParse: pathParse,SvgPath: SvgPath,split: split,pathStringToRing: pathStringToRing, pathToAbsolute: pathToAbsolute,pathToString: pathToString,pathStringToRing: pathStringToRing,
exactRing: exactRing,approximateRing: approximateRing,measure: measure,rotateRing: rotateRing,polygonLength: polygonLength,polygonArea: polygonArea,addPoints: addPoints,bisect: bisect,normalizeRing: normalizeRing,validRing: validRing,getInterpolationPoints: getInterpolationPoints} exactRing: exactRing,approximateRing: approximateRing,measure: measure,rotateRing: rotateRing,polygonLength: polygonLength,polygonArea: polygonArea,
addPoints: addPoints,bisect: bisect,normalizeRing: normalizeRing,validRing: validRing,getInterpolationPoints: getInterpolationPoints
}
}; };
Components.SVGMorph = svgMorph; Components.SVGMorph = svgMorph;

File diff suppressed because one or more lines are too long

533
dist/kute.js vendored
View file

@ -21,7 +21,7 @@
var Interpolate = {}; var Interpolate = {};
var onStart$1 = {}; var onStart = {};
var Time = {}; var Time = {};
Time.now = self.performance.now.bind(self.performance); Time.now = self.performance.now.bind(self.performance);
@ -42,11 +42,11 @@
if (!Tweens.length && Tick) { if (!Tweens.length && Tick) {
cancelAnimationFrame(Tick); cancelAnimationFrame(Tick);
Tick = null; Tick = null;
for (var obj in onStart$1) { for (var obj in onStart) {
if (typeof (onStart$1[obj]) === 'function') { if (typeof (onStart[obj]) === 'function') {
KUTE[obj] && (delete KUTE[obj]); KUTE[obj] && (delete KUTE[obj]);
} else { } else {
for (var prop in onStart$1[obj]) { for (var prop in onStart[obj]) {
KUTE[prop] && (delete KUTE[prop]); KUTE[prop] && (delete KUTE[prop]);
} }
} }
@ -92,7 +92,7 @@
prepareProperty: prepareProperty, prepareProperty: prepareProperty,
prepareStart: prepareStart, prepareStart: prepareStart,
crossCheck: crossCheck, crossCheck: crossCheck,
onStart: onStart$1, onStart: onStart,
onComplete: onComplete, onComplete: onComplete,
linkProperty: linkProperty linkProperty: linkProperty
}; };
@ -112,7 +112,7 @@
function removeAll () { Tweens.length = 0; } function removeAll () { Tweens.length = 0; }
function linkInterpolation$1() { function linkInterpolation() {
var this$1 = this; var this$1 = this;
var loop = function ( component ) { var loop = function ( component ) {
var componentLink = linkProperty[component]; var componentLink = linkProperty[component];
@ -149,7 +149,7 @@
getAll: getAll, getAll: getAll,
removeAll: removeAll, removeAll: removeAll,
stop: stop, stop: stop,
linkInterpolation: linkInterpolation$1 linkInterpolation: linkInterpolation
}; };
function getInlineStyle(el) { function getInlineStyle(el) {
@ -352,6 +352,19 @@
} }
} }
function queueStart(){
for (var obj in onStart) {
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj);
} else {
for (var prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
linkInterpolation.call(this);
}
var TweenBase = function TweenBase(targetElement, startObject, endObject, options){ var TweenBase = function TweenBase(targetElement, startObject, endObject, options){
this.element = targetElement; this.element = targetElement;
this.playing = false; this.playing = false;
@ -369,8 +382,8 @@
if( !(internalOption in this ) ) { this[internalOption] = options[op]; } if( !(internalOption in this ) ) { this[internalOption] = options[op]; }
} }
var easingFnName = this._easing.name; var easingFnName = this._easing.name;
if (!onStart$1[easingFnName]) { if (!onStart[easingFnName]) {
onStart$1[easingFnName] = function(prop){ onStart[easingFnName] = function(prop){
!KUTE[prop] && prop === this._easing.name && (KUTE[prop] = this._easing); !KUTE[prop] && prop === this._easing.name && (KUTE[prop] = this._easing);
}; };
} }
@ -385,16 +398,7 @@
if (this._onStart) { if (this._onStart) {
this._onStart.call(this); this._onStart.call(this);
} }
for (var obj in onStart$1) { queueStart.call(this);
if (typeof (onStart$1[obj]) === 'function') {
onStart$1[obj].call(this,obj);
} else {
for (var prop in onStart$1[obj]) {
onStart$1[obj][prop].call(this,prop);
}
}
}
linkInterpolation$1.call(this);
this._startFired = true; this._startFired = true;
} }
!Tick && Ticker(); !Tick && Ticker();
@ -540,16 +544,7 @@
if (this._onResume !== undefined) { if (this._onResume !== undefined) {
this._onResume.call(this); this._onResume.call(this);
} }
for (var obj in onStart) { queueStart.call(this);
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj);
} else {
for (var prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
linkInterpolation.call(this);
this._startTime += KUTE.Time() - this._pauseTime; this._startTime += KUTE.Time() - this._pauseTime;
add(this); add(this);
!Tick && Ticker(); !Tick && Ticker();
@ -714,7 +709,7 @@
Animation.prototype.setComponent = function setComponent (Component){ Animation.prototype.setComponent = function setComponent (Component){
var propertyInfo = this; var propertyInfo = this;
var ComponentName = Component.component; var ComponentName = Component.component;
var Functions = { prepareProperty: prepareProperty, prepareStart: prepareStart, onStart: onStart$1, onComplete: onComplete, crossCheck: crossCheck }; var Functions = { prepareProperty: prepareProperty, prepareStart: prepareStart, onStart: onStart, onComplete: onComplete, crossCheck: crossCheck };
var Category = Component.category; var Category = Component.category;
var Property = Component.property; var Property = Component.property;
var Length = Component.properties && Component.properties.length || Component.subProperties && Component.subProperties.length; var Length = Component.properties && Component.properties.length || Component.subProperties && Component.subProperties.length;
@ -942,7 +937,7 @@
if ( currentValue !== null && regex.test(currentValue) ) { if ( currentValue !== null && regex.test(currentValue) ) {
var unit = trueDimension(currentValue).u || trueDimension(attrObj[p]).u; var unit = trueDimension(currentValue).u || trueDimension(attrObj[p]).u;
var suffix = /%/.test(unit) ? '_percent' : ("_" + unit); var suffix = /%/.test(unit) ? '_percent' : ("_" + unit);
onStart$1[ComponentName][prop+suffix] = function(tp) { onStart[ComponentName][prop+suffix] = function(tp) {
if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) { if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) {
attributes[tp] = function (elem, p, a, b, v) { attributes[tp] = function (elem, p, a, b, v) {
var _p = p.replace(suffix,''); var _p = p.replace(suffix,'');
@ -952,7 +947,7 @@
}; };
attributesObject[prop+suffix] = trueDimension(attrObj[p]); attributesObject[prop+suffix] = trueDimension(attrObj[p]);
} else if ( !regex.test(attrObj[p]) || currentValue === null || currentValue !== null && !regex.test(currentValue) ) { } else if ( !regex.test(attrObj[p]) || currentValue === null || currentValue !== null && !regex.test(currentValue) ) {
onStart$1[ComponentName][prop] = function(tp) { onStart[ComponentName][prop] = function(tp) {
if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) { if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) {
attributes[tp] = function (elem, oneAttr, a, b, v) { attributes[tp] = function (elem, oneAttr, a, b, v) {
elem.setAttribute(oneAttr, (numbers(a,b,v) * 1000 >> 0) / 1000 ); elem.setAttribute(oneAttr, (numbers(a,b,v) * 1000 >> 0) / 1000 );
@ -962,7 +957,7 @@
attributesObject[prop] = parseFloat(attrObj[p]); attributesObject[prop] = parseFloat(attrObj[p]);
} }
} else { } else {
onStart$1[ComponentName][prop] = function(tp) { onStart[ComponentName][prop] = function(tp) {
if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) { if ( this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes) ) {
attributes[tp] = function (elem, oneAttr, a, b, v) { attributes[tp] = function (elem, oneAttr, a, b, v) {
elem.setAttribute(oneAttr, colors(a,b,v)); elem.setAttribute(oneAttr, colors(a,b,v));
@ -1468,30 +1463,45 @@
} }
} }
var INVALID_INPUT = 'Invalid path value'; function clonePath(pathArray){
function isFiniteNumber(number) { return pathArray.map(function (x) { return Array.isArray(x) ? clonePath(x) : !isNaN(+x) ? +x : x; } )
return typeof number === "number" && isFinite(number);
} }
function distance(a, b) {
return Math.sqrt( var SVGPCOps = {
(a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) decimals:3,
); round:1
};
function roundPath(pathArray) {
return pathArray.map( function (seg) { return seg.map(function (c,i) {
var nr = +c, dc = Math.pow(10,SVGPCOps.decimals);
return i ? (nr % 1 === 0 ? nr : (nr*dc>>0)/dc) : c
}
); })
} }
function pointAlong(a, b, pct) {
return [a[0] + (b[0] - a[0]) * pct, a[1] + (b[1] - a[1]) * pct]; function SVGPathArray(pathString){
} this.segments = [];
function samePoint(a, b) { this.isClosed = 0;
return distance(a, b) < 1e-9; this.isAbsolute = 0;
this.pathValue = pathString;
this.max = pathString.length;
this.index = 0;
this.param = 0.0;
this.segmentStart = 0;
this.data = [];
this.err = '';
return this
} }
var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 }; var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 };
var SPECIAL_SPACES = [
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF
];
function isSpace(ch) { function isSpace(ch) {
var specialSpaces = [
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF ];
return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) || return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) ||
(ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) || (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
(ch >= 0x1680 && SPECIAL_SPACES.indexOf(ch) >= 0); (ch >= 0x1680 && specialSpaces.indexOf(ch) >= 0);
} }
function isCommand(code) { function isCommand(code) {
switch (code | 0x20) { switch (code | 0x20) {
@ -1522,23 +1532,13 @@
code === 0x2D || code === 0x2D ||
code === 0x2E; code === 0x2E;
} }
function State(path) {
this.index = 0;
this.path = path;
this.max = path.length;
this.result = [];
this.param = 0.0;
this.err = '';
this.segmentStart = 0;
this.data = [];
}
function skipSpaces(state) { function skipSpaces(state) {
while (state.index < state.max && isSpace(state.path.charCodeAt(state.index))) { while (state.index < state.max && isSpace(state.pathValue.charCodeAt(state.index))) {
state.index++; state.index++;
} }
} }
function scanFlag(state) { function scanFlag(state) {
var ch = state.path.charCodeAt(state.index); var ch = state.pathValue.charCodeAt(state.index);
if (ch === 0x30) { if (ch === 0x30) {
state.param = 0; state.param = 0;
state.index++; state.index++;
@ -1549,7 +1549,7 @@
state.index++; state.index++;
return; return;
} }
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: arc flag can be 0 or 1 only (at pos ' + state.index + ')';
} }
function scanParam(state) { function scanParam(state) {
var start = state.index, var start = state.index,
@ -1561,81 +1561,78 @@
hasDot = false, hasDot = false,
ch; ch;
if (index >= max) { if (index >= max) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: missed param (at pos ' + index + ')';
return; return;
} }
ch = state.path.charCodeAt(index); ch = state.pathValue.charCodeAt(index);
if (ch === 0x2B || ch === 0x2D) { if (ch === 0x2B || ch === 0x2D) {
index++; index++;
ch = (index < max) ? state.path.charCodeAt(index) : 0; ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
} }
if (!isDigit(ch) && ch !== 0x2E) { if (!isDigit(ch) && ch !== 0x2E) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
return; return;
} }
if (ch !== 0x2E) { if (ch !== 0x2E) {
zeroFirst = (ch === 0x30); zeroFirst = (ch === 0x30);
index++; index++;
ch = (index < max) ? state.path.charCodeAt(index) : 0; ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
if (zeroFirst && index < max) { if (zeroFirst && index < max) {
if (ch && isDigit(ch)) { if (ch && isDigit(ch)) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: numbers started with `0` such as `09` are illegal (at pos ' + start + ')';
return; return;
} }
} }
while (index < max && isDigit(state.path.charCodeAt(index))) { while (index < max && isDigit(state.pathValue.charCodeAt(index))) {
index++; index++;
hasCeiling = true; hasCeiling = true;
} }
ch = (index < max) ? state.path.charCodeAt(index) : 0; ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
} }
if (ch === 0x2E) { if (ch === 0x2E) {
hasDot = true; hasDot = true;
index++; index++;
while (isDigit(state.path.charCodeAt(index))) { while (isDigit(state.pathValue.charCodeAt(index))) {
index++; index++;
hasDecimal = true; hasDecimal = true;
} }
ch = (index < max) ? state.path.charCodeAt(index) : 0; ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
} }
if (ch === 0x65 || ch === 0x45) { if (ch === 0x65 || ch === 0x45) {
if (hasDot && !hasCeiling && !hasDecimal) { if (hasDot && !hasCeiling && !hasDecimal) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
return; return;
} }
index++; index++;
ch = (index < max) ? state.path.charCodeAt(index) : 0; ch = (index < max) ? state.pathValue.charCodeAt(index) : 0;
if (ch === 0x2B || ch === 0x2D) { if (ch === 0x2B || ch === 0x2D) {
index++; index++;
} }
if (index < max && isDigit(state.path.charCodeAt(index))) { if (index < max && isDigit(state.pathValue.charCodeAt(index))) {
while (index < max && isDigit(state.path.charCodeAt(index))) { while (index < max && isDigit(state.pathValue.charCodeAt(index))) {
index++; index++;
} }
} else { } else {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
return; return;
} }
} }
state.index = index; state.index = index;
state.param = parseFloat(state.path.slice(start, index)) + 0.0; state.param = parseFloat(state.pathValue.slice(start, index)) + 0.0;
} }
function finalizeSegment(state) { function finalizeSegment(state) {
var cmd, cmdLC; var cmd = state.pathValue[state.segmentStart], cmdLC = cmd.toLowerCase(), params = state.data;
cmd = state.path[state.segmentStart];
cmdLC = cmd.toLowerCase();
var params = state.data;
if (cmdLC === 'm' && params.length > 2) { if (cmdLC === 'm' && params.length > 2) {
state.result.push([ cmd, params[0], params[1] ]); state.segments.push([ cmd, params[0], params[1] ]);
params = params.slice(2); params = params.slice(2);
cmdLC = 'l'; cmdLC = 'l';
cmd = (cmd === 'm') ? 'l' : 'L'; cmd = (cmd === 'm') ? 'l' : 'L';
} }
if (cmdLC === 'r') { if (cmdLC === 'r') {
state.result.push([ cmd ].concat(params)); state.segments.push([ cmd ].concat(params));
} else { } else {
while (params.length >= paramCounts[cmdLC]) { while (params.length >= paramCounts[cmdLC]) {
state.result.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC]))); state.segments.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC])));
if (!paramCounts[cmdLC]) { if (!paramCounts[cmdLC]) {
break; break;
} }
@ -1645,17 +1642,18 @@
function scanSegment(state) { function scanSegment(state) {
var max = state.max, cmdCode, is_arc, comma_found, need_params, i; var max = state.max, cmdCode, is_arc, comma_found, need_params, i;
state.segmentStart = state.index; state.segmentStart = state.index;
cmdCode = state.path.charCodeAt(state.index); cmdCode = state.pathValue.charCodeAt(state.index);
is_arc = isArc(cmdCode); is_arc = isArc(cmdCode);
if (!isCommand(cmdCode)) { if (!isCommand(cmdCode)) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: bad command ' + state.pathValue[state.index] + ' (at pos ' + state.index + ')';
return; return;
} }
need_params = paramCounts[state.path[state.index].toLowerCase()]; need_params = paramCounts[state.pathValue[state.index].toLowerCase()];
state.index++; state.index++;
skipSpaces(state); skipSpaces(state);
state.data = []; state.data = [];
if (!need_params) { if (!need_params) {
state.isClosed = 1;
finalizeSegment(state); finalizeSegment(state);
return; return;
} }
@ -1670,7 +1668,7 @@
state.data.push(state.param); state.data.push(state.param);
skipSpaces(state); skipSpaces(state);
comma_found = false; comma_found = false;
if (state.index < max && state.path.charCodeAt(state.index) === 0x2C) { if (state.index < max && state.pathValue.charCodeAt(state.index) === 0x2C) {
state.index++; state.index++;
skipSpaces(state); skipSpaces(state);
comma_found = true; comma_found = true;
@ -1682,148 +1680,250 @@
if (state.index >= state.max) { if (state.index >= state.max) {
break; break;
} }
if (!isDigitStart(state.path.charCodeAt(state.index))) { if (!isDigitStart(state.pathValue.charCodeAt(state.index))) {
break; break;
} }
} }
finalizeSegment(state); finalizeSegment(state);
} }
function pathParse(svgPath) { function parsePathString(pathString) {
var state = new State(svgPath), max = state.max; if ( Array.isArray(pathString) ) {
return clonePath(pathString)
}
var state = new SVGPathArray(pathString), max = state.max;
skipSpaces(state); skipSpaces(state);
while (state.index < max && !state.err.length) { while (state.index < max && !state.err.length) {
scanSegment(state); scanSegment(state);
} }
if (state.err.length) { if (state.err.length) {
state.result = []; state.segments = [];
} else if (state.result.length) { } else if (state.segments.length) {
if ('mM'.indexOf(state.result[0][0]) < 0) { if ('mM'.indexOf(state.segments[0][0]) < 0) {
state.err = "KUTE.js - " + INVALID_INPUT; state.err = 'SvgPath: string should start with `M` or `m`';
state.result = []; state.segments = [];
} else { } else {
state.result[0][0] = 'M'; state.segments[0][0] = 'M';
} }
} }
return { return roundPath(state.segments)
err: state.err,
segments: state.result
};
} }
var SvgPath = function SvgPath(path){
if (!(this instanceof SvgPath)) { return new SvgPath(path); } function catmullRom2bezier(crp, z) {
var pstate = pathParse(path); var d = [];
this.segments = pstate.segments; for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
this.err = pstate.err; var p = [
}; {x: +crp[i - 2], y: +crp[i - 1]},
SvgPath.prototype.iterate = function iterate (iterator) { {x: +crp[i], y: +crp[i + 1]},
var segments = this.segments, {x: +crp[i + 2], y: +crp[i + 3]},
replacements = {}, {x: +crp[i + 4], y: +crp[i + 5]}
needReplace = false, ];
lastX = 0, if (z) {
lastY = 0, if (!i) {
countourStartX = 0, p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]};
countourStartY = 0, } else if (iLen - 4 == i) {
newSegments; p[3] = {x: +crp[0], y: +crp[1]};
segments.map( function (s,index) { } else if (iLen - 2 == i) {
var res = iterator(s, index, lastX, lastY); p[2] = {x: +crp[0], y: +crp[1]};
if (Array.isArray(res)) { p[3] = {x: +crp[2], y: +crp[3]};
replacements[index] = res; }
needReplace = true;
}
var isRelative = (s[0] === s[0].toLowerCase());
switch (s[0]) {
case 'm':
case 'M':
lastX = s[1] + (isRelative ? lastX : 0);
lastY = s[2] + (isRelative ? lastY : 0);
countourStartX = lastX;
countourStartY = lastY;
return;
case 'h':
case 'H':
lastX = s[1] + (isRelative ? lastX : 0);
return;
case 'v':
case 'V':
lastY = s[1] + (isRelative ? lastY : 0);
return;
case 'z':
case 'Z':
lastX = countourStartX;
lastY = countourStartY;
return;
default:
lastX = s[s.length - 2] + (isRelative ? lastX : 0);
lastY = s[s.length - 1] + (isRelative ? lastY : 0);
}
});
if (!needReplace) { return this; }
newSegments = [];
segments.map(function (s,i){
if (typeof replacements[i] !== 'undefined') {
replacements[i].map(function (r){
newSegments.push(r);
});
} else { } else {
newSegments.push(s); if (iLen - 4 == i) {
p[3] = p[2];
} else if (!i) {
p[0] = {x: +crp[i], y: +crp[i + 1]};
}
} }
}); d.push([
this.segments = newSegments; "C",
return this; (-p[0].x + 6 * p[1].x + p[2].x) / 6,
}; (-p[0].y + 6 * p[1].y + p[2].y) / 6,
SvgPath.prototype.abs = function abs () { (p[1].x + 6 * p[2].x - p[3].x) / 6,
this.iterate(function (s, index, x, y) { (p[1].y + 6*p[2].y - p[3].y) / 6,
var name = s[0], p[2].x,
nameUC = name.toUpperCase(), p[2].y
i; ]);
if (name === nameUC) { return; } }
s[0] = nameUC; return d
switch (name) { }
case 'v':
s[1] += y; function ellipsePath(x, y, rx, ry, a) {
return; if (a == null && ry == null) {
case 'a': ry = rx;
s[6] += x; }
s[7] += y; x = +x;
return; y = +y;
default: rx = +rx;
for (i = 1; i < s.length; i++) { ry = +ry;
s[i] += i % 2 ? x : y; var res;
} if (a != null) {
var rad = Math.PI / 180,
x1 = x + rx * Math.cos(-ry * rad),
x2 = x + rx * Math.cos(-a * rad),
y1 = y + rx * Math.sin(-ry * rad),
y2 = y + rx * Math.sin(-a * rad);
res = [["M", x1, y1], ["A", rx, rx, 0, +(a - ry > 180), 0, x2, y2]];
} else {
res = [
["M", x, y],
["m", 0, -ry],
["a", rx, ry, 0, 1, 1, 0, 2 * ry],
["a", rx, ry, 0, 1, 1, 0, -2 * ry],
["z"]
];
}
return res;
}
function pathToAbsolute(pathArray) {
pathArray = parsePathString(pathArray);
if (!pathArray || !pathArray.length) {
return [["M", 0, 0]];
}
var res = [], x = 0, y = 0, mx = 0, my = 0, start = 0,
ii = pathArray.length,
crz = pathArray.length === 3 &&
pathArray[0][0] === "M" &&
pathArray[1][0].toUpperCase() === "R" &&
pathArray[2][0].toUpperCase() === "Z";
if (pathArray[0][0] === "M") {
x = +pathArray[0][1];
y = +pathArray[0][2];
mx = x;
my = y;
start++;
res[0] = ["M", x, y];
}
var loop = function ( i ) {
var r = (void 0), pa = pathArray[i], pa0 = pa[0];
res.push(r = []);
if (pa0 !== pa0.toUpperCase()) {
r[0] = pa0.toUpperCase();
switch (r[0]) {
case "A":
r[1] = pa[1];
r[2] = pa[2];
r[3] = pa[3];
r[4] = pa[4];
r[5] = pa[5];
r[6] = +pa[6] + x;
r[7] = +pa[7] + y;
break;
case "V":
r[1] = +pa[1] + y;
break;
case "H":
r[1] = +pa[1] + x;
break;
case "R":
var dots$1 = [x, y].concat(pa.slice(1));
for (var j = 2, jj = dots$1.length; j < jj; j++) {
dots$1[j] = +dots$1[j] + x;
dots$1[++j] = +dots$1[j] + y;
}
res.pop();
res = res.concat(catmullRom2bezier(dots$1, crz));
break;
case "O":
res.pop();
dots$1 = ellipsePath(x, y, +pa[1], +pa[2]);
dots$1.push(dots$1[0]);
res = res.concat(dots$1);
break;
case "U":
res.pop();
res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3]));
r = ["U"].concat(res[res.length - 1].slice(-2));
break;
case "M":
mx = +pa[1] + x;
my = +pa[2] + y;
default:
for (var j$1 = 1, jj$1 = pa.length; j$1 < jj$1; j$1++) {
r[j$1] = +pa[j$1] + ((j$1 % 2) ? x : y);
}
}
} else if (pa0 === "R") {
dots = [x, y].concat(pa.slice(1));
res.pop();
res = res.concat(catmullRom2bezier(dots, crz));
r = ["R"].concat(pa.slice(-2));
} else if (pa0 === "O") {
res.pop();
dots = ellipsePath(x, y, +pa[1], +pa[2]);
dots.push(dots[0]);
res = res.concat(dots);
} else if (pa0 === "U") {
res.pop();
res = res.concat(ellipsePath(x, y, +pa[1], +pa[2], +pa[3]));
r = ["U"].concat(res[res.length - 1].slice(-2));
} else {
pa.map(function (k){ return r.push(k); });
} }
}, true); pa0 = pa0.toUpperCase();
return this; if (pa0 !== "O") {
}; switch (r[0]) {
SvgPath.prototype.toString = function toString () { case "Z":
var this$1 = this; x = mx;
var elements = [], skipCmd, cmd; y = my;
this.segments.map( function (s,i) { break;
cmd = s[0]; case "H":
skipCmd = i > 0 && cmd !== 'm' && cmd !== 'M' && cmd === this$1.segments[i - 1][0]; x = +r[1];
elements = elements.concat(skipCmd ? s.slice(1) : s); break;
}); case "V":
return elements.join(' ') y = +r[1];
.replace(/ ?([achlmqrstvz]) ?/gi, '$1') break;
.replace(/ \-/g, '-') case "M":
.replace(/zm/g, 'z m'); mx = +r[r.length - 2];
}; my = +r[r.length - 1];
function split(parsed) { default:
return parsed x = +r[r.length - 2];
.toString() y = +r[r.length - 1];
.split("M") }
.map(function (d, i) { }
d = d.trim(); };
return i && d ? "M" + d : d; for (var i = start; i < ii; i++) loop( i );
}) return roundPath(res)
.filter(function (d) { return d; }); }
function pathToString(pathArray) {
return pathArray.map( function (c) {
if (typeof c === 'string') {
return c
} else {
return c.shift() + c.join(',')
}
}).join(' ')
}
function splitPath(pathString) {
return pathString
.replace( /(m|M)/g, "|$1")
.split('|')
.map(function (s){ return s.trim(); })
.filter(function (s){ return s; })
}
var INVALID_INPUT = 'Invalid path value';
function isFiniteNumber(number) {
return typeof number === "number" && isFinite(number);
}
function distance(a, b) {
return Math.sqrt(
(a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1])
);
}
function pointAlong(a, b, pct) {
return [a[0] + (b[0] - a[0]) * pct, a[1] + (b[1] - a[1]) * pct];
}
function samePoint(a, b) {
return distance(a, b) < 1e-9;
} }
function pathStringToRing(str, maxSegmentLength) { function pathStringToRing(str, maxSegmentLength) {
var parsed = new SvgPath(str).abs(); var parsed = pathToAbsolute(str);
return exactRing(parsed) || approximateRing(parsed, maxSegmentLength); return exactRing(parsed) || approximateRing(parsed, maxSegmentLength);
} }
function exactRing(parsed) { function exactRing(segments) {
var segments = parsed.segments || [], var ring = [];
ring = [];
if (!segments.length || segments[0][0] !== "M") { if (!segments.length || segments[0][0] !== "M") {
return false; return false;
} }
@ -1847,7 +1947,8 @@
return ring.length ? { ring: ring } : false; return ring.length ? { ring: ring } : false;
} }
function approximateRing(parsed, maxSegmentLength) { function approximateRing(parsed, maxSegmentLength) {
var ringPath = split(parsed)[0], ring = [], len, testPath, numPoints = 3; var ringPath = splitPath(pathToString(parsed))[0],
ring = [], len, testPath, numPoints = 3;
if (!ringPath) { if (!ringPath) {
throw new TypeError(INVALID_INPUT); throw new TypeError(INVALID_INPUT);
} }
@ -2038,9 +2139,11 @@
defaultOptions: {morphPrecision : 10, morphIndex:0}, defaultOptions: {morphPrecision : 10, morphIndex:0},
functions: svgMorphFunctions, functions: svgMorphFunctions,
Util: { Util: {
INVALID_INPUT: INVALID_INPUT,isFiniteNumber: isFiniteNumber,distance: distance,pointAlong: pointAlong,samePoint: samePoint,paramCounts: paramCounts,SPECIAL_SPACES: SPECIAL_SPACES,isSpace: isSpace,isCommand: isCommand,isArc: isArc,isDigit: isDigit, INVALID_INPUT: INVALID_INPUT,isFiniteNumber: isFiniteNumber,distance: distance,pointAlong: pointAlong,samePoint: samePoint,
isDigitStart: isDigitStart,State: State,skipSpaces: skipSpaces,scanFlag: scanFlag,scanParam: scanParam,finalizeSegment: finalizeSegment,scanSegment: scanSegment,pathParse: pathParse,SvgPath: SvgPath,split: split,pathStringToRing: pathStringToRing, pathToAbsolute: pathToAbsolute,pathToString: pathToString,pathStringToRing: pathStringToRing,
exactRing: exactRing,approximateRing: approximateRing,measure: measure,rotateRing: rotateRing,polygonLength: polygonLength,polygonArea: polygonArea,addPoints: addPoints,bisect: bisect,normalizeRing: normalizeRing,validRing: validRing,getInterpolationPoints: getInterpolationPoints} exactRing: exactRing,approximateRing: approximateRing,measure: measure,rotateRing: rotateRing,polygonLength: polygonLength,polygonArea: polygonArea,
addPoints: addPoints,bisect: bisect,normalizeRing: normalizeRing,validRing: validRing,getInterpolationPoints: getInterpolationPoints
}
}; };
Components.SVGMorph = svgMorph; Components.SVGMorph = svgMorph;

2
dist/kute.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -54,6 +54,7 @@
}, },
"homepage": "http://thednp.github.io/kute.js", "homepage": "http://thednp.github.io/kute.js",
"dependencies": { "dependencies": {
"svg-path-commander": "^0.0.1-c",
"cubic-bezier-easing": "^1.0.2", "cubic-bezier-easing": "^1.0.2",
"minifill": "^0.0.13", "minifill": "^0.0.13",
"shorter-js": "^0.1.4" "shorter-js": "^0.1.4"

View file

@ -2,504 +2,17 @@ import Components from '../objects/components.js'
import selector from '../util/selector.js' import selector from '../util/selector.js'
import numbers from '../interpolation/numbers.js' import numbers from '../interpolation/numbers.js'
import {toPathString,onStartCubicMorph} from './svgCubicMorphBase.js' import {onStartCubicMorph} from './svgCubicMorphBase.js'
import parsePathString from 'svg-path-commander/src/process/parsePathString.js'
import pathToAbsolute from 'svg-path-commander/src/convert/pathToAbsolute.js'
import pathToCurve from 'svg-path-commander/src/convert/pathToCurve.js'
import pathToString from 'svg-path-commander/src/convert/pathToString.js'
import reverseCurve from 'svg-path-commander/src/process/reverseCurve.js'
// const SVGMorph = { property : 'path', defaultValue: [], interpolators: {numbers} }, functions = { prepareStart, prepareProperty, onStart, crossCheck } // const SVGMorph = { property : 'path', defaultValue: [], interpolators: {numbers} }, functions = { prepareStart, prepareProperty, onStart, crossCheck }
// Component Util // Component Util
const INVALID_INPUT = 'Invalid path value'
/* Raphael.js - path (https://github.com/DmitryBaranovskiy/raphael)
* Copyright © 2008-2013 Dmitry Baranovskiy (http://dmitrybaranovskiy.github.io/raphael/)
* Copyright © 2008-2013 Sencha Labs (http://sencha.com)
* Licensed under the MIT (http://dmitrybaranovskiy.github.io/raphael/license.html) license.
* KUTE.js modifications
* - parsePathString is now moved outside of main functions
* - processPath moved outside pathToAbsolute function body
* - fixArc moved outside path2curve function body
* - fixM moved outside path2curve function body
* - minor fixes like "a != b" => "a !== b", various undefined/unused variables
*/
// http://schepers.cc/getting-to-the-point
function catmullRom2bezier(crp, z) {
const d = [];
for (let i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
const p = [
{x: +crp[i - 2], y: +crp[i - 1]},
{x: +crp[i], y: +crp[i + 1]},
{x: +crp[i + 2], y: +crp[i + 3]},
{x: +crp[i + 4], y: +crp[i + 5]}
];
if (z) {
if (!i) {
p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]};
} else if (iLen - 4 == i) {
p[3] = {x: +crp[0], y: +crp[1]};
} else if (iLen - 2 == i) {
p[2] = {x: +crp[0], y: +crp[1]};
p[3] = {x: +crp[2], y: +crp[3]};
}
} else {
if (iLen - 4 == i) {
p[3] = p[2];
} else if (!i) {
p[0] = {x: +crp[i], y: +crp[i + 1]};
}
}
d.push(["C",
(-p[0].x + 6 * p[1].x + p[2].x) / 6,
(-p[0].y + 6 * p[1].y + p[2].y) / 6,
(p[1].x + 6 * p[2].x - p[3].x) / 6,
(p[1].y + 6*p[2].y - p[3].y) / 6,
p[2].x,
p[2].y
])
}
return d
}
function ellipsePath(x, y, rx, ry, a) {
if (a == null && ry == null) {
ry = rx;
}
x = +x;
y = +y;
rx = +rx;
ry = +ry;
let res;
if (a != null) {
const rad = Math.PI / 180,
x1 = x + rx * Math.cos(-ry * rad),
x2 = x + rx * Math.cos(-a * rad),
y1 = y + rx * Math.sin(-ry * rad),
y2 = y + rx * Math.sin(-a * rad);
res = [["M", x1, y1], ["A", rx, rx, 0, +(a - ry > 180), 0, x2, y2]];
} else {
res = [
["M", x, y],
["m", 0, -ry],
["a", rx, ry, 0, 1, 1, 0, 2 * ry],
["a", rx, ry, 0, 1, 1, 0, -2 * ry],
["z"]
];
}
return res;
}
// Parses given path string into an array of arrays of path segments
function parsePathString(pathString) {
if (!pathString) {
return null;
}
if( pathString instanceof Array ) {
return pathString;
} else {
// tracer minifier cannot compute this string for some reason
// let spaces = "\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029";
let spaces = `\\${("x09|x0a|x0b|x0c|x0d|x20|xa0|u1680|u180e|u2000|u2001|u2002|u2003|u2004|u2005|u2006|u2007|u2008|u2009|u200a|u202f|u205f|u3000|u2028|u2029").split('|').join('\\')}`,
pathCommand = new RegExp(`([a-z])[${spaces},]*((-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?[${spaces}]*,?[${spaces}]*)+)`, `ig`),
pathValues = new RegExp(`(-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?)[${spaces}]*,?[${spaces}]*`, `ig`),
paramCounts = {a: 7, c: 6, o: 2, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, u: 3, z: 0},
data = [];
pathString.replace(pathCommand, (a, b, c) => {
let params = [], name = b.toLowerCase();
c.replace(pathValues, (a, b) => {
b && params.push(+b);
});
if (name == "m" && params.length > 2) {
data.push([b].concat(params.splice(0, 2)));
name = "l";
b = b == "m" ? "l" : "L";
}
if (name == "o" && params.length == 1) {
data.push([b, params[0]]);
}
if (name == "r") {
data.push([b].concat(params));
} else while (params.length >= paramCounts[name]) {
data.push([b].concat(params.splice(0, paramCounts[name])));
if (!paramCounts[name]) {
break;
}
}
});
return data;
}
}
function pathToAbsolute(pathArray) {
pathArray = parsePathString(pathArray);
if (!pathArray || !pathArray.length) {
return [["M", 0, 0]];
}
let res = [], x = 0, y = 0, mx = 0, my = 0, start = 0, pa0;
if (pathArray[0][0] === "M") {
x = +pathArray[0][1];
y = +pathArray[0][2];
mx = x;
my = y;
start++;
res[0] = ["M", x, y];
}
const crz = pathArray.length === 3 &&
pathArray[0][0] === "M" &&
pathArray[1][0].toUpperCase() === "R" &&
pathArray[2][0].toUpperCase() === "Z";
for (let r, pa, i = start, ii = pathArray.length; i < ii; i++) {
res.push(r = []);
pa = pathArray[i];
pa0 = pa[0];
if (pa0 !== pa0.toUpperCase()) {
r[0] = pa0.toUpperCase();
switch (r[0]) {
case "A":
r[1] = pa[1];
r[2] = pa[2];
r[3] = pa[3];
r[4] = pa[4];
r[5] = pa[5];
r[6] = +pa[6] + x;
r[7] = +pa[7] + y;
break;
case "V":
r[1] = +pa[1] + y;
break;
case "H":
r[1] = +pa[1] + x;
break;
case "R":
var dots = [x, y].concat(pa.slice(1));
for (var j = 2, jj = dots.length; j < jj; j++) {
dots[j] = +dots[j] + x;
dots[++j] = +dots[j] + y;
}
res.pop();
res = res.concat(catmullRom2bezier(dots, crz));
break;
case "O":
res.pop();
dots = ellipsePath(x, y, pa[1], pa[2]);
dots.push(dots[0]);
res = res.concat(dots);
break;
case "U":
res.pop();
res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3]));
r = ["U"].concat(res[res.length - 1].slice(-2));
break;
case "M":
mx = +pa[1] + x;
my = +pa[2] + y;
default:
for (j = 1, jj = pa.length; j < jj; j++) {
r[j] = +pa[j] + ((j % 2) ? x : y);
}
}
} else if (pa0 == "R") {
dots = [x, y].concat(pa.slice(1));
res.pop();
res = res.concat(catmullRom2bezier(dots, crz));
r = ["R"].concat(pa.slice(-2));
} else if (pa0 == "O") {
res.pop();
dots = ellipsePath(x, y, pa[1], pa[2]);
dots.push(dots[0]);
res = res.concat(dots);
} else if (pa0 == "U") {
res.pop();
res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3]));
r = ["U"].concat(res[res.length - 1].slice(-2));
} else {
for (let k = 0, kk = pa.length; k < kk; k++) {
r[k] = pa[k];
}
}
pa0 = pa0.toUpperCase();
if (pa0 != "O") {
switch (r[0]) {
case "Z":
x = +mx;
y = +my;
break;
case "H":
x = r[1];
break;
case "V":
y = r[1];
break;
case "M":
mx = r[r.length - 2];
my = r[r.length - 1];
default:
x = r[r.length - 2];
y = r[r.length - 1];
}
}
}
return res
}
function l2c(x1, y1, x2, y2) {
return [x1, y1, x2, y2, x2, y2];
}
function q2c(x1, y1, ax, ay, x2, y2) {
const _13 = 1 / 3;
const _23 = 2 / 3;
return [
_13 * x1 + _23 * ax,
_13 * y1 + _23 * ay,
_13 * x2 + _23 * ax,
_13 * y2 + _23 * ay,
x2,
y2
]
}
// for more information of where this math came from visit:
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
const _120 = Math.PI * 120 / 180, rad = Math.PI / 180 * (+angle || 0);
let res = [], xy, f1, f2, cx, cy;
function rotateVector(x, y, rad) {
const X = x * Math.cos(rad) - y * Math.sin(rad),
Y = x * Math.sin(rad) + y * Math.cos(rad);
return {x: X, y: Y};
}
if (!recursive) {
xy = rotateVector(x1, y1, -rad);
x1 = xy.x;
y1 = xy.y;
xy = rotateVector(x2, y2, -rad);
x2 = xy.x;
y2 = xy.y;
// const cos = Math.cos(Math.PI / 180 * angle);
// const sin = Math.sin(Math.PI / 180 * angle);
let x = (x1 - x2) / 2, y = (y1 - y2) / 2, h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
if (h > 1) {
h = Math.sqrt(h);
rx = h * rx;
ry = h * ry;
}
let rx2 = rx * rx,
ry2 = ry * ry,
k = (large_arc_flag == sweep_flag ? -1 : 1)
* Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x)
/ (rx2 * y * y + ry2 * x * x)));
cx = k * rx * y / ry + (x1 + x2) / 2,
cy = k * -ry * x / rx + (y1 + y2) / 2;
f1 = Math.asin(((y1 - cy) / ry).toFixed(9)),
f2 = Math.asin(((y2 - cy) / ry).toFixed(9));
f1 = x1 < cx ? Math.PI - f1 : f1;
f2 = x2 < cx ? Math.PI - f2 : f2;
f1 < 0 && (f1 = Math.PI * 2 + f1);
f2 < 0 && (f2 = Math.PI * 2 + f2);
if (sweep_flag && f1 > f2) {
f1 = f1 - Math.PI * 2;
}
if (!sweep_flag && f2 > f1) {
f2 = f2 - Math.PI * 2;
}
} else {
f1 = recursive[0];
f2 = recursive[1];
cx = recursive[2];
cy = recursive[3];
}
let df = f2 - f1;
if (Math.abs(df) > _120) {
const f2old = f2, x2old = x2, y2old = y2;
f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
x2 = cx + rx * Math.cos(f2);
y2 = cy + ry * Math.sin(f2);
res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
}
df = f2 - f1;
const c1 = Math.cos(f1),
s1 = Math.sin(f1),
c2 = Math.cos(f2),
s2 = Math.sin(f2),
t = Math.tan(df / 4),
hx = 4 / 3 * rx * t,
hy = 4 / 3 * ry * t,
m1 = [x1, y1],
m2 = [x1 + hx * s1, y1 - hy * c1],
m3 = [x2 + hx * s2, y2 - hy * c2],
m4 = [x2, y2];
m2[0] = 2 * m1[0] - m2[0];
m2[1] = 2 * m1[1] - m2[1];
if (recursive) {
return [m2, m3, m4].concat(res);
} else {
res = [m2, m3, m4].concat(res).join().split(",");
const newres = [];
for (let i = 0, ii = res.length; i < ii; i++) {
newres[i] = i % 2 ? rotateVector(res[i - 1], res[i], rad).y : rotateVector(res[i], res[i + 1], rad).x;
}
return newres;
}
}
function processPath (path, d, pcom) {
let nx, ny;
if (!path) {
return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
}
!(path[0] in {T: 1, Q: 1}) && (d.qx = d.qy = null);
switch (path[0]) {
case "M":
d.X = path[1];
d.Y = path[2];
break;
case "A":
path = ["C"].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1))));
break;
case "S":
if (pcom == "C" || pcom == "S") { // In "S" case we have to take into account, if the previous command is C/S.
nx = d.x * 2 - d.bx; // And reflect the previous
ny = d.y * 2 - d.by; // command's control point relative to the current point.
}
else { // or some else or nothing
nx = d.x;
ny = d.y;
}
path = ["C", nx, ny].concat(path.slice(1));
break;
case "T":
if (pcom == "Q" || pcom == "T") { // In "T" case we have to take into account, if the previous command is Q/T.
d.qx = d.x * 2 - d.qx; // And make a reflection similar
d.qy = d.y * 2 - d.qy; // to case "S".
}
else { // or something else or nothing
d.qx = d.x;
d.qy = d.y;
}
path = ["C"].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
break;
case "Q":
d.qx = path[1];
d.qy = path[2];
path = ["C"].concat(q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
break;
case "L":
path = ["C"].concat(l2c(d.x, d.y, path[1], path[2]));
break;
case "H":
path = ["C"].concat(l2c(d.x, d.y, path[1], d.y));
break;
case "V":
path = ["C"].concat(l2c(d.x, d.y, d.x, path[1]));
break;
case "Z":
path = ["C"].concat(l2c(d.x, d.y, d.X, d.Y));
break;
}
path.map((x,i)=>i?x.toFixed(3):x)
return path;
}
function fixM (path1, path2, a1, a2, i) {
if (path1 && path2 && path1[i][0] === "M" && path2[i][0] !== "M") {
path2.splice(i, 0, ["M", a2.x, a2.y]);
a1.bx = 0;
a1.by = 0;
a1.x = path1[i][1];
a1.y = path1[i][2];
// ii = Math.max(p.length, p2 && p2.length || 0);
}
}
function fixArc (p, p2, pcoms1, pcoms2, i) {
if (p[i].length > 7) {
p[i].shift();
const pi = p[i];
while (pi.length) {
pcoms1[i] = "A"; // if created multiple C:s, their original seg is saved
p2 && (pcoms2[i] = "A"); // the same as above
p.splice(i++, 0, ["C"].concat(pi.splice(0, 6)));
}
p.splice(i, 1);
}
}
function path2curve(path, path2) {
let p = pathToAbsolute(path), // holder for previous path command of original path
p2 = path2 && pathToAbsolute(path2),
// p2 = path2 ? pathToAbsolute(path2) : pathToAbsolute('M0,0L0,0'),
attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null};
// path commands of original path p
// path commands of original path p2
// temporary holder for original path command
let pcoms1 = [], pcoms2 = [], pfirst = "", pcom = "";
for (let i = 0, ii = Math.max(p.length, p2 && p2.length || 0); i < ii; i++) {
p[i] && (pfirst = p[i][0]); // save current path command
if (pfirst !== "C") { // C is not saved yet, because it may be result of conversion
pcoms1[i] = pfirst; // Save current path command
i && ( pcom = pcoms1[i - 1]); // Get previous path command pcom
}
p[i] = processPath(p[i], attrs, pcom); // Previous path command is inputted to processPath
// A is the only command
// which may produce multiple C:s
// so we have to make sure that C is also C in original path
if (pcoms1[i] !== "A" && pfirst === "C") pcoms1[i] = "C";
// fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1
fixArc(p, p2, pcoms1, pcoms2, i); // fixArc adds also the right amount of A:s to pcoms1 fixArc (p, p2, pcoms1, pcoms2, i) {
ii = Math.max(p.length, p2 && p2.length || 0);
if (p2) { // the same procedures is done to p2
p2[i] && (pfirst = p2[i][0]);
if (pfirst !== "C") {
pcoms2[i] = pfirst;
i && (pcom = pcoms2[i - 1]);
}
p2[i] = processPath(p2[i], attrs2, pcom);
if (pcoms2[i] !== "A" && pfirst === "C") {
pcoms2[i] = "C";
}
// fixArc(p2, i);
fixArc(p2, p, pcoms2, pcoms1, i); // fixArc (p, p2, pcoms1, pcoms2, i) {
ii = Math.max(p.length, p2 && p2.length || 0);
}
fixM(p, p2, attrs, attrs2, i);
p2 && fixM(p2, p, attrs2, attrs, i);
ii = Math.max(p.length, p2 && p2.length || 0);
let seg = p[i],
seg2 = p2 && p2[i],
seglen = seg.length,
seg2len = p2 && seg2.length;
attrs.x = seg[seglen - 2];
attrs.y = seg[seglen - 1];
attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
attrs2.bx = p2 && (parseFloat(seg2[seg2len - 4]) || attrs2.x);
attrs2.by = p2 && (parseFloat(seg2[seg2len - 3]) || attrs2.y);
attrs2.x = p2 && seg2[seg2len - 2];
attrs2.y = p2 && seg2[seg2len - 1];
}
return p2 ? [p, p2] : p;
}
function createPath (path) { // create a <path> when glyph function createPath (path) { // create a <path> when glyph
const np = document.createElementNS('http://www.w3.org/2000/svg','path'), const np = document.createElementNS('http://www.w3.org/2000/svg','path'),
d = path instanceof SVGElement ? path.getAttribute('d') : path d = path instanceof SVGElement ? path.getAttribute('d') : path
@ -507,82 +20,42 @@ function createPath (path) { // create a <path> when glyph
return np return np
} }
function getSegments(curveArray) {
// let result = []
// curveArray.map((seg, i) => {
// result[i] = {
// x: seg[seg[0] === 'M' ? 1 : 5],
// y: seg[seg[0] === 'M' ? 2 : 6],
// seg: seg
// }
// })
// return result
return curveArray.map(seg => {
return {
x: seg[seg[0] === 'M' ? 1 : 5],
y: seg[seg[0] === 'M' ? 2 : 6],
seg: seg
}
})
}
function reverseCurve(path){
let newSegments = [],
oldSegments = getSegments(path),
segsCount = oldSegments.length,
pointCount = segsCount - 1,
oldSegIdx = pointCount,
oldSegs = []
oldSegments.map((p,i)=>{
if (i === 0||oldSegments[oldSegIdx].seg[0] === 'M') {
newSegments[i] = ['M',oldSegments[oldSegIdx].x,oldSegments[oldSegIdx].y]
} else {
oldSegIdx = pointCount - i > 0 ? pointCount - i : pointCount;
oldSegs = oldSegments[oldSegIdx].seg
newSegments[i] = [oldSegs[0], oldSegs[5],oldSegs[6],oldSegs[3],oldSegs[4], oldSegs[1], oldSegs[2]]
}
})
return newSegments
}
function getRotationSegments(s,idx) { function getRotationSegments(s,idx) {
let newSegments = [], segsCount = s.length, pointCount = segsCount - 1 let segsCount = s.length, pointCount = segsCount - 1
s.map((p,i)=>{ return s.map((p,i)=>{
let oldSegIdx = idx + i; let oldSegIdx = idx + i, seg;
if (i===0 || s[oldSegIdx] && s[oldSegIdx].seg[0] === 'M') { if (i===0 || s[oldSegIdx] && s[oldSegIdx][0] === 'M') {
newSegments[i] = ['M',s[oldSegIdx].x,s[oldSegIdx].y] seg = s[oldSegIdx]
return ['M',seg[seg.length-2],seg[seg.length-1]]
} else { } else {
if (oldSegIdx >= segsCount) oldSegIdx -= pointCount; if (oldSegIdx >= segsCount) oldSegIdx -= pointCount;
newSegments[i] = s[oldSegIdx].seg return s[oldSegIdx]
} }
}) })
return newSegments
} }
function getRotations(a) { function getRotations(a) {
let startSegments = getSegments(a), rotations = []; return a.map((s,i) => getRotationSegments(a,i))
startSegments.map((s,i)=>{rotations[i] = getRotationSegments(startSegments,i)})
return rotations
} }
function getRotatedCurve(a,b) { function getRotatedCurve(a,b) {
let startSegments = getSegments(a), let segCount = a.length - 1,
endSegments = getSegments(b),
segsCount = startSegments.length,
pointCount = segsCount - 1,
linePaths = [], linePaths = [],
lineLengths = [], lineLengths = [],
rotations = getRotations(a); rotations = getRotations(a);
rotations.map((r,i)=>{ rotations.map((r,i)=>{
let sumLensSqrd = 0, linePath = createPath('M0,0L0,0'); let sumLensSqrd = 0,
for (let j = 0; j < pointCount; j++) { linePath = createPath('M0,0L0,0'),
let linePt1 = startSegments[(i + j) % pointCount]; linePt1, ll1,
let linePt2 = endSegments[ j % pointCount]; linePt2, ll2,
let linePathStr = `M${linePt1.x},${linePt1.y}L${linePt2.x},${linePt2.y}`; linePathStr
for (let j = 0; j < segCount; j++) {
linePt1 = a[(i + j) % segCount]; ll1 = linePt1.length
linePt2 = b[ j % segCount]; ll2 = linePt2.length
linePathStr = `M${linePt1[ll1-2]},${linePt1[ll1-1]}L${linePt2[ll2-2]},${linePt2[ll2-1]}`
linePath.setAttribute('d',linePathStr); linePath.setAttribute('d',linePathStr);
sumLensSqrd += Math.pow(linePath.getTotalLength(),2); sumLensSqrd += Math.pow(linePath.getTotalLength(),2);
linePaths[j] = linePath; linePaths[j] = linePath;
@ -591,9 +64,9 @@ function getRotatedCurve(a,b) {
sumLensSqrd = 0 sumLensSqrd = 0
}) })
let computedIndex = lineLengths.indexOf(Math.min.apply(null,lineLengths)), let computedIndex = lineLengths.indexOf(Math.min.apply(null,lineLengths))
newPath = rotations[computedIndex];
return newPath return rotations[computedIndex]
} }
// Component Functions // Component Functions
@ -630,10 +103,10 @@ function crossCheckCubicMorph(tweenProp){
if ( !pathCurve1 || !pathCurve2 || ( pathCurve1 && pathCurve2 && pathCurve1[0][0] === 'M' && pathCurve1.length !== pathCurve2.length) ) { if ( !pathCurve1 || !pathCurve2 || ( pathCurve1 && pathCurve2 && pathCurve1[0][0] === 'M' && pathCurve1.length !== pathCurve2.length) ) {
let path1 = this.valuesStart[tweenProp].original, let path1 = this.valuesStart[tweenProp].original,
path2 = this.valuesEnd[tweenProp].original, path2 = this.valuesEnd[tweenProp].original,
curves = path2curve(path1,path2) curves = pathToCurve(path1,path2)
let curve0 = this._reverseFirstPath ? reverseCurve.call(this,curves[0]) : curves[0], let curve0 = this._reverseFirstPath ? reverseCurve(curves[0]) : curves[0],
curve1 = this._reverseSecondPath ? reverseCurve.call(this,curves[1]) : curves[1] curve1 = this._reverseSecondPath ? reverseCurve(curves[1]) : curves[1]
curve0 = getRotatedCurve.call(this,curve0,curve1) curve0 = getRotatedCurve.call(this,curve0,curve1)
this.valuesStart[tweenProp].curve = curve0; this.valuesStart[tweenProp].curve = curve0;
@ -655,14 +128,13 @@ const svgCubicMorph = {
component: 'svgCubicMorph', component: 'svgCubicMorph',
property: 'path', property: 'path',
defaultValue: [], defaultValue: [],
Interpolate: {numbers,toPathString}, Interpolate: {numbers,pathToString},
functions: svgCubicMorphFunctions, functions: svgCubicMorphFunctions,
// export utils to global for faster execution // export utils to global for faster execution
Util: { Util: {
l2c, q2c, a2c, catmullRom2bezier, ellipsePath, pathToCurve, pathToAbsolute, pathToString, parsePathString,
path2curve, pathToAbsolute, toPathString, parsePathString,
getRotatedCurve, getRotations, getRotatedCurve, getRotations,
getRotationSegments, reverseCurve, getSegments, createPath getRotationSegments, reverseCurve, createPath
} }
} }

View file

@ -1,25 +1,17 @@
import KUTE from '../objects/kute.js' import KUTE from '../objects/kute.js'
import numbers from '../interpolation/numbers.js' import numbers from '../interpolation/numbers.js'
import pathToString from 'svg-path-commander/src/convert/pathToString.js'
// const SVGMorph = { property : 'path', defaultValue: [], interpolators: {numbers} }, functions = { prepareStart, prepareProperty, onStart, crossCheck } // const SVGMorph = { property : 'path', defaultValue: [], interpolators: {numbers} }, functions = { prepareStart, prepareProperty, onStart, crossCheck }
// Component Interpolation
export function toPathString(pathArray) {
let newPath = pathArray.map( (c) => {
if (typeof(c) === 'string') {
return c
} else {
let c0 = c.shift();
return c0 + c.join(',')
}
})
return newPath.join('');
}
// Component Functions // Component Functions
export function onStartCubicMorph(tweenProp){ export function onStartCubicMorph(tweenProp){
if (!KUTE[tweenProp] && this.valuesEnd[tweenProp]) { if (!KUTE[tweenProp] && this.valuesEnd[tweenProp]) {
KUTE[tweenProp] = function(elem,a,b,v){ KUTE[tweenProp] = function(elem,a,b,v){
// v === 1 && console.log(a.curve,b.curve)
let curve = [], path1 = a.curve, path2 = b.curve; let curve = [], path1 = a.curve, path2 = b.curve;
for(let i=0, l=path2.length; i<l; i++) { // each path command for(let i=0, l=path2.length; i<l; i++) { // each path command
curve.push([path1[i][0]]); curve.push([path1[i][0]]);
@ -27,7 +19,7 @@ export function onStartCubicMorph(tweenProp){
curve[i].push( (numbers(path1[i][j], path2[i][j], v) * 1000 >>0)/1000 ); curve[i].push( (numbers(path1[i][j], path2[i][j], v) * 1000 >>0)/1000 );
} }
} }
elem.setAttribute("d", v === 1 ? b.original : toPathString(curve) ); elem.setAttribute("d", v === 1 ? b.original : pathToString(curve) )
} }
} }
} }
@ -37,7 +29,7 @@ const baseSvgCubicMorph = {
component: 'baseSVGCubicMorph', component: 'baseSVGCubicMorph',
property: 'path', property: 'path',
// defaultValue: [], // defaultValue: [],
Interpolate: {numbers,toPathString}, Interpolate: {numbers,pathToString},
functions: {onStart:onStartCubicMorph} functions: {onStart:onStartCubicMorph}
} }

View file

@ -3,13 +3,16 @@ import defaultOptions from '../objects/defaultOptions.js'
import Components from '../objects/components.js' import Components from '../objects/components.js'
import coords from '../interpolation/coords.js' import coords from '../interpolation/coords.js'
import {onStartSVGMorph} from './svgMorphBase.js' import {onStartSVGMorph} from './svgMorphBase.js'
import pathToAbsolute from 'svg-path-commander/src/convert/pathToAbsolute.js'
import pathToString from 'svg-path-commander/src/convert/pathToString.js'
import splitPath from 'svg-path-commander/src/util/splitPath.js'
// const SVGMorph = { property : 'path', defaultValue: [], interpolators: {numbers,coords} }, functions = { prepareStart, prepareProperty, onStart, crossCheck } // const SVGMorph = { property : 'path', defaultValue: [], interpolators: {numbers,coords} }, functions = { prepareStart, prepareProperty, onStart, crossCheck }
// Component Interpolation // Component Interpolation
// function function(array1, array2, length, progress) // function function(array1, array2, length, progress)
// Component Util // Component Util
const INVALID_INPUT = 'Invalid path value' const INVALID_INPUT = 'Invalid path value'
@ -30,487 +33,15 @@ function samePoint(a, b) {
return distance(a, b) < 1e-9; return distance(a, b) < 1e-9;
} }
// SVGPATH
// https://github.com/fontello/svgpath/
const paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 };
const SPECIAL_SPACES = [
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF
];
function isSpace(ch) {
return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) || // Line terminators
// White spaces
(ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
(ch >= 0x1680 && SPECIAL_SPACES.indexOf(ch) >= 0);
}
function isCommand(code) {
/*eslint-disable no-bitwise*/
switch (code | 0x20) {
case 0x6D/* m */:
case 0x7A/* z */:
case 0x6C/* l */:
case 0x68/* h */:
case 0x76/* v */:
case 0x63/* c */:
case 0x73/* s */:
case 0x71/* q */:
case 0x74/* t */:
case 0x61/* a */:
case 0x72/* r */:
return true;
}
return false;
}
function isArc(code) {
return (code | 0x20) === 0x61;
}
function isDigit(code) {
return (code >= 48 && code <= 57); // 0..9
}
function isDigitStart(code) {
return (code >= 48 && code <= 57) || /* 0..9 */
code === 0x2B || /* + */
code === 0x2D || /* - */
code === 0x2E; /* . */
}
function State(path) {
this.index = 0;
this.path = path;
this.max = path.length;
this.result = [];
this.param = 0.0;
this.err = '';
this.segmentStart = 0;
this.data = [];
}
function skipSpaces(state) {
while (state.index < state.max && isSpace(state.path.charCodeAt(state.index))) {
state.index++;
}
}
function scanFlag(state) {
let ch = state.path.charCodeAt(state.index);
if (ch === 0x30/* 0 */) {
state.param = 0;
state.index++;
return;
}
if (ch === 0x31/* 1 */) {
state.param = 1;
state.index++;
return;
}
// state.err = 'SvgPath: arc flag can be 0 or 1 only (at pos ' + state.index + ')';
state.err = `KUTE.js - ${INVALID_INPUT}`;
}
function scanParam(state) {
let start = state.index,
index = start,
max = state.max,
zeroFirst = false,
hasCeiling = false,
hasDecimal = false,
hasDot = false,
ch;
if (index >= max) {
// state.err = 'SvgPath: missed param (at pos ' + index + ')';.
state.err = `KUTE.js - ${INVALID_INPUT}`;
return;
}
ch = state.path.charCodeAt(index);
if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
index++;
ch = (index < max) ? state.path.charCodeAt(index) : 0;
}
// This logic is shamelessly borrowed from Esprima
// https://github.com/ariya/esprimas
//
if (!isDigit(ch) && ch !== 0x2E/* . */) {
// state.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
state.err = `KUTE.js - ${INVALID_INPUT}`;
return;
}
if (ch !== 0x2E/* . */) {
zeroFirst = (ch === 0x30/* 0 */);
index++;
ch = (index < max) ? state.path.charCodeAt(index) : 0;
if (zeroFirst && index < max) {
// decimal number starts with '0' such as '09' is illegal.
if (ch && isDigit(ch)) {
// state.err = 'SvgPath: numbers started with `0` such as `09` are illegal (at pos ' + start + ')';
state.err = `KUTE.js - ${INVALID_INPUT}`;
return;
}
}
while (index < max && isDigit(state.path.charCodeAt(index))) {
index++;
hasCeiling = true;
}
ch = (index < max) ? state.path.charCodeAt(index) : 0;
}
if (ch === 0x2E/* . */) {
hasDot = true;
index++;
while (isDigit(state.path.charCodeAt(index))) {
index++;
hasDecimal = true;
}
ch = (index < max) ? state.path.charCodeAt(index) : 0;
}
if (ch === 0x65/* e */ || ch === 0x45/* E */) {
if (hasDot && !hasCeiling && !hasDecimal) {
// state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
state.err = `KUTE.js - ${INVALID_INPUT}`;
return;
}
index++;
ch = (index < max) ? state.path.charCodeAt(index) : 0;
if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
index++;
}
if (index < max && isDigit(state.path.charCodeAt(index))) {
while (index < max && isDigit(state.path.charCodeAt(index))) {
index++;
}
} else {
// state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
state.err = `KUTE.js - ${INVALID_INPUT}`;
return;
}
}
state.index = index;
state.param = parseFloat(state.path.slice(start, index)) + 0.0;
}
function finalizeSegment(state) {
let cmd, cmdLC;
// Process duplicated commands (without comand name)
// This logic is shamelessly borrowed from Raphael
// https://github.com/DmitryBaranovskiy/raphael/
//
cmd = state.path[state.segmentStart];
cmdLC = cmd.toLowerCase();
let params = state.data;
if (cmdLC === 'm' && params.length > 2) {
state.result.push([ cmd, params[0], params[1] ]);
params = params.slice(2);
cmdLC = 'l';
cmd = (cmd === 'm') ? 'l' : 'L';
}
if (cmdLC === 'r') {
state.result.push([ cmd ].concat(params));
} else {
while (params.length >= paramCounts[cmdLC]) {
state.result.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC])));
if (!paramCounts[cmdLC]) {
break;
}
}
}
}
function scanSegment(state) {
let max = state.max, cmdCode, is_arc, comma_found, need_params, i;
state.segmentStart = state.index;
cmdCode = state.path.charCodeAt(state.index);
is_arc = isArc(cmdCode);
if (!isCommand(cmdCode)) {
// state.err = 'SvgPath: bad command ' + state.path[state.index] + ' (at pos ' + state.index + ')';
state.err = `KUTE.js - ${INVALID_INPUT}`;
return;
}
need_params = paramCounts[state.path[state.index].toLowerCase()];
state.index++;
skipSpaces(state);
state.data = [];
if (!need_params) {
// Z
finalizeSegment(state);
return;
}
comma_found = false;
for (;;) {
for (i = need_params; i > 0; i--) {
if (is_arc && (i === 3 || i === 4)) scanFlag(state);
else scanParam(state);
if (state.err.length) {
return;
}
state.data.push(state.param);
skipSpaces(state);
comma_found = false;
if (state.index < max && state.path.charCodeAt(state.index) === 0x2C/* , */) {
state.index++;
skipSpaces(state);
comma_found = true;
}
}
// after ',' param is mandatory
if (comma_found) {
continue;
}
if (state.index >= state.max) {
break;
}
// Stop on next segment
if (!isDigitStart(state.path.charCodeAt(state.index))) {
break;
}
}
finalizeSegment(state);
}
// Returns array of segments
function pathParse(svgPath) {
let state = new State(svgPath), max = state.max;
skipSpaces(state);
while (state.index < max && !state.err.length) {
scanSegment(state);
}
if (state.err.length) {
state.result = [];
} else if (state.result.length) {
if ('mM'.indexOf(state.result[0][0]) < 0) {
// state.err = 'SvgPath: string should start with `M` or `m`';
state.err = `KUTE.js - ${INVALID_INPUT}`;
state.result = [];
} else {
state.result[0][0] = 'M';
}
}
return {
err: state.err,
segments: state.result
};
}
class SvgPath {
constructor(path){
if (!(this instanceof SvgPath)) { return new SvgPath(path); }
let pstate = pathParse(path);
// Array of path segments.
// Each segment is array [command, param1, param2, ...]
this.segments = pstate.segments;
// Error message on parse error.
this.err = pstate.err;
}
// Apply iterator function to all segments. If function returns result,
// current segment will be replaced to array of returned segments.
// If empty array is returned, current regment will be deleted.
//
iterate(iterator) {
let segments = this.segments,
replacements = {},
needReplace = false,
lastX = 0,
lastY = 0,
countourStartX = 0,
countourStartY = 0,
newSegments;
segments.map( (s,index) => {
let res = iterator(s, index, lastX, lastY);
if (Array.isArray(res)) {
replacements[index] = res;
needReplace = true;
}
let isRelative = (s[0] === s[0].toLowerCase());
// calculate absolute X and Y
switch (s[0]) {
case 'm':
case 'M':
lastX = s[1] + (isRelative ? lastX : 0);
lastY = s[2] + (isRelative ? lastY : 0);
countourStartX = lastX;
countourStartY = lastY;
return;
case 'h':
case 'H':
lastX = s[1] + (isRelative ? lastX : 0);
return;
case 'v':
case 'V':
lastY = s[1] + (isRelative ? lastY : 0);
return;
case 'z':
case 'Z':
// That make sence for multiple contours
lastX = countourStartX;
lastY = countourStartY;
return;
default:
lastX = s[s.length - 2] + (isRelative ? lastX : 0);
lastY = s[s.length - 1] + (isRelative ? lastY : 0);
}
});
// Replace segments if iterator return results
if (!needReplace) { return this; }
newSegments = [];
segments.map((s,i)=>{
if (typeof replacements[i] !== 'undefined') {
replacements[i].map((r)=>{
newSegments.push(r);
})
} else {
newSegments.push(s);
}
})
this.segments = newSegments;
return this;
}
// Converts segments from relative to absolute
abs() {
this.iterate(function (s, index, x, y) {
let name = s[0],
nameUC = name.toUpperCase(),
i;
// Skip absolute commands
if (name === nameUC) { return; }
s[0] = nameUC;
switch (name) {
case 'v':
// v has shifted coords parity
s[1] += y;
return;
case 'a':
// ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
// touch x, y only
s[6] += x;
s[7] += y;
return;
default:
for (i = 1; i < s.length; i++) {
s[i] += i % 2 ? x : y; // odd values are X, even - Y
}
}
}, true);
return this;
}
// Convert processed SVG Path back to string
//
toString () {
let elements = [], skipCmd, cmd;
this.segments.map( (s,i) => {
// remove repeating commands names
cmd = s[0];
skipCmd = i > 0 && cmd !== 'm' && cmd !== 'M' && cmd === this.segments[i - 1][0];
elements = elements.concat(skipCmd ? s.slice(1) : s);
})
return elements.join(' ')
// Optimizations: remove spaces around commands & before `-`
//
// We could also remove leading zeros for `0.5`-like values,
// but their count is too small to spend time for.
.replace(/ ?([achlmqrstvz]) ?/gi, '$1')
.replace(/ \-/g, '-')
// workaround for FontForge SVG importing bug
.replace(/zm/g, 'z m');
}
}
// flubber // flubber
// https://github.com/veltman/flubber // https://github.com/veltman/flubber
function split(parsed) {
return parsed
.toString()
.split("M")
.map((d, i) => {
d = d.trim();
return i && d ? "M" + d : d;
})
.filter(d => d);
}
function pathStringToRing(str, maxSegmentLength) { function pathStringToRing(str, maxSegmentLength) {
let parsed = new SvgPath(str).abs(); let parsed = pathToAbsolute(str);
return exactRing(parsed) || approximateRing(parsed, maxSegmentLength); return exactRing(parsed) || approximateRing(parsed, maxSegmentLength);
} }
function exactRing(parsed) { function exactRing(segments) {
let segments = parsed.segments || [], let ring = [];
ring = [];
if (!segments.length || segments[0][0] !== "M") { if (!segments.length || segments[0][0] !== "M") {
return false; return false;
@ -535,7 +66,8 @@ function exactRing(parsed) {
} }
function approximateRing(parsed, maxSegmentLength) { function approximateRing(parsed, maxSegmentLength) {
let ringPath = split(parsed)[0], ring = [], len, testPath, numPoints = 3; let ringPath = splitPath(pathToString(parsed))[0],
ring = [], len, testPath, numPoints = 3;
if (!ringPath) { if (!ringPath) {
throw new TypeError(INVALID_INPUT); throw new TypeError(INVALID_INPUT);
@ -774,9 +306,11 @@ const svgMorph = {
functions: svgMorphFunctions, functions: svgMorphFunctions,
// Export utils to global for faster execution // Export utils to global for faster execution
Util: { Util: {
INVALID_INPUT,isFiniteNumber,distance,pointAlong,samePoint,paramCounts,SPECIAL_SPACES,isSpace,isCommand,isArc,isDigit, INVALID_INPUT,isFiniteNumber,distance,pointAlong,samePoint,
isDigitStart,State,skipSpaces,scanFlag,scanParam,finalizeSegment,scanSegment,pathParse,SvgPath,split,pathStringToRing, pathToAbsolute,pathToString,pathStringToRing,
exactRing,approximateRing,measure,rotateRing,polygonLength,polygonArea,addPoints,bisect,normalizeRing,validRing,getInterpolationPoints} exactRing,approximateRing,measure,rotateRing,polygonLength,polygonArea,
addPoints,bisect,normalizeRing,validRing,getInterpolationPoints
}
} }
export default svgMorph export default svgMorph

17
src/core/queueStart.js Normal file
View file

@ -0,0 +1,17 @@
import onStart from '../objects/onStart.js'
import linkInterpolation from './linkInterpolation.js'
export default function(){
// fire onStart actions
for (let obj in onStart) {
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj) // easing functions
} else {
for (let prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
// add interpolations
linkInterpolation.call(this)
}

View file

@ -9,6 +9,7 @@ import crossCheck from '../objects/crossCheck.js'
import prepareObject from '../process/prepareObject.js' import prepareObject from '../process/prepareObject.js'
import getStartValues from '../process/getStartValues.js' import getStartValues from '../process/getStartValues.js'
import {Tick,Ticker} from '../core/render.js' import {Tick,Ticker} from '../core/render.js'
import queueStart from '../core/queueStart.js'
defaultOptions.repeat = 0 defaultOptions.repeat = 0
defaultOptions.repeatDelay = 0 defaultOptions.repeatDelay = 0
@ -83,8 +84,8 @@ export default class Tween extends TweenBase {
getStartValues.call(this); getStartValues.call(this);
// this is where we do the valuesStart and valuesEnd check for fromTo() method // this is where we do the valuesStart and valuesEnd check for fromTo() method
for ( const component in crossCheck ) { for ( let component in crossCheck ) {
for ( const prop in crossCheck[component] ) { for ( let prop in crossCheck[component] ) {
crossCheck[component][prop].call(this,prop); crossCheck[component][prop].call(this,prop);
} }
} }
@ -94,14 +95,13 @@ export default class Tween extends TweenBase {
// set yoyo values // set yoyo values
if (this._yoyo) { if (this._yoyo) {
for ( const endProp in this.valuesEnd ) { for ( let endProp in this.valuesEnd ) {
this.valuesRepeat[endProp] = this.valuesStart[endProp] this.valuesRepeat[endProp] = this.valuesStart[endProp]
} }
} }
super.start(time); super.start(time);
return this return this
} }
// updates to super methods // updates to super methods
@ -135,16 +135,7 @@ export default class Tween extends TweenBase {
this._onResume.call(this); this._onResume.call(this);
} }
// re-queue execution context // re-queue execution context
for (let obj in onStart) { queueStart.call(this)
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj);
} else {
for (let prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
linkInterpolation.call(this);
// update time and let it roll // update time and let it roll
this._startTime += KUTE.Time() - this._pauseTime; this._startTime += KUTE.Time() - this._pauseTime;
add(this); add(this);

View file

@ -8,7 +8,7 @@ import {Tick,Ticker,stop} from '../core/render.js'
import add from '../core/add.js' import add from '../core/add.js'
import remove from '../core/remove.js' import remove from '../core/remove.js'
import linkInterpolation from '../core/linkInterpolation.js' import queueStart from '../core/queueStart.js'
// single Tween object construct // single Tween object construct
// TweenBase is meant to be use for pre-processed values // TweenBase is meant to be use for pre-processed values
@ -74,18 +74,7 @@ export default class TweenBase {
this._onStart.call(this); this._onStart.call(this);
} }
// fire onStart actions queueStart.call(this)
for (let obj in onStart) {
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj) // easing functions
} else {
for (let prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
// add interpolations
linkInterpolation.call(this)
this._startFired = true; this._startFired = true;
} }