* @copyright Wout Fierens <wout@mick-wout.com>
* @license MIT
*
-* BUILT: Mon Nov 12 2018 12:58:46 GMT+0100 (GMT+01:00)
+* BUILT: Mon Nov 12 2018 13:00:40 GMT+0100 (GMT+01:00)
*/;
var SVG = (function () {
'use strict';
var subClassArray = function () {
try {
// try es6 subclassing
- return Function('name', 'baseClass', '_constructor', ['baseClass = baseClass || Array', 'return {', '[name]: class extends baseClass {', 'constructor (...args) {', 'super(...args)', '_constructor && _constructor.apply(this, args)', '}', '}', '}[name]'].join('\n'));
+ return Function('name', 'baseClass', '_constructor', ['baseClass = baseClass || Array', 'return {', ' [name]: class extends baseClass {', ' constructor (...args) {', ' super(...args)', ' _constructor && _constructor.apply(this, args)', ' }', ' }', '}[name]'].join('\n'));
} catch (e) {
// Use es5 approach
return function (name) {
Arr.prototype = Object.create(baseClass.prototype);
Arr.prototype.constructor = Arr;
+
+ Arr.prototype.map = function (fn) {
+ var arr = new Arr();
+ arr.push.apply(arr, Array.prototype.map.call(this, fn));
+ return arr;
+ };
+
return Arr;
};
}
});
extend(SVGArray, {
init: function init(arr) {
+ // This catches the case, that native map tries to create an array with new Array(1)
+ if (typeof arr === 'number') return this;
this.length = 0;
this.push.apply(this, _toConsumableArray(this.parse(arr)));
return this;
}
});
- /***
- Base Class
- ==========
- The base stepper class that will be
- ***/
+ var sugar = {
+ stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'],
+ fill: ['color', 'opacity', 'rule'],
+ prefix: function prefix(t, a) {
+ return a === 'color' ? t : t + '-' + a;
+ } // Add sugar for fill and stroke
+
+ };
+ ['fill', 'stroke'].forEach(function (m) {
+ var extension = {};
+ var i;
+
+ extension[m] = function (o) {
+ if (typeof o === 'undefined') {
+ return this.attr(m);
+ }
+
+ if (typeof o === 'string' || Color.isRgb(o) || o instanceof Element) {
+ this.attr(m, o);
+ } else {
+ // set all attributes from sugar.fill and sugar.stroke list
+ for (i = sugar[m].length - 1; i >= 0; i--) {
+ if (o[sugar[m][i]] != null) {
+ this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]);
+ }
+ }
+ }
- function makeSetterGetter(k, f) {
- return function (v) {
- if (v == null) return this[v];
- this[k] = v;
- if (f) f.call(this);
return this;
};
- }
- var easing = {
- '-': function _(pos) {
- return pos;
+ registerMethods(['Shape', 'Runner'], extension);
+ });
+ registerMethods(['Element', 'Runner'], {
+ // Let the user set the matrix directly
+ matrix: function matrix(mat, b, c, d, e, f) {
+ // Act as a getter
+ if (mat == null) {
+ return new Matrix(this);
+ } // Act as a setter, the user can pass a matrix or a set of numbers
+
+
+ return this.attr('transform', new Matrix(mat, b, c, d, e, f));
},
- '<>': function _(pos) {
- return -Math.cos(pos * Math.PI) / 2 + 0.5;
+ // Map rotation to transform
+ rotate: function rotate(angle, cx, cy) {
+ return this.transform({
+ rotate: angle,
+ ox: cx,
+ oy: cy
+ }, true);
},
- '>': function _(pos) {
- return Math.sin(pos * Math.PI / 2);
+ // Map skew to transform
+ skew: function skew(x, y, cx, cy) {
+ return arguments.length === 1 || arguments.length === 3 ? this.transform({
+ skew: x,
+ ox: y,
+ oy: cx
+ }, true) : this.transform({
+ skew: [x, y],
+ ox: cx,
+ oy: cy
+ }, true);
},
- '<': function _(pos) {
- return -Math.cos(pos * Math.PI / 2) + 1;
+ shear: function shear(lam, cx, cy) {
+ return this.transform({
+ shear: lam,
+ ox: cx,
+ oy: cy
+ }, true);
},
- bezier: function bezier(x1, y1, x2, y2) {
- // see https://www.w3.org/TR/css-easing-1/#cubic-bezier-algo
- return function (t) {
- if (t < 0) {
- if (x1 > 0) {
- return y1 / x1 * t;
- } else if (x2 > 0) {
- return y2 / x2 * t;
- } else {
- return 0;
- }
- } else if (t > 1) {
- if (x2 < 1) {
- return (1 - y2) / (1 - x2) * t + (y2 - x2) / (1 - x2);
- } else if (x1 < 1) {
- return (1 - y1) / (1 - x1) * t + (y1 - x1) / (1 - x1);
- } else {
- return 1;
- }
- } else {
- return 3 * t * Math.pow(1 - t, 2) * y1 + 3 * Math.pow(t, 2) * (1 - t) * y2 + Math.pow(t, 3);
- }
- };
+ // Map scale to transform
+ scale: function scale(x, y, cx, cy) {
+ return arguments.length === 1 || arguments.length === 3 ? this.transform({
+ scale: x,
+ ox: y,
+ oy: cx
+ }, true) : this.transform({
+ scale: [x, y],
+ ox: cx,
+ oy: cy
+ }, true);
},
- // https://www.w3.org/TR/css-easing-1/#step-timing-function-algo
- steps: function steps(_steps) {
- var stepPosition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'end';
- // deal with "jump-" prefix
- stepPosition = stepPosition.split('-').reverse()[0];
- var jumps = _steps;
-
- if (stepPosition === 'none') {
- --jumps;
- } else if (stepPosition === 'both') {
- ++jumps;
- } // The beforeFlag is essentially useless
-
-
- return function (t) {
- var beforeFlag = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
- // Step is called currentStep in referenced url
- var step = Math.floor(t * _steps);
- var jumping = t * step % 1 === 0;
-
- if (stepPosition === 'start' || stepPosition === 'both') {
- ++step;
- }
-
- if (beforeFlag && jumping) {
- --step;
- }
-
- if (t >= 0 && step < 0) {
- step = 0;
- }
-
- if (t <= 1 && step > jumps) {
- step = jumps;
+ // Map translate to transform
+ translate: function translate(x, y) {
+ return this.transform({
+ translate: [x, y]
+ }, true);
+ },
+ // Map relative translations to transform
+ relative: function relative(x, y) {
+ return this.transform({
+ relative: [x, y]
+ }, true);
+ },
+ // Map flip to transform
+ flip: function flip(direction, around) {
+ var directionString = typeof direction === 'string' ? direction : isFinite(direction) ? 'both' : 'both';
+ var origin = direction === 'both' && isFinite(around) ? [around, around] : direction === 'x' ? [around, 0] : direction === 'y' ? [0, around] : isFinite(direction) ? [direction, direction] : [0, 0];
+ this.transform({
+ flip: directionString,
+ origin: origin
+ }, true);
+ },
+ // Opacity
+ opacity: function opacity(value) {
+ return this.attr('opacity', value);
+ },
+ // Relative move over x and y axes
+ dmove: function dmove(x, y) {
+ return this.dx(x).dy(y);
+ }
+ });
+ registerMethods('Element', {
+ // Relative move over x axis
+ dx: function dx(x) {
+ return this.x(new SVGNumber(x).plus(this.x()));
+ },
+ // Relative move over y axis
+ dy: function dy(y) {
+ return this.y(new SVGNumber(y).plus(this.y()));
+ }
+ });
+ registerMethods('radius', {
+ // Add x and y radius
+ radius: function radius(x, y) {
+ var type = (this._element || this).type;
+ return type === 'radialGradient' || type === 'radialGradient' ? this.attr('r', new SVGNumber(x)) : this.rx(x).ry(y == null ? x : y);
+ }
+ });
+ registerMethods('Path', {
+ // Get path length
+ length: function length() {
+ return this.node.getTotalLength();
+ },
+ // Get point at length
+ pointAt: function pointAt(length) {
+ return new Point(this.node.getPointAtLength(length));
+ }
+ });
+ registerMethods(['Element', 'Runner'], {
+ // Set font
+ font: function font(a, v) {
+ if (_typeof(a) === 'object') {
+ for (v in a) {
+ this.font(v, a[v]);
}
+ }
- return step / jumps;
- };
+ return a === 'leading' ? this.leading(v) : a === 'anchor' ? this.attr('text-anchor', v) : a === 'size' || a === 'family' || a === 'weight' || a === 'stretch' || a === 'variant' || a === 'style' ? this.attr('font-' + a, v) : this.attr(a, v);
}
- };
- var Stepper =
- /*#__PURE__*/
- function () {
- function Stepper() {
- _classCallCheck(this, Stepper);
+ });
+ registerMethods('Text', {
+ ax: function ax(x) {
+ return this.attr('x', x);
+ },
+ ay: function ay(y) {
+ return this.attr('y', y);
+ },
+ amove: function amove(x, y) {
+ return this.ax(x).ay(y);
}
+ }); // Add events to elements
- _createClass(Stepper, [{
- key: "done",
- value: function done() {
- return false;
+ var methods$1 = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'mouseenter', 'mouseleave', 'touchstart', 'touchmove', 'touchleave', 'touchend', 'touchcancel'].reduce(function (last, event) {
+ // add event to Element
+ var fn = function fn(f) {
+ if (f === null) {
+ off(this, event);
+ } else {
+ on(this, event, f);
}
- }]);
- return Stepper;
- }();
- /***
- Easing Functions
- ================
- ***/
+ return this;
+ };
- var Ease =
- /*#__PURE__*/
- function (_Stepper) {
- _inherits(Ease, _Stepper);
+ last[event] = fn;
+ return last;
+ }, {});
+ registerMethods('Element', methods$1);
- function Ease(fn) {
- var _this;
+ function untransform() {
+ return this.attr('transform', null);
+ } // merge the whole transformation chain into one matrix and returns it
- _classCallCheck(this, Ease);
+ function matrixify() {
+ var matrix = (this.attr('transform') || ''). // split transformations
+ split(transforms).slice(0, -1).map(function (str) {
+ // generate key => value pairs
+ var kv = str.trim().split('(');
+ return [kv[0], kv[1].split(delimiter).map(function (str) {
+ return parseFloat(str);
+ })];
+ }).reverse() // merge every transformation into one matrix
+ .reduce(function (matrix, transform) {
+ if (transform[0] === 'matrix') {
+ return matrix.lmultiply(Matrix.fromArray(transform[1]));
+ }
- _this = _possibleConstructorReturn(this, _getPrototypeOf(Ease).call(this));
- _this.ease = easing[fn || timeline.ease] || fn;
- return _this;
+ return matrix[transform[0]].apply(matrix, transform[1]);
+ }, new Matrix());
+ return matrix;
+ } // add an element to another parent without changing the visual representation on the screen
+
+ function toParent(parent) {
+ if (this === parent) return this;
+ var ctm = this.screenCTM();
+ var pCtm = parent.screenCTM().inverse();
+ this.addTo(parent).untransform().transform(pCtm.multiply(ctm));
+ return this;
+ } // same as above with parent equals root-svg
+
+ function toDoc() {
+ return this.toParent(this.doc());
+ } // Add transformations
+
+ function transform(o, relative) {
+ // Act as a getter if no object was passed
+ if (o == null || typeof o === 'string') {
+ var decomposed = new Matrix(this).decompose();
+ return decomposed[o] || decomposed;
}
- _createClass(Ease, [{
- key: "step",
- value: function step(from, to, pos) {
- if (typeof from !== 'number') {
- return pos < 1 ? from : to;
- }
+ if (!Matrix.isMatrixLike(o)) {
+ // Set the origin according to the defined transform
+ o = _objectSpread({}, o, {
+ origin: getOrigin(o, this)
+ });
+ } // The user can pass a boolean, an Element or an Matrix or nothing
- return from + (to - from) * this.ease(pos);
+
+ var cleanRelative = relative === true ? this : relative || false;
+ var result = new Matrix(cleanRelative).transform(o);
+ return this.attr('transform', result);
+ }
+ registerMethods('Element', {
+ untransform: untransform,
+ matrixify: matrixify,
+ toParent: toParent,
+ toDoc: toDoc,
+ transform: transform
+ });
+
+ function isNulledBox(box) {
+ return !box.w && !box.h && !box.x && !box.y;
+ }
+
+ function domContains(node) {
+ return (globals.document.documentElement.contains || function (node) {
+ // This is IE - it does not support contains() for top-level SVGs
+ while (node.parentNode) {
+ node = node.parentNode;
}
- }]);
- return Ease;
- }(Stepper);
- /***
- Controller Types
- ================
- ***/
+ return node === document;
+ }).call(globals.document.documentElement, node);
+ }
- var Controller =
+ var Box =
/*#__PURE__*/
- function (_Stepper2) {
- _inherits(Controller, _Stepper2);
+ function () {
+ function Box() {
+ _classCallCheck(this, Box);
- function Controller(fn) {
- var _this2;
+ this.init.apply(this, arguments);
+ }
- _classCallCheck(this, Controller);
+ _createClass(Box, [{
+ key: "init",
+ value: function init(source) {
+ var base = [0, 0, 0, 0];
+ source = typeof source === 'string' ? source.split(delimiter).map(parseFloat) : Array.isArray(source) ? source : _typeof(source) === 'object' ? [source.left != null ? source.left : source.x, source.top != null ? source.top : source.y, source.width, source.height] : arguments.length === 4 ? [].slice.call(arguments) : base;
+ this.x = source[0] || 0;
+ this.y = source[1] || 0;
+ this.width = this.w = source[2] || 0;
+ this.height = this.h = source[3] || 0; // Add more bounding box properties
- _this2 = _possibleConstructorReturn(this, _getPrototypeOf(Controller).call(this));
- _this2.stepper = fn;
- return _this2;
- }
+ this.x2 = this.x + this.w;
+ this.y2 = this.y + this.h;
+ this.cx = this.x + this.w / 2;
+ this.cy = this.y + this.h / 2;
+ return this;
+ } // Merge rect box with another, return a new instance
- _createClass(Controller, [{
- key: "step",
- value: function step(current, target, dt, c) {
- return this.stepper(current, target, dt, c);
+ }, {
+ key: "merge",
+ value: function merge(box) {
+ var x = Math.min(this.x, box.x);
+ var y = Math.min(this.y, box.y);
+ var width = Math.max(this.x + this.width, box.x + box.width) - x;
+ var height = Math.max(this.y + this.height, box.y + box.height) - y;
+ return new Box(x, y, width, height);
}
}, {
- key: "done",
- value: function done(c) {
- return c.done;
+ key: "transform",
+ value: function transform(m) {
+ var xMin = Infinity;
+ var xMax = -Infinity;
+ var yMin = Infinity;
+ var yMax = -Infinity;
+ var pts = [new Point(this.x, this.y), new Point(this.x2, this.y), new Point(this.x, this.y2), new Point(this.x2, this.y2)];
+ pts.forEach(function (p) {
+ p = p.transform(m);
+ xMin = Math.min(xMin, p.x);
+ xMax = Math.max(xMax, p.x);
+ yMin = Math.min(yMin, p.y);
+ yMax = Math.max(yMax, p.y);
+ });
+ return new Box(xMin, yMin, xMax - xMin, yMax - yMin);
+ }
+ }, {
+ key: "addOffset",
+ value: function addOffset() {
+ // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
+ this.x += globals.window.pageXOffset;
+ this.y += globals.window.pageYOffset;
+ return this;
+ }
+ }, {
+ key: "toString",
+ value: function toString() {
+ return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height;
+ }
+ }, {
+ key: "toArray",
+ value: function toArray() {
+ return [this.x, this.y, this.width, this.height];
+ }
+ }, {
+ key: "isNulled",
+ value: function isNulled() {
+ return isNulledBox(this);
}
}]);
- return Controller;
- }(Stepper);
+ return Box;
+ }();
- function recalculate() {
- // Apply the default parameters
- var duration = (this._duration || 500) / 1000;
- var overshoot = this._overshoot || 0; // Calculate the PID natural response
+ function getBox(cb) {
+ var box;
- var eps = 1e-10;
- var pi = Math.PI;
- var os = Math.log(overshoot / 100 + eps);
- var zeta = -os / Math.sqrt(pi * pi + os * os);
- var wn = 3.9 / (zeta * duration); // Calculate the Spring values
+ try {
+ box = cb(this.node);
- this.d = 2 * zeta * wn;
- this.k = wn * wn;
+ if (isNulledBox(box) && !domContains(this.node)) {
+ throw new Error('Element not in the dom');
+ }
+ } catch (e) {
+ try {
+ var clone = this.clone().addTo(parser().svg).show();
+ box = cb(clone.node);
+ clone.remove();
+ } catch (e) {
+ throw new Error('Getting a bounding box of element "' + this.node.nodeName + '" is not possible');
+ }
+ }
+
+ return box;
}
- var Spring =
- /*#__PURE__*/
- function (_Controller) {
- _inherits(Spring, _Controller);
+ registerMethods({
+ Element: {
+ // Get bounding box
+ bbox: function bbox() {
+ return new Box(getBox.call(this, function (node) {
+ return node.getBBox();
+ }));
+ },
+ rbox: function rbox(el) {
+ var box = new Box(getBox.call(this, function (node) {
+ return node.getBoundingClientRect();
+ }));
+ if (el) return box.transform(el.screenCTM().inverse());
+ return box.addOffset();
+ }
+ },
+ viewbox: {
+ viewbox: function viewbox(x, y, width, height) {
+ // act as getter
+ if (x == null) return new Box(this.attr('viewBox')); // act as setter
- function Spring(duration, overshoot) {
- var _this3;
+ return this.attr('viewBox', new Box(x, y, width, height));
+ }
+ }
+ });
- _classCallCheck(this, Spring);
+ function rx(rx) {
+ return this.attr('rx', rx);
+ } // Radius y value
- _this3 = _possibleConstructorReturn(this, _getPrototypeOf(Spring).call(this));
+ function ry(ry) {
+ return this.attr('ry', ry);
+ } // Move over x-axis
- _this3.duration(duration || 500).overshoot(overshoot || 0);
+ function x(x) {
+ return x == null ? this.cx() - this.rx() : this.cx(x + this.rx());
+ } // Move over y-axis
- return _this3;
+ function y(y) {
+ return y == null ? this.cy() - this.ry() : this.cy(y + this.ry());
+ } // Move by center over x-axis
+
+ function cx(x) {
+ return x == null ? this.attr('cx') : this.attr('cx', x);
+ } // Move by center over y-axis
+
+ function cy(y) {
+ return y == null ? this.attr('cy') : this.attr('cy', y);
+ } // Set width of element
+
+ function width(width) {
+ return width == null ? this.rx() * 2 : this.rx(new SVGNumber(width).divide(2));
+ } // Set height of element
+
+ function height(height) {
+ return height == null ? this.ry() * 2 : this.ry(new SVGNumber(height).divide(2));
+ }
+
+ var circled = /*#__PURE__*/Object.freeze({
+ rx: rx,
+ ry: ry,
+ x: x,
+ y: y,
+ cx: cx,
+ cy: cy,
+ width: width,
+ height: height
+ });
+
+ var Shape =
+ /*#__PURE__*/
+ function (_Element) {
+ _inherits(Shape, _Element);
+
+ function Shape() {
+ _classCallCheck(this, Shape);
+
+ return _possibleConstructorReturn(this, _getPrototypeOf(Shape).apply(this, arguments));
}
- _createClass(Spring, [{
- key: "step",
- value: function step(current, target, dt, c) {
- if (typeof current === 'string') return current;
- c.done = dt === Infinity;
- if (dt === Infinity) return target;
- if (dt === 0) return current;
- if (dt > 100) dt = 16;
- dt /= 1000; // Get the previous velocity
+ return Shape;
+ }(Element);
+ register(Shape);
- var velocity = c.velocity || 0; // Apply the control to get the new position and store it
+ var Circle =
+ /*#__PURE__*/
+ function (_Shape) {
+ _inherits(Circle, _Shape);
- var acceleration = -this.d * velocity - this.k * (current - target);
- var newPosition = current + velocity * dt + acceleration * dt * dt / 2; // Store the velocity
+ function Circle(node) {
+ _classCallCheck(this, Circle);
- c.velocity = velocity + acceleration * dt; // Figure out if we have converged, and if so, pass the value
+ return _possibleConstructorReturn(this, _getPrototypeOf(Circle).call(this, nodeOrNew('circle', node), node));
+ }
- c.done = Math.abs(target - newPosition) + Math.abs(velocity) < 0.002;
- return c.done ? target : newPosition;
+ _createClass(Circle, [{
+ key: "radius",
+ value: function radius(r) {
+ return this.attr('r', r);
+ } // Radius x value
+
+ }, {
+ key: "rx",
+ value: function rx$$1(_rx) {
+ return this.attr('r', _rx);
+ } // Alias radius x value
+
+ }, {
+ key: "ry",
+ value: function ry$$1(_ry) {
+ return this.rx(_ry);
+ }
+ }, {
+ key: "size",
+ value: function size(_size) {
+ return this.radius(new SVGNumber(_size).divide(2));
}
}]);
- return Spring;
- }(Controller);
- extend(Spring, {
- duration: makeSetterGetter('_duration', recalculate),
- overshoot: makeSetterGetter('_overshoot', recalculate)
+ return Circle;
+ }(Shape);
+ extend(Circle, {
+ x: x,
+ y: y,
+ cx: cx,
+ cy: cy,
+ width: width,
+ height: height
});
- var PID =
+ registerMethods({
+ Element: {
+ // Create circle element
+ circle: wrapWithAttrCheck(function (size) {
+ return this.put(new Circle()).size(size).move(0, 0);
+ })
+ }
+ });
+ register(Circle);
+
+ var Ellipse =
/*#__PURE__*/
- function (_Controller2) {
- _inherits(PID, _Controller2);
+ function (_Shape) {
+ _inherits(Ellipse, _Shape);
- function PID(p, i, d, windup) {
- var _this4;
+ function Ellipse(node) {
+ _classCallCheck(this, Ellipse);
- _classCallCheck(this, PID);
+ return _possibleConstructorReturn(this, _getPrototypeOf(Ellipse).call(this, nodeOrNew('ellipse', node), node));
+ }
- _this4 = _possibleConstructorReturn(this, _getPrototypeOf(PID).call(this));
- p = p == null ? 0.1 : p;
- i = i == null ? 0.01 : i;
- d = d == null ? 0 : d;
- windup = windup == null ? 1000 : windup;
+ _createClass(Ellipse, [{
+ key: "size",
+ value: function size(width$$1, height$$1) {
+ var p = proportionalSize(this, width$$1, height$$1);
+ return this.rx(new SVGNumber(p.width).divide(2)).ry(new SVGNumber(p.height).divide(2));
+ }
+ }]);
- _this4.p(p).i(i).d(d).windup(windup);
+ return Ellipse;
+ }(Shape);
+ extend(Ellipse, circled);
+ registerMethods('Container', {
+ // Create an ellipse
+ ellipse: wrapWithAttrCheck(function (width$$1, height$$1) {
+ return this.put(new Ellipse()).size(width$$1, height$$1).move(0, 0);
+ })
+ });
+ register(Ellipse);
- return _this4;
- }
+ var Stop =
+ /*#__PURE__*/
+ function (_Element) {
+ _inherits(Stop, _Element);
- _createClass(PID, [{
- key: "step",
- value: function step(current, target, dt, c) {
- if (typeof current === 'string') return current;
- c.done = dt === Infinity;
- if (dt === Infinity) return target;
- if (dt === 0) return current;
- var p = target - current;
- var i = (c.integral || 0) + p * dt;
- var d = (p - (c.error || 0)) / dt;
- var windup = this.windup; // antiwindup
+ function Stop(node) {
+ _classCallCheck(this, Stop);
- if (windup !== false) {
- i = Math.max(-windup, Math.min(i, windup));
- }
+ return _possibleConstructorReturn(this, _getPrototypeOf(Stop).call(this, nodeOrNew('stop', node), node));
+ } // add color stops
- c.error = p;
- c.integral = i;
- c.done = Math.abs(p) < 0.001;
- return c.done ? target : current + (this.P * p + this.I * i + this.D * d);
+
+ _createClass(Stop, [{
+ key: "update",
+ value: function update(o) {
+ if (typeof o === 'number' || o instanceof SVGNumber) {
+ o = {
+ offset: arguments[0],
+ color: arguments[1],
+ opacity: arguments[2]
+ };
+ } // set attributes
+
+
+ if (o.opacity != null) this.attr('stop-opacity', o.opacity);
+ if (o.color != null) this.attr('stop-color', o.color);
+ if (o.offset != null) this.attr('offset', new SVGNumber(o.offset));
+ return this;
}
}]);
- return PID;
- }(Controller);
- extend(PID, {
- windup: makeSetterGetter('windup'),
- p: makeSetterGetter('P'),
- i: makeSetterGetter('I'),
- d: makeSetterGetter('D')
+ return Stop;
+ }(Element);
+ register(Stop);
+
+ function baseFind(query, parent) {
+ return map((parent || globals.document).querySelectorAll(query), function (node) {
+ return adopt(node);
+ });
+ } // Scoped find method
+
+ function find(query) {
+ return baseFind(query, this.node);
+ }
+ registerMethods('Dom', {
+ find: find
});
function from(x, y) {
to: to
});
- function rx(rx) {
- return this.attr('rx', rx);
- } // Radius y value
-
- function ry(ry) {
- return this.attr('ry', ry);
- } // Move over x-axis
+ var Gradient =
+ /*#__PURE__*/
+ function (_Container) {
+ _inherits(Gradient, _Container);
- function x(x) {
- return x == null ? this.cx() - this.rx() : this.cx(x + this.rx());
- } // Move over y-axis
+ function Gradient(type, attrs) {
+ _classCallCheck(this, Gradient);
- function y(y) {
- return y == null ? this.cy() - this.ry() : this.cy(y + this.ry());
- } // Move by center over x-axis
+ return _possibleConstructorReturn(this, _getPrototypeOf(Gradient).call(this, nodeOrNew(type + 'Gradient', typeof type === 'string' ? null : type), attrs));
+ } // Add a color stop
- function cx(x) {
- return x == null ? this.attr('cx') : this.attr('cx', x);
- } // Move by center over y-axis
- function cy(y) {
- return y == null ? this.attr('cy') : this.attr('cy', y);
- } // Set width of element
+ _createClass(Gradient, [{
+ key: "stop",
+ value: function stop(offset, color, opacity) {
+ return this.put(new Stop()).update(offset, color, opacity);
+ } // Update gradient
- function width(width) {
- return width == null ? this.rx() * 2 : this.rx(new SVGNumber(width).divide(2));
- } // Set height of element
+ }, {
+ key: "update",
+ value: function update(block) {
+ // remove all stops
+ this.clear(); // invoke passed block
- function height(height) {
- return height == null ? this.ry() * 2 : this.ry(new SVGNumber(height).divide(2));
- }
+ if (typeof block === 'function') {
+ block.call(this, this);
+ }
- var circled = /*#__PURE__*/Object.freeze({
- rx: rx,
- ry: ry,
- x: x,
- y: y,
- cx: cx,
- cy: cy,
- width: width,
- height: height
- });
+ return this;
+ } // Return the fill id
- var Queue =
- /*#__PURE__*/
- function () {
- function Queue() {
- _classCallCheck(this, Queue);
+ }, {
+ key: "url",
+ value: function url() {
+ return 'url(#' + this.id() + ')';
+ } // Alias string convertion to fill
- this._first = null;
- this._last = null;
+ }, {
+ key: "toString",
+ value: function toString() {
+ return this.url();
+ } // custom attr to handle transform
+
+ }, {
+ key: "attr",
+ value: function attr(a, b, c) {
+ if (a === 'transform') a = 'gradientTransform';
+ return _get(_getPrototypeOf(Gradient.prototype), "attr", this).call(this, a, b, c);
+ }
+ }, {
+ key: "targets",
+ value: function targets() {
+ return baseFind('svg [fill*="' + this.id() + '"]');
+ }
+ }, {
+ key: "bbox",
+ value: function bbox() {
+ return new Box();
+ }
+ }]);
+
+ return Gradient;
+ }(Container);
+ extend(Gradient, gradiented);
+ registerMethods({
+ Container: {
+ // Create gradient element in defs
+ gradient: wrapWithAttrCheck(function (type, block) {
+ return this.defs().gradient(type, block);
+ })
+ },
+ // define gradient
+ Defs: {
+ gradient: wrapWithAttrCheck(function (type, block) {
+ return this.put(new Gradient(type)).update(block);
+ })
}
+ });
+ register(Gradient);
- _createClass(Queue, [{
- key: "push",
- value: function push(value) {
- // An item stores an id and the provided value
- var item = value.next ? value : {
- value: value,
- next: null,
- prev: null // Deal with the queue being empty or populated
+ var Pattern =
+ /*#__PURE__*/
+ function (_Container) {
+ _inherits(Pattern, _Container);
- };
+ // Initialize node
+ function Pattern(node) {
+ _classCallCheck(this, Pattern);
- if (this._last) {
- item.prev = this._last;
- this._last.next = item;
- this._last = item;
- } else {
- this._last = item;
- this._first = item;
- } // Update the length and return the current item
+ return _possibleConstructorReturn(this, _getPrototypeOf(Pattern).call(this, nodeOrNew('pattern', node), node));
+ } // Return the fill id
- return item;
- }
+ _createClass(Pattern, [{
+ key: "url",
+ value: function url() {
+ return 'url(#' + this.id() + ')';
+ } // Update pattern by rebuilding
+
}, {
- key: "shift",
- value: function shift() {
- // Check if we have a value
- var remove = this._first;
- if (!remove) return null; // If we do, remove it and relink things
+ key: "update",
+ value: function update(block) {
+ // remove content
+ this.clear(); // invoke passed block
- this._first = remove.next;
- if (this._first) this._first.prev = null;
- this._last = this._first ? this._last : null;
- return remove.value;
- } // Shows us the first item in the list
+ if (typeof block === 'function') {
+ block.call(this, this);
+ }
- }, {
- key: "first",
- value: function first() {
- return this._first && this._first.value;
- } // Shows us the last item in the list
+ return this;
+ } // Alias string convertion to fill
}, {
- key: "last",
- value: function last() {
- return this._last && this._last.value;
- } // Removes the item that was returned from the push
+ key: "toString",
+ value: function toString() {
+ return this.url();
+ } // custom attr to handle transform
}, {
- key: "remove",
- value: function remove(item) {
- // Relink the previous item
- if (item.prev) item.prev.next = item.next;
- if (item.next) item.next.prev = item.prev;
- if (item === this._last) this._last = item.prev;
- if (item === this._first) this._first = item.next; // Invalidate item
-
- item.prev = null;
- item.next = null;
+ key: "attr",
+ value: function attr(a, b, c) {
+ if (a === 'transform') a = 'patternTransform';
+ return _get(_getPrototypeOf(Pattern.prototype), "attr", this).call(this, a, b, c);
+ }
+ }, {
+ key: "targets",
+ value: function targets() {
+ return baseFind('svg [fill*="' + this.id() + '"]');
+ }
+ }, {
+ key: "bbox",
+ value: function bbox() {
+ return new Box();
}
}]);
- return Queue;
- }();
-
- var Animator = {
- nextDraw: null,
- frames: new Queue(),
- timeouts: new Queue(),
- timer: globals.window.performance || globals.window.Date,
- transforms: [],
- frame: function frame(fn) {
- // Store the node
- var node = Animator.frames.push({
- run: fn
- }); // Request an animation frame if we don't have one
-
- if (Animator.nextDraw === null) {
- Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw);
- } // Return the node so we can remove it easily
-
-
- return node;
- },
- transform_frame: function transform_frame(fn, id) {
- Animator.transforms[id] = fn;
- },
- timeout: function timeout(fn, delay) {
- delay = delay || 0; // Work out when the event should fire
-
- var time = Animator.timer.now() + delay; // Add the timeout to the end of the queue
-
- var node = Animator.timeouts.push({
- run: fn,
- time: time
- }); // Request another animation frame if we need one
+ return Pattern;
+ }(Container);
+ registerMethods({
+ Container: {
+ // Create pattern element in defs
+ pattern: function pattern() {
+ var _this$defs;
- if (Animator.nextDraw === null) {
- Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw);
+ return (_this$defs = this.defs()).pattern.apply(_this$defs, arguments);
}
-
- return node;
- },
- cancelFrame: function cancelFrame(node) {
- Animator.frames.remove(node);
- },
- clearTimeout: function clearTimeout(node) {
- Animator.timeouts.remove(node);
},
- _draw: function _draw(now) {
- // Run all the timeouts we can run, if they are not ready yet, add them
- // to the end of the queue immediately! (bad timeouts!!! [sarcasm])
- var nextTimeout = null;
- var lastTimeout = Animator.timeouts.last();
+ Defs: {
+ pattern: wrapWithAttrCheck(function (width, height, block) {
+ return this.put(new Pattern()).update(block).attr({
+ x: 0,
+ y: 0,
+ width: width,
+ height: height,
+ patternUnits: 'userSpaceOnUse'
+ });
+ })
+ }
+ });
+ register(Pattern);
- while (nextTimeout = Animator.timeouts.shift()) {
- // Run the timeout if its time, or push it to the end
- if (now >= nextTimeout.time) {
- nextTimeout.run();
- } else {
- Animator.timeouts.push(nextTimeout);
- } // If we hit the last item, we should stop shifting out more items
+ var Image =
+ /*#__PURE__*/
+ function (_Shape) {
+ _inherits(Image, _Shape);
+ function Image(node) {
+ _classCallCheck(this, Image);
- if (nextTimeout === lastTimeout) break;
- } // Run all of the animation frames
+ return _possibleConstructorReturn(this, _getPrototypeOf(Image).call(this, nodeOrNew('image', node), node));
+ } // (re)load image
- var nextFrame = null;
- var lastFrame = Animator.frames.last();
+ _createClass(Image, [{
+ key: "load",
+ value: function load(url, callback) {
+ if (!url) return this;
+ var img = new globals.window.Image();
+ on(img, 'load', function (e) {
+ var p = this.parent(Pattern); // ensure image size
- while (nextFrame !== lastFrame && (nextFrame = Animator.frames.shift())) {
- nextFrame.run();
- }
-
- Animator.transforms.forEach(function (el) {
- el();
- }); // If we have remaining timeouts or frames, draw until we don't anymore
-
- Animator.nextDraw = Animator.timeouts.first() || Animator.frames.first() ? globals.window.requestAnimationFrame(Animator._draw) : null;
- }
- };
+ if (this.width() === 0 && this.height() === 0) {
+ this.size(img.width, img.height);
+ }
- function isNulledBox(box) {
- return !box.w && !box.h && !box.x && !box.y;
- }
+ if (p instanceof Pattern) {
+ // ensure pattern size if not set
+ if (p.width() === 0 && p.height() === 0) {
+ p.size(this.width(), this.height());
+ }
+ }
- function domContains(node) {
- return (globals.document.documentElement.contains || function (node) {
- // This is IE - it does not support contains() for top-level SVGs
- while (node.parentNode) {
- node = node.parentNode;
+ if (typeof callback === 'function') {
+ callback.call(this, {
+ width: img.width,
+ height: img.height,
+ ratio: img.width / img.height,
+ url: url
+ });
+ }
+ }, this);
+ on(img, 'load error', function () {
+ // dont forget to unbind memory leaking events
+ off(img);
+ });
+ return this.attr('href', img.src = url, xlink);
}
+ }]);
- return node === document;
- }).call(globals.document.documentElement, node);
- }
+ return Image;
+ }(Shape);
+ registerAttrHook(function (attr$$1, val, _this) {
+ // convert image fill and stroke to patterns
+ if (attr$$1 === 'fill' || attr$$1 === 'stroke') {
+ if (isImage.test(val)) {
+ val = _this.doc().defs().image(val);
+ }
+ }
- var Box =
- /*#__PURE__*/
- function () {
- function Box() {
- _classCallCheck(this, Box);
+ if (val instanceof Image) {
+ val = _this.doc().defs().pattern(0, 0, function (pattern) {
+ pattern.add(val);
+ });
+ }
- this.init.apply(this, arguments);
+ return val;
+ });
+ registerMethods({
+ Container: {
+ // create image element, load image and set its size
+ image: wrapWithAttrCheck(function (source, callback) {
+ return this.put(new Image()).size(0, 0).load(source, callback);
+ })
}
+ });
+ register(Image);
- _createClass(Box, [{
- key: "init",
- value: function init(source) {
- var base = [0, 0, 0, 0];
- source = typeof source === 'string' ? source.split(delimiter).map(parseFloat) : Array.isArray(source) ? source : _typeof(source) === 'object' ? [source.left != null ? source.left : source.x, source.top != null ? source.top : source.y, source.width, source.height] : arguments.length === 4 ? [].slice.call(arguments) : base;
- this.x = source[0] || 0;
- this.y = source[1] || 0;
- this.width = this.w = source[2] || 0;
- this.height = this.h = source[3] || 0; // Add more bounding box properties
+ var PointArray = subClassArray('PointArray', SVGArray);
+ extend(PointArray, {
+ // Convert array to string
+ toString: function toString() {
+ // convert to a poly point string
+ for (var i = 0, il = this.length, array = []; i < il; i++) {
+ array.push(this[i].join(','));
+ }
- this.x2 = this.x + this.w;
- this.y2 = this.y + this.h;
- this.cx = this.x + this.w / 2;
- this.cy = this.y + this.h / 2;
- return this;
- } // Merge rect box with another, return a new instance
+ return array.join(' ');
+ },
+ // Convert array to line object
+ toLine: function toLine() {
+ return {
+ x1: this[0][0],
+ y1: this[0][1],
+ x2: this[1][0],
+ y2: this[1][1]
+ };
+ },
+ // Get morphed array at given position
+ at: function at(pos) {
+ // make sure a destination is defined
+ if (!this.destination) return this; // generate morphed point string
- }, {
- key: "merge",
- value: function merge(box) {
- var x = Math.min(this.x, box.x);
- var y = Math.min(this.y, box.y);
- var width = Math.max(this.x + this.width, box.x + box.width) - x;
- var height = Math.max(this.y + this.height, box.y + box.height) - y;
- return new Box(x, y, width, height);
- }
- }, {
- key: "transform",
- value: function transform(m) {
- var xMin = Infinity;
- var xMax = -Infinity;
- var yMin = Infinity;
- var yMax = -Infinity;
- var pts = [new Point(this.x, this.y), new Point(this.x2, this.y), new Point(this.x, this.y2), new Point(this.x2, this.y2)];
- pts.forEach(function (p) {
- p = p.transform(m);
- xMin = Math.min(xMin, p.x);
- xMax = Math.max(xMax, p.x);
- yMin = Math.min(yMin, p.y);
- yMax = Math.max(yMax, p.y);
- });
- return new Box(xMin, yMin, xMax - xMin, yMax - yMin);
- }
- }, {
- key: "addOffset",
- value: function addOffset() {
- // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
- this.x += globals.window.pageXOffset;
- this.y += globals.window.pageYOffset;
- return this;
- }
- }, {
- key: "toString",
- value: function toString() {
- return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height;
- }
- }, {
- key: "toArray",
- value: function toArray() {
- return [this.x, this.y, this.width, this.height];
- }
- }, {
- key: "isNulled",
- value: function isNulled() {
- return isNulledBox(this);
+ for (var i = 0, il = this.length, array = []; i < il; i++) {
+ array.push([this[i][0] + (this.destination[i][0] - this[i][0]) * pos, this[i][1] + (this.destination[i][1] - this[i][1]) * pos]);
}
- }]);
-
- return Box;
- }();
- function getBox(cb) {
- var box;
+ return new PointArray(array);
+ },
+ // Parse point string and flat array
+ parse: function parse() {
+ var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [[0, 0]];
+ var points = []; // if it is an array
- try {
- box = cb(this.node);
+ if (array instanceof Array) {
+ // and it is not flat, there is no need to parse it
+ if (array[0] instanceof Array) {
+ return array;
+ }
+ } else {
+ // Else, it is considered as a string
+ // parse points
+ array = array.trim().split(delimiter).map(parseFloat);
+ } // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
+ // Odd number of coordinates is an error. In such cases, drop the last odd coordinate.
- if (isNulledBox(box) && !domContains(this.node)) {
- throw new Error('Element not in the dom');
- }
- } catch (e) {
- try {
- var clone = this.clone().addTo(parser().svg).show();
- box = cb(clone.node);
- clone.remove();
- } catch (e) {
- throw new Error('Getting a bounding box of element "' + this.node.nodeName + '" is not possible');
- }
- }
- return box;
- }
+ if (array.length % 2 !== 0) array.pop(); // wrap points in two-tuples and parse points as floats
- registerMethods({
- Element: {
- // Get bounding box
- bbox: function bbox() {
- return new Box(getBox.call(this, function (node) {
- return node.getBBox();
- }));
- },
- rbox: function rbox(el) {
- var box = new Box(getBox.call(this, function (node) {
- return node.getBoundingClientRect();
- }));
- if (el) return box.transform(el.screenCTM().inverse());
- return box.addOffset();
+ for (var i = 0, len = array.length; i < len; i = i + 2) {
+ points.push([array[i], array[i + 1]]);
}
+
+ return points;
},
- viewbox: {
- viewbox: function viewbox(x, y, width, height) {
- // act as getter
- if (x == null) return new Box(this.attr('viewBox')); // act as setter
+ // Move point string
+ move: function move(x, y) {
+ var box = this.bbox(); // get relative offset
- return this.attr('viewBox', new Box(x, y, width, height));
+ x -= box.x;
+ y -= box.y; // move every point
+
+ if (!isNaN(x) && !isNaN(y)) {
+ for (var i = this.length - 1; i >= 0; i--) {
+ this[i] = [this[i][0] + x, this[i][1] + y];
+ }
}
- }
- });
- var PathArray = subClassArray('PathArray', SVGArray);
- function pathRegReplace(a, b, c, d) {
- return c + d.replace(dots, ' .');
- }
+ return this;
+ },
+ // Resize poly string
+ size: function size(width, height) {
+ var i;
+ var box = this.bbox(); // recalculate position of all points according to new size
- function arrayToString(a) {
- for (var i = 0, il = a.length, s = ''; i < il; i++) {
- s += a[i][0];
+ for (i = this.length - 1; i >= 0; i--) {
+ if (box.width) this[i][0] = (this[i][0] - box.x) * width / box.width + box.x;
+ if (box.height) this[i][1] = (this[i][1] - box.y) * height / box.height + box.y;
+ }
- if (a[i][1] != null) {
- s += a[i][1];
+ return this;
+ },
+ // Get bounding box of points
+ bbox: function bbox() {
+ var maxX = -Infinity;
+ var maxY = -Infinity;
+ var minX = Infinity;
+ var minY = Infinity;
+ this.forEach(function (el) {
+ maxX = Math.max(el[0], maxX);
+ maxY = Math.max(el[1], maxY);
+ minX = Math.min(el[0], minX);
+ minY = Math.min(el[1], minY);
+ });
+ return {
+ x: minX,
+ y: minY,
+ width: maxX - minX,
+ height: maxY - minY
+ };
+ }
+ });
- if (a[i][2] != null) {
- s += ' ';
- s += a[i][2];
+ var MorphArray = PointArray; // Move by left top corner over x-axis
- if (a[i][3] != null) {
- s += ' ';
- s += a[i][3];
- s += ' ';
- s += a[i][4];
+ function x$1(x) {
+ return x == null ? this.bbox().x : this.move(x, this.bbox().y);
+ } // Move by left top corner over y-axis
- if (a[i][5] != null) {
- s += ' ';
- s += a[i][5];
- s += ' ';
- s += a[i][6];
+ function y$1(y) {
+ return y == null ? this.bbox().y : this.move(this.bbox().x, y);
+ } // Set width of element
- if (a[i][7] != null) {
- s += ' ';
- s += a[i][7];
- }
- }
- }
- }
- }
- }
+ function width$1(width) {
+ var b = this.bbox();
+ return width == null ? b.width : this.size(width, b.height);
+ } // Set height of element
- return s + ' ';
+ function height$1(height) {
+ var b = this.bbox();
+ return height == null ? b.height : this.size(b.width, height);
}
- var pathHandlers = {
- M: function M(c, p, p0) {
- p.x = p0.x = c[0];
- p.y = p0.y = c[1];
- return ['M', p.x, p.y];
- },
- L: function L(c, p) {
- p.x = c[0];
- p.y = c[1];
- return ['L', c[0], c[1]];
- },
- H: function H(c, p) {
- p.x = c[0];
- return ['H', c[0]];
- },
- V: function V(c, p) {
- p.y = c[0];
- return ['V', c[0]];
- },
- C: function C(c, p) {
- p.x = c[4];
- p.y = c[5];
- return ['C', c[0], c[1], c[2], c[3], c[4], c[5]];
- },
- S: function S(c, p) {
- p.x = c[2];
- p.y = c[3];
- return ['S', c[0], c[1], c[2], c[3]];
- },
- Q: function Q(c, p) {
- p.x = c[2];
- p.y = c[3];
- return ['Q', c[0], c[1], c[2], c[3]];
- },
- T: function T(c, p) {
- p.x = c[0];
- p.y = c[1];
- return ['T', c[0], c[1]];
- },
- Z: function Z(c, p, p0) {
- p.x = p0.x;
- p.y = p0.y;
- return ['Z'];
- },
- A: function A(c, p) {
- p.x = c[5];
- p.y = c[6];
- return ['A', c[0], c[1], c[2], c[3], c[4], c[5], c[6]];
- }
- };
- var mlhvqtcsaz = 'mlhvqtcsaz'.split('');
+ var pointed = /*#__PURE__*/Object.freeze({
+ MorphArray: MorphArray,
+ x: x$1,
+ y: y$1,
+ width: width$1,
+ height: height$1
+ });
- for (var i = 0, il = mlhvqtcsaz.length; i < il; ++i) {
- pathHandlers[mlhvqtcsaz[i]] = function (i) {
- return function (c, p, p0) {
- if (i === 'H') c[0] = c[0] + p.x;else if (i === 'V') c[0] = c[0] + p.y;else if (i === 'A') {
- c[5] = c[5] + p.x;
- c[6] = c[6] + p.y;
- } else {
- for (var j = 0, jl = c.length; j < jl; ++j) {
- c[j] = c[j] + (j % 2 ? p.y : p.x);
- }
- }
- return pathHandlers[i](c, p, p0);
- };
- }(mlhvqtcsaz[i].toUpperCase());
- }
+ var Line =
+ /*#__PURE__*/
+ function (_Shape) {
+ _inherits(Line, _Shape);
- extend(PathArray, {
- // Convert array to string
- toString: function toString() {
- return arrayToString(this);
- },
- // Move path string
- move: function move(x, y) {
- // get bounding box of current situation
- var box = this.bbox(); // get relative offset
+ // Initialize node
+ function Line(node) {
+ _classCallCheck(this, Line);
- x -= box.x;
- y -= box.y;
+ return _possibleConstructorReturn(this, _getPrototypeOf(Line).call(this, nodeOrNew('line', node), node));
+ } // Get array
- if (!isNaN(x) && !isNaN(y)) {
- // move every point
- for (var l, i = this.length - 1; i >= 0; i--) {
- l = this[i][0];
- if (l === 'M' || l === 'L' || l === 'T') {
- this[i][1] += x;
- this[i][2] += y;
- } else if (l === 'H') {
- this[i][1] += x;
- } else if (l === 'V') {
- this[i][1] += y;
- } else if (l === 'C' || l === 'S' || l === 'Q') {
- this[i][1] += x;
- this[i][2] += y;
- this[i][3] += x;
- this[i][4] += y;
+ _createClass(Line, [{
+ key: "array",
+ value: function array() {
+ return new PointArray([[this.attr('x1'), this.attr('y1')], [this.attr('x2'), this.attr('y2')]]);
+ } // Overwrite native plot() method
- if (l === 'C') {
- this[i][5] += x;
- this[i][6] += y;
- }
- } else if (l === 'A') {
- this[i][6] += x;
- this[i][7] += y;
- }
+ }, {
+ key: "plot",
+ value: function plot(x1, y1, x2, y2) {
+ if (x1 == null) {
+ return this.array();
+ } else if (typeof y1 !== 'undefined') {
+ x1 = {
+ x1: x1,
+ y1: y1,
+ x2: x2,
+ y2: y2
+ };
+ } else {
+ x1 = new PointArray(x1).toLine();
}
- }
-
- return this;
- },
- // Resize path string
- size: function size(width, height) {
- // get bounding box of current situation
- var box = this.bbox();
- var i, l; // recalculate position of all points according to new size
- for (i = this.length - 1; i >= 0; i--) {
- l = this[i][0];
+ return this.attr(x1);
+ } // Move by left top corner
- if (l === 'M' || l === 'L' || l === 'T') {
- this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
- this[i][2] = (this[i][2] - box.y) * height / box.height + box.y;
- } else if (l === 'H') {
- this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
- } else if (l === 'V') {
- this[i][1] = (this[i][1] - box.y) * height / box.height + box.y;
- } else if (l === 'C' || l === 'S' || l === 'Q') {
- this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
- this[i][2] = (this[i][2] - box.y) * height / box.height + box.y;
- this[i][3] = (this[i][3] - box.x) * width / box.width + box.x;
- this[i][4] = (this[i][4] - box.y) * height / box.height + box.y;
+ }, {
+ key: "move",
+ value: function move(x, y) {
+ return this.attr(this.array().move(x, y).toLine());
+ } // Set element size to given width and height
- if (l === 'C') {
- this[i][5] = (this[i][5] - box.x) * width / box.width + box.x;
- this[i][6] = (this[i][6] - box.y) * height / box.height + box.y;
- }
- } else if (l === 'A') {
- // resize radii
- this[i][1] = this[i][1] * width / box.width;
- this[i][2] = this[i][2] * height / box.height; // move position values
+ }, {
+ key: "size",
+ value: function size(width, height) {
+ var p = proportionalSize(this, width, height);
+ return this.attr(this.array().size(p.width, p.height).toLine());
+ }
+ }]);
- this[i][6] = (this[i][6] - box.x) * width / box.width + box.x;
- this[i][7] = (this[i][7] - box.y) * height / box.height + box.y;
+ return Line;
+ }(Shape);
+ extend(Line, pointed);
+ registerMethods({
+ Container: {
+ // Create a line element
+ line: wrapWithAttrCheck(function () {
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
}
- }
- return this;
- },
- // Test if the passed path array use the same path data commands as this path array
- equalCommands: function equalCommands(pathArray) {
- var i, il, equalCommands;
- pathArray = new PathArray(pathArray);
- equalCommands = this.length === pathArray.length;
+ // make sure plot is called as a setter
+ // x1 is not necessarily a number, it can also be an array, a string and a PointArray
+ return Line.prototype.plot.apply(this.put(new Line()), args[0] != null ? args : [0, 0, 0, 0]);
+ })
+ }
+ });
+ register(Line);
- for (i = 0, il = this.length; equalCommands && i < il; i++) {
- equalCommands = this[i][0] === pathArray[i][0];
+ var List = subClassArray('List', Array, function () {
+ var arr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+ // This catches the case, that native map tries to create an array with new Array(1)
+ if (typeof arr === 'number') return this;
+ this.length = 0;
+ this.push.apply(this, _toConsumableArray(arr));
+ });
+ extend(List, {
+ each: function each(fnOrMethodName) {
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
}
- return equalCommands;
- },
- // Make path array morphable
- morph: function morph(pathArray) {
- pathArray = new PathArray(pathArray);
-
- if (this.equalCommands(pathArray)) {
- this.destination = pathArray;
+ if (typeof fnOrMethodName === 'function') {
+ this.forEach(function (el) {
+ fnOrMethodName.call(el, el);
+ });
} else {
- this.destination = null;
+ return this.map(function (el) {
+ return el[fnOrMethodName].apply(el, args);
+ }); // this.forEach((el) => {
+ // el[fnOrMethodName](...args)
+ // })
}
return this;
},
- // Get morphed path array at given position
- at: function at(pos) {
- // make sure a destination is defined
- if (!this.destination) return this;
- var sourceArray = this;
- var destinationArray = this.destination.value;
- var array = [];
- var pathArray = new PathArray();
- var i, il, j, jl; // Animate has specified in the SVG spec
- // See: https://www.w3.org/TR/SVG11/paths.html#PathElement
-
- for (i = 0, il = sourceArray.length; i < il; i++) {
- array[i] = [sourceArray[i][0]];
-
- for (j = 1, jl = sourceArray[i].length; j < jl; j++) {
- array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos;
- } // For the two flags of the elliptical arc command, the SVG spec say:
- // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true
- // Elliptical arc command as an array followed by corresponding indexes:
- // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
- // 0 1 2 3 4 5 6 7
-
+ toArray: function toArray() {
+ return Array.prototype.concat.apply([], this);
+ }
+ });
- if (array[i][0] === 'A') {
- array[i][4] = +(array[i][4] !== 0);
- array[i][5] = +(array[i][5] !== 0);
+ List.extend = function (methods) {
+ methods = methods.reduce(function (obj, name) {
+ obj[name] = function () {
+ for (var _len2 = arguments.length, attrs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+ attrs[_key2] = arguments[_key2];
}
- } // Directly modify the value of a path array, this is done this way for performance
-
-
- pathArray.value = array;
- return pathArray;
- },
- // Absolutize and parse path to array
- parse: function parse() {
- var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [['M', 0, 0]];
- // if it's already a patharray, no need to parse it
- if (array instanceof PathArray) return array; // prepare for parsing
- var s;
- var paramCnt = {
- 'M': 2,
- 'L': 2,
- 'H': 1,
- 'V': 1,
- 'C': 6,
- 'S': 4,
- 'Q': 4,
- 'T': 2,
- 'A': 7,
- 'Z': 0
+ return this.each.apply(this, [name].concat(attrs));
};
- if (typeof array === 'string') {
- array = array.replace(numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123
- .replace(pathLetters, ' $& ') // put some room between letters and numbers
- .replace(hyphen, '$1 -') // add space before hyphen
- .trim() // trim
- .split(delimiter); // split into array
- } else {
- array = array.reduce(function (prev, curr) {
- return [].concat.call(prev, curr);
- }, []);
- } // array now is an array containing all parts of a path e.g. ['M', '0', '0', 'L', '30', '30' ...]
-
-
- var result = [];
- var p = new Point();
- var p0 = new Point();
- var index = 0;
- var len = array.length;
-
- do {
- // Test if we have a path letter
- if (isPathLetter.test(array[index])) {
- s = array[index];
- ++index; // If last letter was a move command and we got no new, it defaults to [L]ine
- } else if (s === 'M') {
- s = 'L';
- } else if (s === 'm') {
- s = 'l';
- }
+ return obj;
+ }, {});
+ extend(List, methods);
+ };
- result.push(pathHandlers[s].call(null, array.slice(index, index = index + paramCnt[s.toUpperCase()]).map(parseFloat), p, p0));
- } while (len > index);
+ var Marker =
+ /*#__PURE__*/
+ function (_Container) {
+ _inherits(Marker, _Container);
- return result;
- },
- // Get bounding box of path
- bbox: function bbox() {
- parser().path.setAttribute('d', this.toString());
- return parser.nodes.path.getBBox();
- }
- });
+ // Initialize node
+ function Marker(node) {
+ _classCallCheck(this, Marker);
- var Morphable =
- /*#__PURE__*/
- function () {
- function Morphable(stepper) {
- _classCallCheck(this, Morphable);
+ return _possibleConstructorReturn(this, _getPrototypeOf(Marker).call(this, nodeOrNew('marker', node), node));
+ } // Set width of element
- this._stepper = stepper || new Ease('-');
- this._from = null;
- this._to = null;
- this._type = null;
- this._context = null;
- this._morphObj = null;
- }
- _createClass(Morphable, [{
- key: "from",
- value: function from(val) {
- if (val == null) {
- return this._from;
- }
+ _createClass(Marker, [{
+ key: "width",
+ value: function width(_width) {
+ return this.attr('markerWidth', _width);
+ } // Set height of element
- this._from = this._set(val);
- return this;
- }
}, {
- key: "to",
- value: function to(val) {
- if (val == null) {
- return this._to;
- }
+ key: "height",
+ value: function height(_height) {
+ return this.attr('markerHeight', _height);
+ } // Set marker refX and refY
- this._to = this._set(val);
- return this;
- }
}, {
- key: "type",
- value: function type(_type) {
- // getter
- if (_type == null) {
- return this._type;
- } // setter
-
+ key: "ref",
+ value: function ref(x, y) {
+ return this.attr('refX', x).attr('refY', y);
+ } // Update marker
- this._type = _type;
- return this;
- }
}, {
- key: "_set",
- value: function _set$$1(value) {
- if (!this._type) {
- var type = _typeof(value);
+ key: "update",
+ value: function update(block) {
+ // remove all content
+ this.clear(); // invoke passed block
- if (type === 'number') {
- this.type(SVGNumber);
- } else if (type === 'string') {
- if (Color.isColor(value)) {
- this.type(Color);
- } else if (delimiter.test(value)) {
- this.type(pathLetters.test(value) ? PathArray : SVGArray);
- } else if (numberAndUnit.test(value)) {
- this.type(SVGNumber);
- } else {
- this.type(NonMorphable);
- }
- } else if (morphableTypes.indexOf(value.constructor) > -1) {
- this.type(value.constructor);
- } else if (Array.isArray(value)) {
- this.type(SVGArray);
- } else if (type === 'object') {
- this.type(ObjectBag);
- } else {
- this.type(NonMorphable);
- }
+ if (typeof block === 'function') {
+ block.call(this, this);
}
- var result = new this._type(value).toArray();
- this._morphObj = this._morphObj || new this._type();
- this._context = this._context || Array.apply(null, Array(result.length)).map(Object);
- return result;
- }
- }, {
- key: "stepper",
- value: function stepper(_stepper) {
- if (_stepper == null) return this._stepper;
- this._stepper = _stepper;
return this;
- }
- }, {
- key: "done",
- value: function done() {
- var complete = this._context.map(this._stepper.done).reduce(function (last, curr) {
- return last && curr;
- }, true);
+ } // Return the fill id
- return complete;
- }
}, {
- key: "at",
- value: function at(pos) {
- var _this = this;
-
- return this._morphObj.fromArray(this._from.map(function (i, index) {
- return _this._stepper.step(i, _this._to[index], pos, _this._context[index], _this._context);
- }));
+ key: "toString",
+ value: function toString() {
+ return 'url(#' + this.id() + ')';
}
}]);
- return Morphable;
- }();
- var NonMorphable =
- /*#__PURE__*/
- function () {
- function NonMorphable() {
- _classCallCheck(this, NonMorphable);
-
- this.init.apply(this, arguments);
- }
+ return Marker;
+ }(Container);
+ registerMethods({
+ Container: {
+ marker: function marker() {
+ var _this$defs;
- _createClass(NonMorphable, [{
- key: "init",
- value: function init(val) {
- val = Array.isArray(val) ? val[0] : val;
- this.value = val;
- return this;
- }
- }, {
- key: "valueOf",
- value: function valueOf() {
- return this.value;
- }
- }, {
- key: "toArray",
- value: function toArray() {
- return [this.value];
+ // Create marker element in defs
+ return (_this$defs = this.defs()).marker.apply(_this$defs, arguments);
}
- }]);
+ },
+ Defs: {
+ // Create marker
+ marker: wrapWithAttrCheck(function (width, height, block) {
+ // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto
+ return this.put(new Marker()).size(width, height).ref(width / 2, height / 2).viewbox(0, 0, width, height).attr('orient', 'auto').update(block);
+ })
+ },
+ marker: {
+ // Create and attach markers
+ marker: function marker(_marker, width, height, block) {
+ var attr = ['marker']; // Build attribute name
- return NonMorphable;
- }();
- var TransformBag =
- /*#__PURE__*/
- function () {
- function TransformBag() {
- _classCallCheck(this, TransformBag);
+ if (_marker !== 'all') attr.push(_marker);
+ attr = attr.join('-'); // Set marker attribute
- this.init.apply(this, arguments);
+ _marker = arguments[1] instanceof Marker ? arguments[1] : this.defs().marker(width, height, block);
+ return this.attr(attr, _marker);
+ }
}
+ });
+ register(Marker);
- _createClass(TransformBag, [{
- key: "init",
- value: function init(obj) {
- if (Array.isArray(obj)) {
- obj = {
- scaleX: obj[0],
- scaleY: obj[1],
- shear: obj[2],
- rotate: obj[3],
- translateX: obj[4],
- translateY: obj[5],
- originX: obj[6],
- originY: obj[7]
- };
+ /***
+ Base Class
+ ==========
+ The base stepper class that will be
+ ***/
+
+ function makeSetterGetter(k, f) {
+ return function (v) {
+ if (v == null) return this[v];
+ this[k] = v;
+ if (f) f.call(this);
+ return this;
+ };
+ }
+
+ var easing = {
+ '-': function _(pos) {
+ return pos;
+ },
+ '<>': function _(pos) {
+ return -Math.cos(pos * Math.PI) / 2 + 0.5;
+ },
+ '>': function _(pos) {
+ return Math.sin(pos * Math.PI / 2);
+ },
+ '<': function _(pos) {
+ return -Math.cos(pos * Math.PI / 2) + 1;
+ },
+ bezier: function bezier(x1, y1, x2, y2) {
+ // see https://www.w3.org/TR/css-easing-1/#cubic-bezier-algo
+ return function (t) {
+ if (t < 0) {
+ if (x1 > 0) {
+ return y1 / x1 * t;
+ } else if (x2 > 0) {
+ return y2 / x2 * t;
+ } else {
+ return 0;
+ }
+ } else if (t > 1) {
+ if (x2 < 1) {
+ return (1 - y2) / (1 - x2) * t + (y2 - x2) / (1 - x2);
+ } else if (x1 < 1) {
+ return (1 - y1) / (1 - x1) * t + (y1 - x1) / (1 - x1);
+ } else {
+ return 1;
+ }
+ } else {
+ return 3 * t * Math.pow(1 - t, 2) * y1 + 3 * Math.pow(t, 2) * (1 - t) * y2 + Math.pow(t, 3);
}
+ };
+ },
+ // https://www.w3.org/TR/css-easing-1/#step-timing-function-algo
+ steps: function steps(_steps) {
+ var stepPosition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'end';
+ // deal with "jump-" prefix
+ stepPosition = stepPosition.split('-').reverse()[0];
+ var jumps = _steps;
- Object.assign(this, TransformBag.defaults, obj);
- return this;
- }
- }, {
- key: "toArray",
- value: function toArray() {
- var v = this;
- return [v.scaleX, v.scaleY, v.shear, v.rotate, v.translateX, v.translateY, v.originX, v.originY];
- }
- }]);
+ if (stepPosition === 'none') {
+ --jumps;
+ } else if (stepPosition === 'both') {
+ ++jumps;
+ } // The beforeFlag is essentially useless
- return TransformBag;
- }();
- TransformBag.defaults = {
- scaleX: 1,
- scaleY: 1,
- shear: 0,
- rotate: 0,
- translateX: 0,
- translateY: 0,
- originX: 0,
- originY: 0
- };
- var ObjectBag =
- /*#__PURE__*/
- function () {
- function ObjectBag() {
- _classCallCheck(this, ObjectBag);
- this.init.apply(this, arguments);
- }
+ return function (t) {
+ var beforeFlag = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
+ // Step is called currentStep in referenced url
+ var step = Math.floor(t * _steps);
+ var jumping = t * step % 1 === 0;
- _createClass(ObjectBag, [{
- key: "init",
- value: function init(objOrArr) {
- this.values = [];
+ if (stepPosition === 'start' || stepPosition === 'both') {
+ ++step;
+ }
- if (Array.isArray(objOrArr)) {
- this.values = objOrArr;
- return;
+ if (beforeFlag && jumping) {
+ --step;
}
- var entries = Object.entries(objOrArr || {}).sort(function (a, b) {
- return a[0] - b[0];
- });
- this.values = entries.reduce(function (last, curr) {
- return last.concat(curr);
- }, []);
- return this;
- }
- }, {
- key: "valueOf",
- value: function valueOf() {
- var obj = {};
- var arr = this.values;
+ if (t >= 0 && step < 0) {
+ step = 0;
+ }
- for (var i = 0, len = arr.length; i < len; i += 2) {
- obj[arr[i]] = arr[i + 1];
+ if (t <= 1 && step > jumps) {
+ step = jumps;
}
- return obj;
- }
- }, {
- key: "toArray",
- value: function toArray() {
- return this.values;
+ return step / jumps;
+ };
+ }
+ };
+ var Stepper =
+ /*#__PURE__*/
+ function () {
+ function Stepper() {
+ _classCallCheck(this, Stepper);
+ }
+
+ _createClass(Stepper, [{
+ key: "done",
+ value: function done() {
+ return false;
}
}]);
- return ObjectBag;
+ return Stepper;
}();
- var morphableTypes = [NonMorphable, TransformBag, ObjectBag];
- function registerMorphableType() {
- var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
- morphableTypes.push.apply(morphableTypes, _toConsumableArray([].concat(type)));
- }
- function makeMorphable() {
- extend(morphableTypes, {
- to: function to(val) {
- return new Morphable().type(this.constructor).from(this.valueOf()).to(val);
- },
- fromArray: function fromArray(arr) {
- this.init(arr);
- return this;
- }
- });
- }
+ /***
+ Easing Functions
+ ================
+ ***/
- var time = globals.window.performance || Date;
+ var Ease =
+ /*#__PURE__*/
+ function (_Stepper) {
+ _inherits(Ease, _Stepper);
- var makeSchedule = function makeSchedule(runnerInfo) {
- var start = runnerInfo.start;
- var duration = runnerInfo.runner.duration();
- var end = start + duration;
- return {
- start: start,
- duration: duration,
- end: end,
- runner: runnerInfo.runner
- };
- };
+ function Ease(fn) {
+ var _this;
- var Timeline =
- /*#__PURE__*/
- function () {
- // Construct a new timeline on the given element
- function Timeline() {
- _classCallCheck(this, Timeline);
+ _classCallCheck(this, Ease);
- this._timeSource = function () {
- return time.now();
- };
+ _this = _possibleConstructorReturn(this, _getPrototypeOf(Ease).call(this));
+ _this.ease = easing[fn || timeline.ease] || fn;
+ return _this;
+ }
- this._dispatcher = globals.document.createElement('div'); // Store the timing variables
+ _createClass(Ease, [{
+ key: "step",
+ value: function step(from, to, pos) {
+ if (typeof from !== 'number') {
+ return pos < 1 ? from : to;
+ }
- this._startTime = 0;
- this._speed = 1.0; // Play control variables control how the animation proceeds
+ return from + (to - from) * this.ease(pos);
+ }
+ }]);
- this._reverse = false;
- this._persist = 0; // Keep track of the running animations and their starting parameters
+ return Ease;
+ }(Stepper);
+ /***
+ Controller Types
+ ================
+ ***/
- this._nextFrame = null;
- this._paused = false;
- this._runners = [];
- this._order = [];
- this._time = 0;
- this._lastSourceTime = 0;
- this._lastStepTime = 0;
+ var Controller =
+ /*#__PURE__*/
+ function (_Stepper2) {
+ _inherits(Controller, _Stepper2);
+
+ function Controller(fn) {
+ var _this2;
+
+ _classCallCheck(this, Controller);
+
+ _this2 = _possibleConstructorReturn(this, _getPrototypeOf(Controller).call(this));
+ _this2.stepper = fn;
+ return _this2;
}
- _createClass(Timeline, [{
- key: "getEventTarget",
- value: function getEventTarget() {
- return this._dispatcher;
+ _createClass(Controller, [{
+ key: "step",
+ value: function step(current, target, dt, c) {
+ return this.stepper(current, target, dt, c);
}
- /**
- *
- */
- // schedules a runner on the timeline
-
}, {
- key: "schedule",
- value: function schedule(runner, delay, when) {
- if (runner == null) {
- return this._runners.map(makeSchedule).sort(function (a, b) {
- return a.start - b.start || a.duration - b.duration;
- });
- }
+ key: "done",
+ value: function done(c) {
+ return c.done;
+ }
+ }]);
- if (!this.active()) {
- this._step();
+ return Controller;
+ }(Stepper);
- if (when == null) {
- when = 'now';
- }
- } // The start time for the next animation can either be given explicitly,
- // derived from the current timeline time or it can be relative to the
- // last start time to chain animations direclty
+ function recalculate() {
+ // Apply the default parameters
+ var duration = (this._duration || 500) / 1000;
+ var overshoot = this._overshoot || 0; // Calculate the PID natural response
+ var eps = 1e-10;
+ var pi = Math.PI;
+ var os = Math.log(overshoot / 100 + eps);
+ var zeta = -os / Math.sqrt(pi * pi + os * os);
+ var wn = 3.9 / (zeta * duration); // Calculate the Spring values
- var absoluteStartTime = 0;
- delay = delay || 0; // Work out when to start the animation
+ this.d = 2 * zeta * wn;
+ this.k = wn * wn;
+ }
- if (when == null || when === 'last' || when === 'after') {
- // Take the last time and increment
- absoluteStartTime = this._startTime;
- } else if (when === 'absolute' || when === 'start') {
- absoluteStartTime = delay;
- delay = 0;
- } else if (when === 'now') {
- absoluteStartTime = this._time;
- } else if (when === 'relative') {
- var runnerInfo = this._runners[runner.id];
+ var Spring =
+ /*#__PURE__*/
+ function (_Controller) {
+ _inherits(Spring, _Controller);
- if (runnerInfo) {
- absoluteStartTime = runnerInfo.start + delay;
- delay = 0;
- }
- } else {
- throw new Error('Invalid value for the "when" parameter');
- } // Manage runner
+ function Spring(duration, overshoot) {
+ var _this3;
+ _classCallCheck(this, Spring);
- runner.unschedule();
- runner.timeline(this);
- runner.time(-delay); // Save startTime for next runner
+ _this3 = _possibleConstructorReturn(this, _getPrototypeOf(Spring).call(this));
- this._startTime = absoluteStartTime + runner.duration() + delay; // Save runnerInfo
+ _this3.duration(duration || 500).overshoot(overshoot || 0);
- this._runners[runner.id] = {
- persist: this.persist(),
- runner: runner,
- start: absoluteStartTime // Save order and continue
+ return _this3;
+ }
- };
+ _createClass(Spring, [{
+ key: "step",
+ value: function step(current, target, dt, c) {
+ if (typeof current === 'string') return current;
+ c.done = dt === Infinity;
+ if (dt === Infinity) return target;
+ if (dt === 0) return current;
+ if (dt > 100) dt = 16;
+ dt /= 1000; // Get the previous velocity
- this._order.push(runner.id);
+ var velocity = c.velocity || 0; // Apply the control to get the new position and store it
- this._continue();
+ var acceleration = -this.d * velocity - this.k * (current - target);
+ var newPosition = current + velocity * dt + acceleration * dt * dt / 2; // Store the velocity
- return this;
- } // Remove the runner from this timeline
+ c.velocity = velocity + acceleration * dt; // Figure out if we have converged, and if so, pass the value
- }, {
- key: "unschedule",
- value: function unschedule(runner) {
- var index = this._order.indexOf(runner.id);
+ c.done = Math.abs(target - newPosition) + Math.abs(velocity) < 0.002;
+ return c.done ? target : newPosition;
+ }
+ }]);
- if (index < 0) return this;
- delete this._runners[runner.id];
+ return Spring;
+ }(Controller);
+ extend(Spring, {
+ duration: makeSetterGetter('_duration', recalculate),
+ overshoot: makeSetterGetter('_overshoot', recalculate)
+ });
+ var PID =
+ /*#__PURE__*/
+ function (_Controller2) {
+ _inherits(PID, _Controller2);
- this._order.splice(index, 1);
+ function PID(p, i, d, windup) {
+ var _this4;
- runner.timeline(null);
- return this;
- }
- }, {
- key: "play",
- value: function play() {
- // Now make sure we are not paused and continue the animation
- this._paused = false;
- return this._continue();
- }
- }, {
- key: "pause",
- value: function pause() {
- // Cancel the next animation frame and pause
- this._nextFrame = null;
- this._paused = true;
- return this;
- }
- }, {
- key: "stop",
- value: function stop() {
- // Cancel the next animation frame and go to start
- this.seek(-this._time);
- return this.pause();
- }
- }, {
- key: "finish",
- value: function finish() {
- this.seek(Infinity);
- return this.pause();
+ _classCallCheck(this, PID);
+
+ _this4 = _possibleConstructorReturn(this, _getPrototypeOf(PID).call(this));
+ p = p == null ? 0.1 : p;
+ i = i == null ? 0.01 : i;
+ d = d == null ? 0 : d;
+ windup = windup == null ? 1000 : windup;
+
+ _this4.p(p).i(i).d(d).windup(windup);
+
+ return _this4;
+ }
+
+ _createClass(PID, [{
+ key: "step",
+ value: function step(current, target, dt, c) {
+ if (typeof current === 'string') return current;
+ c.done = dt === Infinity;
+ if (dt === Infinity) return target;
+ if (dt === 0) return current;
+ var p = target - current;
+ var i = (c.integral || 0) + p * dt;
+ var d = (p - (c.error || 0)) / dt;
+ var windup = this.windup; // antiwindup
+
+ if (windup !== false) {
+ i = Math.max(-windup, Math.min(i, windup));
+ }
+
+ c.error = p;
+ c.integral = i;
+ c.done = Math.abs(p) < 0.001;
+ return c.done ? target : current + (this.P * p + this.I * i + this.D * d);
}
- }, {
- key: "speed",
- value: function speed(_speed) {
- if (_speed == null) return this._speed;
- this._speed = _speed;
- return this;
- }
- }, {
- key: "reverse",
- value: function reverse(yes) {
- var currentSpeed = this.speed();
- if (yes == null) return this.speed(-currentSpeed);
- var positive = Math.abs(currentSpeed);
- return this.speed(yes ? positive : -positive);
- }
- }, {
- key: "seek",
- value: function seek(dt) {
- this._time += dt;
- return this._continue();
- }
- }, {
- key: "time",
- value: function time(_time) {
- if (_time == null) return this._time;
- this._time = _time;
- return this;
- }
- }, {
- key: "persist",
- value: function persist(dtOrForever) {
- if (dtOrForever == null) return this._persist;
- this._persist = dtOrForever;
- return this;
- }
- }, {
- key: "source",
- value: function source(fn) {
- if (fn == null) return this._timeSource;
- this._timeSource = fn;
- return this;
- }
- }, {
- key: "_step",
- value: function _step() {
- // If the timeline is paused, just do nothing
- if (this._paused) return; // Get the time delta from the last time and update the time
-
- var time = this._timeSource();
-
- var dtSource = time - this._lastSourceTime;
- var dtTime = this._speed * dtSource + (this._time - this._lastStepTime);
- this._lastSourceTime = time; // Update the time
-
- this._time += dtTime;
- this._lastStepTime = this._time; // this.fire('time', this._time)
- // Run all of the runners directly
+ }]);
- var runnersLeft = false;
+ return PID;
+ }(Controller);
+ extend(PID, {
+ windup: makeSetterGetter('windup'),
+ p: makeSetterGetter('P'),
+ i: makeSetterGetter('I'),
+ d: makeSetterGetter('D')
+ });
- for (var i = 0, len = this._order.length; i < len; i++) {
- // Get and run the current runner and ignore it if its inactive
- var runnerInfo = this._runners[this._order[i]];
- var runner = runnerInfo.runner;
- var dt = dtTime; // Make sure that we give the actual difference
- // between runner start time and now
+ var PathArray = subClassArray('PathArray', SVGArray);
+ function pathRegReplace(a, b, c, d) {
+ return c + d.replace(dots, ' .');
+ }
- var dtToStart = this._time - runnerInfo.start; // Dont run runner if not started yet
+ function arrayToString(a) {
+ for (var i = 0, il = a.length, s = ''; i < il; i++) {
+ s += a[i][0];
- if (dtToStart < 0) {
- runnersLeft = true;
- continue;
- } else if (dtToStart < dt) {
- // Adjust dt to make sure that animation is on point
- dt = dtToStart;
- }
+ if (a[i][1] != null) {
+ s += a[i][1];
- if (!runner.active()) continue; // If this runner is still going, signal that we need another animation
- // frame, otherwise, remove the completed runner
+ if (a[i][2] != null) {
+ s += ' ';
+ s += a[i][2];
- var finished = runner.step(dt).done;
+ if (a[i][3] != null) {
+ s += ' ';
+ s += a[i][3];
+ s += ' ';
+ s += a[i][4];
- if (!finished) {
- runnersLeft = true; // continue
- } else if (runnerInfo.persist !== true) {
- // runner is finished. And runner might get removed
- var endTime = runner.duration() - runner.time() + this._time;
+ if (a[i][5] != null) {
+ s += ' ';
+ s += a[i][5];
+ s += ' ';
+ s += a[i][6];
- if (endTime + this._persist < this._time) {
- // Delete runner and correct index
- delete this._runners[this._order[i]];
- this._order.splice(i--, 1) && --len;
- runner.timeline(null);
+ if (a[i][7] != null) {
+ s += ' ';
+ s += a[i][7];
+ }
}
}
- } // Get the next animation frame to keep the simulation going
+ }
+ }
+ }
+ return s + ' ';
+ }
- if (runnersLeft) {
- this._nextFrame = Animator.frame(this._step.bind(this));
+ var pathHandlers = {
+ M: function M(c, p, p0) {
+ p.x = p0.x = c[0];
+ p.y = p0.y = c[1];
+ return ['M', p.x, p.y];
+ },
+ L: function L(c, p) {
+ p.x = c[0];
+ p.y = c[1];
+ return ['L', c[0], c[1]];
+ },
+ H: function H(c, p) {
+ p.x = c[0];
+ return ['H', c[0]];
+ },
+ V: function V(c, p) {
+ p.y = c[0];
+ return ['V', c[0]];
+ },
+ C: function C(c, p) {
+ p.x = c[4];
+ p.y = c[5];
+ return ['C', c[0], c[1], c[2], c[3], c[4], c[5]];
+ },
+ S: function S(c, p) {
+ p.x = c[2];
+ p.y = c[3];
+ return ['S', c[0], c[1], c[2], c[3]];
+ },
+ Q: function Q(c, p) {
+ p.x = c[2];
+ p.y = c[3];
+ return ['Q', c[0], c[1], c[2], c[3]];
+ },
+ T: function T(c, p) {
+ p.x = c[0];
+ p.y = c[1];
+ return ['T', c[0], c[1]];
+ },
+ Z: function Z(c, p, p0) {
+ p.x = p0.x;
+ p.y = p0.y;
+ return ['Z'];
+ },
+ A: function A(c, p) {
+ p.x = c[5];
+ p.y = c[6];
+ return ['A', c[0], c[1], c[2], c[3], c[4], c[5], c[6]];
+ }
+ };
+ var mlhvqtcsaz = 'mlhvqtcsaz'.split('');
+
+ for (var i = 0, il = mlhvqtcsaz.length; i < il; ++i) {
+ pathHandlers[mlhvqtcsaz[i]] = function (i) {
+ return function (c, p, p0) {
+ if (i === 'H') c[0] = c[0] + p.x;else if (i === 'V') c[0] = c[0] + p.y;else if (i === 'A') {
+ c[5] = c[5] + p.x;
+ c[6] = c[6] + p.y;
} else {
- this._nextFrame = null;
+ for (var j = 0, jl = c.length; j < jl; ++j) {
+ c[j] = c[j] + (j % 2 ? p.y : p.x);
+ }
}
+ return pathHandlers[i](c, p, p0);
+ };
+ }(mlhvqtcsaz[i].toUpperCase());
+ }
- return this;
- } // Checks if we are running and continues the animation
+ extend(PathArray, {
+ // Convert array to string
+ toString: function toString() {
+ return arrayToString(this);
+ },
+ // Move path string
+ move: function move(x, y) {
+ // get bounding box of current situation
+ var box = this.bbox(); // get relative offset
- }, {
- key: "_continue",
- value: function _continue() {
- if (this._paused) return this;
+ x -= box.x;
+ y -= box.y;
- if (!this._nextFrame) {
- this._nextFrame = Animator.frame(this._step.bind(this));
- }
+ if (!isNaN(x) && !isNaN(y)) {
+ // move every point
+ for (var l, i = this.length - 1; i >= 0; i--) {
+ l = this[i][0];
- return this;
- }
- }, {
- key: "active",
- value: function active() {
- return !!this._nextFrame;
- }
- }]);
+ if (l === 'M' || l === 'L' || l === 'T') {
+ this[i][1] += x;
+ this[i][2] += y;
+ } else if (l === 'H') {
+ this[i][1] += x;
+ } else if (l === 'V') {
+ this[i][1] += y;
+ } else if (l === 'C' || l === 'S' || l === 'Q') {
+ this[i][1] += x;
+ this[i][2] += y;
+ this[i][3] += x;
+ this[i][4] += y;
- return Timeline;
- }();
- registerMethods({
- Element: {
- timeline: function timeline() {
- this._timeline = this._timeline || new Timeline();
- return this._timeline;
+ if (l === 'C') {
+ this[i][5] += x;
+ this[i][6] += y;
+ }
+ } else if (l === 'A') {
+ this[i][6] += x;
+ this[i][7] += y;
+ }
+ }
}
- }
- });
-
- var Runner =
- /*#__PURE__*/
- function (_EventTarget) {
- _inherits(Runner, _EventTarget);
- function Runner(options) {
- var _this;
+ return this;
+ },
+ // Resize path string
+ size: function size(width, height) {
+ // get bounding box of current situation
+ var box = this.bbox();
+ var i, l; // recalculate position of all points according to new size
- _classCallCheck(this, Runner);
+ for (i = this.length - 1; i >= 0; i--) {
+ l = this[i][0];
- _this = _possibleConstructorReturn(this, _getPrototypeOf(Runner).call(this)); // Store a unique id on the runner, so that we can identify it later
+ if (l === 'M' || l === 'L' || l === 'T') {
+ this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
+ this[i][2] = (this[i][2] - box.y) * height / box.height + box.y;
+ } else if (l === 'H') {
+ this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
+ } else if (l === 'V') {
+ this[i][1] = (this[i][1] - box.y) * height / box.height + box.y;
+ } else if (l === 'C' || l === 'S' || l === 'Q') {
+ this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
+ this[i][2] = (this[i][2] - box.y) * height / box.height + box.y;
+ this[i][3] = (this[i][3] - box.x) * width / box.width + box.x;
+ this[i][4] = (this[i][4] - box.y) * height / box.height + box.y;
- _this.id = Runner.id++; // Ensure a default value
+ if (l === 'C') {
+ this[i][5] = (this[i][5] - box.x) * width / box.width + box.x;
+ this[i][6] = (this[i][6] - box.y) * height / box.height + box.y;
+ }
+ } else if (l === 'A') {
+ // resize radii
+ this[i][1] = this[i][1] * width / box.width;
+ this[i][2] = this[i][2] * height / box.height; // move position values
- options = options == null ? timeline.duration : options; // Ensure that we get a controller
+ this[i][6] = (this[i][6] - box.x) * width / box.width + box.x;
+ this[i][7] = (this[i][7] - box.y) * height / box.height + box.y;
+ }
+ }
- options = typeof options === 'function' ? new Controller(options) : options; // Declare all of the variables
+ return this;
+ },
+ // Test if the passed path array use the same path data commands as this path array
+ equalCommands: function equalCommands(pathArray) {
+ var i, il, equalCommands;
+ pathArray = new PathArray(pathArray);
+ equalCommands = this.length === pathArray.length;
- _this._element = null;
- _this._timeline = null;
- _this.done = false;
- _this._queue = []; // Work out the stepper and the duration
+ for (i = 0, il = this.length; equalCommands && i < il; i++) {
+ equalCommands = this[i][0] === pathArray[i][0];
+ }
- _this._duration = typeof options === 'number' && options;
- _this._isDeclarative = options instanceof Controller;
- _this._stepper = _this._isDeclarative ? options : new Ease(); // We copy the current values from the timeline because they can change
+ return equalCommands;
+ },
+ // Make path array morphable
+ morph: function morph(pathArray) {
+ pathArray = new PathArray(pathArray);
- _this._history = {}; // Store the state of the runner
+ if (this.equalCommands(pathArray)) {
+ this.destination = pathArray;
+ } else {
+ this.destination = null;
+ }
- _this.enabled = true;
- _this._time = 0;
- _this._last = 0; // Save transforms applied to this runner
+ return this;
+ },
+ // Get morphed path array at given position
+ at: function at(pos) {
+ // make sure a destination is defined
+ if (!this.destination) return this;
+ var sourceArray = this;
+ var destinationArray = this.destination.value;
+ var array = [];
+ var pathArray = new PathArray();
+ var i, il, j, jl; // Animate has specified in the SVG spec
+ // See: https://www.w3.org/TR/SVG11/paths.html#PathElement
- _this.transforms = new Matrix();
- _this.transformId = 1; // Looping variables
+ for (i = 0, il = sourceArray.length; i < il; i++) {
+ array[i] = [sourceArray[i][0]];
- _this._haveReversed = false;
- _this._reverse = false;
- _this._loopsDone = 0;
- _this._swing = false;
- _this._wait = 0;
- _this._times = 1;
- return _this;
+ for (j = 1, jl = sourceArray[i].length; j < jl; j++) {
+ array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos;
+ } // For the two flags of the elliptical arc command, the SVG spec say:
+ // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true
+ // Elliptical arc command as an array followed by corresponding indexes:
+ // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
+ // 0 1 2 3 4 5 6 7
+
+
+ if (array[i][0] === 'A') {
+ array[i][4] = +(array[i][4] !== 0);
+ array[i][5] = +(array[i][5] !== 0);
+ }
+ } // Directly modify the value of a path array, this is done this way for performance
+
+
+ pathArray.value = array;
+ return pathArray;
+ },
+ // Absolutize and parse path to array
+ parse: function parse() {
+ var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [['M', 0, 0]];
+ // if it's already a patharray, no need to parse it
+ if (array instanceof PathArray) return array; // prepare for parsing
+
+ var s;
+ var paramCnt = {
+ 'M': 2,
+ 'L': 2,
+ 'H': 1,
+ 'V': 1,
+ 'C': 6,
+ 'S': 4,
+ 'Q': 4,
+ 'T': 2,
+ 'A': 7,
+ 'Z': 0
+ };
+
+ if (typeof array === 'string') {
+ array = array.replace(numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123
+ .replace(pathLetters, ' $& ') // put some room between letters and numbers
+ .replace(hyphen, '$1 -') // add space before hyphen
+ .trim() // trim
+ .split(delimiter); // split into array
+ } else {
+ array = array.reduce(function (prev, curr) {
+ return [].concat.call(prev, curr);
+ }, []);
+ } // array now is an array containing all parts of a path e.g. ['M', '0', '0', 'L', '30', '30' ...]
+
+
+ var result = [];
+ var p = new Point();
+ var p0 = new Point();
+ var index = 0;
+ var len = array.length;
+
+ do {
+ // Test if we have a path letter
+ if (isPathLetter.test(array[index])) {
+ s = array[index];
+ ++index; // If last letter was a move command and we got no new, it defaults to [L]ine
+ } else if (s === 'M') {
+ s = 'L';
+ } else if (s === 'm') {
+ s = 'l';
+ }
+
+ result.push(pathHandlers[s].call(null, array.slice(index, index = index + paramCnt[s.toUpperCase()]).map(parseFloat), p, p0));
+ } while (len > index);
+
+ return result;
+ },
+ // Get bounding box of path
+ bbox: function bbox() {
+ parser().path.setAttribute('d', this.toString());
+ return parser.nodes.path.getBBox();
}
- /*
- Runner Definitions
- ==================
- These methods help us define the runtime behaviour of the Runner or they
- help us make new runners from the current runner
- */
+ });
+ var Morphable =
+ /*#__PURE__*/
+ function () {
+ function Morphable(stepper) {
+ _classCallCheck(this, Morphable);
- _createClass(Runner, [{
- key: "element",
- value: function element(_element) {
- if (_element == null) return this._element;
- this._element = _element;
+ this._stepper = stepper || new Ease('-');
+ this._from = null;
+ this._to = null;
+ this._type = null;
+ this._context = null;
+ this._morphObj = null;
+ }
- _element._prepareRunner();
+ _createClass(Morphable, [{
+ key: "from",
+ value: function from(val) {
+ if (val == null) {
+ return this._from;
+ }
+ this._from = this._set(val);
return this;
}
}, {
- key: "timeline",
- value: function timeline$$1(_timeline) {
- // check explicitly for undefined so we can set the timeline to null
- if (typeof _timeline === 'undefined') return this._timeline;
- this._timeline = _timeline;
+ key: "to",
+ value: function to(val) {
+ if (val == null) {
+ return this._to;
+ }
+
+ this._to = this._set(val);
return this;
}
}, {
- key: "animate",
- value: function animate(duration, delay, when) {
- var o = Runner.sanitise(duration, delay, when);
- var runner = new Runner(o.duration);
- if (this._timeline) runner.timeline(this._timeline);
- if (this._element) runner.element(this._element);
- return runner.loop(o).schedule(delay, when);
- }
- }, {
- key: "schedule",
- value: function schedule(timeline$$1, delay, when) {
- // The user doesn't need to pass a timeline if we already have one
- if (!(timeline$$1 instanceof Timeline)) {
- when = delay;
- delay = timeline$$1;
- timeline$$1 = this.timeline();
- } // If there is no timeline, yell at the user...
-
-
- if (!timeline$$1) {
- throw Error('Runner cannot be scheduled without timeline');
- } // Schedule the runner on the timeline provided
+ key: "type",
+ value: function type(_type) {
+ // getter
+ if (_type == null) {
+ return this._type;
+ } // setter
- timeline$$1.schedule(this, delay, when);
- return this;
- }
- }, {
- key: "unschedule",
- value: function unschedule() {
- var timeline$$1 = this.timeline();
- timeline$$1 && timeline$$1.unschedule(this);
+ this._type = _type;
return this;
}
}, {
- key: "loop",
- value: function loop(times, swing, wait) {
- // Deal with the user passing in an object
- if (_typeof(times) === 'object') {
- swing = times.swing;
- wait = times.wait;
- times = times.times;
- } // Sanitise the values and store them
+ key: "_set",
+ value: function _set$$1(value) {
+ if (!this._type) {
+ var type = _typeof(value);
+ if (type === 'number') {
+ this.type(SVGNumber);
+ } else if (type === 'string') {
+ if (Color.isColor(value)) {
+ this.type(Color);
+ } else if (delimiter.test(value)) {
+ this.type(pathLetters.test(value) ? PathArray : SVGArray);
+ } else if (numberAndUnit.test(value)) {
+ this.type(SVGNumber);
+ } else {
+ this.type(NonMorphable);
+ }
+ } else if (morphableTypes.indexOf(value.constructor) > -1) {
+ this.type(value.constructor);
+ } else if (Array.isArray(value)) {
+ this.type(SVGArray);
+ } else if (type === 'object') {
+ this.type(ObjectBag);
+ } else {
+ this.type(NonMorphable);
+ }
+ }
- this._times = times || Infinity;
- this._swing = swing || false;
- this._wait = wait || 0;
- return this;
- }
- }, {
- key: "delay",
- value: function delay(_delay) {
- return this.animate(0, _delay);
+ var result = new this._type(value).toArray();
+ this._morphObj = this._morphObj || new this._type();
+ this._context = this._context || Array.apply(null, Array(result.length)).map(Object);
+ return result;
}
- /*
- Basic Functionality
- ===================
- These methods allow us to attach basic functions to the runner directly
- */
-
}, {
- key: "queue",
- value: function queue(initFn, runFn, isTransform) {
- this._queue.push({
- initialiser: initFn || noop,
- runner: runFn || noop,
- isTransform: isTransform,
- initialised: false,
- finished: false
- });
-
- var timeline$$1 = this.timeline();
- timeline$$1 && this.timeline()._continue();
+ key: "stepper",
+ value: function stepper(_stepper) {
+ if (_stepper == null) return this._stepper;
+ this._stepper = _stepper;
return this;
}
}, {
- key: "during",
- value: function during(fn) {
- return this.queue(null, fn);
+ key: "done",
+ value: function done() {
+ var complete = this._context.map(this._stepper.done).reduce(function (last, curr) {
+ return last && curr;
+ }, true);
+
+ return complete;
}
}, {
- key: "after",
- value: function after(fn) {
- return this.on('finish', fn);
+ key: "at",
+ value: function at(pos) {
+ var _this = this;
+
+ return this._morphObj.fromArray(this._from.map(function (i, index) {
+ return _this._stepper.step(i, _this._to[index], pos, _this._context[index], _this._context);
+ }));
}
- /*
- Runner animation methods
- ========================
- Control how the animation plays
- */
+ }]);
- }, {
- key: "time",
- value: function time(_time) {
- if (_time == null) {
- return this._time;
- }
+ return Morphable;
+ }();
+ var NonMorphable =
+ /*#__PURE__*/
+ function () {
+ function NonMorphable() {
+ _classCallCheck(this, NonMorphable);
- var dt = _time - this._time;
- this.step(dt);
+ this.init.apply(this, arguments);
+ }
+
+ _createClass(NonMorphable, [{
+ key: "init",
+ value: function init(val) {
+ val = Array.isArray(val) ? val[0] : val;
+ this.value = val;
return this;
}
}, {
- key: "duration",
- value: function duration() {
- return this._times * (this._wait + this._duration) - this._wait;
+ key: "valueOf",
+ value: function valueOf() {
+ return this.value;
}
}, {
- key: "loops",
- value: function loops(p) {
- var loopDuration = this._duration + this._wait;
+ key: "toArray",
+ value: function toArray() {
+ return [this.value];
+ }
+ }]);
- if (p == null) {
- var loopsDone = Math.floor(this._time / loopDuration);
- var relativeTime = this._time - loopsDone * loopDuration;
- var position = relativeTime / this._duration;
- return Math.min(loopsDone + position, this._times);
+ return NonMorphable;
+ }();
+ var TransformBag =
+ /*#__PURE__*/
+ function () {
+ function TransformBag() {
+ _classCallCheck(this, TransformBag);
+
+ this.init.apply(this, arguments);
+ }
+
+ _createClass(TransformBag, [{
+ key: "init",
+ value: function init(obj) {
+ if (Array.isArray(obj)) {
+ obj = {
+ scaleX: obj[0],
+ scaleY: obj[1],
+ shear: obj[2],
+ rotate: obj[3],
+ translateX: obj[4],
+ translateY: obj[5],
+ originX: obj[6],
+ originY: obj[7]
+ };
}
- var whole = Math.floor(p);
- var partial = p % 1;
- var time = loopDuration * whole + this._duration * partial;
- return this.time(time);
+ Object.assign(this, TransformBag.defaults, obj);
+ return this;
}
}, {
- key: "position",
- value: function position(p) {
- // Get all of the variables we need
- var x$$1 = this._time;
- var d = this._duration;
- var w = this._wait;
- var t = this._times;
- var s = this._swing;
- var r = this._reverse;
- var position;
+ key: "toArray",
+ value: function toArray() {
+ var v = this;
+ return [v.scaleX, v.scaleY, v.shear, v.rotate, v.translateX, v.translateY, v.originX, v.originY];
+ }
+ }]);
- if (p == null) {
- /*
- This function converts a time to a position in the range [0, 1]
- The full explanation can be found in this desmos demonstration
- https://www.desmos.com/calculator/u4fbavgche
- The logic is slightly simplified here because we can use booleans
- */
- // Figure out the value without thinking about the start or end time
- var f = function f(x$$1) {
- var swinging = s * Math.floor(x$$1 % (2 * (w + d)) / (w + d));
- var backwards = swinging && !r || !swinging && r;
- var uncliped = Math.pow(-1, backwards) * (x$$1 % (w + d)) / d + backwards;
- var clipped = Math.max(Math.min(uncliped, 1), 0);
- return clipped;
- }; // Figure out the value by incorporating the start time
+ return TransformBag;
+ }();
+ TransformBag.defaults = {
+ scaleX: 1,
+ scaleY: 1,
+ shear: 0,
+ rotate: 0,
+ translateX: 0,
+ translateY: 0,
+ originX: 0,
+ originY: 0
+ };
+ var ObjectBag =
+ /*#__PURE__*/
+ function () {
+ function ObjectBag() {
+ _classCallCheck(this, ObjectBag);
+ this.init.apply(this, arguments);
+ }
- var endTime = t * (w + d) - w;
- position = x$$1 <= 0 ? Math.round(f(1e-5)) : x$$1 < endTime ? f(x$$1) : Math.round(f(endTime - 1e-5));
- return position;
- } // Work out the loops done and add the position to the loops done
+ _createClass(ObjectBag, [{
+ key: "init",
+ value: function init(objOrArr) {
+ this.values = [];
+ if (Array.isArray(objOrArr)) {
+ this.values = objOrArr;
+ return;
+ }
- var loopsDone = Math.floor(this.loops());
- var swingForward = s && loopsDone % 2 === 0;
- var forwards = swingForward && !r || r && swingForward;
- position = loopsDone + (forwards ? p : 1 - p);
- return this.loops(position);
+ var entries = Object.entries(objOrArr || {}).sort(function (a, b) {
+ return a[0] - b[0];
+ });
+ this.values = entries.reduce(function (last, curr) {
+ return last.concat(curr);
+ }, []);
+ return this;
}
}, {
- key: "progress",
- value: function progress(p) {
- if (p == null) {
- return Math.min(1, this._time / this.duration());
+ key: "valueOf",
+ value: function valueOf() {
+ var obj = {};
+ var arr = this.values;
+
+ for (var i = 0, len = arr.length; i < len; i += 2) {
+ obj[arr[i]] = arr[i + 1];
}
- return this.time(p * this.duration());
+ return obj;
}
}, {
- key: "step",
- value: function step(dt) {
- // If we are inactive, this stepper just gets skipped
- if (!this.enabled) return this; // Update the time and get the new position
-
- dt = dt == null ? 16 : dt;
- this._time += dt;
- var position = this.position(); // Figure out if we need to run the stepper in this frame
-
- var running = this._lastPosition !== position && this._time >= 0;
- this._lastPosition = position; // Figure out if we just started
-
- var duration = this.duration();
- var justStarted = this._lastTime < 0 && this._time > 0;
- var justFinished = this._lastTime < this._time && this.time > duration;
- this._lastTime = this._time;
+ key: "toArray",
+ value: function toArray() {
+ return this.values;
+ }
+ }]);
- if (justStarted) {
- this.fire('start', this);
- } // Work out if the runner is finished set the done flag here so animations
- // know, that they are running in the last step (this is good for
- // transformations which can be merged)
+ return ObjectBag;
+ }();
+ var morphableTypes = [NonMorphable, TransformBag, ObjectBag];
+ function registerMorphableType() {
+ var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+ morphableTypes.push.apply(morphableTypes, _toConsumableArray([].concat(type)));
+ }
+ function makeMorphable() {
+ extend(morphableTypes, {
+ to: function to(val) {
+ return new Morphable().type(this.constructor).from(this.valueOf()).to(val);
+ },
+ fromArray: function fromArray(arr) {
+ this.init(arr);
+ return this;
+ }
+ });
+ }
+ var Path =
+ /*#__PURE__*/
+ function (_Shape) {
+ _inherits(Path, _Shape);
- var declarative = this._isDeclarative;
- this.done = !declarative && !justFinished && this._time >= duration; // Call initialise and the run function
+ // Initialize node
+ function Path(node) {
+ _classCallCheck(this, Path);
- if (running || declarative) {
- this._initialise(running); // clear the transforms on this runner so they dont get added again and again
+ return _possibleConstructorReturn(this, _getPrototypeOf(Path).call(this, nodeOrNew('path', node), node));
+ } // Get array
- this.transforms = new Matrix();
+ _createClass(Path, [{
+ key: "array",
+ value: function array() {
+ return this._array || (this._array = new PathArray(this.attr('d')));
+ } // Plot new path
- var converged = this._run(declarative ? dt : position);
+ }, {
+ key: "plot",
+ value: function plot(d) {
+ return d == null ? this.array() : this.clear().attr('d', typeof d === 'string' ? d : this._array = new PathArray(d));
+ } // Clear array cache
- this.fire('step', this);
- } // correct the done flag here
- // declaritive animations itself know when they converged
+ }, {
+ key: "clear",
+ value: function clear() {
+ delete this._array;
+ return this;
+ } // Move by left top corner
+ }, {
+ key: "move",
+ value: function move(x, y) {
+ return this.attr('d', this.array().move(x, y));
+ } // Move by left top corner over x-axis
- this.done = this.done || converged && declarative;
+ }, {
+ key: "x",
+ value: function x(_x) {
+ return _x == null ? this.bbox().x : this.move(_x, this.bbox().y);
+ } // Move by left top corner over y-axis
- if (this.done) {
- this.fire('finish', this);
- }
+ }, {
+ key: "y",
+ value: function y(_y) {
+ return _y == null ? this.bbox().y : this.move(this.bbox().x, _y);
+ } // Set element size to given width and height
- return this;
- }
}, {
- key: "finish",
- value: function finish() {
- return this.step(Infinity);
- }
+ key: "size",
+ value: function size(width, height) {
+ var p = proportionalSize(this, width, height);
+ return this.attr('d', this.array().size(p.width, p.height));
+ } // Set width of element
+
}, {
- key: "reverse",
- value: function reverse(_reverse) {
- this._reverse = _reverse == null ? !this._reverse : _reverse;
- return this;
- }
+ key: "width",
+ value: function width(_width) {
+ return _width == null ? this.bbox().width : this.size(_width, this.bbox().height);
+ } // Set height of element
+
}, {
- key: "ease",
- value: function ease(fn) {
- this._stepper = new Ease(fn);
- return this;
+ key: "height",
+ value: function height(_height) {
+ return _height == null ? this.bbox().height : this.size(this.bbox().width, _height);
}
}, {
- key: "active",
- value: function active(enabled) {
- if (enabled == null) return this.enabled;
- this.enabled = enabled;
- return this;
+ key: "targets",
+ value: function targets() {
+ return baseFind('svg textpath [href*="' + this.id() + '"]');
}
- /*
- Private Methods
- ===============
- Methods that shouldn't be used externally
- */
- // Save a morpher to the morpher list so that we can retarget it later
+ }]);
- }, {
- key: "_rememberMorpher",
- value: function _rememberMorpher(method, morpher) {
- this._history[method] = {
- morpher: morpher,
- caller: this._queue[this._queue.length - 1]
- };
- } // Try to set the target for a morpher if the morpher exists, otherwise
- // do nothing and return false
+ return Path;
+ }(Shape); // Define morphable array
+ Path.prototype.MorphArray = PathArray; // Add parent method
- }, {
- key: "_tryRetarget",
- value: function _tryRetarget(method, target) {
- if (this._history[method]) {
- // if the last method wasnt even initialised, throw it away
- if (!this._history[method].caller.initialised) {
- var index = this._queue.indexOf(this._history[method].caller);
+ registerMethods({
+ Container: {
+ // Create a wrapped path element
+ path: wrapWithAttrCheck(function (d) {
+ // make sure plot is called as a setter
+ return this.put(new Path()).plot(d || new PathArray());
+ })
+ }
+ });
+ register(Path);
- this._queue.splice(index, 1);
+ function array() {
+ return this._array || (this._array = new PointArray(this.attr('points')));
+ } // Plot new path
- return false;
- } // for the case of transformations, we use the special retarget function
- // which has access to the outer scope
+ function plot(p) {
+ return p == null ? this.array() : this.clear().attr('points', typeof p === 'string' ? p : this._array = new PointArray(p));
+ } // Clear array cache
+ function clear() {
+ delete this._array;
+ return this;
+ } // Move by left top corner
- if (this._history[method].caller.isTransform) {
- this._history[method].caller.isTransform(target); // for everything else a simple morpher change is sufficient
+ function move(x, y) {
+ return this.attr('points', this.array().move(x, y));
+ } // Set element size to given width and height
- } else {
- this._history[method].morpher.to(target);
- }
+ function size(width, height) {
+ var p = proportionalSize(this, width, height);
+ return this.attr('points', this.array().size(p.width, p.height));
+ }
- this._history[method].caller.finished = false;
- var timeline$$1 = this.timeline();
- timeline$$1 && timeline$$1._continue();
- return true;
- }
+ var poly = /*#__PURE__*/Object.freeze({
+ array: array,
+ plot: plot,
+ clear: clear,
+ move: move,
+ size: size
+ });
- return false;
- } // Run each initialise function in the runner if required
+ var Polygon =
+ /*#__PURE__*/
+ function (_Shape) {
+ _inherits(Polygon, _Shape);
- }, {
- key: "_initialise",
- value: function _initialise(running) {
- // If we aren't running, we shouldn't initialise when not declarative
- if (!running && !this._isDeclarative) return; // Loop through all of the initialisers
+ // Initialize node
+ function Polygon(node) {
+ _classCallCheck(this, Polygon);
- for (var i = 0, len = this._queue.length; i < len; ++i) {
- // Get the current initialiser
- var current = this._queue[i]; // Determine whether we need to initialise
+ return _possibleConstructorReturn(this, _getPrototypeOf(Polygon).call(this, nodeOrNew('polygon', node), node));
+ }
- var needsIt = this._isDeclarative || !current.initialised && running;
- running = !current.finished; // Call the initialiser if we need to
+ return Polygon;
+ }(Shape);
+ registerMethods({
+ Container: {
+ // Create a wrapped polygon element
+ polygon: wrapWithAttrCheck(function (p) {
+ // make sure plot is called as a setter
+ return this.put(new Polygon()).plot(p || new PointArray());
+ })
+ }
+ });
+ extend(Polygon, pointed);
+ extend(Polygon, poly);
+ register(Polygon);
- if (needsIt && running) {
- current.initialiser.call(this);
- current.initialised = true;
- }
- }
- } // Run each run function for the position or dt given
+ var Polyline =
+ /*#__PURE__*/
+ function (_Shape) {
+ _inherits(Polyline, _Shape);
- }, {
- key: "_run",
- value: function _run(positionOrDt) {
- // Run all of the _queue directly
- var allfinished = true;
+ // Initialize node
+ function Polyline(node) {
+ _classCallCheck(this, Polyline);
- for (var i = 0, len = this._queue.length; i < len; ++i) {
- // Get the current function to run
- var current = this._queue[i]; // Run the function if its not finished, we keep track of the finished
- // flag for the sake of declarative _queue
-
- var converged = current.runner.call(this, positionOrDt);
- current.finished = current.finished || converged === true;
- allfinished = allfinished && current.finished;
- } // We report when all of the constructors are finished
-
-
- return allfinished;
- }
- }, {
- key: "addTransform",
- value: function addTransform(transform, index) {
- this.transforms.lmultiplyO(transform);
- return this;
- }
- }, {
- key: "clearTransform",
- value: function clearTransform() {
- this.transforms = new Matrix();
- return this;
- }
- }], [{
- key: "sanitise",
- value: function sanitise(duration, delay, when) {
- // Initialise the default parameters
- var times = 1;
- var swing = false;
- var wait = 0;
- duration = duration || timeline.duration;
- delay = delay || timeline.delay;
- when = when || 'last'; // If we have an object, unpack the values
-
- if (_typeof(duration) === 'object' && !(duration instanceof Stepper)) {
- delay = duration.delay || delay;
- when = duration.when || when;
- swing = duration.swing || swing;
- times = duration.times || times;
- wait = duration.wait || wait;
- duration = duration.duration || timeline.duration;
- }
-
- return {
- duration: duration,
- delay: delay,
- swing: swing,
- times: times,
- wait: wait,
- when: when
- };
- }
- }]);
-
- return Runner;
- }(EventTarget);
- Runner.id = 0;
-
- var FakeRunner = function FakeRunner() {
- var transforms = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Matrix();
- var id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
- var done = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
-
- _classCallCheck(this, FakeRunner);
-
- this.transforms = transforms;
- this.id = id;
- this.done = done;
- };
-
- extend([Runner, FakeRunner], {
- mergeWith: function mergeWith(runner) {
- return new FakeRunner(runner.transforms.lmultiply(this.transforms), runner.id);
+ return _possibleConstructorReturn(this, _getPrototypeOf(Polyline).call(this, nodeOrNew('polyline', node), node));
}
- }); // FakeRunner.emptyRunner = new FakeRunner()
- var lmultiply = function lmultiply(last, curr) {
- return last.lmultiplyO(curr);
- };
+ return Polyline;
+ }(Shape);
+ registerMethods({
+ Container: {
+ // Create a wrapped polygon element
+ polyline: wrapWithAttrCheck(function (p) {
+ // make sure plot is called as a setter
+ return this.put(new Polyline()).plot(p || new PointArray());
+ })
+ }
+ });
+ extend(Polyline, pointed);
+ extend(Polyline, poly);
+ register(Polyline);
- var getRunnerTransform = function getRunnerTransform(runner) {
- return runner.transforms;
- };
+ var Rect =
+ /*#__PURE__*/
+ function (_Shape) {
+ _inherits(Rect, _Shape);
- function mergeTransforms() {
- // Find the matrix to apply to the element and apply it
- var runners = this._transformationRunners.runners;
- var netTransform = runners.map(getRunnerTransform).reduce(lmultiply, new Matrix());
- this.transform(netTransform);
+ // Initialize node
+ function Rect(node) {
+ _classCallCheck(this, Rect);
- this._transformationRunners.merge();
+ return _possibleConstructorReturn(this, _getPrototypeOf(Rect).call(this, nodeOrNew('rect', node), node));
+ }
- if (this._transformationRunners.length() === 1) {
- this._frameId = null;
+ return Rect;
+ }(Shape);
+ extend(Rect, {
+ rx: rx,
+ ry: ry
+ });
+ registerMethods({
+ Container: {
+ // Create a rect element
+ rect: wrapWithAttrCheck(function (width$$1, height$$1) {
+ return this.put(new Rect()).size(width$$1, height$$1);
+ })
}
- }
+ });
+ register(Rect);
- var RunnerArray =
+ var Queue =
/*#__PURE__*/
function () {
- function RunnerArray() {
- _classCallCheck(this, RunnerArray);
+ function Queue() {
+ _classCallCheck(this, Queue);
- this.runners = [];
- this.ids = [];
+ this._first = null;
+ this._last = null;
}
- _createClass(RunnerArray, [{
- key: "add",
- value: function add(runner) {
- if (this.runners.includes(runner)) return;
- var id = runner.id + 1;
- var leftSibling = this.ids.reduce(function (last, curr) {
- if (curr > last && curr < id) return curr;
- return last;
- }, 0);
- var index = this.ids.indexOf(leftSibling) + 1;
- this.ids.splice(index, 0, id);
- this.runners.splice(index, 0, runner);
- return this;
- }
- }, {
- key: "getByID",
- value: function getByID(id) {
- return this.runners[this.ids.indexOf(id + 1)];
- }
- }, {
- key: "remove",
- value: function remove(id) {
- var index = this.ids.indexOf(id + 1);
- this.ids.splice(index, 1);
- this.runners.splice(index, 1);
- return this;
- }
- }, {
- key: "merge",
- value: function merge() {
- var _this2 = this;
+ _createClass(Queue, [{
+ key: "push",
+ value: function push(value) {
+ // An item stores an id and the provided value
+ var item = value.next ? value : {
+ value: value,
+ next: null,
+ prev: null // Deal with the queue being empty or populated
- var lastRunner = null;
- this.runners.forEach(function (runner, i) {
- if (lastRunner && runner.done && lastRunner.done) {
- _this2.remove(runner.id);
+ };
- _this2.edit(lastRunner.id, runner.mergeWith(lastRunner));
- }
+ if (this._last) {
+ item.prev = this._last;
+ this._last.next = item;
+ this._last = item;
+ } else {
+ this._last = item;
+ this._first = item;
+ } // Update the length and return the current item
- lastRunner = runner;
- });
- return this;
+
+ return item;
}
}, {
- key: "edit",
- value: function edit(id, newRunner) {
- var index = this.ids.indexOf(id + 1);
- this.ids.splice(index, 1, id);
- this.runners.splice(index, 1, newRunner);
- return this;
- }
+ key: "shift",
+ value: function shift() {
+ // Check if we have a value
+ var remove = this._first;
+ if (!remove) return null; // If we do, remove it and relink things
+
+ this._first = remove.next;
+ if (this._first) this._first.prev = null;
+ this._last = this._first ? this._last : null;
+ return remove.value;
+ } // Shows us the first item in the list
+
}, {
- key: "length",
- value: function length() {
- return this.ids.length;
- }
+ key: "first",
+ value: function first() {
+ return this._first && this._first.value;
+ } // Shows us the last item in the list
+
}, {
- key: "clearBefore",
- value: function clearBefore(id) {
- var deleteCnt = this.ids.indexOf(id + 1) || 1;
- this.ids.splice(0, deleteCnt, 0);
- this.runners.splice(0, deleteCnt, new FakeRunner());
- return this;
+ key: "last",
+ value: function last() {
+ return this._last && this._last.value;
+ } // Removes the item that was returned from the push
+
+ }, {
+ key: "remove",
+ value: function remove(item) {
+ // Relink the previous item
+ if (item.prev) item.prev.next = item.next;
+ if (item.next) item.next.prev = item.prev;
+ if (item === this._last) this._last = item.prev;
+ if (item === this._first) this._first = item.next; // Invalidate item
+
+ item.prev = null;
+ item.next = null;
}
}]);
- return RunnerArray;
+ return Queue;
}();
- var frameId = 0;
- registerMethods({
- Element: {
- animate: function animate(duration, delay, when) {
- var o = Runner.sanitise(duration, delay, when);
- var timeline$$1 = this.timeline();
- return new Runner(o.duration).loop(o).element(this).timeline(timeline$$1).schedule(delay, when);
- },
- delay: function delay(by, when) {
- return this.animate(0, by, when);
- },
- // this function searches for all runners on the element and deletes the ones
- // which run before the current one. This is because absolute transformations
- // overwfrite anything anyway so there is no need to waste time computing
- // other runners
- _clearTransformRunnersBefore: function _clearTransformRunnersBefore(currentRunner) {
- this._transformationRunners.clearBefore(currentRunner.id);
- },
- _currentTransform: function _currentTransform(current) {
- return this._transformationRunners.runners // we need the equal sign here to make sure, that also transformations
- // on the same runner which execute before the current transformation are
- // taken into account
- .filter(function (runner) {
- return runner.id <= current.id;
- }).map(getRunnerTransform).reduce(lmultiply, new Matrix());
- },
- addRunner: function addRunner(runner) {
- this._transformationRunners.add(runner);
+ var Animator = {
+ nextDraw: null,
+ frames: new Queue(),
+ timeouts: new Queue(),
+ timer: globals.window.performance || globals.window.Date,
+ transforms: [],
+ frame: function frame(fn) {
+ // Store the node
+ var node = Animator.frames.push({
+ run: fn
+ }); // Request an animation frame if we don't have one
- Animator.transform_frame(mergeTransforms.bind(this), this._frameId);
- },
- _prepareRunner: function _prepareRunner() {
- if (this._frameId == null) {
- this._transformationRunners = new RunnerArray().add(new FakeRunner(new Matrix(this)));
- this._frameId = frameId++;
- }
- }
- }
- });
- extend(Runner, {
- attr: function attr(a, v) {
- return this.styleAttr('attr', a, v);
+ if (Animator.nextDraw === null) {
+ Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw);
+ } // Return the node so we can remove it easily
+
+
+ return node;
},
- // Add animatable styles
- css: function css(s, v) {
- return this.styleAttr('css', s, v);
+ transform_frame: function transform_frame(fn, id) {
+ Animator.transforms[id] = fn;
},
- styleAttr: function styleAttr(type, name, val) {
- // apply attributes individually
- if (_typeof(name) === 'object') {
- for (var key in val) {
- this.styleAttr(type, key, val[key]);
- }
+ timeout: function timeout(fn, delay) {
+ delay = delay || 0; // Work out when the event should fire
+
+ var time = Animator.timer.now() + delay; // Add the timeout to the end of the queue
+
+ var node = Animator.timeouts.push({
+ run: fn,
+ time: time
+ }); // Request another animation frame if we need one
+
+ if (Animator.nextDraw === null) {
+ Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw);
}
- var morpher = new Morphable(this._stepper).to(val);
- this.queue(function () {
- morpher = morpher.from(this.element()[type](name));
- }, function (pos) {
- this.element()[type](name, morpher.at(pos));
- return morpher.done();
- });
- return this;
+ return node;
},
- zoom: function zoom(level, point) {
- var morpher = new Morphable(this._stepper).to(new SVGNumber(level));
- this.queue(function () {
- morpher = morpher.from(this.zoom());
- }, function (pos) {
- this.element().zoom(morpher.at(pos), point);
- return morpher.done();
- });
- return this;
+ cancelFrame: function cancelFrame(node) {
+ Animator.frames.remove(node);
+ },
+ clearTimeout: function clearTimeout(node) {
+ Animator.timeouts.remove(node);
},
+ _draw: function _draw(now) {
+ // Run all the timeouts we can run, if they are not ready yet, add them
+ // to the end of the queue immediately! (bad timeouts!!! [sarcasm])
+ var nextTimeout = null;
+ var lastTimeout = Animator.timeouts.last();
- /**
- ** absolute transformations
- **/
- //
- // M v -----|-----(D M v = F v)------|-----> T v
- //
- // 1. define the final state (T) and decompose it (once)
- // t = [tx, ty, the, lam, sy, sx]
- // 2. on every frame: pull the current state of all previous transforms
- // (M - m can change)
- // and then write this as m = [tx0, ty0, the0, lam0, sy0, sx0]
- // 3. Find the interpolated matrix F(pos) = m + pos * (t - m)
- // - Note F(0) = M
- // - Note F(1) = T
- // 4. Now you get the delta matrix as a result: D = F * inv(M)
- transform: function transform(transforms, relative, affine) {
- // If we have a declarative function, we should retarget it if possible
- relative = transforms.relative || relative;
+ while (nextTimeout = Animator.timeouts.shift()) {
+ // Run the timeout if its time, or push it to the end
+ if (now >= nextTimeout.time) {
+ nextTimeout.run();
+ } else {
+ Animator.timeouts.push(nextTimeout);
+ } // If we hit the last item, we should stop shifting out more items
- if (this._isDeclarative && !relative && this._tryRetarget('transform', transforms)) {
- return this;
- } // Parse the parameters
+ if (nextTimeout === lastTimeout) break;
+ } // Run all of the animation frames
- var isMatrix = Matrix.isMatrixLike(transforms);
- affine = transforms.affine != null ? transforms.affine : affine != null ? affine : !isMatrix; // Create a morepher and set its type
- var morpher = new Morphable(this._stepper).type(affine ? TransformBag : Matrix);
- var origin;
- var element;
- var current;
- var currentAngle;
- var startTransform;
+ var nextFrame = null;
+ var lastFrame = Animator.frames.last();
- function setup() {
- // make sure element and origin is defined
- element = element || this.element();
- origin = origin || getOrigin(transforms, element);
- startTransform = new Matrix(relative ? undefined : element); // add the runner to the element so it can merge transformations
+ while (nextFrame !== lastFrame && (nextFrame = Animator.frames.shift())) {
+ nextFrame.run();
+ }
- element.addRunner(this); // Deactivate all transforms that have run so far if we are absolute
+ Animator.transforms.forEach(function (el) {
+ el();
+ }); // If we have remaining timeouts or frames, draw until we don't anymore
- if (!relative) {
- element._clearTransformRunnersBefore(this);
- }
- }
+ Animator.nextDraw = Animator.timeouts.first() || Animator.frames.first() ? globals.window.requestAnimationFrame(Animator._draw) : null;
+ }
+ };
- function run(pos) {
- // clear all other transforms before this in case something is saved
- // on this runner. We are absolute. We dont need these!
- if (!relative) this.clearTransform();
+ var time = globals.window.performance || Date;
- var _transform = new Point(origin).transform(element._currentTransform(this)),
- x$$1 = _transform.x,
- y$$1 = _transform.y;
+ var makeSchedule = function makeSchedule(runnerInfo) {
+ var start = runnerInfo.start;
+ var duration = runnerInfo.runner.duration();
+ var end = start + duration;
+ return {
+ start: start,
+ duration: duration,
+ end: end,
+ runner: runnerInfo.runner
+ };
+ };
- var target = new Matrix(_objectSpread({}, transforms, {
- origin: [x$$1, y$$1]
- }));
- var start = this._isDeclarative && current ? current : startTransform;
+ var Timeline =
+ /*#__PURE__*/
+ function () {
+ // Construct a new timeline on the given element
+ function Timeline() {
+ _classCallCheck(this, Timeline);
- if (affine) {
- target = target.decompose(x$$1, y$$1);
- start = start.decompose(x$$1, y$$1); // Get the current and target angle as it was set
+ this._timeSource = function () {
+ return time.now();
+ };
- var rTarget = target.rotate;
- var rCurrent = start.rotate; // Figure out the shortest path to rotate directly
+ this._dispatcher = globals.document.createElement('div'); // Store the timing variables
- var possibilities = [rTarget - 360, rTarget, rTarget + 360];
- var distances = possibilities.map(function (a) {
- return Math.abs(a - rCurrent);
+ this._startTime = 0;
+ this._speed = 1.0; // Play control variables control how the animation proceeds
+
+ this._reverse = false;
+ this._persist = 0; // Keep track of the running animations and their starting parameters
+
+ this._nextFrame = null;
+ this._paused = false;
+ this._runners = [];
+ this._order = [];
+ this._time = 0;
+ this._lastSourceTime = 0;
+ this._lastStepTime = 0;
+ }
+
+ _createClass(Timeline, [{
+ key: "getEventTarget",
+ value: function getEventTarget() {
+ return this._dispatcher;
+ }
+ /**
+ *
+ */
+ // schedules a runner on the timeline
+
+ }, {
+ key: "schedule",
+ value: function schedule(runner, delay, when) {
+ if (runner == null) {
+ return this._runners.map(makeSchedule).sort(function (a, b) {
+ return a.start - b.start || a.duration - b.duration;
});
- var shortest = Math.min.apply(Math, _toConsumableArray(distances));
- var index = distances.indexOf(shortest);
- target.rotate = possibilities[index];
}
- if (relative) {
- // we have to be careful here not to overwrite the rotation
- // with the rotate method of Matrix
- if (!isMatrix) {
- target.rotate = transforms.rotate || 0;
- }
+ if (!this.active()) {
+ this._step();
- if (this._isDeclarative && currentAngle) {
- start.rotate = currentAngle;
+ if (when == null) {
+ when = 'now';
}
- }
+ } // The start time for the next animation can either be given explicitly,
+ // derived from the current timeline time or it can be relative to the
+ // last start time to chain animations direclty
- morpher.from(start);
- morpher.to(target);
- var affineParameters = morpher.at(pos);
- currentAngle = affineParameters.rotate;
- current = new Matrix(affineParameters);
- this.addTransform(current);
- return morpher.done();
- }
- function retarget(newTransforms) {
- // only get a new origin if it changed since the last call
- if ((newTransforms.origin || 'center').toString() !== (transforms.origin || 'center').toString()) {
- origin = getOrigin(transforms, element);
- } // overwrite the old transformations with the new ones
+ var absoluteStartTime = 0;
+ delay = delay || 0; // Work out when to start the animation
+ if (when == null || when === 'last' || when === 'after') {
+ // Take the last time and increment
+ absoluteStartTime = this._startTime;
+ } else if (when === 'absolute' || when === 'start') {
+ absoluteStartTime = delay;
+ delay = 0;
+ } else if (when === 'now') {
+ absoluteStartTime = this._time;
+ } else if (when === 'relative') {
+ var runnerInfo = this._runners[runner.id];
- transforms = _objectSpread({}, newTransforms, {
- origin: origin
- });
- }
+ if (runnerInfo) {
+ absoluteStartTime = runnerInfo.start + delay;
+ delay = 0;
+ }
+ } else {
+ throw new Error('Invalid value for the "when" parameter');
+ } // Manage runner
- this.queue(setup, run, retarget);
- this._isDeclarative && this._rememberMorpher('transform', morpher);
- return this;
- },
- // Animatable x-axis
- x: function x$$1(_x, relative) {
- return this._queueNumber('x', _x);
- },
- // Animatable y-axis
- y: function y$$1(_y) {
- return this._queueNumber('y', _y);
- },
- dx: function dx(x$$1) {
- return this._queueNumberDelta('dx', x$$1);
- },
- dy: function dy(y$$1) {
- return this._queueNumberDelta('dy', y$$1);
- },
- _queueNumberDelta: function _queueNumberDelta(method, to$$1) {
- to$$1 = new SVGNumber(to$$1); // Try to change the target if we have this method already registerd
- if (this._tryRetargetDelta(method, to$$1)) return this; // Make a morpher and queue the animation
+ runner.unschedule();
+ runner.timeline(this);
+ runner.time(-delay); // Save startTime for next runner
- var morpher = new Morphable(this._stepper).to(to$$1);
- this.queue(function () {
- var from$$1 = this.element()[method]();
- morpher.from(from$$1);
- morpher.to(from$$1 + to$$1);
- }, function (pos) {
- this.element()[method](morpher.at(pos));
- return morpher.done();
- }); // Register the morpher so that if it is changed again, we can retarget it
+ this._startTime = absoluteStartTime + runner.duration() + delay; // Save runnerInfo
- this._rememberMorpher(method, morpher);
+ this._runners[runner.id] = {
+ persist: this.persist(),
+ runner: runner,
+ start: absoluteStartTime // Save order and continue
- return this;
- },
- _queueObject: function _queueObject(method, to$$1) {
- // Try to change the target if we have this method already registerd
- if (this._tryRetarget(method, to$$1)) return this; // Make a morpher and queue the animation
+ };
- var morpher = new Morphable(this._stepper).to(to$$1);
- this.queue(function () {
- morpher.from(this.element()[method]());
- }, function (pos) {
- this.element()[method](morpher.at(pos));
- return morpher.done();
- }); // Register the morpher so that if it is changed again, we can retarget it
+ this._order.push(runner.id);
- this._rememberMorpher(method, morpher);
+ this._continue();
- return this;
- },
- _queueNumber: function _queueNumber(method, value) {
- return this._queueObject(method, new SVGNumber(value));
- },
- // Animatable center x-axis
- cx: function cx$$1(x$$1) {
- return this._queueNumber('cx', x$$1);
- },
- // Animatable center y-axis
- cy: function cy$$1(y$$1) {
- return this._queueNumber('cy', y$$1);
- },
- // Add animatable move
- move: function move(x$$1, y$$1) {
- return this.x(x$$1).y(y$$1);
- },
- // Add animatable center
- center: function center(x$$1, y$$1) {
- return this.cx(x$$1).cy(y$$1);
- },
- // Add animatable size
- size: function size(width$$1, height$$1) {
- // animate bbox based size for all other elements
- var box;
+ return this;
+ } // Remove the runner from this timeline
- if (!width$$1 || !height$$1) {
- box = this._element.bbox();
- }
+ }, {
+ key: "unschedule",
+ value: function unschedule(runner) {
+ var index = this._order.indexOf(runner.id);
- if (!width$$1) {
- width$$1 = box.width / box.height * height$$1;
- }
+ if (index < 0) return this;
+ delete this._runners[runner.id];
- if (!height$$1) {
- height$$1 = box.height / box.width * width$$1;
- }
+ this._order.splice(index, 1);
- return this.width(width$$1).height(height$$1);
- },
- // Add animatable width
- width: function width$$1(_width) {
- return this._queueNumber('width', _width);
- },
- // Add animatable height
- height: function height$$1(_height) {
- return this._queueNumber('height', _height);
- },
- // Add animatable plot
- plot: function plot(a, b, c, d) {
- // Lines can be plotted with 4 arguments
- if (arguments.length === 4) {
- return this.plot([a, b, c, d]);
+ runner.timeline(null);
+ return this;
}
-
- var morpher = this._element.MorphArray().to(a);
-
- this.queue(function () {
- morpher.from(this._element.array());
- }, function (pos) {
- this._element.plot(morpher.at(pos));
- });
- return this;
- },
- // Add leading method
- leading: function leading(value) {
- return this._queueNumber('leading', value);
- },
- // Add animatable viewbox
- viewbox: function viewbox(x$$1, y$$1, width$$1, height$$1) {
- return this._queueObject('viewbox', new Box(x$$1, y$$1, width$$1, height$$1));
- },
- update: function update(o) {
- if (_typeof(o) !== 'object') {
- return this.update({
- offset: arguments[0],
- color: arguments[1],
- opacity: arguments[2]
- });
+ }, {
+ key: "play",
+ value: function play() {
+ // Now make sure we are not paused and continue the animation
+ this._paused = false;
+ return this._continue();
}
-
- if (o.opacity != null) this.attr('stop-opacity', o.opacity);
- if (o.color != null) this.attr('stop-color', o.color);
- if (o.offset != null) this.attr('offset', o.offset);
- return this;
- }
- });
- extend(Runner, {
- rx: rx,
- ry: ry,
- from: from,
- to: to
- });
-
- var sugar = {
- stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'],
- fill: ['color', 'opacity', 'rule'],
- prefix: function prefix(t, a) {
- return a === 'color' ? t : t + '-' + a;
- } // Add sugar for fill and stroke
-
- };
- ['fill', 'stroke'].forEach(function (m) {
- var extension = {};
- var i;
-
- extension[m] = function (o) {
- if (typeof o === 'undefined') {
- return this.attr(m);
+ }, {
+ key: "pause",
+ value: function pause() {
+ // Cancel the next animation frame and pause
+ this._nextFrame = null;
+ this._paused = true;
+ return this;
}
-
- if (typeof o === 'string' || Color.isRgb(o) || o instanceof Element) {
- this.attr(m, o);
- } else {
- // set all attributes from sugar.fill and sugar.stroke list
- for (i = sugar[m].length - 1; i >= 0; i--) {
- if (o[sugar[m][i]] != null) {
- this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]);
- }
- }
+ }, {
+ key: "stop",
+ value: function stop() {
+ // Cancel the next animation frame and go to start
+ this.seek(-this._time);
+ return this.pause();
}
-
- return this;
- };
-
- registerMethods(['Shape', 'Runner'], extension);
- });
- registerMethods(['Element', 'Runner'], {
- // Let the user set the matrix directly
- matrix: function matrix(mat, b, c, d, e, f) {
- // Act as a getter
- if (mat == null) {
- return new Matrix(this);
- } // Act as a setter, the user can pass a matrix or a set of numbers
-
-
- return this.attr('transform', new Matrix(mat, b, c, d, e, f));
- },
- // Map rotation to transform
- rotate: function rotate(angle, cx, cy) {
- return this.transform({
- rotate: angle,
- ox: cx,
- oy: cy
- }, true);
- },
- // Map skew to transform
- skew: function skew(x, y, cx, cy) {
- return arguments.length === 1 || arguments.length === 3 ? this.transform({
- skew: x,
- ox: y,
- oy: cx
- }, true) : this.transform({
- skew: [x, y],
- ox: cx,
- oy: cy
- }, true);
- },
- shear: function shear(lam, cx, cy) {
- return this.transform({
- shear: lam,
- ox: cx,
- oy: cy
- }, true);
- },
- // Map scale to transform
- scale: function scale(x, y, cx, cy) {
- return arguments.length === 1 || arguments.length === 3 ? this.transform({
- scale: x,
- ox: y,
- oy: cx
- }, true) : this.transform({
- scale: [x, y],
- ox: cx,
- oy: cy
- }, true);
- },
- // Map translate to transform
- translate: function translate(x, y) {
- return this.transform({
- translate: [x, y]
- }, true);
- },
- // Map relative translations to transform
- relative: function relative(x, y) {
- return this.transform({
- relative: [x, y]
- }, true);
- },
- // Map flip to transform
- flip: function flip(direction, around) {
- var directionString = typeof direction === 'string' ? direction : isFinite(direction) ? 'both' : 'both';
- var origin = direction === 'both' && isFinite(around) ? [around, around] : direction === 'x' ? [around, 0] : direction === 'y' ? [0, around] : isFinite(direction) ? [direction, direction] : [0, 0];
- this.transform({
- flip: directionString,
- origin: origin
- }, true);
- },
- // Opacity
- opacity: function opacity(value) {
- return this.attr('opacity', value);
- },
- // Relative move over x axis
- dx: function dx(x) {
- return this.x(new SVGNumber(x).plus(this instanceof Runner ? 0 : this.x()), true);
- },
- // Relative move over y axis
- dy: function dy(y) {
- return this.y(new SVGNumber(y).plus(this instanceof Runner ? 0 : this.y()), true);
- },
- // Relative move over x and y axes
- dmove: function dmove(x, y) {
- return this.dx(x).dy(y);
- }
- });
- registerMethods('radius', {
- // Add x and y radius
- radius: function radius(x, y) {
- var type = (this._element || this).type;
- return type === 'radialGradient' || type === 'radialGradient' ? this.attr('r', new SVGNumber(x)) : this.rx(x).ry(y == null ? x : y);
- }
- });
- registerMethods('Path', {
- // Get path length
- length: function length() {
- return this.node.getTotalLength();
- },
- // Get point at length
- pointAt: function pointAt(length) {
- return new Point(this.node.getPointAtLength(length));
- }
- });
- registerMethods(['Element', 'Runner'], {
- // Set font
- font: function font(a, v) {
- if (_typeof(a) === 'object') {
- for (v in a) {
- this.font(v, a[v]);
- }
+ }, {
+ key: "finish",
+ value: function finish() {
+ this.seek(Infinity);
+ return this.pause();
}
-
- return a === 'leading' ? this.leading(v) : a === 'anchor' ? this.attr('text-anchor', v) : a === 'size' || a === 'family' || a === 'weight' || a === 'stretch' || a === 'variant' || a === 'style' ? this.attr('font-' + a, v) : this.attr(a, v);
- }
- });
- registerMethods('Text', {
- ax: function ax(x) {
- return this.attr('x', x);
- },
- ay: function ay(y) {
- return this.attr('y', y);
- },
- amove: function amove(x, y) {
- return this.ax(x).ay(y);
- }
- }); // Add events to elements
-
- var methods$1 = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'mouseenter', 'mouseleave', 'touchstart', 'touchmove', 'touchleave', 'touchend', 'touchcancel'].reduce(function (last, event) {
- // add event to Element
- var fn = function fn(f) {
- if (f === null) {
- off(this, event);
- } else {
- on(this, event, f);
+ }, {
+ key: "speed",
+ value: function speed(_speed) {
+ if (_speed == null) return this._speed;
+ this._speed = _speed;
+ return this;
}
-
- return this;
- };
-
- last[event] = fn;
- return last;
- }, {});
- registerMethods('Element', methods$1);
-
- function untransform() {
- return this.attr('transform', null);
- } // merge the whole transformation chain into one matrix and returns it
-
- function matrixify() {
- var matrix = (this.attr('transform') || ''). // split transformations
- split(transforms).slice(0, -1).map(function (str) {
- // generate key => value pairs
- var kv = str.trim().split('(');
- return [kv[0], kv[1].split(delimiter).map(function (str) {
- return parseFloat(str);
- })];
- }).reverse() // merge every transformation into one matrix
- .reduce(function (matrix, transform) {
- if (transform[0] === 'matrix') {
- return matrix.lmultiply(Matrix.fromArray(transform[1]));
+ }, {
+ key: "reverse",
+ value: function reverse(yes) {
+ var currentSpeed = this.speed();
+ if (yes == null) return this.speed(-currentSpeed);
+ var positive = Math.abs(currentSpeed);
+ return this.speed(yes ? positive : -positive);
}
-
- return matrix[transform[0]].apply(matrix, transform[1]);
- }, new Matrix());
- return matrix;
- } // add an element to another parent without changing the visual representation on the screen
-
- function toParent(parent) {
- if (this === parent) return this;
- var ctm = this.screenCTM();
- var pCtm = parent.screenCTM().inverse();
- this.addTo(parent).untransform().transform(pCtm.multiply(ctm));
- return this;
- } // same as above with parent equals root-svg
-
- function toDoc() {
- return this.toParent(this.doc());
- } // Add transformations
-
- function transform(o, relative) {
- // Act as a getter if no object was passed
- if (o == null || typeof o === 'string') {
- var decomposed = new Matrix(this).decompose();
- return decomposed[o] || decomposed;
- }
-
- if (!Matrix.isMatrixLike(o)) {
- // Set the origin according to the defined transform
- o = _objectSpread({}, o, {
- origin: getOrigin(o, this)
- });
- } // The user can pass a boolean, an Element or an Matrix or nothing
-
-
- var cleanRelative = relative === true ? this : relative || false;
- var result = new Matrix(cleanRelative).transform(o);
- return this.attr('transform', result);
- }
- registerMethods('Element', {
- untransform: untransform,
- matrixify: matrixify,
- toParent: toParent,
- toDoc: toDoc,
- transform: transform
- });
-
- var List = subClassArray('List', Array, function (arr) {
- this.length = 0;
- this.push.apply(this, _toConsumableArray(arr));
- });
- extend(List, {
- each: function each(cbOrName) {
- for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
- args[_key - 1] = arguments[_key];
+ }, {
+ key: "seek",
+ value: function seek(dt) {
+ this._time += dt;
+ return this._continue();
}
-
- if (typeof cbOrName === 'function') {
- this.forEach(function (el) {
- cbOrName.call(el, el);
- });
- } else {
- this.forEach(function (el) {
- el[cbOrName].apply(el, args);
- });
+ }, {
+ key: "time",
+ value: function time(_time) {
+ if (_time == null) return this._time;
+ this._time = _time;
+ return this;
+ }
+ }, {
+ key: "persist",
+ value: function persist(dtOrForever) {
+ if (dtOrForever == null) return this._persist;
+ this._persist = dtOrForever;
+ return this;
+ }
+ }, {
+ key: "source",
+ value: function source(fn) {
+ if (fn == null) return this._timeSource;
+ this._timeSource = fn;
+ return this;
}
+ }, {
+ key: "_step",
+ value: function _step() {
+ // If the timeline is paused, just do nothing
+ if (this._paused) return; // Get the time delta from the last time and update the time
+
+ var time = this._timeSource();
- return this;
- },
- toArray: function toArray() {
- return Array.prototype.concat.apply([], this);
- }
- });
+ var dtSource = time - this._lastSourceTime;
+ var dtTime = this._speed * dtSource + (this._time - this._lastStepTime);
+ this._lastSourceTime = time; // Update the time
- List.extend = function (methods) {
- methods = methods.reduce(function (obj, name) {
- obj[name] = function () {
- for (var _len2 = arguments.length, attrs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
- attrs[_key2] = arguments[_key2];
- }
+ this._time += dtTime;
+ this._lastStepTime = this._time; // this.fire('time', this._time)
+ // Run all of the runners directly
- return this.each.apply(this, [name].concat(attrs));
- };
+ var runnersLeft = false;
- return obj;
- }, {});
- extend(List, methods);
- };
+ for (var i = 0, len = this._order.length; i < len; i++) {
+ // Get and run the current runner and ignore it if its inactive
+ var runnerInfo = this._runners[this._order[i]];
+ var runner = runnerInfo.runner;
+ var dt = dtTime; // Make sure that we give the actual difference
+ // between runner start time and now
- var Shape =
- /*#__PURE__*/
- function (_Element) {
- _inherits(Shape, _Element);
+ var dtToStart = this._time - runnerInfo.start; // Dont run runner if not started yet
- function Shape() {
- _classCallCheck(this, Shape);
+ if (dtToStart < 0) {
+ runnersLeft = true;
+ continue;
+ } else if (dtToStart < dt) {
+ // Adjust dt to make sure that animation is on point
+ dt = dtToStart;
+ }
- return _possibleConstructorReturn(this, _getPrototypeOf(Shape).apply(this, arguments));
- }
+ if (!runner.active()) continue; // If this runner is still going, signal that we need another animation
+ // frame, otherwise, remove the completed runner
- return Shape;
- }(Element);
- register(Shape);
+ var finished = runner.step(dt).done;
- var Circle =
- /*#__PURE__*/
- function (_Shape) {
- _inherits(Circle, _Shape);
+ if (!finished) {
+ runnersLeft = true; // continue
+ } else if (runnerInfo.persist !== true) {
+ // runner is finished. And runner might get removed
+ var endTime = runner.duration() - runner.time() + this._time;
- function Circle(node) {
- _classCallCheck(this, Circle);
+ if (endTime + this._persist < this._time) {
+ // Delete runner and correct index
+ delete this._runners[this._order[i]];
+ this._order.splice(i--, 1) && --len;
+ runner.timeline(null);
+ }
+ }
+ } // Get the next animation frame to keep the simulation going
- return _possibleConstructorReturn(this, _getPrototypeOf(Circle).call(this, nodeOrNew('circle', node), node));
- }
- _createClass(Circle, [{
- key: "radius",
- value: function radius(r) {
- return this.attr('r', r);
- } // Radius x value
+ if (runnersLeft) {
+ this._nextFrame = Animator.frame(this._step.bind(this));
+ } else {
+ this._nextFrame = null;
+ }
- }, {
- key: "rx",
- value: function rx$$1(_rx) {
- return this.attr('r', _rx);
- } // Alias radius x value
+ return this;
+ } // Checks if we are running and continues the animation
}, {
- key: "ry",
- value: function ry$$1(_ry) {
- return this.rx(_ry);
+ key: "_continue",
+ value: function _continue() {
+ if (this._paused) return this;
+
+ if (!this._nextFrame) {
+ this._nextFrame = Animator.frame(this._step.bind(this));
+ }
+
+ return this;
}
}, {
- key: "size",
- value: function size(_size) {
- return this.radius(new SVGNumber(_size).divide(2));
+ key: "active",
+ value: function active() {
+ return !!this._nextFrame;
}
}]);
- return Circle;
- }(Shape);
- extend(Circle, {
- x: x,
- y: y,
- cx: cx,
- cy: cy,
- width: width,
- height: height
- });
+ return Timeline;
+ }();
registerMethods({
Element: {
- // Create circle element
- circle: wrapWithAttrCheck(function (size) {
- return this.put(new Circle()).size(size).move(0, 0);
- })
- }
- });
- register(Circle);
-
- var Ellipse =
- /*#__PURE__*/
- function (_Shape) {
- _inherits(Ellipse, _Shape);
-
- function Ellipse(node) {
- _classCallCheck(this, Ellipse);
-
- return _possibleConstructorReturn(this, _getPrototypeOf(Ellipse).call(this, nodeOrNew('ellipse', node), node));
- }
-
- _createClass(Ellipse, [{
- key: "size",
- value: function size(width$$1, height$$1) {
- var p = proportionalSize(this, width$$1, height$$1);
- return this.rx(new SVGNumber(p.width).divide(2)).ry(new SVGNumber(p.height).divide(2));
+ timeline: function timeline() {
+ this._timeline = this._timeline || new Timeline();
+ return this._timeline;
}
- }]);
-
- return Ellipse;
- }(Shape);
- extend(Ellipse, circled);
- registerMethods('Container', {
- // Create an ellipse
- ellipse: wrapWithAttrCheck(function (width$$1, height$$1) {
- return this.put(new Ellipse()).size(width$$1, height$$1).move(0, 0);
- })
+ }
});
- register(Ellipse);
- var Stop =
+ var Runner =
/*#__PURE__*/
- function (_Element) {
- _inherits(Stop, _Element);
-
- function Stop(node) {
- _classCallCheck(this, Stop);
-
- return _possibleConstructorReturn(this, _getPrototypeOf(Stop).call(this, nodeOrNew('stop', node), node));
- } // add color stops
-
-
- _createClass(Stop, [{
- key: "update",
- value: function update(o) {
- if (typeof o === 'number' || o instanceof SVGNumber) {
- o = {
- offset: arguments[0],
- color: arguments[1],
- opacity: arguments[2]
- };
- } // set attributes
+ function (_EventTarget) {
+ _inherits(Runner, _EventTarget);
+ function Runner(options) {
+ var _this;
- if (o.opacity != null) this.attr('stop-opacity', o.opacity);
- if (o.color != null) this.attr('stop-color', o.color);
- if (o.offset != null) this.attr('offset', new SVGNumber(o.offset));
- return this;
- }
- }]);
+ _classCallCheck(this, Runner);
- return Stop;
- }(Element);
- register(Stop);
+ _this = _possibleConstructorReturn(this, _getPrototypeOf(Runner).call(this)); // Store a unique id on the runner, so that we can identify it later
- function baseFind(query, parent) {
- return map((parent || globals.document).querySelectorAll(query), function (node) {
- return adopt(node);
- });
- } // Scoped find method
+ _this.id = Runner.id++; // Ensure a default value
- function find(query) {
- return baseFind(query, this.node);
- }
- registerMethods('Dom', {
- find: find
- });
+ options = options == null ? timeline.duration : options; // Ensure that we get a controller
- var Gradient =
- /*#__PURE__*/
- function (_Container) {
- _inherits(Gradient, _Container);
+ options = typeof options === 'function' ? new Controller(options) : options; // Declare all of the variables
- function Gradient(type, attrs) {
- _classCallCheck(this, Gradient);
+ _this._element = null;
+ _this._timeline = null;
+ _this.done = false;
+ _this._queue = []; // Work out the stepper and the duration
- return _possibleConstructorReturn(this, _getPrototypeOf(Gradient).call(this, nodeOrNew(type + 'Gradient', typeof type === 'string' ? null : type), attrs));
- } // Add a color stop
+ _this._duration = typeof options === 'number' && options;
+ _this._isDeclarative = options instanceof Controller;
+ _this._stepper = _this._isDeclarative ? options : new Ease(); // We copy the current values from the timeline because they can change
+ _this._history = {}; // Store the state of the runner
- _createClass(Gradient, [{
- key: "stop",
- value: function stop(offset, color, opacity) {
- return this.put(new Stop()).update(offset, color, opacity);
- } // Update gradient
+ _this.enabled = true;
+ _this._time = 0;
+ _this._last = 0; // Save transforms applied to this runner
- }, {
- key: "update",
- value: function update(block) {
- // remove all stops
- this.clear(); // invoke passed block
+ _this.transforms = new Matrix();
+ _this.transformId = 1; // Looping variables
- if (typeof block === 'function') {
- block.call(this, this);
- }
+ _this._haveReversed = false;
+ _this._reverse = false;
+ _this._loopsDone = 0;
+ _this._swing = false;
+ _this._wait = 0;
+ _this._times = 1;
+ return _this;
+ }
+ /*
+ Runner Definitions
+ ==================
+ These methods help us define the runtime behaviour of the Runner or they
+ help us make new runners from the current runner
+ */
- return this;
- } // Return the fill id
- }, {
- key: "url",
- value: function url() {
- return 'url(#' + this.id() + ')';
- } // Alias string convertion to fill
+ _createClass(Runner, [{
+ key: "element",
+ value: function element(_element) {
+ if (_element == null) return this._element;
+ this._element = _element;
- }, {
- key: "toString",
- value: function toString() {
- return this.url();
- } // custom attr to handle transform
+ _element._prepareRunner();
- }, {
- key: "attr",
- value: function attr(a, b, c) {
- if (a === 'transform') a = 'gradientTransform';
- return _get(_getPrototypeOf(Gradient.prototype), "attr", this).call(this, a, b, c);
+ return this;
}
}, {
- key: "targets",
- value: function targets() {
- return baseFind('svg [fill*="' + this.id() + '"]');
+ key: "timeline",
+ value: function timeline$$1(_timeline) {
+ // check explicitly for undefined so we can set the timeline to null
+ if (typeof _timeline === 'undefined') return this._timeline;
+ this._timeline = _timeline;
+ return this;
}
}, {
- key: "bbox",
- value: function bbox() {
- return new Box();
+ key: "animate",
+ value: function animate(duration, delay, when) {
+ var o = Runner.sanitise(duration, delay, when);
+ var runner = new Runner(o.duration);
+ if (this._timeline) runner.timeline(this._timeline);
+ if (this._element) runner.element(this._element);
+ return runner.loop(o).schedule(delay, when);
}
- }]);
-
- return Gradient;
- }(Container);
- extend(Gradient, gradiented);
- registerMethods({
- Container: {
- // Create gradient element in defs
- gradient: wrapWithAttrCheck(function (type, block) {
- return this.defs().gradient(type, block);
- })
- },
- // define gradient
- Defs: {
- gradient: wrapWithAttrCheck(function (type, block) {
- return this.put(new Gradient(type)).update(block);
- })
- }
- });
- register(Gradient);
-
- var Pattern =
- /*#__PURE__*/
- function (_Container) {
- _inherits(Pattern, _Container);
-
- // Initialize node
- function Pattern(node) {
- _classCallCheck(this, Pattern);
+ }, {
+ key: "schedule",
+ value: function schedule(timeline$$1, delay, when) {
+ // The user doesn't need to pass a timeline if we already have one
+ if (!(timeline$$1 instanceof Timeline)) {
+ when = delay;
+ delay = timeline$$1;
+ timeline$$1 = this.timeline();
+ } // If there is no timeline, yell at the user...
- return _possibleConstructorReturn(this, _getPrototypeOf(Pattern).call(this, nodeOrNew('pattern', node), node));
- } // Return the fill id
+ if (!timeline$$1) {
+ throw Error('Runner cannot be scheduled without timeline');
+ } // Schedule the runner on the timeline provided
- _createClass(Pattern, [{
- key: "url",
- value: function url() {
- return 'url(#' + this.id() + ')';
- } // Update pattern by rebuilding
+ timeline$$1.schedule(this, delay, when);
+ return this;
+ }
}, {
- key: "update",
- value: function update(block) {
- // remove content
- this.clear(); // invoke passed block
+ key: "unschedule",
+ value: function unschedule() {
+ var timeline$$1 = this.timeline();
+ timeline$$1 && timeline$$1.unschedule(this);
+ return this;
+ }
+ }, {
+ key: "loop",
+ value: function loop(times, swing, wait) {
+ // Deal with the user passing in an object
+ if (_typeof(times) === 'object') {
+ swing = times.swing;
+ wait = times.wait;
+ times = times.times;
+ } // Sanitise the values and store them
- if (typeof block === 'function') {
- block.call(this, this);
- }
+ this._times = times || Infinity;
+ this._swing = swing || false;
+ this._wait = wait || 0;
return this;
- } // Alias string convertion to fill
-
+ }
}, {
- key: "toString",
- value: function toString() {
- return this.url();
- } // custom attr to handle transform
+ key: "delay",
+ value: function delay(_delay) {
+ return this.animate(0, _delay);
+ }
+ /*
+ Basic Functionality
+ ===================
+ These methods allow us to attach basic functions to the runner directly
+ */
}, {
- key: "attr",
- value: function attr(a, b, c) {
- if (a === 'transform') a = 'patternTransform';
- return _get(_getPrototypeOf(Pattern.prototype), "attr", this).call(this, a, b, c);
+ key: "queue",
+ value: function queue(initFn, runFn, retargetFn, isTransform) {
+ this._queue.push({
+ initialiser: initFn || noop,
+ runner: runFn || noop,
+ retarget: retargetFn,
+ isTransform: isTransform,
+ initialised: false,
+ finished: false
+ });
+
+ var timeline$$1 = this.timeline();
+ timeline$$1 && this.timeline()._continue();
+ return this;
}
}, {
- key: "targets",
- value: function targets() {
- return baseFind('svg [fill*="' + this.id() + '"]');
+ key: "during",
+ value: function during(fn) {
+ return this.queue(null, fn);
}
}, {
- key: "bbox",
- value: function bbox() {
- return new Box();
+ key: "after",
+ value: function after(fn) {
+ return this.on('finish', fn);
}
- }]);
+ /*
+ Runner animation methods
+ ========================
+ Control how the animation plays
+ */
- return Pattern;
- }(Container);
- registerMethods({
- Container: {
- // Create pattern element in defs
- pattern: function pattern() {
- var _this$defs;
+ }, {
+ key: "time",
+ value: function time(_time) {
+ if (_time == null) {
+ return this._time;
+ }
- return (_this$defs = this.defs()).pattern.apply(_this$defs, arguments);
+ var dt = _time - this._time;
+ this.step(dt);
+ return this;
}
- },
- Defs: {
- pattern: wrapWithAttrCheck(function (width, height, block) {
- return this.put(new Pattern()).update(block).attr({
- x: 0,
- y: 0,
- width: width,
- height: height,
- patternUnits: 'userSpaceOnUse'
- });
- })
- }
- });
- register(Pattern);
-
- var Image =
- /*#__PURE__*/
- function (_Shape) {
- _inherits(Image, _Shape);
+ }, {
+ key: "duration",
+ value: function duration() {
+ return this._times * (this._wait + this._duration) - this._wait;
+ }
+ }, {
+ key: "loops",
+ value: function loops(p) {
+ var loopDuration = this._duration + this._wait;
- function Image(node) {
- _classCallCheck(this, Image);
+ if (p == null) {
+ var loopsDone = Math.floor(this._time / loopDuration);
+ var relativeTime = this._time - loopsDone * loopDuration;
+ var position = relativeTime / this._duration;
+ return Math.min(loopsDone + position, this._times);
+ }
- return _possibleConstructorReturn(this, _getPrototypeOf(Image).call(this, nodeOrNew('image', node), node));
- } // (re)load image
+ var whole = Math.floor(p);
+ var partial = p % 1;
+ var time = loopDuration * whole + this._duration * partial;
+ return this.time(time);
+ }
+ }, {
+ key: "position",
+ value: function position(p) {
+ // Get all of the variables we need
+ var x$$1 = this._time;
+ var d = this._duration;
+ var w = this._wait;
+ var t = this._times;
+ var s = this._swing;
+ var r = this._reverse;
+ var position;
+ if (p == null) {
+ /*
+ This function converts a time to a position in the range [0, 1]
+ The full explanation can be found in this desmos demonstration
+ https://www.desmos.com/calculator/u4fbavgche
+ The logic is slightly simplified here because we can use booleans
+ */
+ // Figure out the value without thinking about the start or end time
+ var f = function f(x$$1) {
+ var swinging = s * Math.floor(x$$1 % (2 * (w + d)) / (w + d));
+ var backwards = swinging && !r || !swinging && r;
+ var uncliped = Math.pow(-1, backwards) * (x$$1 % (w + d)) / d + backwards;
+ var clipped = Math.max(Math.min(uncliped, 1), 0);
+ return clipped;
+ }; // Figure out the value by incorporating the start time
- _createClass(Image, [{
- key: "load",
- value: function load(url, callback) {
- if (!url) return this;
- var img = new globals.window.Image();
- on(img, 'load', function (e) {
- var p = this.parent(Pattern); // ensure image size
- if (this.width() === 0 && this.height() === 0) {
- this.size(img.width, img.height);
- }
+ var endTime = t * (w + d) - w;
+ position = x$$1 <= 0 ? Math.round(f(1e-5)) : x$$1 < endTime ? f(x$$1) : Math.round(f(endTime - 1e-5));
+ return position;
+ } // Work out the loops done and add the position to the loops done
- if (p instanceof Pattern) {
- // ensure pattern size if not set
- if (p.width() === 0 && p.height() === 0) {
- p.size(this.width(), this.height());
- }
- }
- if (typeof callback === 'function') {
- callback.call(this, {
- width: img.width,
- height: img.height,
- ratio: img.width / img.height,
- url: url
- });
- }
- }, this);
- on(img, 'load error', function () {
- // dont forget to unbind memory leaking events
- off(img);
- });
- return this.attr('href', img.src = url, xlink);
+ var loopsDone = Math.floor(this.loops());
+ var swingForward = s && loopsDone % 2 === 0;
+ var forwards = swingForward && !r || r && swingForward;
+ position = loopsDone + (forwards ? p : 1 - p);
+ return this.loops(position);
}
- }]);
+ }, {
+ key: "progress",
+ value: function progress(p) {
+ if (p == null) {
+ return Math.min(1, this._time / this.duration());
+ }
- return Image;
- }(Shape);
- registerAttrHook(function (attr$$1, val, _this) {
- // convert image fill and stroke to patterns
- if (attr$$1 === 'fill' || attr$$1 === 'stroke') {
- if (isImage.test(val)) {
- val = _this.doc().defs().image(val);
+ return this.time(p * this.duration());
}
- }
+ }, {
+ key: "step",
+ value: function step(dt) {
+ // If we are inactive, this stepper just gets skipped
+ if (!this.enabled) return this; // Update the time and get the new position
- if (val instanceof Image) {
- val = _this.doc().defs().pattern(0, 0, function (pattern) {
- pattern.add(val);
- });
- }
+ dt = dt == null ? 16 : dt;
+ this._time += dt;
+ var position = this.position(); // Figure out if we need to run the stepper in this frame
- return val;
- });
- registerMethods({
- Container: {
- // create image element, load image and set its size
- image: wrapWithAttrCheck(function (source, callback) {
- return this.put(new Image()).size(0, 0).load(source, callback);
- })
- }
- });
- register(Image);
+ var running = this._lastPosition !== position && this._time >= 0;
+ this._lastPosition = position; // Figure out if we just started
- var PointArray = subClassArray('PointArray', SVGArray);
- extend(PointArray, {
- // Convert array to string
- toString: function toString() {
- // convert to a poly point string
- for (var i = 0, il = this.length, array = []; i < il; i++) {
- array.push(this[i].join(','));
- }
+ var duration = this.duration();
+ var justStarted = this._lastTime < 0 && this._time > 0;
+ var justFinished = this._lastTime < this._time && this.time > duration;
+ this._lastTime = this._time;
- return array.join(' ');
- },
- // Convert array to line object
- toLine: function toLine() {
- return {
- x1: this[0][0],
- y1: this[0][1],
- x2: this[1][0],
- y2: this[1][1]
- };
- },
- // Get morphed array at given position
- at: function at(pos) {
- // make sure a destination is defined
- if (!this.destination) return this; // generate morphed point string
+ if (justStarted) {
+ this.fire('start', this);
+ } // Work out if the runner is finished set the done flag here so animations
+ // know, that they are running in the last step (this is good for
+ // transformations which can be merged)
- for (var i = 0, il = this.length, array = []; i < il; i++) {
- array.push([this[i][0] + (this.destination[i][0] - this[i][0]) * pos, this[i][1] + (this.destination[i][1] - this[i][1]) * pos]);
- }
- return new PointArray(array);
- },
- // Parse point string and flat array
- parse: function parse() {
- var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [[0, 0]];
- var points = []; // if it is an array
+ var declarative = this._isDeclarative;
+ this.done = !declarative && !justFinished && this._time >= duration; // Call initialise and the run function
- if (array instanceof Array) {
- // and it is not flat, there is no need to parse it
- if (array[0] instanceof Array) {
- return array;
- }
- } else {
- // Else, it is considered as a string
- // parse points
- array = array.trim().split(delimiter).map(parseFloat);
- } // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
- // Odd number of coordinates is an error. In such cases, drop the last odd coordinate.
+ if (running || declarative) {
+ this._initialise(running); // clear the transforms on this runner so they dont get added again and again
- if (array.length % 2 !== 0) array.pop(); // wrap points in two-tuples and parse points as floats
+ this.transforms = new Matrix();
- for (var i = 0, len = array.length; i < len; i = i + 2) {
- points.push([array[i], array[i + 1]]);
- }
+ var converged = this._run(declarative ? dt : position);
- return points;
- },
- // Move point string
- move: function move(x, y) {
- var box = this.bbox(); // get relative offset
+ this.fire('step', this);
+ } // correct the done flag here
+ // declaritive animations itself know when they converged
- x -= box.x;
- y -= box.y; // move every point
- if (!isNaN(x) && !isNaN(y)) {
- for (var i = this.length - 1; i >= 0; i--) {
- this[i] = [this[i][0] + x, this[i][1] + y];
- }
- }
+ this.done = this.done || converged && declarative;
- return this;
- },
- // Resize poly string
- size: function size(width, height) {
- var i;
- var box = this.bbox(); // recalculate position of all points according to new size
+ if (this.done) {
+ this.fire('finish', this);
+ }
- for (i = this.length - 1; i >= 0; i--) {
- if (box.width) this[i][0] = (this[i][0] - box.x) * width / box.width + box.x;
- if (box.height) this[i][1] = (this[i][1] - box.y) * height / box.height + box.y;
+ return this;
}
+ }, {
+ key: "finish",
+ value: function finish() {
+ return this.step(Infinity);
+ }
+ }, {
+ key: "reverse",
+ value: function reverse(_reverse) {
+ this._reverse = _reverse == null ? !this._reverse : _reverse;
+ return this;
+ }
+ }, {
+ key: "ease",
+ value: function ease(fn) {
+ this._stepper = new Ease(fn);
+ return this;
+ }
+ }, {
+ key: "active",
+ value: function active(enabled) {
+ if (enabled == null) return this.enabled;
+ this.enabled = enabled;
+ return this;
+ }
+ /*
+ Private Methods
+ ===============
+ Methods that shouldn't be used externally
+ */
+ // Save a morpher to the morpher list so that we can retarget it later
- return this;
- },
- // Get bounding box of points
- bbox: function bbox() {
- var maxX = -Infinity;
- var maxY = -Infinity;
- var minX = Infinity;
- var minY = Infinity;
- this.forEach(function (el) {
- maxX = Math.max(el[0], maxX);
- maxY = Math.max(el[1], maxY);
- minX = Math.min(el[0], minX);
- minY = Math.min(el[1], minY);
- });
- return {
- x: minX,
- y: minY,
- width: maxX - minX,
- height: maxY - minY
- };
- }
- });
-
- var MorphArray = PointArray; // Move by left top corner over x-axis
-
- function x$1(x) {
- return x == null ? this.bbox().x : this.move(x, this.bbox().y);
- } // Move by left top corner over y-axis
-
- function y$1(y) {
- return y == null ? this.bbox().y : this.move(this.bbox().x, y);
- } // Set width of element
+ }, {
+ key: "_rememberMorpher",
+ value: function _rememberMorpher(method, morpher) {
+ this._history[method] = {
+ morpher: morpher,
+ caller: this._queue[this._queue.length - 1]
+ };
+ } // Try to set the target for a morpher if the morpher exists, otherwise
+ // do nothing and return false
- function width$1(width) {
- var b = this.bbox();
- return width == null ? b.width : this.size(width, b.height);
- } // Set height of element
+ }, {
+ key: "_tryRetarget",
+ value: function _tryRetarget(method, target) {
+ if (this._history[method]) {
+ // if the last method wasnt even initialised, throw it away
+ if (!this._history[method].caller.initialised) {
+ var index = this._queue.indexOf(this._history[method].caller);
- function height$1(height) {
- var b = this.bbox();
- return height == null ? b.height : this.size(b.width, height);
- }
+ this._queue.splice(index, 1);
- var pointed = /*#__PURE__*/Object.freeze({
- MorphArray: MorphArray,
- x: x$1,
- y: y$1,
- width: width$1,
- height: height$1
- });
+ return false;
+ } // for the case of transformations, we use the special retarget function
+ // which has access to the outer scope
- var Line =
- /*#__PURE__*/
- function (_Shape) {
- _inherits(Line, _Shape);
- // Initialize node
- function Line(node) {
- _classCallCheck(this, Line);
+ if (this._history[method].caller.retarget) {
+ this._history[method].caller.retarget(target); // for everything else a simple morpher change is sufficient
- return _possibleConstructorReturn(this, _getPrototypeOf(Line).call(this, nodeOrNew('line', node), node));
- } // Get array
+ } else {
+ this._history[method].morpher.to(target);
+ }
+ this._history[method].caller.finished = false;
+ var timeline$$1 = this.timeline();
+ timeline$$1 && timeline$$1._continue();
+ return true;
+ }
- _createClass(Line, [{
- key: "array",
- value: function array() {
- return new PointArray([[this.attr('x1'), this.attr('y1')], [this.attr('x2'), this.attr('y2')]]);
- } // Overwrite native plot() method
+ return false;
+ } // Run each initialise function in the runner if required
}, {
- key: "plot",
- value: function plot(x1, y1, x2, y2) {
- if (x1 == null) {
- return this.array();
- } else if (typeof y1 !== 'undefined') {
- x1 = {
- x1: x1,
- y1: y1,
- x2: x2,
- y2: y2
- };
- } else {
- x1 = new PointArray(x1).toLine();
+ key: "_initialise",
+ value: function _initialise(running) {
+ // If we aren't running, we shouldn't initialise when not declarative
+ if (!running && !this._isDeclarative) return; // Loop through all of the initialisers
+
+ for (var i = 0, len = this._queue.length; i < len; ++i) {
+ // Get the current initialiser
+ var current = this._queue[i]; // Determine whether we need to initialise
+
+ var needsIt = this._isDeclarative || !current.initialised && running;
+ running = !current.finished; // Call the initialiser if we need to
+
+ if (needsIt && running) {
+ current.initialiser.call(this);
+ current.initialised = true;
+ }
}
+ } // Run each run function for the position or dt given
- return this.attr(x1);
- } // Move by left top corner
+ }, {
+ key: "_run",
+ value: function _run(positionOrDt) {
+ // Run all of the _queue directly
+ var allfinished = true;
+
+ for (var i = 0, len = this._queue.length; i < len; ++i) {
+ // Get the current function to run
+ var current = this._queue[i]; // Run the function if its not finished, we keep track of the finished
+ // flag for the sake of declarative _queue
+
+ var converged = current.runner.call(this, positionOrDt);
+ current.finished = current.finished || converged === true;
+ allfinished = allfinished && current.finished;
+ } // We report when all of the constructors are finished
+
+ return allfinished;
+ }
}, {
- key: "move",
- value: function move(x, y) {
- return this.attr(this.array().move(x, y).toLine());
- } // Set element size to given width and height
+ key: "addTransform",
+ value: function addTransform(transform, index) {
+ this.transforms.lmultiplyO(transform);
+ return this;
+ }
+ }, {
+ key: "clearTransform",
+ value: function clearTransform() {
+ this.transforms = new Matrix();
+ return this;
+ } // TODO: Keep track of all transformations so that deletion is faster
}, {
- key: "size",
- value: function size(width, height) {
- var p = proportionalSize(this, width, height);
- return this.attr(this.array().size(p.width, p.height).toLine());
+ key: "clearTransformsFromQueue",
+ value: function clearTransformsFromQueue() {
+ if (!this.done) {
+ this._queue = this._queue.filter(function (item) {
+ return !item.isTransform;
+ });
+ }
}
- }]);
+ }], [{
+ key: "sanitise",
+ value: function sanitise(duration, delay, when) {
+ // Initialise the default parameters
+ var times = 1;
+ var swing = false;
+ var wait = 0;
+ duration = duration || timeline.duration;
+ delay = delay || timeline.delay;
+ when = when || 'last'; // If we have an object, unpack the values
- return Line;
- }(Shape);
- extend(Line, pointed);
- registerMethods({
- Container: {
- // Create a line element
- line: wrapWithAttrCheck(function () {
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
- args[_key] = arguments[_key];
+ if (_typeof(duration) === 'object' && !(duration instanceof Stepper)) {
+ delay = duration.delay || delay;
+ when = duration.when || when;
+ swing = duration.swing || swing;
+ times = duration.times || times;
+ wait = duration.wait || wait;
+ duration = duration.duration || timeline.duration;
}
- // make sure plot is called as a setter
- // x1 is not necessarily a number, it can also be an array, a string and a PointArray
- return Line.prototype.plot.apply(this.put(new Line()), args[0] != null ? args : [0, 0, 0, 0]);
- })
- }
- });
- register(Line);
+ return {
+ duration: duration,
+ delay: delay,
+ swing: swing,
+ times: times,
+ wait: wait,
+ when: when
+ };
+ }
+ }]);
- var Marker =
+ return Runner;
+ }(EventTarget);
+ Runner.id = 0;
+
+ var FakeRunner =
/*#__PURE__*/
- function (_Container) {
- _inherits(Marker, _Container);
+ function () {
+ function FakeRunner() {
+ var transforms = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Matrix();
+ var id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
+ var done = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
- // Initialize node
- function Marker(node) {
- _classCallCheck(this, Marker);
+ _classCallCheck(this, FakeRunner);
- return _possibleConstructorReturn(this, _getPrototypeOf(Marker).call(this, nodeOrNew('marker', node), node));
- } // Set width of element
+ this.transforms = transforms;
+ this.id = id;
+ this.done = done;
+ }
+ _createClass(FakeRunner, [{
+ key: "clearTransformsFromQueue",
+ value: function clearTransformsFromQueue() {}
+ }]);
- _createClass(Marker, [{
- key: "width",
- value: function width(_width) {
- return this.attr('markerWidth', _width);
- } // Set height of element
+ return FakeRunner;
+ }();
- }, {
- key: "height",
- value: function height(_height) {
- return this.attr('markerHeight', _height);
- } // Set marker refX and refY
+ extend([Runner, FakeRunner], {
+ mergeWith: function mergeWith(runner) {
+ return new FakeRunner(runner.transforms.lmultiply(this.transforms), runner.id);
+ }
+ }); // FakeRunner.emptyRunner = new FakeRunner()
- }, {
- key: "ref",
- value: function ref(x, y) {
- return this.attr('refX', x).attr('refY', y);
- } // Update marker
+ var lmultiply = function lmultiply(last, curr) {
+ return last.lmultiplyO(curr);
+ };
+ var getRunnerTransform = function getRunnerTransform(runner) {
+ return runner.transforms;
+ };
+
+ function mergeTransforms() {
+ // Find the matrix to apply to the element and apply it
+ var runners = this._transformationRunners.runners;
+ var netTransform = runners.map(getRunnerTransform).reduce(lmultiply, new Matrix());
+ this.transform(netTransform);
+
+ this._transformationRunners.merge();
+
+ if (this._transformationRunners.length() === 1) {
+ this._frameId = null;
+ }
+ }
+
+ var RunnerArray =
+ /*#__PURE__*/
+ function () {
+ function RunnerArray() {
+ _classCallCheck(this, RunnerArray);
+
+ this.runners = [];
+ this.ids = [];
+ }
+
+ _createClass(RunnerArray, [{
+ key: "add",
+ value: function add(runner) {
+ if (this.runners.includes(runner)) return;
+ var id = runner.id + 1;
+ var leftSibling = this.ids.reduce(function (last, curr) {
+ if (curr > last && curr < id) return curr;
+ return last;
+ }, 0);
+ var index = this.ids.indexOf(leftSibling) + 1;
+ this.ids.splice(index, 0, id);
+ this.runners.splice(index, 0, runner);
+ return this;
+ }
}, {
- key: "update",
- value: function update(block) {
- // remove all content
- this.clear(); // invoke passed block
+ key: "getByID",
+ value: function getByID(id) {
+ return this.runners[this.ids.indexOf(id + 1)];
+ }
+ }, {
+ key: "remove",
+ value: function remove(id) {
+ var index = this.ids.indexOf(id + 1);
+ this.ids.splice(index, 1);
+ this.runners.splice(index, 1);
+ return this;
+ }
+ }, {
+ key: "merge",
+ value: function merge() {
+ var _this2 = this;
+
+ var lastRunner = null;
+ this.runners.forEach(function (runner, i) {
+ if (lastRunner && runner.done && lastRunner.done) {
+ _this2.remove(runner.id);
- if (typeof block === 'function') {
- block.call(this, this);
- }
+ _this2.edit(lastRunner.id, runner.mergeWith(lastRunner));
+ }
+ lastRunner = runner;
+ });
return this;
- } // Return the fill id
-
+ }
}, {
- key: "toString",
- value: function toString() {
- return 'url(#' + this.id() + ')';
+ key: "edit",
+ value: function edit(id, newRunner) {
+ var index = this.ids.indexOf(id + 1);
+ this.ids.splice(index, 1, id);
+ this.runners.splice(index, 1, newRunner);
+ return this;
+ }
+ }, {
+ key: "length",
+ value: function length() {
+ return this.ids.length;
+ }
+ }, {
+ key: "clearBefore",
+ value: function clearBefore(id) {
+ var deleteCnt = this.ids.indexOf(id + 1) || 1;
+ this.ids.splice(0, deleteCnt, 0);
+ this.runners.splice(0, deleteCnt, new FakeRunner()).forEach(function (r) {
+ return r.clearTransformsFromQueue();
+ });
+ return this;
}
}]);
- return Marker;
- }(Container);
+ return RunnerArray;
+ }();
+
+ var frameId = 0;
registerMethods({
- Container: {
- marker: function marker() {
- var _this$defs;
+ Element: {
+ animate: function animate(duration, delay, when) {
+ var o = Runner.sanitise(duration, delay, when);
+ var timeline$$1 = this.timeline();
+ return new Runner(o.duration).loop(o).element(this).timeline(timeline$$1).schedule(delay, when);
+ },
+ delay: function delay(by, when) {
+ return this.animate(0, by, when);
+ },
+ // this function searches for all runners on the element and deletes the ones
+ // which run before the current one. This is because absolute transformations
+ // overwfrite anything anyway so there is no need to waste time computing
+ // other runners
+ _clearTransformRunnersBefore: function _clearTransformRunnersBefore(currentRunner) {
+ this._transformationRunners.clearBefore(currentRunner.id);
+ },
+ _currentTransform: function _currentTransform(current) {
+ return this._transformationRunners.runners // we need the equal sign here to make sure, that also transformations
+ // on the same runner which execute before the current transformation are
+ // taken into account
+ .filter(function (runner) {
+ return runner.id <= current.id;
+ }).map(getRunnerTransform).reduce(lmultiply, new Matrix());
+ },
+ addRunner: function addRunner(runner) {
+ this._transformationRunners.add(runner);
- // Create marker element in defs
- return (_this$defs = this.defs()).marker.apply(_this$defs, arguments);
+ Animator.transform_frame(mergeTransforms.bind(this), this._frameId);
+ },
+ _prepareRunner: function _prepareRunner() {
+ if (this._frameId == null) {
+ this._transformationRunners = new RunnerArray().add(new FakeRunner(new Matrix(this)));
+ this._frameId = frameId++;
+ }
}
+ }
+ });
+ extend(Runner, {
+ attr: function attr(a, v) {
+ return this.styleAttr('attr', a, v);
},
- Defs: {
- // Create marker
- marker: wrapWithAttrCheck(function (width, height, block) {
- // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto
- return this.put(new Marker()).size(width, height).ref(width / 2, height / 2).viewbox(0, 0, width, height).attr('orient', 'auto').update(block);
- })
+ // Add animatable styles
+ css: function css(s, v) {
+ return this.styleAttr('css', s, v);
},
- marker: {
- // Create and attach markers
- marker: function marker(_marker, width, height, block) {
- var attr = ['marker']; // Build attribute name
+ styleAttr: function styleAttr(type, name, val) {
+ // apply attributes individually
+ if (_typeof(name) === 'object') {
+ for (var key in val) {
+ this.styleAttr(type, key, val[key]);
+ }
+ }
- if (_marker !== 'all') attr.push(_marker);
- attr = attr.join('-'); // Set marker attribute
+ var morpher = new Morphable(this._stepper).to(val);
+ this.queue(function () {
+ morpher = morpher.from(this.element()[type](name));
+ }, function (pos) {
+ this.element()[type](name, morpher.at(pos));
+ return morpher.done();
+ });
+ return this;
+ },
+ zoom: function zoom(level, point) {
+ var morpher = new Morphable(this._stepper).to(new SVGNumber(level));
+ this.queue(function () {
+ morpher = morpher.from(this.zoom());
+ }, function (pos) {
+ this.element().zoom(morpher.at(pos), point);
+ return morpher.done();
+ });
+ return this;
+ },
- _marker = arguments[1] instanceof Marker ? arguments[1] : this.defs().marker(width, height, block);
- return this.attr(attr, _marker);
- }
- }
- });
- register(Marker);
+ /**
+ ** absolute transformations
+ **/
+ //
+ // M v -----|-----(D M v = F v)------|-----> T v
+ //
+ // 1. define the final state (T) and decompose it (once)
+ // t = [tx, ty, the, lam, sy, sx]
+ // 2. on every frame: pull the current state of all previous transforms
+ // (M - m can change)
+ // and then write this as m = [tx0, ty0, the0, lam0, sy0, sx0]
+ // 3. Find the interpolated matrix F(pos) = m + pos * (t - m)
+ // - Note F(0) = M
+ // - Note F(1) = T
+ // 4. Now you get the delta matrix as a result: D = F * inv(M)
+ transform: function transform(transforms, relative, affine) {
+ // If we have a declarative function, we should retarget it if possible
+ relative = transforms.relative || relative;
- var Path =
- /*#__PURE__*/
- function (_Shape) {
- _inherits(Path, _Shape);
+ if (this._isDeclarative && !relative && this._tryRetarget('transform', transforms)) {
+ return this;
+ } // Parse the parameters
- // Initialize node
- function Path(node) {
- _classCallCheck(this, Path);
- return _possibleConstructorReturn(this, _getPrototypeOf(Path).call(this, nodeOrNew('path', node), node));
- } // Get array
+ var isMatrix = Matrix.isMatrixLike(transforms);
+ affine = transforms.affine != null ? transforms.affine : affine != null ? affine : !isMatrix; // Create a morepher and set its type
+ var morpher = new Morphable(this._stepper).type(affine ? TransformBag : Matrix);
+ var origin;
+ var element;
+ var current;
+ var currentAngle;
+ var startTransform;
- _createClass(Path, [{
- key: "array",
- value: function array() {
- return this._array || (this._array = new PathArray(this.attr('d')));
- } // Plot new path
+ function setup() {
+ // make sure element and origin is defined
+ element = element || this.element();
+ origin = origin || getOrigin(transforms, element);
+ startTransform = new Matrix(relative ? undefined : element); // add the runner to the element so it can merge transformations
- }, {
- key: "plot",
- value: function plot(d) {
- return d == null ? this.array() : this.clear().attr('d', typeof d === 'string' ? d : this._array = new PathArray(d));
- } // Clear array cache
+ element.addRunner(this); // Deactivate all transforms that have run so far if we are absolute
- }, {
- key: "clear",
- value: function clear() {
- delete this._array;
- return this;
- } // Move by left top corner
+ if (!relative) {
+ element._clearTransformRunnersBefore(this);
+ }
+ }
- }, {
- key: "move",
- value: function move(x, y) {
- return this.attr('d', this.array().move(x, y));
- } // Move by left top corner over x-axis
+ function run(pos) {
+ // clear all other transforms before this in case something is saved
+ // on this runner. We are absolute. We dont need these!
+ if (!relative) this.clearTransform();
- }, {
- key: "x",
- value: function x(_x) {
- return _x == null ? this.bbox().x : this.move(_x, this.bbox().y);
- } // Move by left top corner over y-axis
+ var _transform = new Point(origin).transform(element._currentTransform(this)),
+ x$$1 = _transform.x,
+ y$$1 = _transform.y;
- }, {
- key: "y",
- value: function y(_y) {
- return _y == null ? this.bbox().y : this.move(this.bbox().x, _y);
- } // Set element size to given width and height
+ var target = new Matrix(_objectSpread({}, transforms, {
+ origin: [x$$1, y$$1]
+ }));
+ var start = this._isDeclarative && current ? current : startTransform;
- }, {
- key: "size",
- value: function size(width, height) {
- var p = proportionalSize(this, width, height);
- return this.attr('d', this.array().size(p.width, p.height));
- } // Set width of element
+ if (affine) {
+ target = target.decompose(x$$1, y$$1);
+ start = start.decompose(x$$1, y$$1); // Get the current and target angle as it was set
- }, {
- key: "width",
- value: function width(_width) {
- return _width == null ? this.bbox().width : this.size(_width, this.bbox().height);
- } // Set height of element
+ var rTarget = target.rotate;
+ var rCurrent = start.rotate; // Figure out the shortest path to rotate directly
+
+ var possibilities = [rTarget - 360, rTarget, rTarget + 360];
+ var distances = possibilities.map(function (a) {
+ return Math.abs(a - rCurrent);
+ });
+ var shortest = Math.min.apply(Math, _toConsumableArray(distances));
+ var index = distances.indexOf(shortest);
+ target.rotate = possibilities[index];
+ }
+
+ if (relative) {
+ // we have to be careful here not to overwrite the rotation
+ // with the rotate method of Matrix
+ if (!isMatrix) {
+ target.rotate = transforms.rotate || 0;
+ }
+
+ if (this._isDeclarative && currentAngle) {
+ start.rotate = currentAngle;
+ }
+ }
- }, {
- key: "height",
- value: function height(_height) {
- return _height == null ? this.bbox().height : this.size(this.bbox().width, _height);
- }
- }, {
- key: "targets",
- value: function targets() {
- return baseFind('svg textpath [href*="' + this.id() + '"]');
+ morpher.from(start);
+ morpher.to(target);
+ var affineParameters = morpher.at(pos);
+ currentAngle = affineParameters.rotate;
+ current = new Matrix(affineParameters);
+ this.addTransform(current);
+ return morpher.done();
}
- }]);
-
- return Path;
- }(Shape); // Define morphable array
- Path.prototype.MorphArray = PathArray; // Add parent method
- registerMethods({
- Container: {
- // Create a wrapped path element
- path: wrapWithAttrCheck(function (d) {
- // make sure plot is called as a setter
- return this.put(new Path()).plot(d || new PathArray());
- })
- }
- });
- register(Path);
+ function retarget(newTransforms) {
+ // only get a new origin if it changed since the last call
+ if ((newTransforms.origin || 'center').toString() !== (transforms.origin || 'center').toString()) {
+ origin = getOrigin(transforms, element);
+ } // overwrite the old transformations with the new ones
- function array() {
- return this._array || (this._array = new PointArray(this.attr('points')));
- } // Plot new path
- function plot(p) {
- return p == null ? this.array() : this.clear().attr('points', typeof p === 'string' ? p : this._array = new PointArray(p));
- } // Clear array cache
+ transforms = _objectSpread({}, newTransforms, {
+ origin: origin
+ });
+ }
- function clear() {
- delete this._array;
- return this;
- } // Move by left top corner
+ this.queue(setup, run, retarget, true);
+ this._isDeclarative && this._rememberMorpher('transform', morpher);
+ return this;
+ },
+ // Animatable x-axis
+ x: function x$$1(_x, relative) {
+ return this._queueNumber('x', _x);
+ },
+ // Animatable y-axis
+ y: function y$$1(_y) {
+ return this._queueNumber('y', _y);
+ },
+ dx: function dx(x$$1) {
+ return this._queueNumberDelta('x', x$$1);
+ },
+ dy: function dy(y$$1) {
+ return this._queueNumberDelta('y', y$$1);
+ },
+ _queueNumberDelta: function _queueNumberDelta(method, to$$1) {
+ to$$1 = new SVGNumber(to$$1); // Try to change the target if we have this method already registerd
- function move(x, y) {
- return this.attr('points', this.array().move(x, y));
- } // Set element size to given width and height
+ if (this._tryRetarget(method, to$$1)) return this; // Make a morpher and queue the animation
- function size(width, height) {
- var p = proportionalSize(this, width, height);
- return this.attr('points', this.array().size(p.width, p.height));
- }
+ var morpher = new Morphable(this._stepper).to(to$$1);
+ var from$$1 = null;
+ this.queue(function () {
+ from$$1 = this.element()[method]();
+ morpher.from(from$$1);
+ morpher.to(from$$1 + to$$1);
+ }, function (pos) {
+ this.element()[method](morpher.at(pos));
+ return morpher.done();
+ }, function (newTo) {
+ morpher.to(from$$1 + new SVGNumber(newTo));
+ }); // Register the morpher so that if it is changed again, we can retarget it
- var poly = /*#__PURE__*/Object.freeze({
- array: array,
- plot: plot,
- clear: clear,
- move: move,
- size: size
- });
+ this._rememberMorpher(method, morpher);
- var Polygon =
- /*#__PURE__*/
- function (_Shape) {
- _inherits(Polygon, _Shape);
+ return this;
+ },
+ _queueObject: function _queueObject(method, to$$1) {
+ // Try to change the target if we have this method already registerd
+ if (this._tryRetarget(method, to$$1)) return this; // Make a morpher and queue the animation
- // Initialize node
- function Polygon(node) {
- _classCallCheck(this, Polygon);
+ var morpher = new Morphable(this._stepper).to(to$$1);
+ this.queue(function () {
+ morpher.from(this.element()[method]());
+ }, function (pos) {
+ this.element()[method](morpher.at(pos));
+ return morpher.done();
+ }); // Register the morpher so that if it is changed again, we can retarget it
- return _possibleConstructorReturn(this, _getPrototypeOf(Polygon).call(this, nodeOrNew('polygon', node), node));
- }
+ this._rememberMorpher(method, morpher);
- return Polygon;
- }(Shape);
- registerMethods({
- Container: {
- // Create a wrapped polygon element
- polygon: wrapWithAttrCheck(function (p) {
- // make sure plot is called as a setter
- return this.put(new Polygon()).plot(p || new PointArray());
- })
- }
- });
- extend(Polygon, pointed);
- extend(Polygon, poly);
- register(Polygon);
+ return this;
+ },
+ _queueNumber: function _queueNumber(method, value) {
+ return this._queueObject(method, new SVGNumber(value));
+ },
+ // Animatable center x-axis
+ cx: function cx$$1(x$$1) {
+ return this._queueNumber('cx', x$$1);
+ },
+ // Animatable center y-axis
+ cy: function cy$$1(y$$1) {
+ return this._queueNumber('cy', y$$1);
+ },
+ // Add animatable move
+ move: function move(x$$1, y$$1) {
+ return this.x(x$$1).y(y$$1);
+ },
+ // Add animatable center
+ center: function center(x$$1, y$$1) {
+ return this.cx(x$$1).cy(y$$1);
+ },
+ // Add animatable size
+ size: function size(width$$1, height$$1) {
+ // animate bbox based size for all other elements
+ var box;
- var Polyline =
- /*#__PURE__*/
- function (_Shape) {
- _inherits(Polyline, _Shape);
+ if (!width$$1 || !height$$1) {
+ box = this._element.bbox();
+ }
- // Initialize node
- function Polyline(node) {
- _classCallCheck(this, Polyline);
+ if (!width$$1) {
+ width$$1 = box.width / box.height * height$$1;
+ }
- return _possibleConstructorReturn(this, _getPrototypeOf(Polyline).call(this, nodeOrNew('polyline', node), node));
- }
+ if (!height$$1) {
+ height$$1 = box.height / box.width * width$$1;
+ }
- return Polyline;
- }(Shape);
- registerMethods({
- Container: {
- // Create a wrapped polygon element
- polyline: wrapWithAttrCheck(function (p) {
- // make sure plot is called as a setter
- return this.put(new Polyline()).plot(p || new PointArray());
- })
- }
- });
- extend(Polyline, pointed);
- extend(Polyline, poly);
- register(Polyline);
+ return this.width(width$$1).height(height$$1);
+ },
+ // Add animatable width
+ width: function width$$1(_width) {
+ return this._queueNumber('width', _width);
+ },
+ // Add animatable height
+ height: function height$$1(_height) {
+ return this._queueNumber('height', _height);
+ },
+ // Add animatable plot
+ plot: function plot(a, b, c, d) {
+ // Lines can be plotted with 4 arguments
+ if (arguments.length === 4) {
+ return this.plot([a, b, c, d]);
+ }
- var Rect =
- /*#__PURE__*/
- function (_Shape) {
- _inherits(Rect, _Shape);
+ var morpher = this._element.MorphArray().to(a);
- // Initialize node
- function Rect(node) {
- _classCallCheck(this, Rect);
+ this.queue(function () {
+ morpher.from(this._element.array());
+ }, function (pos) {
+ this._element.plot(morpher.at(pos));
+ });
+ return this;
+ },
+ // Add leading method
+ leading: function leading(value) {
+ return this._queueNumber('leading', value);
+ },
+ // Add animatable viewbox
+ viewbox: function viewbox(x$$1, y$$1, width$$1, height$$1) {
+ return this._queueObject('viewbox', new Box(x$$1, y$$1, width$$1, height$$1));
+ },
+ update: function update(o) {
+ if (_typeof(o) !== 'object') {
+ return this.update({
+ offset: arguments[0],
+ color: arguments[1],
+ opacity: arguments[2]
+ });
+ }
- return _possibleConstructorReturn(this, _getPrototypeOf(Rect).call(this, nodeOrNew('rect', node), node));
+ if (o.opacity != null) this.attr('stop-opacity', o.opacity);
+ if (o.color != null) this.attr('stop-color', o.color);
+ if (o.offset != null) this.attr('offset', o.offset);
+ return this;
}
-
- return Rect;
- }(Shape);
- extend(Rect, {
- rx: rx,
- ry: ry
});
- registerMethods({
- Container: {
- // Create a rect element
- rect: wrapWithAttrCheck(function (width$$1, height$$1) {
- return this.put(new Rect()).size(width$$1, height$$1);
- })
- }
+ extend(Runner, {
+ rx: rx,
+ ry: ry,
+ from: from,
+ to: to
});
- register(Rect);
function plain(text) {
// clear if build mode is disabled
extend(Shape, getMethodsFor('Shape')); // extend(Element, getConstructor('Memory'))
extend(Container, getMethodsFor('Container'));
+ extend(Runner, getMethodsFor('Runner'));
List.extend(getMethodNames());
registerMorphableType([SVGNumber, Color, Box, Matrix, SVGArray, PointArray, PathArray]);
makeMorphable();