git / moonbite-network / templates/break.html · raw
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"><title>MoonBite — Break</title><meta name="robots" content="noindex"><link rel="icon" type="image/svg+xml" href="/static/moonbite-logo.svg"><style nonce="{{ csp_nonce }}">:root{ --gold:#D9A441; --faint:#5E5A53; --mono:ui-monospace,'SFMono-Regular',Menlo,Consolas,monospace; --ease:cubic-bezier(0.22,1,0.36,1);}*{margin:0;padding:0;box-sizing:border-box}html{-webkit-text-size-adjust:100%}body{background:#000;overflow-x:hidden}#run{position:relative;height:240vh}#stage{position:sticky;top:0;height:100svh;overflow:hidden;background:#000}canvas{position:absolute;inset:0;width:100%;height:100%;display:block}#flash{position:absolute;inset:0;background:#EAF2FF;opacity:0;pointer-events:none;mix-blend-mode:screen}.cue{position:absolute;left:50%;bottom:30px;transform:translateX(-50%);z-index:5; font-family:var(--mono);font-size:.62rem;letter-spacing:.24em;text-transform:uppercase; color:var(--faint);opacity:0;transition:opacity 600ms var(--ease); display:flex;flex-direction:column;align-items:center;gap:9px}body.ready .cue{opacity:1}body.blown .cue{opacity:0}.cue i{display:block;width:1px;height:30px; background:linear-gradient(180deg,var(--gold),transparent); animation:drop 2.2s ease-in-out infinite}@keyframes drop{0%,100%{opacity:.25;transform:scaleY(.5)}50%{opacity:1;transform:scaleY(1)}}@media(prefers-reduced-motion:reduce){ .cue i{animation:none} }</style></head><body><section id="run"> <div id="stage"> <canvas id="view"></canvas> <div id="flash"></div> <div class="cue">Scroll<i></i></div> </div></section><script nonce="{{ csp_nonce }}">(function(){ var $=function(i){return document.getElementById(i)}; var reduce=matchMedia('(prefers-reduced-motion: reduce)').matches && !/[?&]motion=1/.test(location.search); var run=$('run'), stage=$('stage'), view=$('view'), flash=$('flash'), body=document.body; var ctx=view.getContext('2d'); function clamp(v,a,b){return v<a?a:(v>b?b:v)} function smooth(a,b,x){if(b===a)return x<a?0:1;var t=clamp((x-a)/(b-a),0,1);return t*t*(3-2*t)} function lerp(a,b,t){return a+(b-a)*t} function rnd(a,b){return a+Math.random()*(b-a)} var W,H,DPR,CX,CY,SC,BT,B1,B2,BW_GLYPH,IX,IY,RMAX=1; var src=document.createElement('canvas'), sctx=src.getContext('2d'); var mkc=document.createElement('canvas'), mctx=mkc.getContext('2d'); var dmg=document.createElement('canvas'), dctx=dmg.getContext('2d'); var stars=[], impacts=[]; var blown=false, t0=0, barW=0, barTension=0, built=false; function size(){ DPR=Math.min(devicePixelRatio||1,2); W=view.width=src.width=mkc.width=dmg.width=Math.floor(innerWidth*DPR); H=view.height=src.height=mkc.height=dmg.height=Math.floor(innerHeight*DPR); CX=W/2; CY=H/2; var mkSize=Math.min(innerWidth*0.38,260)*DPR; SC=mkSize/300; BT=Math.max(3*DPR,13*SC); BW_GLYPH=255*SC; B1=CY-34*SC; B2=CY+18*SC; IX=CX; IY=(B1+B2)/2; RMAX=Math.sqrt(W*W+H*H)*0.75; if(!blown) barW=BW_GLYPH; stars=[]; var n=Math.round(clamp(innerWidth*innerHeight/9000,80,240)); for(var i=0;i<n;i++) stars.push({x:Math.random()*W,y:Math.random()*H, r:(Math.random()*1.2+0.25)*DPR,z:Math.random()*0.9+0.1,t:Math.random()*6.28}); buildGlass(); } /* the picture that is about to be destroyed */ function paintSrc(bw,glow){ sctx.clearRect(0,0,W,H); var g=sctx.createRadialGradient(CX,CY*0.92,0,CX,CY,Math.max(W,H)*0.72); g.addColorStop(0,'#12141C'); g.addColorStop(.62,'#06070A'); g.addColorStop(1,'#000'); sctx.fillStyle=g; sctx.fillRect(0,0,W,H); for(var i=0;i<stars.length;i++){ var s=stars[i]; sctx.beginPath(); sctx.arc(s.x,s.y,s.r,0,6.283); sctx.fillStyle='rgba(236,230,214,'+(0.16*s.z)+')'; sctx.fill(); } mctx.clearRect(0,0,W,H); mctx.beginPath(); mctx.arc(CX,CY,100*SC,0,6.283); mctx.fillStyle='#F5F2EA'; mctx.fill(); mctx.save(); mctx.globalCompositeOperation='destination-out'; mctx.beginPath(); mctx.arc(CX+67.18*SC,CY-67.18*SC,62*SC,0,6.283); mctx.fill(); mctx.restore(); sctx.drawImage(mkc,0,0); sctx.save(); if(glow>0){ sctx.shadowColor='rgba(217,164,65,.9)'; sctx.shadowBlur=BT*(1.2+glow*3); } sctx.fillStyle='#D9A441'; [B1,B2].forEach(function(y){ sctx.beginPath(); if(sctx.roundRect) sctx.roundRect(CX-bw/2,y-BT/2,bw,BT,BT/2); else sctx.rect(CX-bw/2,y-BT/2,bw,BT); sctx.fill(); }); sctx.restore(); } /* Two strikes. Each builds its own web of radial rays crossed by concentric rings, plus splinters, ink pools and dead lines. The second lands late. */ function buildImpact(x,y,reach,seg,rings,heavy,delay){ var im={x:x,y:y,reach:reach,delay:delay,shards:[],cracks:[],splinters:[],blobs:[],cols:[]}; var ang=[]; for(var a=0;a<seg;a++) ang.push(a/seg*6.283+rnd(-0.11,0.11)); ang.push(ang[0]+6.283); var rr=[]; for(var r=0;r<rings.length;r++){ rr[r]=[]; for(var k=0;k<=seg;k++) rr[r][k]=rings[r]*reach*rnd(0.82,1.20); rr[r][seg]=rr[r][0]; } function P(r,k){ return [x+Math.cos(ang[k])*rr[r][k], y+Math.sin(ang[k])*rr[r][k]]; } for(var r2=0;r2<rings.length-1;r2++){ for(var k2=0;k2<seg;k2++){ var p=[P(r2,k2),P(r2,k2+1),P(r2+1,k2+1),P(r2+1,k2)]; var cx=(p[0][0]+p[1][0]+p[2][0]+p[3][0])/4; var cy=(p[0][1]+p[1][1]+p[2][1]+p[3][1])/4; im.shards.push({p:p,cx:cx,cy:cy, d:Math.sqrt((cx-x)*(cx-x)+(cy-y)*(cy-y)), ox:rnd(-1,1)*DPR*(3.4-r2*0.20)*heavy, oy:rnd(-1,1)*DPR*(3.4-r2*0.20)*heavy, rot:rnd(-0.011,0.011)*(1.5-r2*0.1)*heavy, lift:rnd(-0.16,0.22)}); } } for(var r3=0;r3<rings.length;r3++) for(var k3=0;k3<seg;k3++) im.cracks.push({a:P(r3,k3),b:P(r3,k3+1),d:rings[r3]*reach}); for(var k4=0;k4<seg;k4++) for(var r4=0;r4<rings.length-1;r4++) im.cracks.push({a:P(r4,k4),b:P(r4+1,k4),d:rings[r4]*reach}); /* splinters: short hairlines branching off the web */ for(var sp=0;sp<Math.round(52*heavy);sp++){ var sa=rnd(0,6.283), sd=rnd(0.06,1.0)*reach, ln=rnd(0.02,0.11)*reach; var a2=sa+rnd(-0.6,0.6); im.splinters.push({a:[x+Math.cos(sa)*sd, y+Math.sin(sa)*sd], b:[x+Math.cos(sa)*sd+Math.cos(a2)*ln, y+Math.sin(sa)*sd+Math.sin(a2)*ln], d:sd}); } /* liquid crystal pools, kept off the mark */ var keep=110*SC; for(var b=0;b<Math.round(20*heavy);b++){ var aa=rnd(0,6.283), dd=keep+rnd(0.04,0.42)*reach; im.blobs.push({x:x+Math.cos(aa)*dd, y:y+Math.sin(aa)*dd, r:rnd(0.06,0.20)*reach, sq:rnd(0.4,1.7), rot:rnd(0,3.14), t:rnd(0.05,0.5)}); } for(var c=0;c<Math.round(15*heavy);c++){ im.cols.push({x:x+rnd(-0.5,0.5)*reach, w:rnd(1,6)*DPR, h:rnd(0.3,1.15), hue:Math.random()<0.45?rnd(150,190):rnd(280,330), a:rnd(0.14,0.62)}); } return im; } function buildGlass(){ impacts=[]; /* the strike, where the bars cross the mark */ impacts.push(buildImpact(CX,(B1+B2)/2, RMAX, 22, [0.026,0.062,0.110,0.172,0.250,0.348,0.470,0.620,0.800,1.05], 1.0, 0.00)); /* the second, lower right, landing a beat later */ impacts.push(buildImpact(CX+W*0.27, B2+H*0.19, RMAX*0.60, 18, [0.030,0.075,0.140,0.230,0.350,0.500,0.700,1.02], 0.85, 0.30)); } /* the damage, painted into its own layer and then left there */ function buildDamage(prog){ dctx.clearRect(0,0,W,H); /* the picture, still on screen but no longer in one piece */ for(var n=0;n<impacts.length;n++){ var im=impacts[n]; var lp=clamp((prog-im.delay)/(1-im.delay),0,1); if(lp<=0) { if(n===0) continue; else continue; } var reach=lp*im.reach*1.18; for(var i=0;i<im.shards.length;i++){ var s=im.shards[i]; var on=clamp((reach-s.d)/(0.12*im.reach),0,1); if(on<=0) continue; dctx.save(); dctx.translate(s.cx+s.ox*on, s.cy+s.oy*on); dctx.rotate(s.rot*on); dctx.translate(-s.cx,-s.cy); dctx.beginPath(); dctx.moveTo(s.p[0][0],s.p[0][1]); for(var k=1;k<s.p.length;k++) dctx.lineTo(s.p[k][0],s.p[k][1]); dctx.closePath(); dctx.clip(); dctx.drawImage(src,0,0); if(s.lift>0){ dctx.fillStyle='rgba(190,214,255,'+(0.07*on*s.lift)+')'; dctx.fillRect(0,0,W,H); } else { dctx.fillStyle='rgba(0,0,0,'+(0.30*on*-s.lift)+')'; dctx.fillRect(0,0,W,H); } dctx.restore(); } } for(var n2=0;n2<impacts.length;n2++){ var im2=impacts[n2]; var lp2=clamp((prog-im2.delay)/(1-im2.delay),0,1); if(lp2<=0) continue; var reach2=lp2*im2.reach*1.18; /* liquid crystal bleeding into black pools */ for(var b=0;b<im2.blobs.length;b++){ var bl=im2.blobs[b], bp=clamp((lp2-bl.t)/0.45,0,1); if(bp<=0) continue; dctx.save(); dctx.translate(bl.x,bl.y); dctx.rotate(bl.rot); dctx.scale(1,bl.sq); var rg=dctx.createRadialGradient(0,0,0,0,0,bl.r*bp); rg.addColorStop(0,'rgba(0,0,0,0.95)'); rg.addColorStop(.55,'rgba(0,0,0,0.74)'); rg.addColorStop(.82,'rgba(6,8,16,0.32)'); rg.addColorStop(1,'rgba(6,8,16,0)'); dctx.fillStyle=rg; dctx.beginPath(); dctx.arc(0,0,bl.r*bp,0,6.283); dctx.fill(); dctx.restore(); } /* dead driver lines stuck on one colour */ for(var c=0;c<im2.cols.length;c++){ var cl=im2.cols[c], cp=clamp((lp2-0.22)/0.5,0,1); if(cp<=0) continue; var hh=H*cl.h*cp; var lg=dctx.createLinearGradient(0,im2.y-hh/2,0,im2.y+hh/2); lg.addColorStop(0,'hsla('+cl.hue+',90%,60%,0)'); lg.addColorStop(.5,'hsla('+cl.hue+',90%,60%,'+(cl.a*cp)+')'); lg.addColorStop(1,'hsla('+cl.hue+',90%,60%,0)'); dctx.fillStyle=lg; dctx.fillRect(cl.x-cl.w/2, im2.y-hh/2, cl.w, hh); } /* the cracks: dark fissure, lit edge */ for(var q=0;q<im2.cracks.length;q++){ var cr=im2.cracks[q]; if(cr.d>reach2) continue; var near=1-clamp(cr.d/im2.reach,0,1); dctx.beginPath(); dctx.moveTo(cr.a[0],cr.a[1]); dctx.lineTo(cr.b[0],cr.b[1]); dctx.strokeStyle='rgba(0,0,0,'+(0.62+0.3*near)+')'; dctx.lineWidth=(2.2+3.4*near)*DPR; dctx.stroke(); dctx.beginPath(); dctx.moveTo(cr.a[0],cr.a[1]); dctx.lineTo(cr.b[0],cr.b[1]); dctx.strokeStyle='rgba(220,234,255,'+(0.26+0.58*near)+')'; dctx.lineWidth=(0.7+1.2*near)*DPR; dctx.stroke(); } for(var sp2=0;sp2<im2.splinters.length;sp2++){ var sl=im2.splinters[sp2]; if(sl.d>reach2) continue; dctx.beginPath(); dctx.moveTo(sl.a[0],sl.a[1]); dctx.lineTo(sl.b[0],sl.b[1]); dctx.strokeStyle='rgba(214,230,255,0.30)'; dctx.lineWidth=0.7*DPR; dctx.stroke(); } /* pulverised glass at the point of impact */ var ip=clamp(lp2*2,0,1); var ir=0.11*im2.reach*ip; var ig=dctx.createRadialGradient(im2.x,im2.y,0,im2.x,im2.y,ir); ig.addColorStop(0,'rgba(236,243,255,'+(0.58*ip)+')'); ig.addColorStop(.5,'rgba(190,214,255,'+(0.16*ip)+')'); ig.addColorStop(1,'rgba(190,214,255,0)'); dctx.fillStyle=ig; dctx.beginPath(); dctx.arc(im2.x,im2.y,ir,0,6.283); dctx.fill(); } } function detonate(){ if(blown) return; blown=true; t0=performance.now(); body.classList.add('blown'); paintSrc(barW,1); flash.style.transition='none'; flash.style.opacity='0.85'; setTimeout(function(){ flash.style.transition='opacity 220ms var(--ease)'; flash.style.opacity='0'; },40); } function render(now){ if(!blown){ paintSrc(barW, smooth(0.55,1,barTension)); ctx.clearRect(0,0,W,H); ctx.drawImage(src,0,0); return; } var el=now-t0; var prog=clamp(el/1000,0,1); if(prog<1 || !built){ buildDamage(prog); if(prog>=1) built=true; } ctx.clearRect(0,0,W,H); ctx.fillStyle='#000'; ctx.fillRect(0,0,W,H); ctx.drawImage(dmg,0,0); /* the panel failing: backlight flicker and banding */ if(el>240){ var f=Math.random(); if(f>0.955){ ctx.fillStyle='rgba(0,0,0,'+rnd(0.10,0.34)+')'; ctx.fillRect(0,Math.random()*H,W,rnd(0.02,0.16)*H); } if(f<0.02){ ctx.fillStyle='rgba(150,190,255,0.05)'; ctx.fillRect(0,0,W,H); } } var sh=Math.max(0,1-el/300); stage.style.transform = sh>0 ? 'translate('+(rnd(-1,1)*13*sh)+'px,'+(rnd(-1,1)*13*sh)+'px)' : ''; } var target=0,cur=0; function measure(){ var r=run.getBoundingClientRect(); var total=run.offsetHeight-stage.offsetHeight; target=clamp(-r.top/(total||1),0,1); } function frame(now){ cur+=(target-cur)*0.12; if(!blown){ barTension=smooth(0.008,0.10,cur); barW=lerp(BW_GLYPH, innerWidth*DPR*1.35, barTension); if(barTension>=0.999) detonate(); } render(now); requestAnimationFrame(frame); } addEventListener('scroll',measure,{passive:true}); addEventListener('resize',function(){ built=false; size(); measure(); }); size(); measure(); setTimeout(function(){ body.classList.add('ready') },900); if(reduce){ paintSrc(BW_GLYPH,0); ctx.drawImage(src,0,0); } else requestAnimationFrame(frame); if(/[?&]motion=1/.test(location.search)){ window.__boom=function(){ barW=innerWidth*DPR*1.35; detonate(); return 'boom' }; }})();</script></body></html>