@nent/core
Version:
186 lines (182 loc) • 6.16 kB
JavaScript
/*!
* 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 };