UNPKG

@nent/core

Version:

Functional elements to add routing, data-binding, dynamic HTML, declarative actions, audio, video, and so much more. Supercharge static HTML files into web apps without script or builds.

186 lines (182 loc) 6.16 kB
/*! * NENT 2022 */ import { r as registerInstance, d as createEvent, h, a as getElement, H as Host } from './index-916ca544.js'; import { E as EventEmitter, e as eventBus } from './index-f7016b94.js'; import { R as ROUTE_EVENTS } from './interfaces-3b78db83.js'; import { s as state } from './state-adf07580.js'; import { T as TIMER_EVENTS } from './interfaces-aed4a5ac.js'; import { g as getTimeDetails } from './time-d244c045.js'; import { f as debugIf } from './logging-5a93c8af.js'; import { t as throttle } from './promises-584c4ece.js'; import './index-4bfabbbd.js'; /* It's a timer that uses the browser's animation frame to emit events at a given interval */ class FrameTimer extends EventEmitter { /** * It creates a new timer object that will fire an event every `interval` milliseconds, and will fire * a final event when the timer has run for `durationSeconds` * @param {AnimationFrameProvider} provider - AnimationFrameProvider * @param {number} interval - The interval at which to emit the current time. * @param {number} durationSeconds - The duration of the timer in seconds. * @param getStart - () => number = performance.now, * @param {null | (() => void)} [onInterval=null] - a callback that will be called every interval * milliseconds. * @param {boolean} [debug=false] - boolean - if true, will log to console.log */ constructor(provider, interval, durationSeconds, getStart = performance.now, onInterval = null, debug = false) { super(); this.provider = provider; this.interval = interval; this.durationSeconds = durationSeconds; this.getStart = getStart; this.onInterval = onInterval; this.debug = debug; this.timer = 0; this.start = 0; this.durationMs = 0; this.durationMs = this.durationSeconds * 1000; debugIf(this.debug, `presentation-timer: starting timer w/ ${this.durationSeconds} duration`); this.currentTime = getTimeDetails(this.start, this.start, this.durationMs); if (this.interval > 0) this.debouncedInterval = throttle(this.interval, () => { this.timer = this.provider.requestAnimationFrame(current => { this.doInterval(current); }); }, true, true); else this.debouncedInterval = () => { this.timer = this.provider.requestAnimationFrame(current => { this.doInterval(current); }); }; } /** * We start the timer by setting the start time, and then we call the `doInterval` function */ begin() { if (this.timer) this.stop(); this.start = this.getStart(); this.currentTime = getTimeDetails(this.start, this.start, this.durationMs); this.provider.requestAnimationFrame(async (current) => { await this.doInterval(current); }); } /** * It stops the animation by cancelling the requestAnimationFrame */ stop() { this.provider.cancelAnimationFrame(this.timer); } async doInterval(time) { var _a; this.currentTime = getTimeDetails(this.start, time, this.durationMs); if (this.currentTime.ended) { this.stop(); this.emit(TIMER_EVENTS.OnEnd, this.currentTime); } else { this.emit(TIMER_EVENTS.OnInterval, this.currentTime); await this.debouncedInterval(); } (_a = this.onInterval) === null || _a === void 0 ? void 0 : _a.call(this); } /** * It stops the timer and removes all listeners. */ destroy() { this.stop(); this.removeAllListeners(); } } const PresentationTimer = class { constructor(hostRef) { registerInstance(this, hostRef); this.ready = createEvent(this, "ready", 7); this.elapsed = 0; /** * To debug timed elements, set this value to true. */ this.debug = false; /** * Duration before the timer stops and raises * the ended event (seconds). 0 = never */ this.duration = 0; /** * Interval in milliseconds to request * between the getAnimationFrame. This * affects the precision. */ this.interval = 200; /** * Display elapsed seconds */ this.display = false; /** * If set, disables auto-starting the timer * on render. This will be removed if in a view, * when the view is activated or when the start * method is called. */ this.deferLoad = false; this.now = () => performance.now(); } get currentRoute() { var _a; const parent = this.el.closest('n-view-prompt') || this.el.closest('n-view'); if (parent) return parent.route; return ((_a = state.router) === null || _a === void 0 ? void 0 : _a.exactRoute) || null; } /** * Begin the timer. This is called automatically * by the presentation element. */ async begin() { this.timer.begin(); } /** * Stop the timer. */ async stop() { this.timer.stop(); } componentWillLoad() { this.timer = new FrameTimer(window, this.interval, this.duration, () => this.now(), () => { this.elapsed = this.timer.currentTime.elapsedSeconds; }, this.debug); } render() { return h(Host, null, this.display ? this.elapsed : null); } componentDidLoad() { var _a, _b; this.ready.emit(true); if (this.currentRoute) { debugIf(this.debug, `n-presentation-timer: syncing to route ${this.currentRoute.path}`); if ((_b = (_a = this.currentRoute) === null || _a === void 0 ? void 0 : _a.match) === null || _b === void 0 ? void 0 : _b.isExact) { this.timer.begin(); } this.navigationSubscription = eventBus.on(ROUTE_EVENTS.RouteChanged, () => { var _a, _b; if ((_b = (_a = this.currentRoute) === null || _a === void 0 ? void 0 : _a.match) === null || _b === void 0 ? void 0 : _b.isExact) { this.timer.begin(); } else { this.timer.stop(); } }); } else { this.timer.begin(); } } disconnectedCallback() { var _a; this.timer.destroy(); (_a = this.navigationSubscription) === null || _a === void 0 ? void 0 : _a.call(this); } get el() { return getElement(this); } }; export { PresentationTimer as n_presentation_timer };