UNPKG

wilderness-core

Version:
504 lines (435 loc) 14.3 kB
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; }; /* globals __DEV__ */ import { currentReverse, iterationsComplete, position as getPosition } from './timeline'; /** * An event. * * @typedef {Object} Event * * @property {number} at - The time the event occured. * @property {string} name - The event name. * @property {Object} options - Any additional event data. */ /** * A Timeline event subscription. * * @typedef {Object} EventSubscription * * @property {function} callback * @property {string} name * @property {number} token */ /** * An object to hold Timeline EventSubscriptions, and subscribe/unsubscribe functions. * * @typedef {Object} EventObject * * @property {Object} previousPlaybackOptions * @property {Object} previousState * @property {function} subscribe - A function to subscribe to Timeline events. * @property {EventSubscription[]} subscriptions * @property {function} unsubscribe - A function to unsubscribe to Timeline events. */ /** * Token incrementor. */ var t = 0; /** * Accepted event names. */ var acceptedEventNames = ['timeline.start', 'timeline.finish', 'shape.start', 'shape.finish', 'keyframe', 'frame']; /** * An EventObject creator. * * @param {Timeline} timeline * * @returns {EventObject} * * @example * event(timeline) */ var event = function event(timeline) { return { previousPlaybackOptions: {}, previousState: {}, subscribe: subscribe(timeline), subscriptions: [], unsubscribe: unsubscribe(timeline) }; }; /** * Is a Timeline active? * * @param {Timeline} timeline * * @returns {boolean} * * @example * active(timeline) */ var active = function active(_ref) { var event = _ref.event, state = _ref.state; return state.started && (!state.finished || typeof event.previousState === 'undefined' || !event.previousState.finished); }; /** * A unique list of Timeline EventSubscription names. * * @param {Timeline} timeline * * @returns {string[]} * * @example * activeEventNames(timeline) */ var activeEventNames = function activeEventNames(_ref2) { var subscriptions = _ref2.event.subscriptions; var s = []; for (var i = 0, l = subscriptions.length; i < l; i++) { var name = subscriptions[i].name; if (s.indexOf(name) === -1) { s.push(name); } } return s; }; /** * Run EventSubscription callbacks for every event that has occured since last check. * * @param {Timeline} timeline * * @example * events(timeline) */ var events = function events(timeline) { if (playbackOptionsChanged(timeline)) { timeline.event.previousPlaybackOptions = {}; timeline.event.previousState = {}; } var subscriptions = timeline.event.subscriptions; if (subscriptions.length && active(timeline)) { var eventNames = activeEventNames(timeline); var queue = eventQueue(timeline, eventNames); for (var i = 0, l = queue.length; i < l; i++) { var _event = queue[i]; var eventName = _event.name; var options = _event.options || {}; for (var _i = 0, _l = subscriptions.length; _i < _l; _i++) { var subscription = subscriptions[_i]; if (eventName === subscription.name) { subscription.callback(options); } } } } timeline.event.previousPlaybackOptions = _extends({}, timeline.playbackOptions); timeline.event.previousState = _extends({}, timeline.state); }; /** * An array of Events that have occured since last checked. * * @param {Timeline} timeline * @param {string[]} eventNames * * @returns {Event[]} * * @example * eventQueue(timeline, eventNames) */ var eventQueue = function eventQueue(_ref3, eventNames) { var previousState = _ref3.event.previousState, playbackOptions = _ref3.playbackOptions, state = _ref3.state, timelineShapes = _ref3.timelineShapes; var queue = []; var alternate = playbackOptions.alternate, duration = playbackOptions.duration, initialIterations = playbackOptions.initialIterations, iterations = playbackOptions.iterations, reverse = playbackOptions.reverse, started = playbackOptions.started; var max = started + duration * state.iterationsComplete; var min = typeof previousState.iterationsComplete !== 'undefined' ? started + duration * previousState.iterationsComplete + 1 : 0; var getTimestamps = function getTimestamps(pos) { return positionTimestamps({ alternate: alternate, duration: duration, initialIterations: initialIterations, iterations: iterations, max: max, min: min, position: pos, reverse: reverse, started: started }); }; if (eventNames.indexOf('timeline.start') !== -1) { var timestamps = getTimestamps(0); for (var i = 0, l = timestamps.length; i < l; i++) { queue.push({ name: 'timeline.start', at: timestamps[i] }); } } if (eventNames.indexOf('timeline.finish') !== -1) { var _timestamps = getTimestamps(1); for (var _i2 = 0, _l2 = _timestamps.length; _i2 < _l2; _i2++) { queue.push({ name: 'timeline.finish', at: _timestamps[_i2] }); } } if (eventNames.indexOf('shape.start') !== -1) { for (var _i3 = 0, _l3 = timelineShapes.length; _i3 < _l3; _i3++) { var _timelineShapes$_i = timelineShapes[_i3], shapeName = _timelineShapes$_i.shape.name, start = _timelineShapes$_i.timelinePosition.start; var _timestamps2 = getTimestamps(start); for (var _i = 0, _l = _timestamps2.length; _i < _l; _i++) { queue.push({ name: 'shape.start', at: _timestamps2[_i], options: { shapeName: shapeName } }); } } } if (eventNames.indexOf('shape.finish') !== -1) { for (var _i4 = 0, _l4 = timelineShapes.length; _i4 < _l4; _i4++) { var _timelineShapes$_i2 = timelineShapes[_i4], shapeName = _timelineShapes$_i2.shape.name, finish = _timelineShapes$_i2.timelinePosition.finish; var _timestamps3 = getTimestamps(finish); for (var _i5 = 0, _l5 = _timestamps3.length; _i5 < _l5; _i5++) { queue.push({ name: 'shape.finish', at: _timestamps3[_i5], options: { shapeName: shapeName } }); } } } if (eventNames.indexOf('keyframe') !== -1) { for (var _i6 = 0, _l6 = timelineShapes.length; _i6 < _l6; _i6++) { var _timelineShapes$_i3 = timelineShapes[_i6], _timelineShapes$_i3$s = _timelineShapes$_i3.shape, shapeName = _timelineShapes$_i3$s.name, keyframes = _timelineShapes$_i3$s.keyframes, _timelineShapes$_i3$t = _timelineShapes$_i3.timelinePosition, start = _timelineShapes$_i3$t.start, finish = _timelineShapes$_i3$t.finish; for (var _i7 = 0, _l7 = keyframes.length; _i7 < _l7; _i7++) { var _keyframes$_i = keyframes[_i7], keyframeName = _keyframes$_i.name, position = _keyframes$_i.position; var keyframePosition = start + (finish - start) * position; var _timestamps4 = getTimestamps(keyframePosition); for (var __i = 0, __l = _timestamps4.length; __i < __l; __i++) { queue.push({ name: 'keyframe', at: _timestamps4[__i], options: { keyframeName: keyframeName, shapeName: shapeName } }); } } } } if (eventNames.indexOf('frame') !== -1) { queue.push({ name: 'frame', at: max }); } return queue.sort(oldest); }; /** * A sort function for Events. * * @param {Event} a * @param {Event} b * * @returns {number} * * @example * oldest(event1, event2) */ var oldest = function oldest(a, b) { return a.at === b.at ? 0 : a.at < b.at ? -1 : 1; }; /** * Have playbackOptions changed since last check? * * @param {Timeline} timeline * * @return {boolean} * * @example * playbackOptionsChanged(timeline) */ var playbackOptionsChanged = function playbackOptionsChanged(timeline) { return JSON.stringify(timeline.playbackOptions) !== JSON.stringify(timeline.event.previousPlaybackOptions); }; /** * Timestamps at which a Timeline was at a Position. * * @param {Object} opts * @param {boolean} opts.alternate * @param {number} opts.duration * @param {number} initialIterations * @param {number} iterations * @param {number} opts.max - The maximum bound within which to look for timestamps. * @param {number} opts.min - The minimum bound within which to look for timestamps. * @param {Position} opts.position - The Position in question. * @param {boolean} opts.reverse * @param {number} opts.started * * @returns {number[]} * * @example * positionTimestamps(opts) */ var positionTimestamps = function positionTimestamps(_ref4) { var alternate = _ref4.alternate, duration = _ref4.duration, initialIterations = _ref4.initialIterations, iterations = _ref4.iterations, max = _ref4.max, min = _ref4.min, position = _ref4.position, reverse = _ref4.reverse, started = _ref4.started; var startedPosition = getPosition(initialIterations, reverse); var finishedTimestamp = started + duration * iterations; var timestamps = function timestamps(timestamp) { if (timestamp <= max) { var timestampReverse = currentReverse({ alternate: alternate, initialIterations: initialIterations, iterations: iterations, reverse: reverse }, iterationsComplete({ duration: duration, iterations: iterations, started: started }, timestamp)); var positionAtEnd = position === 0 || position === 1; var timelineFinished = timestamp === finishedTimestamp; var finishedAtPosition = position === 0 && timestampReverse || position === 1 && !timestampReverse; if (timestamp <= finishedTimestamp && (!positionAtEnd || !timelineFinished || finishedAtPosition)) { var _t = timestamp >= min ? [timestamp] : []; return _t.concat(timestamps(timestamp + timeToSamePosition({ alternate: alternate, duration: duration, position: position, reverse: timestampReverse }))); } } return []; }; return timestamps(started + timeToPosition({ alternate: alternate, duration: duration, from: startedPosition, reverse: reverse, to: position })); }; /** * The number of milliseconds between two Positions during Timeline playback. * * @param {Object} opts * @param {boolean} opts.alternate * @param {number} opts.duration * @param {Position} opts.from - The from Position. * @param {boolean} opts.reverse - Is Timeline in reverse at the from Position? * @param {Position} opts.to - The to Position. * * @returns {number} * * @example * timeToPosition(opts) */ var timeToPosition = function timeToPosition(_ref5) { var alternate = _ref5.alternate, duration = _ref5.duration, from = _ref5.from, reverse = _ref5.reverse, to = _ref5.to; return duration * (alternate ? reverse ? from < to ? to + from : from - to : from > to ? 2 - (to + from) : to - from : reverse ? from === 1 && to === 0 ? 1 : (1 - to + from) % 1 : from === 0 && to === 1 ? 1 : (1 - from + to) % 1); }; /** * The number of milliseconds between the same Position during Timeline playback. * * @param {Object} opts * @param {boolean} opts.alternate * @param {number} opts.duration * @param {Position} opts.position * @param {boolean} opts.reverse - Is Timeline in reverse at the Position? * * @returns {number} * * @example * timeToSamePosition(opts) */ var timeToSamePosition = function timeToSamePosition(_ref6) { var alternate = _ref6.alternate, duration = _ref6.duration, position = _ref6.position, reverse = _ref6.reverse; return duration * (alternate ? reverse ? (position === 0 ? 1 : position) * 2 : 2 - (position === 1 ? 0 : position) * 2 : 1); }; /** * Creates a subscribe function. * The created function adds an EventSubscription to the subscriptions * property of an EventObject. * * @param {Timeline} timeline * * @returns {function} * * @example * subscribe(timeline)('timeline.start', () => console.log('timeline.start')) */ var subscribe = function subscribe(timeline) { return function (name, callback) { if (validEventName(name)) { if (process.env.NODE_ENV !== 'production' && typeof callback !== 'function') { throw new TypeError('The subscribe functions second argument must be of type function'); } var token = ++t; timeline.event.subscriptions.push({ name: name, callback: callback, token: token }); return token; } }; }; /** * Is an event name valid? * * @param {string} name * * @throws {TypeError} Throws if not valid * * @returns {true} * * @example * validEventName('timeline.start') */ var validEventName = function validEventName(name) { if (process.env.NODE_ENV !== 'production') { if (typeof name !== 'string') { throw new TypeError('The subscribe functions first argument must be of type string'); } if (acceptedEventNames.indexOf(name) === -1) { throw new TypeError('The subscribe functions first argument was not a valid event name'); } } return true; }; /** * Creates an unsubscribe function. * Created function removes an EventSubscription from the subscriptions * property of an EventObject, given the Event token. * * @param {Timeline} timeline * * @returns {function} * * @example * unsubscribe(timeline)(token) */ var unsubscribe = function unsubscribe(timeline) { return function (token) { var subscriptions = timeline.event.subscriptions; var matchIndex = void 0; for (var i = 0, l = subscriptions.length; i < l; i++) { if (subscriptions[i].token === token) { matchIndex = i; } } if (typeof matchIndex !== 'undefined') { timeline.event.subscriptions.splice(matchIndex, 1); return true; } return false; }; }; export { activeEventNames, event, eventQueue, oldest, playbackOptionsChanged, positionTimestamps, subscribe, timeToPosition, timeToSamePosition, unsubscribe, validEventName }; export default events;