wilderness-core
Version:
The SVG animation engine behind Wilderness
570 lines (473 loc) • 15.4 kB
JavaScript
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
/* globals __DEV__ */
import { add, cubify } from 'points';
import clone from './clone';
import { output } from './middleware';
import { toPoints } from 'svg-points';
import { updateState } from './timeline';
/**
* Shape data as specified by the
* {@link https://github.com/colinmeinke/points Points spec}.
*
* @typedef {Object[]} Points
*/
/**
* The data required to render a shape.
*
* @typedef {Object} FrameShape
*
* @property {Points} points
* @property {Object} attributes
* @property {FrameShape[]} childFrameShapes
*/
/**
* A FrameShape array.
*
* @typedef {FrameShape[]} Frame
*/
/**
* A number between 0 and 1 (inclusive).
*
* @typedef {number} Position
*/
/**
* The structure of FrameShape Points.
* An array represents a shape. A number represents a line.
* An array that has nested arrays represents a group of shapes.
*
* @typedef {(number|number[])[]} PointStructure
*/
/**
* The curve structure of FrameShape Points.
* A boolean represents a point, and designates if the point is a curve.
*
* @typedef {(boolean|boolean[])[]} CurveStructure
*/
/**
* Converts FrameShape Points to curves based on a CurveStructure.
*
* @param {FrameShape} frameShape
* @param {CurveStructure} structure
*
* @returns {FrameShape}
*
* @example
* applyCurveStructure(frameShape, stucture)
*/
var applyCurveStructure = function applyCurveStructure(frameShape, structure) {
var points = frameShape.points;
var childFrameShapes = frameShape.childFrameShapes;
if (childFrameShapes) {
var nextChildFrameShapes = [];
for (var i = 0, l = childFrameShapes.length; i < l; i++) {
nextChildFrameShapes.push(applyCurveStructure(childFrameShapes[i], structure[i]));
}
frameShape.childFrameShapes = nextChildFrameShapes;
} else {
var curves = false;
for (var _i2 = 0, _l2 = structure.length; _i2 < _l2; _i2++) {
if (structure[_i2]) {
curves = true;
break;
}
}
if (curves) {
var nextPoints = [];
var cubifiedPoints = cubify(points);
for (var _i3 = 0, _l3 = cubifiedPoints.length; _i3 < _l3; _i3++) {
var point = cubifiedPoints[_i3];
if (structure[_i3] && !point.curve) {
nextPoints.push(_extends({}, point, {
curve: {
type: 'cubic',
x1: points[_i3 - 1].x,
y1: points[_i3 - 1].y,
x2: points[_i3].x,
y2: points[_i3].y
}
}));
} else {
nextPoints.push(point);
}
}
frameShape.points = nextPoints;
}
}
return frameShape;
};
/**
* Restructures a FrameShape's Points based on a PointStructure.
*
* @param {FrameShape} frameShape
* @param {PointStructure} structure
*
* @returns {FrameShape}
*
* @example
* applyPointStructure(frameShape, stucture)
*/
var applyPointStructure = function applyPointStructure(frameShape, structure) {
if (Array.isArray(structure[0])) {
if (!frameShape.childFrameShapes) {
frameShape.childFrameShapes = [clone(frameShape)];
delete frameShape.points;
}
if (frameShape.childFrameShapes.length !== structure.length) {
for (var i = 0, l = structure.length; i < l; i++) {
if (i >= frameShape.childFrameShapes.length) {
var previous = frameShape.childFrameShapes[i - 1].points;
frameShape.childFrameShapes.push({
attributes: clone(frameShape.attributes),
points: [_extends({}, clone(previous[previous.length - 1]), { moveTo: true }), clone(previous[previous.length - 1])]
});
}
}
}
var nextChildFrameShapes = [];
for (var _i4 = 0, _l4 = frameShape.childFrameShapes.length; _i4 < _l4; _i4++) {
nextChildFrameShapes.push(applyPointStructure(frameShape.childFrameShapes[_i4], structure[_i4]));
}
frameShape.childFrameShapes = nextChildFrameShapes;
} else {
var lines = splitLines(frameShape.points);
for (var _i5 = 0, _l5 = structure.length; _i5 < _l5; _i5++) {
var desiredPoints = structure[_i5];
if (!lines[_i5]) {
var previousLine = lines[_i5 - 1];
lines[_i5] = [_extends({}, clone(previousLine[previousLine.length - 1]), { moveTo: true }), clone(previousLine[previousLine.length - 1])];
}
if (desiredPoints > lines[_i5].length) {
lines[_i5] = add(lines[_i5], desiredPoints);
}
}
frameShape.points = joinLines(lines);
}
return frameShape;
};
/**
* Add a value to a PointStucture at a defined position.
*
* @param {PointStructure} structure
* @param {(number|number[])} value - Value to add to PointStructure.
* @param {number} i - Position to add value at.
*
* @example
* addToPointStructure([], 9, 0)
*/
var addToPointStructure = function addToPointStructure(structure, value, i) {
if (Array.isArray(value)) {
if (!Array.isArray(structure[i])) {
structure[i] = [structure[i]];
}
for (var _i = 0, l = value.length; _i < l; _i++) {
structure[i] = addToPointStructure(structure[i], value[_i], _i);
}
} else {
if (Array.isArray(structure[i])) {
addToPointStructure(structure[i], value, 0);
} else {
structure[i] = Math.max(structure[i] || 0, value);
}
}
return structure;
};
/**
* Creates a common CurveStructure from an array of CurveStructures.
*
* @param {CurveStructure[]} structures
*
* @returns {CurveStructure}
*
* @example
* commonCurveStructure(structures)
*/
var commonCurveStructure = function commonCurveStructure(structures) {
var structure = structures[0];
for (var i = 1, l = structures.length; i < l; i++) {
var s = structures[i];
var c = [];
for (var _i = 0, _l = structure.length; _i < _l; _i++) {
var x = structure[_i];
if (Array.isArray(x)) {
c.push(commonCurveStructure([x, s[_i]]));
} else {
c.push(x || s[_i]);
}
}
structure = c;
}
return structure;
};
/**
* Creates a common PointStructure from an array of PointStructures.
*
* @param {PointStructure[]} structures
*
* @returns {PointStructure}
*
* @example
* commonPointStructure(structures)
*/
var commonPointStructure = function commonPointStructure(structures) {
var structure = [];
for (var i = 0, l = structures.length; i < l; i++) {
var s = structures[i];
for (var _i = 0, _l = s.length; _i < _l; _i++) {
structure = addToPointStructure(structure, s[_i], _i);
}
}
return structure;
};
/**
* The current Frame of a Timeline.
*
* @param {Timeline} timeline
* @param {number} [at]
*
* @returns {Frame}
*
* @example
* frame(timeline)
*/
var frame = function frame(timeline, at) {
if (process.env.NODE_ENV !== 'production' && ((typeof timeline === 'undefined' ? 'undefined' : _typeof(timeline)) !== 'object' || !timeline.timelineShapes || !timeline.playbackOptions)) {
throw new TypeError('The frame function\'s first argument must be a Timeline');
}
if (process.env.NODE_ENV !== 'production' && typeof at !== 'undefined' && typeof at !== 'number') {
throw new TypeError('The frame function\'s second argument must be of type number');
}
updateState(timeline, typeof at !== 'undefined' ? at : Date.now());
var frameShapes = [];
var timelineShapes = timeline.timelineShapes;
for (var i = 0, l = timelineShapes.length; i < l; i++) {
var timelineShape = timelineShapes[i];
var shape = timelineShape.shape;
var keyframes = shape.keyframes;
var timelinePosition = timelineShape.timelinePosition;
var start = timelinePosition.start;
var finish = timelinePosition.finish;
var position = timeline.state.position;
if (position <= start) {
frameShapes.push(output(keyframes[0].frameShape, timeline.middleware));
} else if (position >= finish) {
frameShapes.push(output(keyframes[keyframes.length - 1].frameShape, timeline.middleware));
} else {
var shapePosition = (position - start) / (finish - start);
frameShapes.push(frameShapeFromShape(shape, shapePosition, timeline.middleware));
}
}
return frameShapes;
};
/**
* Creates a FrameShape from a PlainShapeObject.
*
* @param {PlainShapeObject} plainShapeObject
*
* @returns {FrameShape}
*
* @example
* frameShapeFromPlainShapeObject(circle)
*/
var frameShapeFromPlainShapeObject = function frameShapeFromPlainShapeObject(_ref) {
var childPlainShapeObjects = _ref.shapes,
plainShapeObject = _objectWithoutProperties(_ref, ['shapes']);
var type = plainShapeObject.type,
height = plainShapeObject.height,
width = plainShapeObject.width,
x = plainShapeObject.x,
y = plainShapeObject.y,
cx = plainShapeObject.cx,
cy = plainShapeObject.cy,
r = plainShapeObject.r,
rx = plainShapeObject.rx,
ry = plainShapeObject.ry,
x1 = plainShapeObject.x1,
x2 = plainShapeObject.x2,
y1 = plainShapeObject.y1,
y2 = plainShapeObject.y2,
d = plainShapeObject.d,
points = plainShapeObject.points,
shapes = plainShapeObject.shapes,
attributes = _objectWithoutProperties(plainShapeObject, ['type', 'height', 'width', 'x', 'y', 'cx', 'cy', 'r', 'rx', 'ry', 'x1', 'x2', 'y1', 'y2', 'd', 'points', 'shapes']);
if (plainShapeObject.type === 'g' && childPlainShapeObjects) {
var childFrameShapes = [];
for (var i = 0, l = childPlainShapeObjects.length; i < l; i++) {
childFrameShapes.push(frameShapeFromPlainShapeObject(childPlainShapeObjects[i]));
}
return { attributes: attributes, childFrameShapes: childFrameShapes };
}
return {
attributes: attributes,
points: toPoints(plainShapeObject)
};
};
/**
* Creates a FrameShape from a Shape given the Position.
*
* @param {Shape} shape
* @param {Position} position
* @param {Middleware[]} middleware
*
* @returns {FrameShape}
*
* @example
* frameShapeFromShape(shape, 0.75, [])
*/
var frameShapeFromShape = function frameShapeFromShape(shape, position, middleware) {
var keyframes = shape.keyframes;
var fromIndex = 0;
for (var i = 0, l = keyframes.length; i < l; i++) {
if (position > keyframes[i].position) {
fromIndex = i;
}
}
var toIndex = fromIndex + 1;
var from = keyframes[fromIndex];
var to = keyframes[toIndex];
var keyframePosition = (position - from.position) / (to.position - from.position);
var forces = to.tween.forces;
var frameShape = tween(from.frameShape, to.frameShape, to.tween.easing, keyframePosition);
for (var _i6 = 0, _l6 = forces.length; _i6 < _l6; _i6++) {
frameShape = forces[_i6](frameShape, keyframePosition);
}
return output(frameShape, middleware);
};
/**
* Joins an array of Points into Points.
*
* @param {Points[]} lines
*
* @returns {Points}
*
* @example
* joinLines([ shape1, shape2 ])
*/
var joinLines = function joinLines(lines) {
var _ref2;
return (_ref2 = []).concat.apply(_ref2, _toConsumableArray(lines));
};
/**
* Creates a CurveStructure from a FrameShape.
*
* @param {FrameShape} frameShape
*
* @returns {CurveStructure}
*
* @example
* curveStructure(frameShape)
*/
var curveStructure = function curveStructure(_ref3) {
var points = _ref3.points,
childFrameShapes = _ref3.childFrameShapes;
var s = [];
if (childFrameShapes) {
for (var i = 0, l = childFrameShapes.length; i < l; i++) {
s.push(curveStructure(childFrameShapes[i]));
}
} else {
for (var _i7 = 0, _l7 = points.length; _i7 < _l7; _i7++) {
s.push(typeof points[_i7].curve !== 'undefined');
}
}
return s;
};
/**
* Creates a PointStructure from a FrameShape.
*
* @param {FrameShape} frameShape
*
* @returns {PointStructure}
*
* @example
* pointStructure(frameShape)
*/
var pointStructure = function pointStructure(_ref4) {
var points = _ref4.points,
childFrameShapes = _ref4.childFrameShapes;
if (childFrameShapes) {
var s = [];
for (var i = 0, l = childFrameShapes.length; i < l; i++) {
s.push(pointStructure(childFrameShapes[i]));
}
return s;
}
var structure = [];
for (var _i8 = 0, _l8 = points.length; _i8 < _l8; _i8++) {
if (points[_i8].moveTo) {
structure.push(1);
} else {
structure[structure.length - 1]++;
}
}
return structure;
};
/**
* Splits Points at moveTo commands.
*
* @param {Points} points
*
* @return {Points[]}
*
* @example
* splitLines(points)
*/
var splitLines = function splitLines(points) {
var lines = [];
for (var i = 0, l = points.length; i < l; i++) {
var point = points[i];
if (point.moveTo) {
lines.push([point]);
} else {
lines[lines.length - 1].push(point);
}
}
return lines;
};
/**
* Tween between any two values.
*
* @param {*} from
* @param {*} to - An identicle structure to the from param
* @param {function} easing - The easing function to apply
* @param {Position} position
*
* @returns {*}
*
* @example
* tween(0, 100, easeOut, 0.75)
*/
var tween = function tween(from, to, easing, position) {
if (typeof from === 'number') {
if (process.env.NODE_ENV !== 'production' && typeof to !== 'number') {
throw new TypeError('The tween function\'s from and to arguments must be of an identicle structure');
}
if (from === to) {
return from;
}
return easing(position, from, to, 1);
} else if (Array.isArray(from)) {
if (process.env.NODE_ENV !== 'production' && !Array.isArray(to)) {
throw new TypeError('The tween function\'s from and to arguments must be of an identicle structure');
}
var arr = [];
for (var i = 0, l = from.length; i < l; i++) {
arr.push(tween(from[i], to[i], easing, position));
}
return arr;
} else if (from !== null && (typeof from === 'undefined' ? 'undefined' : _typeof(from)) === 'object') {
if (process.env.NODE_ENV !== 'production' && to !== null && (typeof to === 'undefined' ? 'undefined' : _typeof(to)) !== 'object') {
throw new TypeError('The tween function\'s from and to arguments must be of an identicle structure');
}
var obj = {};
for (var k in from) {
obj[k] = tween(from[k], to[k], easing, position);
}
return obj;
}
return from;
};
export { addToPointStructure, applyCurveStructure, applyPointStructure, commonCurveStructure, commonPointStructure, curveStructure, frameShapeFromPlainShapeObject, joinLines, pointStructure, splitLines, tween };
export default frame;