| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- /* html-ppt :: fx-runtime.js
- * Canvas FX autoloader + lifecycle manager.
- * - Dynamically loads all fx modules listed in FX_LIST
- * - Initializes [data-fx] elements when their slide becomes active
- * - Calls handle.stop() when the slide leaves
- */
- (function(){
- 'use strict';
- const FX_LIST = [
- '_util',
- 'particle-burst','confetti-cannon','firework','starfield','matrix-rain',
- 'knowledge-graph','neural-net','constellation','orbit-ring','galaxy-swirl',
- 'word-cascade','letter-explode','chain-react','magnetic-field','data-stream',
- 'gradient-blob','sparkle-trail','shockwave','typewriter-multi','counter-explosion'
- ];
- // Resolve base path of this script so it works from any page location.
- const myScript = document.currentScript || (function(){
- const all = document.getElementsByTagName('script');
- for (const s of all){ if (s.src && s.src.indexOf('fx-runtime.js')>-1) return s; }
- return null;
- })();
- const base = myScript ? myScript.src.replace(/fx-runtime\.js.*$/, 'fx/') : 'assets/animations/fx/';
- let loaded = 0;
- const total = FX_LIST.length;
- const ready = new Promise((resolve) => {
- if (!total) return resolve();
- FX_LIST.forEach((name) => {
- const s = document.createElement('script');
- s.src = base + name + '.js';
- s.async = false;
- s.onload = s.onerror = () => { if (++loaded >= total) resolve(); };
- document.head.appendChild(s);
- });
- });
- window.__hpxActive = window.__hpxActive || new Map();
- function initFxIn(root){
- if (!window.HPX) return;
- const els = root.querySelectorAll('[data-fx]');
- els.forEach((el) => {
- if (window.__hpxActive.has(el)) return;
- const name = el.getAttribute('data-fx');
- const fn = window.HPX[name];
- if (typeof fn !== 'function') return;
- try {
- const handle = fn(el, {}) || { stop(){} };
- window.__hpxActive.set(el, handle);
- } catch(e){ console.warn('[hpx-fx]', name, e); }
- });
- }
- function stopFxIn(root){
- const els = root.querySelectorAll('[data-fx]');
- els.forEach((el) => {
- const h = window.__hpxActive.get(el);
- if (h && typeof h.stop === 'function'){
- try{ h.stop(); }catch(e){}
- }
- window.__hpxActive.delete(el);
- });
- }
- function reinitFxIn(root){
- stopFxIn(root);
- initFxIn(root);
- }
- window.__hpxReinit = reinitFxIn;
- function boot(){
- ready.then(() => {
- const active = document.querySelector('.slide.is-active') || document.querySelector('.slide');
- if (active) initFxIn(active);
- // Watch all slides for class changes
- const slides = document.querySelectorAll('.slide');
- slides.forEach((sl) => {
- const mo = new MutationObserver((muts) => {
- for (const m of muts){
- if (m.attributeName === 'class'){
- if (sl.classList.contains('is-active')) initFxIn(sl);
- else stopFxIn(sl);
- }
- }
- });
- mo.observe(sl, { attributes: true, attributeFilter: ['class'] });
- });
- });
- }
- if (document.readyState === 'loading'){
- document.addEventListener('DOMContentLoaded', boot);
- } else {
- boot();
- }
- })();
|