返回 html-ppt-skill
fx-runtime.js
根目录 / assets / animations / fx-runtime.js
1 /* html-ppt :: fx-runtime.js
2 * Canvas FX autoloader + lifecycle manager.
3 * - Dynamically loads all fx modules listed in FX_LIST
4 * - Initializes [data-fx] elements when their slide becomes active
5 * - Calls handle.stop() when the slide leaves
6 */
7 (function(){
8 'use strict';
9
10 const FX_LIST = [
11 '_util',
12 'particle-burst','confetti-cannon','firework','starfield','matrix-rain',
13 'knowledge-graph','neural-net','constellation','orbit-ring','galaxy-swirl',
14 'word-cascade','letter-explode','chain-react','magnetic-field','data-stream',
15 'gradient-blob','sparkle-trail','shockwave','typewriter-multi','counter-explosion'
16 ];
17
18 // Resolve base path of this script so it works from any page location.
19 const myScript = document.currentScript || (function(){
20 const all = document.getElementsByTagName('script');
21 for (const s of all){ if (s.src && s.src.indexOf('fx-runtime.js')>-1) return s; }
22 return null;
23 })();
24 const base = myScript ? myScript.src.replace(/fx-runtime\.js.*$/, 'fx/') : 'assets/animations/fx/';
25
26 let loaded = 0;
27 const total = FX_LIST.length;
28 const ready = new Promise((resolve) => {
29 if (!total) return resolve();
30 FX_LIST.forEach((name) => {
31 const s = document.createElement('script');
32 s.src = base + name + '.js';
33 s.async = false;
34 s.onload = s.onerror = () => { if (++loaded >= total) resolve(); };
35 document.head.appendChild(s);
36 });
37 });
38
39 window.__hpxActive = window.__hpxActive || new Map();
40
41 /* querySelectorAll never matches root itself, but decks put data-fx on the
42 * <section class="slide"> too (e.g. a cover starfield) — include it. */
43 function fxEls(root){
44 const els = Array.from(root.querySelectorAll('[data-fx]'));
45 if (root.matches && root.matches('[data-fx]')) els.unshift(root);
46 return els;
47 }
48
49 function initFxIn(root){
50 if (!window.HPX) return;
51 const els = fxEls(root);
52 els.forEach((el) => {
53 if (window.__hpxActive.has(el)) return;
54 const name = el.getAttribute('data-fx');
55 const fn = window.HPX[name];
56 if (typeof fn !== 'function') return;
57 try {
58 const handle = fn(el, {}) || { stop(){} };
59 window.__hpxActive.set(el, handle);
60 } catch(e){ console.warn('[hpx-fx]', name, e); }
61 });
62 }
63
64 function stopFxIn(root){
65 const els = fxEls(root);
66 els.forEach((el) => {
67 const h = window.__hpxActive.get(el);
68 if (h && typeof h.stop === 'function'){
69 try{ h.stop(); }catch(e){}
70 }
71 window.__hpxActive.delete(el);
72 });
73 }
74
75 function reinitFxIn(root){
76 stopFxIn(root);
77 initFxIn(root);
78 }
79 window.__hpxReinit = reinitFxIn;
80
81 function boot(){
82 ready.then(() => {
83 const active = document.querySelector('.slide.is-active') || document.querySelector('.slide');
84 if (active) initFxIn(active);
85
86 // Watch all slides for class changes
87 const slides = document.querySelectorAll('.slide');
88 slides.forEach((sl) => {
89 const mo = new MutationObserver((muts) => {
90 for (const m of muts){
91 if (m.attributeName === 'class'){
92 if (sl.classList.contains('is-active')) initFxIn(sl);
93 else stopFxIn(sl);
94 }
95 }
96 });
97 mo.observe(sl, { attributes: true, attributeFilter: ['class'] });
98 });
99 });
100 }
101
102 if (document.readyState === 'loading'){
103 document.addEventListener('DOMContentLoaded', boot);
104 } else {
105 boot();
106 }
107 })();
108
108 lines JAVASCRIPT