UNPKG

@micosmo/ticker

Version:

System for managing processes that are dependent on timer tick events

263 lines (238 loc) 8.29 kB
/* * aframe-ticker.js * * Aframe implementaion of the ticker.js system. */ import aframe from 'aframe'; import ticker from './ticker'; import { declareMethod, method } from '@micosmo/core/method'; import { onLoadedDo } from '@micosmo/aframe/startup'; export { looper, iterator, waiter, sWaiter, msWaiter, timer, sTimer, msTimer } from './ticker'; export function startProcess(...args) { return checkOnLoadedDoTicker(processArgs('startProcess', args), process => process.start()); } export function createProcess(...args) { return checkOnLoadedDoTicker(processArgs('createProcess', args), () => { }); } function checkOnLoadedDoTicker(cfg, fComplete) { if (typeof cfg.ticker === 'function') { const f = cfg.ticker; delete cfg.ticker; const process = ticker.createProcess(cfg); onLoadedDo(() => { process.defaultTicker = f(); fComplete(process); }); return process; } const process = ticker.createProcess(cfg); fComplete(process); return process; } function processArgs(fn, args) { let cfg, fTyTicker, tkr; if (args.length < 1) return args; if (args.length === 1 && typeof args[0] === 'object') { cfg = args[0]; // ticker: ticker | el | selector (non-cascading) | default // locateTicker: el | selector (cascading) // tryLocateTicker: el | selector (cascading) // tocker: ticker | el | selector (non-cascading) | default // locateTocker: el | selector (cascading) // tryLocateTocker: el | selector (cascading) let cnt = 0; if ((tkr = cfg.ticker)) { fTyTicker = sel => getTicker(sel); cnt++; } else if ((tkr = cfg.locateTicker)) { fTyTicker = sel => locateTicker(sel); delete cfg.locateTicker; cnt++; } else if ((tkr = cfg.tryLocateTicker)) { fTyTicker = sel => tryLocateTicker(sel); delete cfg.tryLocateTicker; cnt++; } else if ((tkr = cfg.tocker)) { fTyTicker = sel => getTocker(sel); delete cfg.tocker; cnt++; } else if ((tkr = cfg.locateTocker)) { fTyTicker = sel => locateTocker(sel); delete cfg.locateTocker; cnt++; } else if ((tkr = cfg.tryLocateTocker)) { fTyTicker = sel => tryLocateTocker(sel); delete cfg.tryLocateTocker; cnt++; } if (cnt > 1) throw new Error(`ticker:${fn}: Either 'ticker', 'tocker', 'locateTicker' or 'locateTocker`); } else { cfg = {}; if (args.length >= 1) cfg.onTick = args[0]; if (args.length > 1) { fTyTicker = sel => locateTicker(sel); tkr = args[1]; } } if (!tkr) return cfg; if (typeof tkr === 'object' && tkr.isaTicker) cfg.ticker = tkr; else cfg.ticker = () => fTyTicker(tkr); // tkr must be a selector or element return cfg; } export function locateTicker(startEl) { return _locateTicker(startEl, 'locateTicker', true); } export function tryLocateTicker(startEl) { return _locateTicker(startEl, 'locateTicker', false); } export function getTicker(el) { return _getTicker(el, 'getTicker'); } export function locateTocker(startEl) { return _locateTicker(startEl, 'locateTocker', true); } export function tryLocateTocker(startEl) { return _locateTicker(startEl, 'locateTocker', false); } export function getTocker(el) { return _getTicker(el, 'getTocker'); } function _locateTicker(startEl, fn, flWarning) { if (typeof startEl === 'string') { if (startEl === 'default') return fn === 'locateTocker' ? defTocker : defTicker; const sel = startEl; startEl = document.querySelector(sel); if (!startEl) throw new Error(`ticker:${fn}: Invalid selector '${sel}'`); } else if (startEl.isaTicker) return startEl; for (let el = startEl; el; el = el.parentElement) { const compTicker = findTicker(el, fn === 'locateTocker'); if (compTicker) return compTicker.ticker; if (el === startEl.sceneEl) break; } if (flWarning) console.warn(`ticker:${fn}: No ${fn === 'locateTocker' ? 'tockers' : 'tickers'} visible in element hierarchy. Attaching to default. Start element ${startEl}`); return fn === 'locateTocker' ? defTocker : defTicker; } function _getTicker(el, fn) { if (typeof el === 'string') { if (el === 'default') return fn === 'getTocker' ? defTocker : defTicker; const sel = el; el = document.querySelector(sel); if (!el) throw new Error(`ticker:${fn}: Invalid selector '${sel}'`); } else if (el.isaTicker) return el; const compTicker = findTicker(el, fn === 'getTocker'); if (!compTicker) throw new Error(`ticker:${fn}: Element does not have a '${fn === 'getTocker' ? 'tocker' : 'ticker'}' component`); return compTicker.ticker; } function findTicker(el, flTocker) { for (const name of Object.getOwnPropertyNames(el.components)) { const c = el.components[name]; if ((flTocker && c.isaTocker) || (!flTocker && c.isaTicker)) return c; }; return undefined } const defTicker = ticker.DefaultTicker; const defTocker = ticker.Ticker('DefaultTocker'); var start = declareMethod(function () { return this.ticker.start(); }); var stop = declareMethod(function () { return this.ticker.stop(); }); var pause = declareMethod(function () { return this.ticker.pause(); }); var play = declareMethod(function () { return this.ticker.start(); }); aframe.registerSystem("ticker", { init() { this.ticker = defTicker; this.isaTicker = true; console.info(`system:ticker:init: Ticker '${defTicker.name}' initialised`); }, tick(tm, dt) { defTicker.tick(tm, dt) }, start: method(start), stop: method(stop), pause: method(pause) }); aframe.registerComponent("ticker", { multiple: true, init() { const id = this.id || this.el.id || '<anonymous>'; if (id === '<anonymous>') console.warn('component:ticker:init: Missing element or component id for Ticker name.'); this.ticker = ticker.Ticker(id); this.isaTicker = true; console.info(`component:ticker:init: Ticker '${this.ticker.name}' initialised`); }, tick(tm, dt) { this.ticker.tick(tm, dt) }, start: method(start), stop: method(stop), pause: method(pause), play: method(play) }); var flDefaultTockerStarted = false; export function startDefaultTocker() { if (flDefaultTockerStarted) return; aframe.registerSystem("tocker", { init() { this.ticker = defTocker; this.isaTocker = true; console.info(`system:tocker:init: Tocker '${defTocker.name}' initialised`); }, tock(tm, dt) { defTocker.tick(tm, dt) }, start: method(start), stop: method(stop), pause: method(pause) }); flDefaultTockerStarted = true; } aframe.registerComponent("tocker", { multiple: true, init() { const id = this.id || this.el.id || '<anonymous>'; if (id === '<anonymous>') console.warn('component:tocker:init: Missing element or component id for Ticker name.'); this.ticker = ticker.Ticker(id); this.isaTocker = true; console.info(`component:tocker:init: Ticker '${this.ticker.name}' initialised`); }, tock(tm, dt) { this.ticker.tick(tm, dt) }, start: method(start), stop: method(stop), pause: method(pause), play: method(play) }); export function beater(s, ...args) { return ticker.beater(s, beaterFunction(args)); } export function sBeater(s, ...args) { return ticker.sBeater(s, beaterFunction(args)); } export function msBeater(ms, ...args) { return ticker.msBeater(ms, beaterFunction(args)); } function beaterFunction(args) { const [spec, evt] = args; if (!spec) throw new Error(`micosmo:aframe-ticker:beaterFunction: Function or element spec and event name required`); if (typeof spec === 'function') return spec; const el = typeof spec === 'string' ? document.querySelector(spec) : spec; if (!el) throw new Error(`micosmo:aframe-ticker:beaterFunction: Invalid document selector '${spec}'`); return function (tm, dt, data, name) { el.emit(evt || 'heartbeat', { name, elapsed: data }, false); return 'more'; } }