Add 150 UI and page state stickers in three pending sections

404s, loaders, success, errors, empty states, confirmation, under
construction, offline and more: 50 monochrome (ui), 50 colored (uicolor)
and 50 with an anime girl (uianime), on /test until approved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 03:10:04 +00:00
co-authored by Claude Opus 5.5
parent c59222f593
commit c00ba79178
450 changed files with 5481 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
// a wrapped candy as a progress bar: peppermint stripes scroll in and fill it up, a shine sweeps across when it's full, then it drains and starts again
const{sin,PI,min,max}=Math,T=2*PI
export const frames=24,delay=70
const ST='#f06595',TK='#ffdeeb',WR='#ffd43b',WD='#fab005'
const L=58,R=198,Y=128,H=42
const pill=c=>{c.beginPath();c.roundRect(L,Y-H,R-L,H*2,H)}
const spark=(c,x,y,s)=>{c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.2,-s*.2],[0,s,s*.2,s*.2],[-s,0,-s*.2,s*.2],[0,-s,-s*.2,-s*.2]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
const ss=x=>x<=0?0:x>=1?1:x*x*(3-2*x)
export default(c,t)=>{
const f=ss(t/.66),d=ss((t-.86)/.14),x0=L+(R-L)*d,x1=L+(R-L)*f,sh=(t-.68)/.14
for(const s of[-1,1]){
c.save();c.translate(128,Y);c.scale(s,1);c.translate(-128,-Y)
const w=3*sin(T*t*2),P=[[16,Y-46],[28,Y-30],[12,Y-14],[26,Y],[12,Y+14],[28,Y+30],[16,Y+46]].map(([x,y])=>[x-w,y])
c.fillStyle=WR;c.beginPath();c.moveTo(L-6,Y-12);P.forEach(([x,y])=>c.lineTo(x,y));c.lineTo(L-6,Y+12);c.closePath();c.fill();c.stroke()
c.strokeStyle=WD;c.lineWidth=4;for(const k of[0,2,4,6])c.beginPath(),c.moveTo(L-14,Y+(k-3)*3),c.lineTo(P[k][0]+8,P[k][1]+(3-k)*1.5),c.stroke()
c.strokeStyle='#111';c.fillStyle=WD;c.lineWidth=6;c.beginPath();c.roundRect(L-16,Y-15,22,30,9);c.fill();c.stroke()
c.restore();c.lineWidth=7
}
c.fillStyle=TK;pill(c);c.fill()
c.save();pill(c);c.clip()
if(x1>x0+.5){
c.beginPath();c.rect(x0,0,x1-x0,256);c.clip();c.fillStyle='#fff';c.fillRect(0,0,256,256)
c.fillStyle=ST;const o=t*2*40;for(let x=L-80+o%40;x<R+50;x+=40)c.beginPath(),c.moveTo(x,Y-H),c.lineTo(x+20,Y-H),c.lineTo(x-16,Y+H),c.lineTo(x-36,Y+H),c.closePath(),c.fill()
c.lineWidth=5;c.beginPath();c.moveTo(x1,Y-H);c.lineTo(x1,Y+H);c.stroke()
}
if(sh>0&&sh<1){const gx=L-30+(R-L+60)*sh;c.fillStyle='#fff';c.beginPath();c.moveTo(gx,Y-H);c.lineTo(gx+22,Y-H);c.lineTo(gx-2,Y+H);c.lineTo(gx-24,Y+H);c.closePath();c.fill()}
c.restore()
c.strokeStyle='#fff';c.lineWidth=7;c.beginPath();c.moveTo(L+28,Y-26);c.lineTo(R-34,Y-26);c.stroke()
c.strokeStyle='#111';c.lineWidth=7;pill(c);c.stroke()
if(t>.66&&t<.88)for(const[x,y,o]of[[84,56,0],[178,54,.4],[124,206,.2],[206,198,.6]]){const s=12*sin(PI*min(1,max(0,(t-.66)/.22*1.4-o*.4)));if(s>1.5)spark(c,x,y,s)}
}
+26
View File
@@ -0,0 +1,26 @@
// orange tabby curled in a ring chasing its own tail round and round like a spinner, paws scrabbling, eyes locked on the tail tip
const{sin,cos,PI,atan2}=Math,T=2*PI
export const frames=16,delay=60
const OR='#ffa94d',OD='#f08c00',PK='#ffc9c9',BL='#ff8787',X=128,Y=134,R=58
const at=(a,r=R)=>[X+cos(a)*r,Y+sin(a)*r]
export default(c,t,i)=>{
const h=t*T,tail=[];for(let s=0;s<=1.001;s+=.1){const a=h-3.3-s*1.9,r=R+8*sin(s*PI)+6*s*sin(T*t*4+s*3);tail.push(at(a,r))}
const line=(pts,w,col)=>{c.lineWidth=w;c.strokeStyle=col;c.beginPath();pts.forEach(([x,y])=>c.lineTo(x,y));c.stroke()}
const tp=tail.at(-1)
line(tail,34,'#111');line(tail,20,OR);line(tail.slice(-3),20,'#fff')
for(const[a,ph]of[[h-.75,0],[h-3.1,PI]])for(const s of[-1,1]){const q=a+.22*sin(T*t*4+ph+(s>0?0:PI)),[x,y]=at(q,R+s*26);c.fillStyle='#fff';c.lineWidth=5;c.strokeStyle='#111';c.beginPath();c.ellipse(x,y,11,8,q,0,T);c.fill();c.stroke()}
c.strokeStyle='#111';c.lineWidth=54;c.beginPath();c.arc(X,Y,R,h-3.3,h-.2);c.stroke()
c.lineWidth=40;c.strokeStyle=OR;c.beginPath();c.arc(X,Y,R,h-3.3,h-.2);c.stroke()
c.lineWidth=6;c.strokeStyle=OD;for(let k=0;k<4;k++){const a=h-1.3-k*.52;c.beginPath();c.moveTo(...at(a-.08,R-14));c.lineTo(...at(a,R+14));c.stroke()}
const[hx,hy]=at(h,R+2),d=atan2(tp[1]-hy,tp[0]-hx),ex=4*cos(d),ey=4*sin(d)
c.strokeStyle='#111';c.lineWidth=7
for(const s of[-1,1]){c.fillStyle=OR;c.beginPath();c.moveTo(hx+s*28,hy-6);c.lineTo(hx+s*24,hy-44);c.lineTo(hx+s*4,hy-26);c.closePath();c.fill();c.stroke();c.fillStyle=PK;c.beginPath();c.moveTo(hx+s*22,hy-14);c.lineTo(hx+s*22,hy-34);c.lineTo(hx+s*12,hy-24);c.closePath();c.fill()}
c.fillStyle=OR;c.beginPath();c.ellipse(hx,hy,34,29,0,0,T);c.fill();c.stroke()
c.lineWidth=4;c.strokeStyle=OD;for(const s of[-1,0,1])c.beginPath(),c.moveTo(hx+s*9,hy-27),c.lineTo(hx+s*7,hy-17),c.stroke()
c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy+12,14,10,0,0,T);c.fill()
c.fillStyle='#111';for(const s of[-1,1])c.beginPath(),c.arc(hx+s*14+ex,hy-1+ey,5.5,0,T),c.fill()
c.fillStyle='#fff';for(const s of[-1,1])c.beginPath(),c.arc(hx+s*14+ex-2,hy-3+ey,2,0,T),c.fill()
c.fillStyle='#ff8fab';c.beginPath();c.moveTo(hx-4,hy+6);c.lineTo(hx+4,hy+6);c.lineTo(hx,hy+10);c.closePath();c.fill()
c.strokeStyle='#111';c.lineWidth=3;c.beginPath();c.moveTo(hx-7,hy+13);c.quadraticCurveTo(hx-3.5,hy+17,hx,hy+12);c.quadraticCurveTo(hx+3.5,hy+17,hx+7,hy+13);c.stroke()
c.fillStyle=BL;for(const s of[-1,1])c.beginPath(),c.ellipse(hx+s*24,hy+9,6,3.5,0,0,T),c.fill()
}
+33
View File
@@ -0,0 +1,33 @@
// smiling coffee machine drips coffee into a glass mug that fills up bit by bit, steams when full, then empties to brew again
const{sin,PI,min}=Math,T=2*PI
export const frames=24,delay=80
const BO='#ff8787',BD='#fa5252',RC='#495057',GL='#d0ebff',CO='#a9744f',CR='#e8b27a',BL='#ffc9c9',ME='#ced4da'
const ss=x=>x<=0?0:x>=1?1:x*x*(3-2*x)
export default(c,t)=>{
const f=ss(t/.74)-ss((t-.88)/.12),lv=204-8-62*f,brew=t<.76
c.fillStyle=BO;c.beginPath();c.roundRect(34,14,188,226,24);c.fill();c.stroke()
c.fillStyle=RC;c.beginPath();c.roundRect(56,88,144,124,[10,10,4,4]);c.fill();c.lineWidth=5;c.stroke()
c.fillStyle=ME;c.beginPath();c.moveTo(112,86);c.lineTo(144,86);c.lineTo(136,104);c.lineTo(120,104);c.closePath();c.fill();c.stroke()
c.fillStyle=ME;c.beginPath();c.roundRect(48,208,160,16,6);c.fill();c.stroke()
c.lineWidth=3;for(let x=64;x<200;x+=16)c.beginPath(),c.moveTo(x,212),c.lineTo(x,220),c.stroke()
c.lineWidth=7
for(const[w,s]of[[22,'#111'],[10,GL]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(166,146),c.bezierCurveTo(194,144,194,190,164,188),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
const mug=()=>{c.beginPath();c.roundRect(86,128,82,78,[4,4,16,16])}
c.fillStyle=GL;mug();c.fill()
c.save();mug();c.clip()
if(f>.01){c.fillStyle=CO;c.fillRect(80,lv,96,90);c.fillStyle=CR;c.beginPath();c.ellipse(127,lv,46,4+2*sin(T*t*4),0,0,T);c.fill()}
c.strokeStyle='#fff';c.lineWidth=7;c.beginPath();c.moveTo(100,142);c.lineTo(100,174);c.stroke()
c.restore();mug();c.stroke()
if(brew)for(let k=0;k<4;k++){const p=(t*8+k/4)%1,y=106+p*(lv-106);if(y<lv-6){c.fillStyle=CO;c.lineWidth=3;c.beginPath();c.moveTo(128,y-10);c.quadraticCurveTo(135,y,128,y+6);c.quadraticCurveTo(121,y,128,y-10);c.fill();c.stroke()}}
c.lineWidth=7
const happy=t>.76&&t<.92
if(t>.7&&t<.94)for(let k=0;k<2;k++){const a=min(1,(t-.7)/.08)*min(1,(.94-t)/.06),pts=[];for(let s=0;s<=1;s+=.125)pts.push([116+k*24+5*sin(T*(s-t*2)+k*2),122-26*s*a]);c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();pts.forEach(([x,y])=>c.lineTo(x,y));c.stroke()}
c.strokeStyle='#111'
if(happy){c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.arc(128+d*26,54,7,PI*1.15,PI*1.85),c.stroke()}
else{c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*26,50,6.5,0,T),c.fill();c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*26-2,48,2.3,0,T),c.fill()}
c.lineWidth=5;c.beginPath();c.arc(128,58,happy?11:8,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*48,62,9,5,0,0,T),c.fill()
c.lineWidth=4;c.fillStyle=brew&&Math.floor(t*12)%2?'#ffd43b':'#69db7c';c.beginPath();c.arc(196,36,7,0,T);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.arc(60,36,7,0,T);c.fill();c.stroke()
}
+24
View File
@@ -0,0 +1,24 @@
// three gummy bears, red, yellow and green, hopping one after another in a wave like a loading dots row
const{sin,PI,max}=Math,T=2*PI
export const frames=16,delay=70
const B=[['#ff6b6b','#ffa8a8'],['#ffd43b','#fff3bf'],['#69db7c','#b2f2bb']],BL='#ff8787'
const shapes=c=>[()=>c.ellipse(-14,-12,12,13,0,0,T),()=>c.ellipse(14,-12,12,13,0,0,T),()=>c.ellipse(-25,-44,10,14,.5,0,T),()=>c.ellipse(25,-44,10,14,-.5,0,T),()=>c.ellipse(0,-36,26,29,0,0,T),()=>c.arc(-18,-96,10,0,T),()=>c.arc(18,-96,10,0,T),()=>c.arc(0,-76,25,0,T)]
export default(c,t)=>{
B.forEach(([col,lt],k)=>{
const h=max(0,sin(T*(t-k*.16))),y=-50*h**.8,sq=h<.15?1-(.15-h)*.8:1+.06*h*(1-h)*4
c.save();c.translate(48+k*80,214);c.scale(2-sq,sq);c.translate(0,y/sq);c.scale(1.04,1.04)
const S=shapes(c)
c.fillStyle='#111';for(const f of S)c.beginPath(),f(),c.lineWidth=14,c.strokeStyle='#111',c.stroke()
c.fillStyle=col;for(const f of S)c.beginPath(),f(),c.fill()
c.fillStyle=lt;c.beginPath();c.ellipse(0,-32,15,17,0,0,T);c.fill();c.beginPath();c.ellipse(0,-68,12,8,0,0,T);c.fill()
for(const d of[-1,1])c.beginPath(),c.arc(d*18,-96,4.5,0,T),c.fill()
c.strokeStyle='#111';c.lineWidth=3;c.beginPath();c.moveTo(-6,-22);c.quadraticCurveTo(-8,-12,-14,-6);c.moveTo(6,-22);c.quadraticCurveTo(8,-12,14,-6);c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*10,-80,4,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*10-1.5,-81.5,1.5,0,T),c.fill()
c.fillStyle='#111';c.beginPath();c.ellipse(0,-71,4,3,0,0,T);c.fill()
c.lineWidth=3;c.beginPath();c.moveTo(-5,-65);c.quadraticCurveTo(-2.5,-62,0,-66);c.quadraticCurveTo(2.5,-62,5,-65);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(d*17,-70,4.5,2.8,0,0,T),c.fill()
c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(0,-76,18,PI*1.15,PI*1.4);c.stroke();c.beginPath();c.arc(0,-36,20,PI*1.05,PI*1.3);c.stroke()
c.restore()
})
}
+39
View File
@@ -0,0 +1,39 @@
// mint microwave humming away: light on inside, a smiling soup bowl turning round on the spinning plate, the green timer glowing and counting down
const{sin,cos,PI,floor}=Math,T=2*PI
export const frames=20,delay=90
const BO='#63e6be',BD='#20c997',IN='#fff3bf',BW='#ff8787',SO='#ffd43b',DS='#343a40',GG='#2f9e44',GB='#b2f2bb',BL='#ffa8a8'
const SEG=[63,6,91,79,102,109,125,7,127,111]
const digit=(c,n,x,y,w=11,h=20)=>{const P=[[0,0,w,0],[w,0,w,h/2],[w,h/2,w,h],[0,h,w,h],[0,h/2,0,h],[0,0,0,h/2],[0,h/2,w,h/2]];c.beginPath();P.forEach(([a,b,e,f],k)=>{if(SEG[n]>>k&1)c.moveTo(x+a,y+b),c.lineTo(x+e,y+f)})}
export default(c,t,i)=>{
const th=t*T,PX=98,PY=162
c.fillStyle=BD;for(const x of[44,196])c.beginPath(),c.roundRect(x-14,200,28,22,8),c.fill(),c.stroke()
c.fillStyle=BO;c.beginPath();c.roundRect(14,44,228,166,24);c.fill();c.stroke()
c.fillStyle=BD;c.beginPath();c.roundRect(28,58,144,138,16);c.fill();c.lineWidth=5;c.stroke()
const win=()=>{c.beginPath();c.roundRect(40,70,118,114,10)}
c.fillStyle=IN;win();c.fill()
c.save();win();c.clip()
c.fillStyle='#ffe066';c.beginPath();c.ellipse(99,70,44,16,0,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.ellipse(PX,PY,46,12,0,0,T);c.fill();c.lineWidth=5;c.stroke()
c.fillStyle='#dee2e6';for(let k=0;k<6;k++){const a=th+k*T/6;if(sin(a)>0)continue;c.beginPath();c.arc(PX+cos(a)*36,PY+sin(a)*8,2.6,0,T);c.fill()}
const by=PY-6
c.fillStyle=BW;c.lineWidth=6;c.beginPath();c.ellipse(PX,by-32,32,8,0,0,PI,true);c.ellipse(PX,by-32,32,34,0,0,PI);c.fill();c.stroke()
c.save();c.beginPath();c.ellipse(PX,by-32,32,34,0,0,PI);c.clip()
for(const[o,face]of[[0,1],[PI,0]]){const a=th+o,cx=PX+22*sin(a),k=cos(a);if(k<.05)continue
c.save();c.translate(cx,by-11);c.scale(k,1)
if(face){c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*9,-4,3.5,0,T),c.fill();c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*9-1.2,-5.2,1.3,0,T),c.fill();c.strokeStyle='#111';c.lineWidth=3;c.beginPath();c.arc(0,0,4.5,.15*PI,.85*PI);c.stroke();c.fillStyle='#ffc9c9';for(const d of[-1,1])c.beginPath(),c.ellipse(d*16,1,4,2.5,0,0,T),c.fill()}
else{c.fillStyle='#fff';c.beginPath();c.moveTo(0,6);c.bezierCurveTo(-12,-2,-7,-11,0,-5);c.bezierCurveTo(7,-11,12,-2,0,6);c.fill()}
c.restore()}
c.restore()
c.fillStyle=SO;c.beginPath();c.ellipse(PX,by-32,27,6,0,0,T);c.fill();c.lineWidth=4;c.stroke()
c.lineWidth=6;c.beginPath();c.ellipse(PX,by-32,32,8,0,0,T);c.stroke()
for(let k=0;k<2;k++){const pts=[];for(let s=0;s<=1;s+=.125)pts.push([PX-10+k*20+4*sin(T*(s-t*2)+k*2),by-46-30*s]);for(const[w,st]of[[9,'#111'],[4,'#fff']]){c.lineWidth=w;c.strokeStyle=st;c.beginPath();pts.forEach(([x,y])=>c.lineTo(x,y));c.stroke()}}
c.strokeStyle='#fff';c.lineWidth=7;c.beginPath();c.moveTo(52,92);c.lineTo(62,82);c.moveTo(52,108);c.lineTo(76,84);c.stroke()
c.restore();c.strokeStyle='#111';c.lineWidth=6;win();c.stroke()
c.lineWidth=7;c.fillStyle=DS;c.beginPath();c.roundRect(180,64,50,34,8);c.fill();c.stroke()
const n=20-i;for(const[w,col]of[[7,GG],[3.5,GB]]){c.lineWidth=w;c.strokeStyle=col;digit(c,floor(n/10),189,71);c.stroke();digit(c,n%10,210,71);c.stroke()}
c.strokeStyle='#111';c.lineWidth=4
for(let r=0;r<2;r++)for(let q=0;q<3;q++){c.fillStyle=['#fff','#ffd43b','#ff8787'][q];c.beginPath();c.roundRect(182+q*16,110+r*18,12,12,4);c.fill();c.stroke()}
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.arc(205,170,16,0,T);c.fill();c.stroke()
c.save();c.translate(205,170);c.rotate(-th);c.lineWidth=5;c.beginPath();c.moveTo(0,0);c.lineTo(0,-10);c.stroke();c.restore()
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.roundRect(160,84,10,90,5);c.fill();c.stroke()
}
+21
View File
@@ -0,0 +1,21 @@
// four-color paper pinwheel whirling round on its stick in the breeze, little wind swooshes blowing past
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=60
const C=['#ff6b6b','#ffd43b','#4dabf7','#69db7c'],L=['#ffa8a8','#fff3bf','#a5d8ff','#b2f2bb'],X=128,Y=106,R=88,BL='#ffa8a8'
const p=(a,r)=>[cos(a)*r,sin(a)*r]
export default(c,t)=>{
for(const[y,o,w]of[[178,0,50],[206,.5,58]]){const q=(t+o)%1,x=16+q*40,a=sin(PI*q)
if(a>.15)for(const[lw,st]of[[11,'#111'],[5,'#fff']]){c.lineWidth=lw;c.strokeStyle=st;c.beginPath();c.moveTo(x,y);c.quadraticCurveTo(x+w*a*.5,y-6*a,x+w*a,y);c.stroke()}}
c.strokeStyle='#111'
for(const[w,s]of[[22,'#111'],[10,'#e8a15a']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(X,Y),c.lineTo(X,240),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
c.save();c.translate(X,Y);const rot=-t*T
for(let k=0;k<4;k++){const a=rot+k*PI/2
c.fillStyle=C[k];c.beginPath();c.moveTo(0,0);c.lineTo(...p(a,R));c.quadraticCurveTo(...p(a+1.2,R*1.02),...p(a+PI/2,R*.4));c.closePath();c.fill();c.stroke()
c.fillStyle=L[k];c.lineWidth=5;c.beginPath();c.moveTo(0,0);c.lineTo(...p(a,R-6));c.quadraticCurveTo(...p(a+.55,R*.62),...p(a+.85,R*.26));c.closePath();c.fill();c.stroke();c.lineWidth=7}
c.restore()
c.fillStyle='#fff';c.beginPath();c.arc(X,Y,20,0,T);c.fill();c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(X+d*7,Y-2,3.5,0,T),c.fill()
c.lineWidth=3.5;c.beginPath();c.arc(X,Y+3,4.5,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(X+d*12,Y+5,3.5,2.2,0,0,T),c.fill()
}
+29
View File
@@ -0,0 +1,29 @@
// pizza progress circle: slices pop onto the plate one by one clockwise until the pie is whole and smiling with sparkles, then all the slices shrink away to load again
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=24,delay=80
const CR='#e8a15a',CH='#ffd43b',PP='#fa5252',PR='#a5d8ff',X=128,Y=128,R=92
const ss=x=>x<=0?0:x>=1?1:x*x*(3-2*x)
const back=p=>p<=0?0:p>=1?1:1+2.7*(p-1)**3+1.7*(p-1)**2
export default(c,t)=>{
const out=1-ss((t-.86)/.1)
c.fillStyle=PR;c.beginPath();c.arc(X,Y,112,0,T);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.arc(X,Y,100,0,T);c.fill();c.lineWidth=4;c.stroke()
c.strokeStyle='#ced4da';c.lineWidth=4;c.setLineDash([6,8]);for(let k=0;k<8;k++){const a=-PI/2+k*PI/4;c.beginPath();c.moveTo(X+cos(a)*14,Y+sin(a)*14);c.lineTo(X+cos(a)*R,Y+sin(a)*R);c.stroke()}
c.beginPath();c.arc(X,Y,R-2,0,T);c.stroke();c.setLineDash([]);c.strokeStyle='#111'
for(let k=0;k<8;k++){
const s=back((t-k/12)/.08)*out;if(s<.02)continue
const a0=-PI/2+k*PI/4,a1=a0+PI/4,m=(a0+a1)/2,cx=X+cos(m)*(48+3),cy=Y+sin(m)*(48+3)
c.save();c.translate(cx,cy);c.scale(s,s);c.translate(-cx,-cy);c.translate(cos(m)*3,sin(m)*3)
c.lineWidth=6;c.fillStyle=CR;c.beginPath();c.moveTo(X,Y);c.arc(X,Y,R,a0,a1);c.closePath();c.fill();c.stroke()
c.fillStyle=CH;c.beginPath();c.moveTo(X,Y);c.arc(X,Y,R-16,a0+.03,a1-.03);c.closePath();c.fill()
c.lineWidth=4;c.fillStyle=PP;for(const[r,o]of[[62,-.13],[40,.12]]){const a=m+o;c.beginPath();c.arc(X+cos(a)*r,Y+sin(a)*r,8,0,T);c.fill();c.stroke()}
c.restore()
}
const f=t>.72&&t<.86
if(f){c.fillStyle=CH;c.beginPath();c.arc(X,Y,27,0,T);c.fill()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(X+d*11,Y-4,5,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(X+d*11-1.6,Y-5.6,1.8,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.arc(X,Y+3,7,.15*PI,.85*PI);c.stroke()
c.fillStyle='#ff8787';for(const d of[-1,1])c.beginPath(),c.ellipse(X+d*19,Y+5,5,3,0,0,T),c.fill()}
if(t>.68&&t<.9)for(const[x,y,o]of[[26,30,0],[230,30,.3],[22,66,.45],[234,66,.15]]){const q=(t-.68)/.22*1.5-o,z=12*sin(PI*min(1,max(0,q)));if(z>1.5){c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(x,y-z);for(const[px,py,qx,qy]of[[z,0,z*.2,-z*.2],[0,z,z*.2,z*.2],[-z,0,-z*.2,z*.2],[0,-z,-z*.2,-z*.2]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}}
}
+25
View File
@@ -0,0 +1,25 @@
// rainbow loader between two smiling clouds: the empty arch fills in band by band from red to violet, sparkles when complete, then wipes clear to start over
const{sin,PI,min,max}=Math,T=2*PI
export const frames=24,delay=80
const C=['#ff6b6b','#ffa94d','#ffd43b','#69db7c','#4dabf7','#b197fc'],X=128,Y=182,RO=114,RI=42,BW=(RO-RI)/6
const cloud=(c,x,y)=>{
const P=[[x-20,y+2,16],[x+2,y-10,21],[x+22,y+2,15]],base=()=>c.roundRect(x-36,y,72,24,12)
c.lineWidth=14;c.strokeStyle='#111';for(const[a,b,r]of P)c.beginPath(),c.arc(a,b,r,0,T),c.stroke();c.beginPath();base();c.stroke()
c.fillStyle='#fff';for(const[a,b,r]of P)c.beginPath(),c.arc(a,b,r,0,T),c.fill();c.beginPath();base();c.fill()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(x+2+d*9,y+6,3,0,T),c.fill()
c.lineWidth=3;c.beginPath();c.arc(x+2,y+10,4.5,.15*PI,.85*PI);c.stroke()
c.fillStyle='#ffc9c9';for(const d of[-1,1])c.beginPath(),c.ellipse(x+2+d*18,y+11,4,2.5,0,0,T),c.fill();c.lineWidth=7
}
const spark=(c,x,y,s)=>{c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.2,-s*.2],[0,s,s*.2,s*.2],[-s,0,-s*.2,s*.2],[0,-s,-s*.2,-s*.2]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
export default(c,t)=>{
const q=max(0,min(1,(t-.8)/.14))
c.fillStyle='#fff';c.beginPath();c.arc(X,Y,RO,PI,0);c.arc(X,Y,RI,0,PI,true);c.closePath();c.fill()
c.lineCap='butt'
C.forEach((col,k)=>{const p=max(0,min(1,(t-k*.1)/.12)),a0=PI+PI*q,a1=PI+PI*p;if(a1>a0+.01){c.strokeStyle=col;c.lineWidth=BW+.8;c.beginPath();c.arc(X,Y,RO-BW*(k+.5),a0,a1);c.stroke()}})
c.lineCap='round';c.strokeStyle='#111';c.lineWidth=7
c.beginPath();c.arc(X,Y,RO,PI,0);c.arc(X,Y,RI,0,PI,true);c.closePath();c.stroke()
const h0=PI*max(1.1,1+q),h1=PI*min(1.3,1+min(1,t/.12));c.strokeStyle='#fff';c.lineWidth=5;if(h1>h0+.05)c.beginPath(),c.arc(X,Y,RO-7,h0,h1),c.stroke()
c.strokeStyle='#111'
if(t>.68&&t<.84)for(const[x,y,o]of[[128,40,0],[40,72,.33],[216,72,.66]]){const s=13*sin(PI*((t-.68)/.16));if(s>1.5)spark(c,x,y,s*1.2*(1-o*.3))}
cloud(c,X-(RO+RI)/2,Y+4+Math.round(2*sin(T*t)));cloud(c,X+(RO+RI)/2,Y+4+Math.round(2*sin(T*t+PI)))
}
+39
View File
@@ -0,0 +1,39 @@
// a snail slowly crawls along a progress bar dragging the slimy fill along behind it, at the end it ducks into its shell and rolls back to the start
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=24,delay=90
const SH='#b197fc',SD='#7950f2',BD='#ffd8a8',FL='#63e6be',BL='#ff8787'
const GY=170,L=14,R=242,K=1.34,XA=L+56*K,XB=R-66*K
const ss=x=>x<=0?0:x>=1?1:x*x*(3-2*x)
const bar=c=>{c.beginPath();c.roundRect(L,GY,R-L,36,18)}
export default(c,t)=>{
const u=min(1,t/.66),rb=ss((t-.74)/.18),e=t<.66?1:1-ss((t-.66)/.07)+ss((t-.92)/.08),x=t<.74?XA+(XB-XA)*u:XB-(XB-XA)*rb
const F=t<.74?L+(R-L)*u:t<.92?L+(R-L)*(x-XA)/(XB-XA):L,st=t<.66?4*sin(T*t*6):0,rot=-T*rb
c.fillStyle='#fff';bar(c);c.fill()
c.save();bar(c);c.clip();if(F>L+1){c.fillStyle=FL;c.fillRect(L,GY,F-L,40);c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(L+14,GY+9);c.lineTo(max(L+14,F-12),GY+9);c.stroke()}c.restore()
c.strokeStyle='#111';c.lineWidth=7;bar(c);c.stroke()
c.save();c.translate(x,GY);c.scale(K,K)
if(e>.02){
c.save();c.translate(0,-30);c.scale(e,e);c.translate(0,30)
const hx=44+st,hy=-36+st*.3,parts=(o,f)=>{
c.lineWidth=8+o;c.beginPath();c.moveTo(hx-8,hy-12);c.lineTo(hx-16,hy-36);c.moveTo(hx+6,hy-14);c.lineTo(hx+10,hy-38);c.stroke()
c.beginPath();c.arc(hx-16,hy-36,5+o/2,0,T);c.arc(hx+10,hy-38,5+o/2,0,T);f()
c.beginPath();c.roundRect(-50-o/2,-20-o/2,90+st+o,20+o,10+o/2);f()
c.lineWidth=26+o;c.beginPath();c.moveTo(hx-16,-10);c.lineTo(hx-2,hy+6);c.stroke()
c.beginPath();c.arc(hx,hy,19+o/2,0,T);f()}
c.strokeStyle=c.fillStyle='#111';parts(12,()=>c.fill())
c.strokeStyle=c.fillStyle=BD;parts(0,()=>c.fill())
c.strokeStyle='#111'
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(hx+2+d*8,hy-3,3.8,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(hx+.8+d*8,hy-4.5,1.4,0,T),c.fill()
c.lineWidth=3.5;c.beginPath();c.arc(hx+2,hy+3,5,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(hx+2+d*13,hy+5,4,2.5,0,0,T),c.fill()
c.restore()
}
const sy=-34-10*e
c.save();c.translate(-4,sy);c.rotate(rot+.04*st)
c.fillStyle=SH;c.lineWidth=7;c.beginPath();c.arc(0,0,34,0,T);c.fill();c.stroke()
c.strokeStyle=SD;c.lineWidth=6;c.beginPath();for(let a=0;a<4.4*PI;a+=.2){const r=3+a*1.75;c.lineTo(cos(a)*r,sin(a)*r)}c.stroke()
c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(0,0,26,PI*1.1,PI*1.4);c.stroke()
c.restore()
c.restore()
}