| 1 | (function(){ |
| 2 | window.HPX = window.HPX || {}; |
| 3 | window.HPX['chain-react'] = function(el){ |
| 4 | const U = window.HPX._u; |
| 5 | const k = U.canvas(el), ctx = k.ctx; |
| 6 | const ac = U.accent(el,'#7c5cff'), ac2 = U.accent2(el,'#22d3ee'); |
| 7 | const N = 8; |
| 8 | const stop = U.loop((t) => { |
| 9 | ctx.clearRect(0,0,k.w,k.h); |
| 10 | const cy = k.h/2; |
| 11 | const pad = 60; |
| 12 | const dx = (k.w - pad*2)/(N-1); |
| 13 | const period = 2.4; |
| 14 | const phase = (t % period) / period; // 0..1 |
| 15 | for (let i=0;i<N;i++){ |
| 16 | const x = pad + i*dx; |
| 17 | const my = i/(N-1); |
| 18 | const d = Math.abs(phase - my); |
| 19 | const pulse = Math.max(0, 1 - d*6); |
| 20 | const r = 18 + pulse*18; |
| 21 | // glow |
| 22 | const g = ctx.createRadialGradient(x,cy,0,x,cy,r*2); |
| 23 | g.addColorStop(0, `rgba(124,92,255,${0.4*pulse})`); |
| 24 | g.addColorStop(1, 'rgba(0,0,0,0)'); |
| 25 | ctx.fillStyle = g; |
| 26 | ctx.fillRect(x-r*2, cy-r*2, r*4, r*4); |
| 27 | // circle |
| 28 | ctx.fillStyle = pulse>0.1 ? ac2 : ac; |
| 29 | ctx.beginPath(); ctx.arc(x,cy,r,0,Math.PI*2); ctx.fill(); |
| 30 | ctx.strokeStyle='rgba(255,255,255,0.4)'; ctx.lineWidth=2; |
| 31 | ctx.stroke(); |
| 32 | // connectors |
| 33 | if (i<N-1){ |
| 34 | ctx.strokeStyle='rgba(200,200,230,0.3)'; ctx.lineWidth=2; |
| 35 | ctx.beginPath(); ctx.moveTo(x+r,cy); ctx.lineTo(x+dx-r,cy); ctx.stroke(); |
| 36 | } |
| 37 | } |
| 38 | }); |
| 39 | return { stop(){ stop(); k.destroy(); } }; |
| 40 | }; |
| 41 | })(); |
| 42 |