PageSourceSearch

https://getflash.io/js/modules/lightning.js?v=v062

js getflash.io collected 2026-10-08 07:17:34 UTC 13,032 bytes, 284 lines download raw bytes

1/**
2 * lightning.js v3 — Realistic GSAP + SVG lightning
3 *
4 * Improvements over v2:
5 *   - Two-layer rendering: wide diffuse glow + sharp bright core per bolt
6 *   - Tree branching: 3–6 branches per strike (not just one fork)
7 *   - Multiple return strokes: rapid flicker before long afterglow (real lightning physics)
8 *   - More subdivisions + larger displacement = jaggier, more naturalistic paths
9 *   - Color: white core (#fff) over cyan-blue glow — matches real cloud-to-ground lightning
10 *   - Branches taper: thinner, shorter, dimmer than trunk
11 *
12 * Preserves:
13 *   - Easter egg / Keys of the Caribbean (fires at strike 15)
14 *   - IntersectionObserver — only animates when hero is in view
15 *   - API surface expected by main.js
16 */
17
18// ─── Configuration ────────────────────────────────────────────────────────────
19const STRIKE_MIN_DELAY  = 4;    // seconds min between strikes
20const STRIKE_MAX_DELAY  = 11;   // seconds max between strikes
21const TRUNK_SUBDIVISIONS = 5;   // midpoint passes for main bolt (more = jaggier)
22const BRANCH_SUBDIVISIONS = 4;  // midpoint passes for branches
23const TRUNK_DISP        = 110;  // initial displacement for trunk (px)
24const BRANCH_DISP       = 75;   // initial displacement for branches (px)
25const MIN_BRANCHES      = 2;    // min branch bolts per strike
26const MAX_BRANCHES      = 5;    // max branch bolts per strike
27
28// ─── Easter egg state (preserved) ────────────────────────────────────────────
29let lightningStrikeCount = 0;
30let rabbitHoleRevealed   = false;
31
32// ─── DOM setup ────────────────────────────────────────────────────────────────
33const heroSection = document.getElementById('pg-hero');
34const cvsStorm    = document.getElementById('lightning-storm');
35if (cvsStorm) cvsStorm.style.display = 'none';
36
37// main.js compatibility stubs
38const stormInterval = 4500;
39const animate       = () => {};
40function startLightning() { startStorm(); }
41const observer = new IntersectionObserver(() => {}, { threshold: 0.05 });
42
43// ─── SVG setup ────────────────────────────────────────────────────────────────
44if (!heroSection) {
45  console.warn('[lightning] #pg-hero not found — aborting.');
46} else {
47  const svgNS = 'http://www.w3.org/2000/svg';
48
49  const svg = document.createElementNS(svgNS, 'svg');
50  svg.setAttribute('id', 'lightning-svg');
51  svg.setAttribute('aria-hidden', 'true');
52  Object.assign(svg.style, {
53    position:      'absolute',
54    top:           '0',
55    left:          '0',
56    width:         '100%',
57    height:        '100%',
58    pointerEvents: 'none',
59    zIndex:        '1',
60    overflow:      'visible',
61  });
62
63  // Two filters:
64  //   bolt-outer — wide, soft diffuse corona (5px + 12px blur)
65  //   bolt-inner — tight, bright core glow (1.5px blur only)
66  const defs = document.createElementNS(svgNS, 'defs');
67  defs.innerHTML = `
68    <filter id="bolt-outer" x="-120%" y="-20%" width="340%" height="140%"
69            color-interpolation-filters="sRGB">
70      <feGaussianBlur in="SourceGraphic" stdDeviation="5"  result="b1"/>
71      <feGaussianBlur in="SourceGraphic" stdDeviation="12" result="b2"/>
72      <feMerge>
73        <feMergeNode in="b2"/>
74        <feMergeNode in="b1"/>
75      </feMerge>
76    </filter>
77    <filter id="bolt-inner" x="-60%" y="-10%" width="220%" height="120%"
78            color-interpolation-filters="sRGB">
79      <feGaussianBlur in="SourceGraphic" stdDeviation="1.5" result="b"/>
80      <feMerge>
81        <feMergeNode in="b"/>
82        <feMergeNode in="SourceGraphic"/>
83      </feMerge>
84    </filter>
85  `;
86  svg.appendChild(defs);
87  heroSection.appendChild(svg);
88
89  // ─── Midpoint displacement path generation ────────────────────────────────
90  function generatePoints(x0, y0, x1, y1, passes, disp) {
91    let pts = [{ x: x0, y: y0 }, { x: x1, y: y1 }];
92    let d   = disp;
93
94    for (let p = 0; p < passes; p++) {
95      const next = [];
96      for (let i = 0; i < pts.length - 1; i++) {
97        const a = pts[i], b = pts[i + 1];
98        // Slight downward bias on midpoint y keeps bolt moving earthward
99        const my = (a.y + b.y) / 2 + (Math.random() - 0.48) * d * 0.18;
100        const mx = (a.x + b.x) / 2 + (Math.random() - 0.5)  * d;
101        next.push(a, { x: mx, y: my });
102      }
103      next.push(pts[pts.length - 1]);
104      pts = next;
105      d  *= 0.55; // decay displacement each pass
106    }
107    return pts;
108  }
109
110  function toD(pts) {
111    return pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(' ');
112  }
113
114  // Two-layer bolt: outer glow path + inner core path
115  function makeBolt(d, glowWidth, coreWidth, glowColor, coreColor) {
116    const group = document.createElementNS(svgNS, 'g');
117    group.style.opacity = '0';
118
119    const glow = document.createElementNS(svgNS, 'path');
120    glow.setAttribute('d',             d);
121    glow.setAttribute('stroke',        glowColor);
122    glow.setAttribute('stroke-width',  glowWidth);
123    glow.setAttribute('stroke-linecap','round');
124    glow.setAttribute('stroke-linejoin','round');
125    glow.setAttribute('fill',          'none');
126    glow.setAttribute('filter',        'url(#bolt-outer)');
127
128    const core = document.createElementNS(svgNS, 'path');
129    core.setAttribute('d',             d);
130    core.setAttribute('stroke',        coreColor);
131    core.setAttribute('stroke-width',  coreWidth);
132    core.setAttribute('stroke-linecap','round');
133    core.setAttribute('stroke-linejoin','round');
134    core.setAttribute('fill',          'none');
135    core.setAttribute('filter',        'url(#bolt-inner)');
136
137    group.appendChild(glow);
138    group.appendChild(core);
139    return group;
140  }
141
142  // ─── Strike ───────────────────────────────────────────────────────────────
143  function strike() {
144    const w = heroSection.offsetWidth;
145    const h = heroSection.offsetHeight;
146
147    // Trunk: random top-x → random bottom-x, full height
148    const topX   = w * 0.05 + Math.random() * w * 0.9;
149    const botX   = w * 0.05 + Math.random() * w * 0.9;
150    const trunk  = generatePoints(topX, 0, botX, h, TRUNK_SUBDIVISIONS, TRUNK_DISP);
151    const trunkEl = makeBolt(
152      toD(trunk),
153      '5',  '1.2',                       // glow 5px wide, core 1.2px
154      'rgba(120, 180, 255, 0.55)',        // blue glow
155      'rgba(240, 250, 255, 1)'            // white-blue core
156    );
157    svg.appendChild(trunkEl);
158
159    // Branches: pick random attachment points along trunk
160    const numBranches = MIN_BRANCHES + Math.floor(Math.random() * (MAX_BRANCHES - MIN_BRANCHES + 1));
161    const branchEls   = [];
162
163    for (let b = 0; b < numBranches; b++) {
164      // Attach in the lower 70% of the trunk (avoid tiny top stubs)
165      const startIdx = Math.floor(trunk.length * (0.15 + Math.random() * 0.65));
166      const origin   = trunk[startIdx];
167
168      // Branch end: spread horizontally, terminate before floor
169      const endX  = Math.max(5, Math.min(w - 5, origin.x + (Math.random() - 0.5) * w * 0.5));
170      const endY  = origin.y + (h - origin.y) * (0.3 + Math.random() * 0.6);
171
172      const bPts  = generatePoints(origin.x, origin.y, endX, endY, BRANCH_SUBDIVISIONS, BRANCH_DISP);
173
174      // Branches get thinner & dimmer than trunk; deep branches dimmer still
175      const depthFade = 0.5 + 0.5 * (1 - startIdx / trunk.length);
176      const bEl = makeBolt(
177        toD(bPts),
178        `${(3 * depthFade).toFixed(1)}`,
179        `${(0.8 * depthFade).toFixed(1)}`,
180        `rgba(100, 160, 255, ${(0.35 * depthFade).toFixed(2)})`,
181        `rgba(220, 240, 255, ${(0.85 * depthFade).toFixed(2)})`
182      );
183      svg.appendChild(bEl);
184      branchEls.push({ el: bEl, depth: depthFade });
185    }
186
187    // ── Return strokes (realistic flicker) ──
188    // Real lightning: step leader (dim) → 2–3 rapid return strokes → afterglow
189    const returnStrokes = 1 + Math.floor(Math.random() * 2); // 2 or 3 total flashes
190    const tl = gsap.timeline({
191      onComplete: () => {
192        trunkEl.remove();
193        branchEls.forEach(b => b.el.remove());
194      }
195    });
196
197    // Build flicker sequence
198    let t = 0;
199    for (let s = 0; s < returnStrokes; s++) {
200      const peak = s === 0 ? 1 : 0.6 + Math.random() * 0.3; // first stroke brightest
201      tl.to([trunkEl, ...branchEls.map(b => b.el)], {
202        opacity: peak, duration: 0.025, ease: 'none'
203      }, t);
204      t += 0.025;
205      tl.to([trunkEl, ...branchEls.map(b => b.el)], {
206        opacity: peak * 0.25, duration: 0.04, ease: 'none'
207      }, t);
208      t += 0.04;
209    }
210
211    // Final afterglow fade — long, smooth power curve
212    tl.to([trunkEl, ...branchEls.map(b => b.el)], {
213      opacity: 1, duration: 0.02, ease: 'none'
214    }, t);
215    tl.to([trunkEl, ...branchEls.map(b => b.el)], {
216      opacity: 0, duration: 1.1, ease: 'power3.out'
217    }, t + 0.02);
218
219    // ── Easter egg ──
220    lightningStrikeCount++;
221    const logMessages = [
222      'you are not alone',       'there is no spoon',
223      'wake up, sons and daughters', 'the cake is a lie',
224      'the answer is 42',        'dont panic',
225      'are you lost?',           'you are not lost',
226      'we are all Satoshi',      'the revolution is decentralized',
227      'who the cap fit',         'we gotta chase dem crazy',
228      'you cant run away from yourself', 'the truth is out there',
229      'one one cocoa full basket', 'yes i am a pirate',
230      'i love you too',
231    ];
232    console.log(logMessages[Math.floor(Math.random() * logMessages.length)]);
233    if (lightningStrikeCount === 15 && !rabbitHoleRevealed) revealRabbitHole();
234  }
235
236  // ─── Scheduler ────────────────────────────────────────────────────────────
237  let stormActive = false;
238
239  function scheduleNextStrike() {
240    if (!stormActive) return;
241    const delay = STRIKE_MIN_DELAY + Math.random() * (STRIKE_MAX_DELAY - STRIKE_MIN_DELAY);
242    gsap.delayedCall(delay, () => {
243      if (stormActive) { strike(); scheduleNextStrike(); }
244    });
245  }
246
247  function startStorm() {
248    if (stormActive) return;
249    stormActive = true;
250    const firstDelay = 1.5 + Math.random() * 2.5;
251    gsap.delayedCall(firstDelay, () => { if (stormActive) { strike(); scheduleNextStrike(); } });
252  }
253
254  function stopStorm() {
255    stormActive = false;
256    gsap.killDelayedCallsTo(scheduleNextStrike);
257  }
258
259  // IntersectionObserver on hero section
260  const heroObserver = new IntersectionObserver((entries) => {
261    entries.forEach(e => e.isIntersecting ? startStorm() : stopStorm());
262  }, { threshold: 0.05 });
263  heroObserver.observe(heroSection);
264
265  // ─── Easter egg: revealRabbitHole ─────────────────────────────────────────
266  function revealRabbitHole() {
267    rabbitHoleRevealed = true;
268    console.log('%c');
269    console.log('%c═══════════════════════════════════════════════════════════════', 'color: #F6C453;');
270    console.log('%c  ⚡ SIGNAL DETECTED ⚡', 'font-size: 20px; color: #F6C453; font-weight: bold;');
271    console.log('%c═══════════════════════════════════════════════════════════════', 'color: #F6C453;');
272    console.log('%c');
273    console.log('%c  "I\'ve been working on a new electronic cash system', 'font-size: 14px; color: #7C8A92; font-style: italic;');
274    console.log('%c   that\'s fully peer-to-peer, with no trusted third party."', 'font-size: 14px; color: #7C8A92; font-style: italic;');
275    console.log('%c                                        — Satoshi, 2008', 'font-size: 12px; color: #41AD49;');
276    console.log('%c');
277    console.log('%c  The hunt sets sail soon — live, in the real world.', 'font-size: 14px; color: #7C8A92;');
278    console.log('%c  → https://kotc.islandbitcoin.com', 'font-size: 14px; color: #41AD49;');
279    console.log('%c');
280    console.log('%c  "We are all Satoshi."', 'font-size: 14px; color: #41AD49; font-style: italic;');
281    console.log('%c');
282    console.log('%c═══════════════════════════════════════════════════════════════', 'color: #F6C453;');
283  }
284}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.