UNPKG

react-sequencer

Version:

Step based sequencer to give your components reliable states for transitions and animations.

327 lines (313 loc) 13.6 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : (global = global || self, factory(global.reactSequencer = {}, global.React)); }(this, (function (exports, react) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } var PlayStatus; (function (PlayStatus) { PlayStatus["IDLE"] = "STATUS_IDLE"; PlayStatus["PLAYING"] = "STATUS_PLAYING"; })(PlayStatus || (PlayStatus = {})); var onNextTick; var cancelNextTick; if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') { onNextTick = window.requestAnimationFrame; cancelNextTick = window.cancelAnimationFrame; } else if (typeof setTimeout === 'function') { onNextTick = function (func) { return setTimeout(func, 15); }; cancelNextTick = clearTimeout; } else { throw new Error('React sequencer depends on requestAnimationFrame, please use a polyfill if not available in the browser.'); } var Ticker = /** @class */ (function () { function Ticker() { var _this = this; this.startLoop = function () { if (!_this.isActive) { _this.isActive = true; _this.currentTimeStamp = Date.now(); _this.requestID = onNextTick(_this._onLoop); } }; this.stopLoop = function () { if (_this.isActive) { _this.isActive = false; cancelNextTick(_this.requestID); } }; this._onLoop = function () { _this.currentTimeStamp = Date.now(); for (var i = 0; i < _this.subscriptions.length; i++) { var fn = _this.subscriptions[i]; fn(_this.currentTimeStamp); } if (_this.isActive) { _this.requestID = onNextTick(_this._onLoop); } }; this.currentTimeStamp = Date.now(); this.isActive = false; this.requestID = null; this.subscriptions = []; } Ticker.prototype.onTick = function (fn) { if (this.subscriptions.length === 0) { this.startLoop(); } this.subscriptions.push(fn); }; Ticker.prototype.offTick = function (fn) { var index = this.subscriptions.indexOf(fn); if (index !== -1) { this.subscriptions.splice(index, 1); } if (this.subscriptions.length === 0) { this.stopLoop(); } }; return Ticker; }()); var ticker = new Ticker(); var Sequencer = /** @class */ (function () { function Sequencer(props) { var _this = this; this.onLoop = function (now) { if (_this.status !== PlayStatus.PLAYING) { return; } _this.currentTimeIn = now - _this.startedAt; var currentStep = _this.getCurrentStep(); var completesAt = currentStep.endPos; if (_this.currentTimeIn >= completesAt) { if (_this.currentStepIndex === _this.steps.length - 1) { if (_this.endMode === 'start') { _this.stop(); return; } if (_this.endMode === 'end') { _this.complete(); return; } if (_this.endMode === 'loop') { _this.startedAt = now; _this.goToStepByIndex(0); _this.notifyChange(); } } else { _this.currentStepIndex++; _this.notifyChange(); } } }; this.onChange = function (fn) { _this.subscriptions.push(fn); return function () { var index = _this.subscriptions.findIndex(function (f) { return f === fn; }); if (index !== -1) { _this.subscriptions.splice(index, 1); } }; }; this.play = function () { if (_this.status === PlayStatus.PLAYING) { return; } if (_this.isComplete()) { _this.goToStepByIndex(0); } _this.status = PlayStatus.PLAYING; ticker.onTick(_this.onLoop); _this.startedAt = ticker.currentTimeStamp - _this.currentTimeIn; _this.notifyChange(); }; this.pause = function () { if (_this.status === PlayStatus.PLAYING) { _this.status = PlayStatus.IDLE; ticker.offTick(_this.onLoop); _this.notifyChange(); } }; this.stop = function () { _this.goToStepByIndex(0); _this.status = PlayStatus.IDLE; ticker.offTick(_this.onLoop); _this.notifyChange(); }; this.complete = function () { _this.currentStepIndex = _this.steps.length - 1; _this.currentTimeIn = _this.totalDuration; _this.status = PlayStatus.IDLE; ticker.offTick(_this.onLoop); _this.notifyChange(); }; this.isStopped = function () { return _this.currentTimeIn === 0 && _this.status === PlayStatus.IDLE; }; this.isComplete = function () { return _this.currentTimeIn >= _this.totalDuration; }; this.isPlaying = function () { return _this.status === PlayStatus.PLAYING; }; this.isBefore = function (stepName) { var stepIndex = _this.steps.findIndex(function (step) { return step.name === stepName; }); if (stepIndex === -1) { throw new Error("Sequencer step " + stepName + " not found."); } return _this.currentStepIndex < stepIndex; }; this.isAfter = function (stepName) { var stepIndex = _this.steps.findIndex(function (step) { return step.name === stepName; }); if (stepIndex === -1) { throw new Error("Sequencer step " + stepName + " not found."); } return _this.currentStepIndex > stepIndex; }; this.getState = function () { var state = { current: _this.steps[_this.currentStepIndex].name, index: _this.currentStepIndex, isPlaying: _this.isPlaying(), isComplete: _this.isComplete(), isStopped: _this.isStopped(), }; return state; }; this.getApi = function () { var _a = _this, play = _a.play, pause = _a.pause, stop = _a.stop, complete = _a.complete, isBefore = _a.isBefore, isAfter = _a.isAfter; var api = { play: play, pause: pause, stop: stop, complete: complete, isBefore: isBefore, isAfter: isAfter, }; return api; }; var defaults = { steps: [], loop: false, complete: false, endMode: 'end', autoplay: false, }; var options = __assign(__assign({}, defaults), props); if (options.steps.length === 0) { throw new Error('React Sequencer: At least one step required in options'); } this.steps = this.generateSteps(options.steps); this.totalDuration = this.steps[this.steps.length - 1].endPos; this.currentStepIndex = 0; this.currentTimeIn = 0; this.startedAt = 0; this.endMode = options.loop ? 'loop' : options.endMode; this.status = PlayStatus.IDLE; this.subscriptions = []; if (options.complete === true) { this.goToStepByIndex(this.steps.length - 1); } if (options.autoplay === true) { this.play(); } this.prevState = this.getState(); } Sequencer.prototype.generateSteps = function (stepsInput) { if (!stepsInput) { throw new Error('Invalid format.'); } var prev = 0; var steps = stepsInput.map(function (step) { if (!Array.isArray(step) || step.length !== 2) { throw new Error('Invalid format. See docs for correct structure.'); } var startPos = prev; var endPos = step[1] + prev; prev = endPos; return { startPos: startPos, endPos: endPos, name: step[0], }; }); return steps; }; Sequencer.prototype.notifyChange = function () { var state = this.getState(); if (!this.prevState || state.index !== this.prevState.index || state.isPlaying !== this.prevState.isPlaying) { this.subscriptions.forEach(function (fn) { fn(state); }); } this.prevState = state; }; Sequencer.prototype.goToStepByIndex = function (index) { this.currentStepIndex = index; this.currentTimeIn = this.steps[index].startPos; }; Sequencer.prototype.getCurrentStep = function () { return this.steps[this.currentStepIndex]; }; return Sequencer; }()); function useSequencer(options) { var sequencerRef = react.useRef(new Sequencer(options)); var sequencerApi = react.useRef(sequencerRef.current.getApi()); var _a = react.useState(sequencerRef.current.getState()), sequencerState = _a[0], setSequencer = _a[1]; react.useEffect(function () { function handleStateChange(sequencerState) { setSequencer(sequencerState); } return sequencerRef.current.onChange(handleStateChange); }, []); return [sequencerState, sequencerApi.current]; } var SequencerWrapper = function (props) { var children = props.children, options = __rest(props, ["children"]); var _a = useSequencer(options), state = _a[0], api = _a[1]; return children(state, api); }; exports.Sequencer = SequencerWrapper; exports.useSequencer = useSequencer; Object.defineProperty(exports, '__esModule', { value: true }); }))); //# sourceMappingURL=index.umd.js.map