Double the colored section with 50 more stickers

Animals (unicorn, flamingo, peacock, giraffe, chameleon, axolotl, capybara,
otter, corgi, toucan), food (sushi, cupcake, popsicle, pancakes, strawberry,
pineapple, blender, latte, cotton candy, fries), nature and space (sunset,
aurora, comet, galaxy, sunflower, lotus, maple leaf, island, snow globe,
clam), objects (lava lamp, cassette, vinyl, disco ball, neon sign, traffic
light, crayon, watering can, pinata, arcade) and rides and luck (hot-air
balloon, submarine, helicopter, roller coaster, ferris wheel, fire truck,
soccer goal, crown, clover, maneki-neko).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 00:00:26 +00:00
co-authored by Claude Opus 5.5
parent 7108136edd
commit 56666ba0e3
150 changed files with 1621 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
// arcade cabinet whose screen shows a blinking pixel face, joystick wiggling, buttons pressing, marquee lights chasing
const{sin,cos,PI,floor}=Math,T=2*PI
export const frames=16,delay=90
const CAB='#9775fa',CD='#7048e8',MQ='#ffd43b',BUL='#ffa94d',BEZ='#495057',SCR='#212529',PX='#69db7c',HT='#ff6b6b',PANEL='#4dabf7',PD='#1c7ed6',BTN=['#ffd43b','#69db7c'],COIN='#ffa94d'
const FACE=['..........','..##..##..','..##..##..','..........','.#......#.','..######..']
const BLINK=['..........','..........','..##..##..','..........','.#......#.','..######..']
const HEART=['.#.#.','#####','.###.','..#..']
const px=(c,rows,x,y,s,col)=>{c.fillStyle=col;rows.forEach((r,j)=>[...r].forEach((ch,k)=>{if(ch=='#')c.fillRect(x+k*s,y+j*s,s,s)}))}
export default(c,t,i)=>{
const js=sin(T*t*2),b=floor(t*4)%2,pr=(t*4)%1<.5
const body=()=>{c.beginPath();c.roundRect(62,40,132,208,12)}
c.fillStyle=CAB;body();c.fill();c.stroke()
c.save();body();c.clip();c.fillStyle=CD;c.fillRect(62,40,12,208);c.fillRect(182,40,12,208);c.restore()
c.lineWidth=5;for(const x of[74,182])c.beginPath(),c.moveTo(x,40),c.lineTo(x,248),c.stroke()
c.lineWidth=7;body();c.stroke()
c.fillStyle=MQ;c.beginPath();c.roundRect(52,12,152,44,12);c.fill();c.stroke()
for(let k=0;k<5;k++){const lit=(k+floor(t*16))%5<2;c.fillStyle=lit?'#fff':BUL;c.lineWidth=4;c.beginPath();c.arc(80+k*24,34,7,0,T);c.fill();c.stroke()}
c.lineWidth=7;c.fillStyle=BEZ;c.beginPath();c.roundRect(72,64,112,94,10);c.fill();c.stroke()
c.fillStyle=SCR;c.beginPath();c.roundRect(80,72,96,78,6);c.fill()
const fl=i==6
px(c,i==11||i==12?BLINK:FACE,98,92,6,fl?'#fff':PX)
if(floor(t*8)%2==0)px(c,HEART,84,76,3,HT)
c.fillStyle='rgba(255,255,255,.06)';for(let y=74;y<150;y+=4)c.fillRect(80,y,96,1.5)
c.lineWidth=7;c.fillStyle=PANEL;c.beginPath();c.moveTo(48,162);c.lineTo(208,162);c.lineTo(200,194);c.lineTo(56,194);c.closePath();c.fill();c.stroke()
c.fillStyle=PD;c.beginPath();c.ellipse(96,178,15,6,0,0,T);c.fill();c.lineWidth=5;c.stroke()
const bx=96+12*js,by=150
for(const[w,st]of[[10,'#111'],[4,'#ced4da']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(96,177),c.lineTo(bx,by),c.stroke()
c.strokeStyle='#111';c.fillStyle=HT;c.lineWidth=5;c.beginPath();c.arc(bx,by,11,0,T);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.arc(bx-4,by-4,3,0,T);c.fill()
BTN.forEach((col,k)=>{const x=146+k*28,y=178-k*3,on=k==b&&pr;c.fillStyle=col;c.beginPath();c.ellipse(x,y+(on?2:0),11,on?5:7,0,0,T);c.fill();c.lineWidth=5;c.stroke()})
c.fillStyle=COIN;c.lineWidth=6;c.beginPath();c.roundRect(104,206,48,32,6);c.fill();c.stroke()
c.fillStyle='#343a40';for(const x of[114,136])c.beginPath(),c.roundRect(x,213,6,16,2),c.fill()
}
+31
View File
@@ -0,0 +1,31 @@
// northern lights ripple over snowy hills inside a rounded night-sky sticker, stars twinkling
// the dark sky gets a white rim so it doesn't vanish on dark backgrounds
const{sin,max}=Math,T=2*Math.PI
export const frames=16,delay=90
const SKY='#2b3a7a',R=[['#b197fc','#e5dbff',62,3.1,.1],['#38d9a9','#c3fae8',82,1.7,-.08],['#69db7c','#d3f9d8',104,0,.05]]
const spk=(c,x,y,s)=>{c.lineWidth=3;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 pine=(c,x,y)=>{
c.fillStyle='#a9744f';c.lineWidth=5;c.beginPath();c.rect(x-4,y-6,8,12);c.fill();c.stroke()
c.fillStyle='#2f9e44';for(const[w,y0,h]of[[18,-6,18],[14,-18,16],[10,-28,14]])c.beginPath(),c.moveTo(x-w,y+y0),c.lineTo(x,y+y0-h),c.lineTo(x+w,y+y0),c.closePath(),c.fill(),c.stroke()
}
export default(c,t)=>{
const a=t*T,win=()=>{c.beginPath();c.roundRect(14,26,228,206,48)}
win();c.lineWidth=18;c.strokeStyle='#fff';c.stroke();c.fillStyle=SKY;c.fill();c.strokeStyle='#111'
c.save();win();c.clip()
for(const[x,y,o]of[[46,52,0],[208,48,.35],[134,40,.7],[226,100,.2],[34,112,.55]])spk(c,x,y,2+4*max(0,sin((t+o)*2*T)))
for(const[col,lt,b,p,sl]of R){
const top=x=>b+(x-128)*sl+12*sin(x/38+p+a)+5*sin(x/17-p-2*a),h=x=>34+10*sin(x/29+p*2-a)
c.beginPath();for(let x=0;x<=256;x+=8)c.lineTo(x,top(x));for(let x=256;x>=0;x-=8)c.lineTo(x,top(x)+h(x));c.closePath()
c.fillStyle=col;c.fill();c.lineWidth=4;c.stroke()
c.save();c.clip();c.strokeStyle=lt;c.lineWidth=3
for(let x=14*t-14;x<270;x+=14){const y0=top(x);c.beginPath();c.moveTo(x,y0+6);c.lineTo(x,y0+h(x)*.72);c.stroke()}
c.restore();c.strokeStyle='#111'
}
c.lineWidth=7
for(const[x0,x1,px,py]of[[96,300,206,104],[-40,170,56,92]]){
c.fillStyle='#fff';c.beginPath();c.moveTo(x0,250);c.quadraticCurveTo(px,py,x1,250);c.closePath();c.fill();c.stroke()
c.strokeStyle='#a5d8ff';c.lineWidth=5;c.beginPath();c.moveTo(px-30,(250+py)/2+22);c.quadraticCurveTo(px-6,(250+py)/2+12,px+16,(250+py)/2+20);c.stroke();c.strokeStyle='#111';c.lineWidth=7
}
pine(c,190,186);pine(c,214,196);pine(c,58,182)
c.restore();c.lineWidth=7;win();c.stroke()
}
+38
View File
@@ -0,0 +1,38 @@
// pink axolotl waving a little hand, its feathery gill fronds wiggling, bubbles drifting up
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=80
const PK='#faa2c1',PD='#f06595',PL='#ffdeeb',BL='#ff8fab',BU='#a5d8ff'
export default(c,t)=>{
const wv=sin(2*T*t),tl=.12*sin(T*t)
const D=(wd,cl,f)=>{for(const[l,s]of[[wd,'#111'],[wd-10,cl]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
for(const[x,y0,r,o]of[[204,0,9,0],[222,0,6,.4],[196,0,5,.75]]){const p=(t+o)%1,y=70-p*60;if(p<.85){c.fillStyle=BU;c.lineWidth=4;c.beginPath();c.arc(x+3*sin(p*T*2),y,r,0,T);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.arc(x+3*sin(p*T*2)-r*.35,y-r*.35,r*.25,0,T);c.fill()}}
c.lineWidth=7
c.save();c.translate(150,200);c.rotate(tl)
c.fillStyle=PK;c.beginPath();c.moveTo(0,-14);c.quadraticCurveTo(70,-30,92,6);c.quadraticCurveTo(60,20,0,16);c.closePath();c.fill();c.stroke()
c.fillStyle=PL;c.beginPath();c.moveTo(20,-12);c.quadraticCurveTo(66,-24,84,0);c.quadraticCurveTo(60,-6,20,-4);c.closePath();c.fill()
c.restore()
c.fillStyle=PK;for(const[x,d]of[[96,-1],[160,1]])c.beginPath(),c.ellipse(x,226,15,10,d*.3,0,T),c.fill(),c.stroke()
c.beginPath();c.ellipse(128,196,48,36,0,0,T);c.fill();c.stroke()
c.fillStyle=PL;c.beginPath();c.ellipse(128,206,28,20,0,0,T);c.fill()
for(const d of[-1,1])for(let k=0;k<3;k++){
const a=d*(-.55+k*.45)+.1*sin(2*T*t+k*1.3+(d>0?1:0)),bx=128+d*58,by=104+k*18-18
c.save();c.translate(bx,by);c.rotate(d>0?a:PI+a)
D(14,PD,()=>{c.moveTo(0,0);c.lineTo(36,0)})
c.fillStyle=PD;c.lineWidth=4;for(const x of[12,24])for(const s of[-1,1])c.beginPath(),c.ellipse(x,s*7,5,3,s*.6,0,T),c.fill(),c.stroke()
c.restore();c.lineWidth=7
}
c.fillStyle=PK;c.beginPath();c.ellipse(128,118,70,52,0,0,T);c.fill();c.stroke()
for(const d of[-1,1]){c.fillStyle='#111';c.beginPath();c.arc(128+d*34,110,8,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(128+d*34-2,107,3,0,T);c.fill()}
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*46,128,10,6,0,0,T),c.fill()
c.lineWidth=5;c.beginPath();c.moveTo(104,126);c.quadraticCurveTo(128,146,152,126);c.stroke()
c.fillStyle=PK;c.lineWidth=7
c.beginPath();c.ellipse(92,192,12,9,.4,0,T);c.fill();c.stroke()
c.save();c.translate(166,182);c.rotate(-.9+.35*wv)
D(20,PK,()=>{c.moveTo(0,0);c.lineTo(30,0)})
c.translate(36,0);c.fillStyle=PK
c.lineWidth=5;for(const a of[-.7,0,.7])c.beginPath(),c.ellipse(cos(a)*9,sin(a)*9,6,4,a,0,T),c.fill(),c.stroke()
c.beginPath();c.arc(0,0,9,0,T);c.fill();c.stroke()
c.restore()
for(const d of[-1,1])c.lineWidth=4,c.beginPath(),c.arc(224,138+d*12,10,-.6,.6),c.stroke()
c.lineWidth=7
}
+33
View File
@@ -0,0 +1,33 @@
// blender whirling a pink smoothie, fruit chunks circling in the vortex while the lid rattles and the base hums
const{sin,cos,PI,exp}=Math,T=2*PI
export const frames=16,delay=60
const GL='#e7f5ff',SM='#f783ac',SL='#faa2c1',BA='#ff6b6b',BD='#e03131',LID='#b197fc',BL='#ffc9c9',FR=['#ff6b6b','#fff3bf','#69db7c','#748ffc','#ffa94d','#69db7c']
const LV=[[96,34],[120,26],[142,18]]
const jar=c=>{c.beginPath();c.moveTo(70,42);c.lineTo(186,42);c.lineTo(172,164);c.lineTo(84,164);c.closePath()}
export default(c,t,i)=>{
const sp=T*t*2,ly=i%2?-3:1,lr=i%2?.03:-.02
for(const[w,s]of[[20,'#111'],[10,GL]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(182,66),c.lineTo(208,72),c.lineTo(204,128),c.lineTo(176,136),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
jar(c);c.fillStyle=GL;c.fill()
const ch=(back)=>FR.forEach((col,k)=>{const[y,rx]=LV[k%3],a=sp+k*PI+(k%3)*1.1;if((sin(a)<0)!=back)return;const s=back?.75:1;c.save();c.translate(128+cos(a)*rx*1.6,y+sin(a)*6);c.rotate(a*2+k);c.scale(s,s);c.fillStyle=col;c.lineWidth=3.5;c.beginPath();c.roundRect(-7,-7,14,14,4);c.fill();c.stroke();c.restore()})
c.save();jar(c);c.clip()
c.fillStyle=SM;c.beginPath();c.moveTo(60,72);for(let x=60;x<=196;x+=4)c.lineTo(x,72+30*exp(-(((x-128)/22)**2))+2*sin(x/9-sp*2));c.lineTo(196,170);c.lineTo(60,170);c.closePath();c.fill();c.lineWidth=5;c.stroke()
ch(true)
c.strokeStyle=SL;c.lineWidth=5;for(const[y,rx]of LV)for(const o of[0,PI])c.beginPath(),c.ellipse(128,y,rx*1.6,7,0,sp+o+.3,sp+o+1.5),c.stroke()
ch(false)
c.strokeStyle='#fff';c.lineWidth=7;c.beginPath();c.moveTo(86,58);c.lineTo(94,146);c.stroke()
c.restore();c.strokeStyle='#111';c.lineWidth=7;jar(c);c.stroke()
c.lineWidth=4;for(const y of[72,98,124])c.beginPath(),c.moveTo(164-(y-42)*.11,y),c.lineTo(176-(y-42)*.11,y),c.stroke()
c.lineWidth=7
c.save();c.translate(128,36+ly);c.rotate(lr);c.fillStyle=LID;c.beginPath();c.roundRect(-66,-8,132,16,8);c.fill();c.stroke();c.beginPath();c.roundRect(-16,-20,32,14,6);c.fill();c.stroke();c.restore()
c.fillStyle=BD;c.beginPath();c.roundRect(78,160,100,14,6);c.fill();c.stroke()
c.fillStyle=BA;c.beginPath();c.roundRect(66,172,124,62,[12,12,18,18]);c.fill();c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24,192,6,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24-2,190,2.2,0,T),c.fill()
c.fillStyle='#111';c.beginPath();c.ellipse(128,206,7,8,0,0,T);c.fill()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,204,9,5,0,0,T),c.fill()
c.lineWidth=4;['#fff','#ffd43b','#69db7c'].forEach((col,k)=>{c.fillStyle=k==Math.floor(t*3)?col:'#dee2e6';c.beginPath();c.arc(104+k*24,224,4.5,0,T);c.fill();c.stroke()})
c.lineWidth=7
for(const d of[-1,1])for(const[w,s]of[[9,'#111'],[4,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();const x=128+d*(i%2?78:82);c.moveTo(x,186);c.quadraticCurveTo(x+d*6,200,x,214);c.stroke()}
c.strokeStyle='#111';c.lineWidth=7
}
+42
View File
@@ -0,0 +1,42 @@
// capybara soaking in a hot spring with a yuzu on its head, eyes shut in bliss, bobbing gently as steam curls up
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=100
const SN='#d9a77a',BR='#c08552',BD='#8d6346',WA='#74c0fc',WL='#d0ebff',ST='#ced4da',SD='#adb5bd',YZ='#ffd43b',YD='#fab005',LF='#51cf66',BL='#ffa8a8'
export default(c,t)=>{
const b=Math.round(3*sin(T*t)),yw=.1*sin(T*t-.8)
const D=(wd,cl,f)=>{for(const[l,s]of[[wd,'#111'],[wd-8,cl]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
for(const[x,o]of[[56,0],[204,.5]]){const p=(t+o)%1,a=sin(PI*p)
if(a>.15)D(12,'#fff',()=>{for(let k=0;k<=8;k++){const u=k/8;c.lineTo(x+7*sin(u*T+p*T),70-p*24-u*40)}})}
c.fillStyle=ST
for(const[x,y,rx,ry]of[[36,184,26,18],[220,182,28,20],[70,226,30,16],[188,228,32,16]])c.beginPath(),c.ellipse(x,y,rx,ry,0,0,T),c.fill(),c.stroke()
c.fillStyle=WA;c.beginPath();c.ellipse(128,198,104,38,0,0,T);c.fill();c.stroke()
c.save();c.translate(0,b)
c.fillStyle=BR
for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*40,84,11,9,0,0,T),c.fill(),c.stroke()
c.beginPath();c.roundRect(68,80,120,130,[52,52,30,30]);c.fill();c.stroke()
c.fillStyle=BD;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*40,85,5,4,0,0,T),c.fill()
c.fillStyle=SN;c.beginPath();c.ellipse(128,156,40,28,0,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7
c.fillStyle=BD;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*14,148,6,4,0,0,T),c.fill()
c.lineWidth=5;c.beginPath();c.moveTo(128,152);c.lineTo(128,162);c.moveTo(116,166);c.quadraticCurveTo(128,172,140,166);c.stroke()
for(const d of[-1,1])c.beginPath(),c.arc(128+d*30,118,9,1.15*PI,1.85*PI),c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,132,8,5,0,0,T),c.fill()
c.save();c.translate(128,70);c.rotate(yw)
c.lineWidth=7;c.fillStyle=YZ;c.beginPath();c.ellipse(0,-14,26,22,0,0,T);c.fill();c.stroke()
c.fillStyle=YD;for(const[x,y]of[[-10,-20],[6,-8],[12,-22],[-4,-6]])c.beginPath(),c.arc(x,y,2,0,T),c.fill()
c.fillStyle='#fff';c.beginPath();c.ellipse(-10,-24,6,4,-.5,0,T);c.fill()
c.fillStyle=LF;c.lineWidth=5;c.beginPath();c.ellipse(10,-40,12,6,-.4,0,T);c.fill();c.stroke()
c.beginPath();c.moveTo(0,-36);c.lineTo(2,-42);c.stroke()
c.restore()
c.restore()
c.lineWidth=7
const wy=x=>196+3*sin(x/14+T*t*2)
c.save();c.beginPath();c.ellipse(128,198,104,38,0,0,T);c.clip()
c.fillStyle=WA;c.beginPath();c.moveTo(20,240);for(let x=20;x<=236;x+=4)c.lineTo(x,wy(x));c.lineTo(236,240);c.closePath();c.fill()
c.beginPath();for(let x=60;x<=196;x+=4)c.lineTo(x,wy(x));c.stroke()
c.strokeStyle=WL;c.lineWidth=4
for(const[x,w]of[[96,22],[160,18],[128,34]]){const y=x==128?220:212;c.beginPath();c.moveTo(x-w,y);c.lineTo(x+w,y);c.stroke()}
c.restore()
c.strokeStyle='#111';c.lineWidth=7;c.beginPath();c.ellipse(128,198,104,38,0,0,T);c.stroke()
c.fillStyle=ST;for(const[x,y,rx,ry]of[[70,226,30,16],[188,228,32,16]])c.beginPath(),c.ellipse(x,y,rx,ry,0,0,T),c.fill(),c.stroke()
c.fillStyle=SD;for(const[x,y]of[[62,222],[196,224]])c.beginPath(),c.ellipse(x,y,8,4,0,0,T),c.fill()
}
+40
View File
@@ -0,0 +1,40 @@
// cassette tape whose spinning reels are its eyes, tape ticking along between them, notes floating up
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=70
const BODY='#4dabf7',BD='#1c7ed6',LAB='#fff9db',BAND='#ff6b6b',STR='#ffd43b',WIN='#495057',TAPE='#a9744f',TL='#d9a47c',BL='#faa2c1',NOTE=['#b197fc','#69db7c']
const note=(c,x,y,s,col)=>{
c.save();c.translate(x,y);c.scale(s,s);c.fillStyle=col
for(const[w,st]of[[9,'#111'],[3,col]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(5,4),c.lineTo(5,-24),c.lineTo(17,-18),c.stroke()
c.strokeStyle='#111';c.lineWidth=4;c.beginPath();c.ellipse(0,5,8,6,-.4,0,T);c.fill();c.stroke();c.restore()
}
export default(c,t)=>{
const a=T*t*2/3
for(let k=0;k<2;k++){const p=(t+k/2)%1,s=p<.75?1:(1-p)*4;note(c,56+k*140+6*sin(p*T),58-p*40,s,NOTE[k])}
c.lineWidth=7;c.fillStyle=BODY;c.beginPath();c.roundRect(22,56,212,146,18);c.fill();c.stroke()
c.fillStyle='#e7f5ff';c.lineWidth=4;for(const[x,y]of[[38,72],[218,72],[38,186],[218,186]])c.beginPath(),c.arc(x,y,5,0,T),c.fill(),c.stroke()
const lab=()=>{c.beginPath();c.roundRect(42,68,172,98,10)}
c.fillStyle=LAB;lab();c.fill()
c.save();lab();c.clip();c.fillStyle=BAND;c.fillRect(42,68,172,16);c.fillStyle=STR;c.fillRect(42,84,172,6);c.restore()
c.lineWidth=5;lab();c.stroke();c.beginPath();c.moveTo(42,84);c.lineTo(214,84);c.stroke()
const win=()=>{c.beginPath();c.roundRect(68,98,120,46,23)}
c.fillStyle=WIN;win();c.fill()
c.save();win();c.clip()
c.fillStyle=TAPE;for(const x of[98,158])c.beginPath(),c.arc(x,121,19,0,T),c.fill()
c.strokeStyle=TAPE;c.lineWidth=5;c.beginPath();c.moveTo(98,138);c.lineTo(158,138);c.stroke()
c.strokeStyle=TL;c.lineWidth=2;for(let k=0;k<5;k++){const x=98+((k*12+t*24)%60);c.beginPath();c.moveTo(x,136);c.lineTo(x+4,136);c.stroke()}
c.strokeStyle='#111'
for(const x of[98,158]){
c.save();c.translate(x,121);c.rotate(a)
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(0,0,12,0,T);c.fill();c.stroke()
c.fillStyle='#111';for(let k=0;k<6;k++)c.save(),c.rotate(k*PI/3),c.fillRect(-1.5,-12,3,5),c.restore()
c.beginPath();c.arc(0,0,4.5,0,T);c.fill()
c.restore()
}
c.restore()
c.lineWidth=5;win();c.stroke()
c.lineWidth=4;c.beginPath();c.arc(128,150,7,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*58,154,9,5,0,0,T),c.fill()
c.lineWidth=7;c.fillStyle=BD;c.beginPath();c.moveTo(62,202);c.lineTo(78,176);c.lineTo(178,176);c.lineTo(194,202);c.closePath();c.fill();c.stroke()
c.fillStyle=WIN;for(const x of[100,156])c.beginPath(),c.arc(x,190,5,0,T),c.fill()
c.beginPath();c.roundRect(120,184,16,12,3);c.fill()
}
+46
View File
@@ -0,0 +1,46 @@
// chameleon on a branch slowly shifting color, eyeing a buzzing fly, then snapping it up with its long tongue
const{sin,cos,PI,min,max,hypot,atan2}=Math,T=2*PI
export const frames=20,delay=80
const RING=['#69db7c','#38d9a9','#4dabf7','#b197fc','#f783ac','#ffa94d','#ffd43b'],BR='#a9744f',LF='#51cf66',TG='#ff8fab'
const hex=h=>[1,3,5].map(k=>parseInt(h.slice(k,k+2),16))
const mix=(a,b,u,l=0)=>'rgb('+hex(a).map((v,k)=>Math.round((v+(hex(b)[k]-v)*u)*(1-l)+255*l)).join(',')+')'
const ease=x=>x<0?0:x>1?1:x*x*(3-2*x)
export default(c,t,i)=>{
const q=t*RING.length,k=Math.floor(q),u=ease(q-k),A=RING[k%7],B=RING[(k+1)%7],col=mix(A,B,u),lit=mix(A,B,u,.45)
const orb=s=>[222+12*cos(4*T*s),70+8*sin(8*T*s)]
const D=(wd,cl,f)=>{for(const[l,s]of[[wd,'#111'],[wd-10,cl]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
const M=[210,158],hit=orb(.52)
let fly=null,tip=null,fs=1
if(t<.42)fly=orb(t)
else if(t<.52){const p=ease((t-.42)/.1);fly=orb(t);tip=[M[0]+(hit[0]-M[0])*p,M[1]+(hit[1]-M[1])*p]}
else if(t<.64){const p=1-ease((t-.52)/.12);tip=[M[0]+(hit[0]-M[0])*p,M[1]+(hit[1]-M[1])*p];fly=tip}
else if(t>=.8){const p=ease((t-.8)/.2),e=orb(1);fly=[236+(e[0]-236)*p,30+(e[1]-30)*p+4*sin(p*PI*2)];fs=.35+.65*p}
const chew=t>.64&&t<.8?Math.abs(sin((t-.64)/.16*PI*2)):0
D(20,BR,()=>{c.moveTo(12,206);c.quadraticCurveTo(128,196,246,200)})
c.fillStyle=LF;c.beginPath();c.ellipse(40,218,14,7,.5,0,T);c.fill();c.stroke()
D(20,col,()=>{c.moveTo(70,160);c.bezierCurveTo(40,170,30,200,52,206);c.bezierCurveTo(70,210,72,186,56,184)})
for(const[x0,y0,x1]of[[92,170,84],[150,172,158]])D(16,col,()=>{c.moveTo(x0,y0);c.lineTo(x1,190);c.lineTo(x1+(x1>120?8:-8),202)})
c.fillStyle=col
const sil=()=>{c.beginPath();c.ellipse(118,150,58,32,-.05,0,T);c.moveTo(150,128);c.quadraticCurveTo(170,100,190,122);c.lineTo(214,150);c.quadraticCurveTo(214,170,190,172);c.lineTo(150,174);c.closePath()}
sil();c.lineWidth=14;c.stroke();sil();c.fill();c.lineWidth=7
c.save();sil();c.clip();c.fillStyle=lit
for(const[x,y]of[[84,150],[112,146],[140,150]])c.beginPath(),c.ellipse(x,y,7,22,.25,0,T),c.fill()
c.restore()
c.lineWidth=5;c.beginPath();for(let k=0;k<6;k++)c.moveTo(70+k*16,121-(k%2?0:2)),c.lineTo(78+k*16,114);c.stroke()
if(tip){D(11,TG,()=>{c.moveTo(M[0]-6,M[1]);c.lineTo(tip[0],tip[1])});c.fillStyle=TG;c.beginPath();c.arc(tip[0],tip[1],8,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7}
c.lineWidth=5;c.beginPath();c.moveTo(212,156);c.quadraticCurveTo(200,164,186,160);c.stroke()
if(chew){c.fillStyle=col;c.beginPath();c.ellipse(194,166,8+3*chew,5+2*chew,0,0,T);c.fill();c.stroke()}
c.lineWidth=7
const e=fly||orb(.9),ex=180,ey=138,a=atan2(e[1]-ey,e[0]-ex)
c.fillStyle=col;c.beginPath();c.arc(ex,ey,17,0,T);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.arc(ex+cos(a)*5,ey+sin(a)*5,9,0,T);c.fill();c.lineWidth=4;c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(ex+cos(a)*8,ey+sin(a)*8,4.5,0,T);c.fill();c.lineWidth=7
if(fly){
const[fx,fy]=fly,fl=i%2
c.save();c.translate(fx,fy);c.scale(fs,fs);c.translate(-fx,-fy)
c.fillStyle='#fff';c.lineWidth=4
for(const d of[-1,1])c.beginPath(),c.ellipse(fx+d*8,fy-9-fl*3,9,5+fl*2,d*(.6+fl*.3),0,T),c.fill(),c.stroke()
c.lineWidth=10;c.strokeStyle='#fff';c.beginPath();c.ellipse(fx,fy,9,7,0,0,T);c.stroke();c.fillStyle='#111';c.fill()
c.restore();c.strokeStyle='#111';c.lineWidth=7
}
}
+37
View File
@@ -0,0 +1,37 @@
// smiling clam opens up to show off a shining pearl, then shuts again, bubbles rising behind it
const{sin,cos,PI,max,abs}=Math,T=2*PI
export const frames=24,delay=70
const SH='#faa2c1',SHD='#f783ac',IN='#e64980',PL='#f3f0ff',BL='#ff8787',BU='#e7f5ff'
const sm=x=>x<0?0:x>1?1:x*x*(3-2*x)
const spk=(c,x,y,s)=>{c.lineWidth=3;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 o=sm((t-.1)/.2)*(1-sm((t-.72)/.2)),up=48*o,sc=1-.3*o,rim=152
for(let k=0;k<3;k++){const p=(t+k/3)%1,r=5+2*k,x=[54,204,186][k]+5*sin(p*T*2+k),y=150-p*140;if(p<.92){c.fillStyle=BU;c.lineWidth=4;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke();c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.arc(x,y,r*.55,PI*1.1,PI*1.5);c.stroke();c.strokeStyle='#111'}}
c.lineWidth=7
if(up>1){
const mouth=()=>{c.beginPath();c.ellipse(128,rim-up*.5,84,up*.5+8,0,0,T)}
mouth();c.fillStyle=IN;c.fill();c.stroke()
c.save();mouth();c.clip()
c.fillStyle=PL;c.lineWidth=5;c.beginPath();c.arc(128,rim-10,22,0,T);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.ellipse(120,rim-18,7,4.5,-.5,0,T);c.fill()
c.restore()
}
c.lineWidth=7;c.beginPath()
for(let k=0;k<=56;k++){const q=PI+PI*k/56,b=1+.045*abs(sin(7*(q-PI)));c.lineTo(128+88*b*cos(q),rim-up+76*b*sin(q)*sc)}
c.quadraticCurveTo(128,rim-up+10,40,rim-up);c.closePath();c.fillStyle=SH;c.fill();c.stroke()
c.strokeStyle=SHD;c.lineWidth=3.5
for(const q of[1.2,1.35,1.5,1.65,1.8].map(v=>v*PI))c.beginPath(),c.moveTo(128+18*cos(q),rim-up-8+12*sin(q)*sc),c.lineTo(128+74*cos(q),rim-up+66*sin(q)*sc),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
c.beginPath();c.moveTo(40,rim);c.quadraticCurveTo(128,rim+18,216,rim)
for(let k=0;k<=56;k++){const q=PI*k/56,b=1+.045*abs(sin(7*q));c.lineTo(128+88*b*cos(q),rim+68*b*sin(q))}
c.closePath();c.fillStyle=SH;c.fill();c.stroke()
c.strokeStyle=SHD;c.lineWidth=3.5
for(const q of[.25,.36,.64,.75].map(v=>v*PI))c.beginPath(),c.moveTo(128+30*cos(q),rim+30*sin(q)),c.lineTo(128+76*cos(q),rim+60*sin(q)),c.stroke()
c.strokeStyle='#111'
const hap=o>.6
c.fillStyle='#111';c.lineWidth=4.5
for(const d of[-1,1])if(hap)c.beginPath(),c.arc(128+d*22,184,6,PI*1.15,PI*1.85),c.stroke();else c.beginPath(),c.arc(128+d*22,182,5,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.arc(128,190,8,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*40,192,7,4,0,0,T),c.fill()
if(o>.6)for(const[x,y,q]of[[100,rim-up-6,0],[160,rim-up+2,.5]])spk(c,x,y,(o-.6)/.4*(4+4*max(0,sin((t+q)*4*T))))
}
+27
View File
@@ -0,0 +1,27 @@
// four-leaf clover spinning like a pinwheel on its stem with a smiling center, lucky sparkles twinkling around
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=16,delay=70
const L=['#69db7c','#51cf66'],V='#2f9e44',ST='#40c057',BL='#ffa8a8',SP='#ffd43b'
const CX=128,CY=106
const leaf=c=>{c.beginPath();c.moveTo(0,-4);c.bezierCurveTo(-30,-20,-46,-46,-30,-60);c.bezierCurveTo(-18,-70,-4,-64,0,-52);c.bezierCurveTo(4,-64,18,-70,30,-60);c.bezierCurveTo(46,-46,30,-20,0,-4);c.closePath()}
const spark=(c,x,y,s)=>{c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
export default(c,t)=>{
// leaves alternate two greens, so half a turn per loop lands every leaf on one of the same color
for(const[w,s]of[[17,'#111'],[8,ST]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(CX,CY),c.quadraticCurveTo(CX+6,190,CX+30,236),c.stroke()
c.strokeStyle='#111'
c.fillStyle=SP;c.lineWidth=4
for(const[x,y,o]of[[38,52,0],[220,62,.25],[218,178,.5],[40,172,.75]]){const s=11*max(0,sin(PI*((t*2+o)%1)));if(s>1.5)spark(c,x,y,s)}
c.save();c.translate(CX,CY);c.rotate(t*PI)
for(let k=0;k<4;k++){
c.save();c.rotate(k*PI/2);leaf(c);c.fillStyle=L[k%2];c.lineWidth=7;c.fill();c.stroke()
c.strokeStyle=V;c.lineWidth=4;c.beginPath();c.moveTo(0,-10);c.lineTo(0,-46);c.moveTo(0,-30);c.lineTo(-12,-42);c.moveTo(0,-30);c.lineTo(12,-42);c.stroke();c.strokeStyle='#111'
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(-22,-50,8,PI*1.1,PI*1.6);c.stroke();c.strokeStyle='#111'
c.restore()
}
c.restore()
for(const[w,s]of[[10,'#111'],[3,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(CX,CY,92,-PI*.15+t*PI,PI*.15+t*PI),c.stroke(),c.beginPath(),c.arc(CX,CY,92,PI*.85+t*PI,PI*1.15+t*PI),c.stroke()
c.strokeStyle='#111';c.lineWidth=6;c.fillStyle=L[0];c.beginPath();c.arc(CX,CY,18,0,T);c.fill();c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(CX+d*6,CY-3,2.5,3.5,0,0,T),c.fill()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(CX+d*11,CY+4,4,2.5,0,0,T),c.fill()
c.lineWidth=3;c.beginPath();c.arc(CX,CY+2,4,.2*PI,.8*PI);c.stroke()
}
+22
View File
@@ -0,0 +1,22 @@
// comet with a glowing smiley head bobbing in a small loop, its long striped tail swishing, sparks peeling off
const{sin,cos,PI,max,hypot}=Math,T=2*PI
export const frames=16,delay=80
const L=[['#9775fa',1],['#4dabf7',.74],['#d0ebff',.46]],SC=['#ffd43b','#fff','#faa2c1'],HD='#ffd43b',HALO='#fff3bf',BL='#ff8787'
const spk=(c,x,y,s,f)=>{c.lineWidth=3;c.fillStyle=f;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 a=t*T,hx=84+8*cos(a),hy=170+6*sin(a),dx=236-hx,dy=24-hy,D=hypot(dx,dy),ux=dx/D,uy=dy/D,nx=-uy,ny=ux,sw=20*sin(a+.6)
const cen=v=>{const w=1-v,mx=hx+ux*D*.5+nx*sw,my=hy+uy*D*.5+ny*sw;return[w*w*hx+2*v*w*mx+v*v*(hx+ux*D),w*w*hy+2*v*w*my+v*v*(hy+uy*D)]}
for(const[col,f]of L){
const A=[],B=[],n=24
for(let j=0;j<=n;j++){const v=j/n*(.45+.55*f),[x,y]=cen(v),[x1,y1]=cen(v+.01),l=hypot(x1-x,y1-y),w=50*f*(1-v/(.45+.55*f))**.9;A.push([x-(y1-y)/l*w,y+(x1-x)/l*w]);B.unshift([x+(y1-y)/l*w,y-(x1-x)/l*w])}
c.beginPath();for(const[x,y]of[...A,...B])c.lineTo(x,y);c.closePath();c.fillStyle=col;c.fill();c.lineWidth=f==1?7:3;c.stroke()
}
for(let k=0;k<5;k++){const p=(t+k/5)%1,v=.3+.12*k+p*.06,sd=k%2?1:-1,[x,y]=cen(v),o=sd*(40*(1-v)+10+p*26),s=10*sin(PI*p);if(s>1.5)spk(c,x+nx*o,y+ny*o,s,SC[k%3])}
c.lineWidth=5;c.fillStyle=HALO;c.beginPath();c.arc(hx,hy,42,0,T);c.fill();c.stroke()
c.lineWidth=7;c.fillStyle=HD;c.beginPath();c.arc(hx,hy,30,0,T);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.ellipse(hx+12,hy-14,7,4,-.6,0,T);c.fill()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(hx+d*10-2,hy+1,4.5,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(hx+d*10-3,hy-1,1.6,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.arc(hx-2,hy+8,6,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(hx+d*19-2,hy+9,5,3,0,0,T),c.fill()
}
+32
View File
@@ -0,0 +1,32 @@
// corgi seen from behind, fluffy heart-shaped butt wiggling side to side on stubby trotting legs, smiling back over its shoulder
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=16,delay=70
const OR='#ffa94d',OD='#f08c00',PK='#faa2c1',TG='#ff8fab',BL='#ffa8a8'
export default(c,t)=>{
const w=.1*sin(2*T*t),l1=max(0,sin(2*T*t)),l2=max(0,-sin(2*T*t))
for(const[x,l]of[[112,l2],[164,l1]]){c.fillStyle=OR;c.beginPath();c.roundRect(x-13,196-l*8,26,34,10);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.ellipse(x,228-l*8,15,9,0,0,T);c.fill();c.stroke()}
c.save();c.translate(138,214);c.rotate(w);c.translate(-138,-214)
for(const[x,l]of[[92,l1],[184,l2]]){c.fillStyle=OR;c.beginPath();c.roundRect(x-15,186-l*10,30,40,12);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.ellipse(x,230-l*10,18,10,0,0,T);c.fill();c.stroke()}
c.fillStyle=OR;c.beginPath();c.ellipse(138,140,76,66,0,0,T);c.fill();c.stroke()
// the butt: two fluffy scalloped cheeks meeting in a heart
const fluff=(x,y,r,n=10)=>{c.beginPath();c.moveTo(x+r,y);for(let k=0;k<n;k++){const a=(k+.5)/n*T,e=(k+1)/n*T;c.quadraticCurveTo(x+cos(a)*(r+9),y+sin(a)*(r+9),x+cos(e)*r,y+sin(e)*r)}c.closePath()}
c.fillStyle='#fff'
fluff(114,178,30);c.fill();c.stroke();fluff(162,178,30);c.fill();c.stroke()
c.beginPath();c.ellipse(138,170,40,26,0,0,T);c.fill()
c.lineWidth=5;c.beginPath();c.moveTo(138,166);c.quadraticCurveTo(136,190,138,206);c.stroke();c.lineWidth=7
c.save();c.translate(138,124);c.rotate(.4*sin(4*T*t))
c.fillStyle='#fff';fluff(0,-6,11,7);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7
c.restore()
c.restore()
c.save();c.translate(96,78);c.rotate(-.1+.05*sin(2*T*t+1))
for(const[x,a]of[[-24,-.35],[22,.35]]){c.save();c.translate(x,-26);c.rotate(a);c.fillStyle=OR;c.beginPath();c.moveTo(-14,10);c.lineTo(0,-30);c.lineTo(14,10);c.closePath();c.fill();c.stroke();c.fillStyle=PK;c.beginPath();c.moveTo(-6,6);c.lineTo(0,-16);c.lineTo(6,6);c.closePath();c.fill();c.restore()}
c.fillStyle=OR;c.beginPath();c.ellipse(0,0,40,34,0,0,T);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.moveTo(-6,-30);c.quadraticCurveTo(4,-30,6,-8);c.quadraticCurveTo(30,4,22,22);c.quadraticCurveTo(0,38,-22,22);c.quadraticCurveTo(-30,4,-8,-8);c.closePath();c.fill()
c.save();c.beginPath();c.ellipse(0,0,40,34,0,0,T);c.lineWidth=7;c.stroke();c.restore()
for(const d of[-1,1]){c.fillStyle='#111';c.beginPath();c.arc(d*16,-4,5.5,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(d*16-2,-6,2,0,T);c.fill()}
c.fillStyle='#111';c.beginPath();c.ellipse(0,8,7,5,0,0,T);c.fill()
c.lineWidth=5;c.beginPath();c.moveTo(-10,16);c.quadraticCurveTo(-5,22,0,16);c.quadraticCurveTo(5,22,10,16);c.stroke()
c.fillStyle=TG;c.beginPath();c.moveTo(-6,20);c.lineTo(6,20);c.quadraticCurveTo(7,32,0,32);c.quadraticCurveTo(-7,32,-6,20);c.fill();c.lineWidth=4;c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(d*26,10,7,4,0,0,T),c.fill()
c.restore()
}
+24
View File
@@ -0,0 +1,24 @@
// cotton candy on a paper cone, its fluffy tufts swirling round and puffing while sparkles twinkle around it
// all tufts are stroked wide in black first and filled after, so the cloud keeps a single outline
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=20,delay=80
const PK='#faa2c1',PD='#f783ac',BU='#a5d8ff',CN='#fff',ST='#f783ac',BL='#ff8fab',SPK='#ffd43b'
const star=(c,x,y,s)=>{c.beginPath();for(let k=0;k<8;k++){const r=k%2?s*.38:s,a=k*PI/4;c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.closePath();c.fill();c.stroke()}
export default(c,t)=>{
c.fillStyle=CN;c.beginPath();c.moveTo(106,150);c.lineTo(150,150);c.lineTo(132,236);c.lineTo(124,236);c.closePath();c.fill()
c.save();c.clip();c.strokeStyle=ST;c.lineWidth=7;for(let k=-3;k<5;k++)c.beginPath(),c.moveTo(100,160+k*22),c.lineTo(156,140+k*22),c.stroke();c.restore()
c.beginPath();c.moveTo(106,150);c.lineTo(150,150);c.lineTo(132,236);c.lineTo(124,236);c.closePath();c.stroke()
const tf=[...Array(11)].map((_,k)=>{const a=k*T/11+T*t;return[128+cos(a)*68,96+sin(a)*50,26+4*sin(2*T*t+k*1.9),k%3?PK:BU]})
const blob=()=>{c.beginPath();c.ellipse(128,96,62,48,0,0,T);for(const[x,y,r]of tf)c.moveTo(x+r,y),c.arc(x,y,r,0,T)}
c.lineWidth=16;blob();c.stroke();c.lineWidth=7
for(const[x,y,r,col]of tf)c.fillStyle=col,c.beginPath(),c.arc(x,y,r,0,T),c.fill()
c.fillStyle=PK;c.beginPath();c.ellipse(128,96,62,48,0,0,T);c.fill()
c.strokeStyle=PD;c.lineWidth=4.5;for(const[r,o]of[[50,0],[50,PI],[34,1.6]])c.beginPath(),c.ellipse(128,96,r*1.2,r*.85,0,T*t*-1+o,T*t*-1+o+1.1),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*22,94,6,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*22-2,92,2.2,0,T),c.fill()
c.fillStyle='#111';c.beginPath();c.arc(128,104,9,0,PI);c.closePath();c.fill()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*40,110,9,5,0,0,T),c.fill()
c.fillStyle=SPK;c.lineWidth=4
;[[34,36],[222,42],[28,168],[226,160]].forEach(([x,y],k)=>{const s=14*max(0,sin(T*t*2+k*1.6));if(s>3)star(c,x,y,s)})
}
+32
View File
@@ -0,0 +1,32 @@
// purple crayon with a face scribbling a rainbow loop-de-loop, lifting off, then the line wipes away as it floats back
const{sin,cos,PI,min,max,floor}=Math,T=2*PI
export const frames=24,delay=80
const RB=['#ff6b6b','#ffa94d','#ffd43b','#69db7c','#4dabf7','#b197fc'],CR='#9775fa',CL='#d0bfff',CD='#7048e8',BL='#faa2c1'
const X0=36,X1=158,Y0=186,B=17,W=4.25,NS=90
const P=s=>[X0+(X1-X0)*s-B*sin(s*T*W),Y0-B*cos(s*T*W)+B*.3]
const sm=x=>x<0?0:x>1?1:x*x*(3-2*x)
const sparkle=(c,x,y,s)=>{c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
export default(c,t)=>{
let from=0,to=1,tip,lift=0
if(t<.68)to=t/.68,tip=P(to)
else if(t<.78){const u=(t-.68)/.1;tip=P(1);lift=34*sm(u)}
else{const u=sm((t-.78)/.18);from=min(1,(t-.78)/.16);const a=P(1),b=P(0);tip=[a[0]+(b[0]-a[0])*u,a[1]+(b[1]-a[1])*u];lift=34*(1-sm((t-.9)/.1))}
if(to>from){
const pts=[];for(let k=floor(from*NS);k<=to*NS;k++)pts.push([k/NS,...P(k/NS)]);pts.push([to,...P(to)])
c.lineWidth=14;c.beginPath();for(const[,x,y]of pts)c.lineTo(x,y);c.stroke()
c.lineWidth=7;for(let r=0;r<6;r++){const seg=pts.filter(([s])=>s>=r/6-.012&&s<=(r+1)/6+.012);if(seg.length<2)continue;c.strokeStyle=RB[r];c.beginPath();for(const[,x,y]of seg)c.lineTo(x,y);c.stroke()}
c.strokeStyle='#111'
}
if(t>.68&&t<.84){const s=12*sin(PI*(t-.68)/.16),[x,y]=P(1);sparkle(c,x+20,y-24,s);sparkle(c,x-6,y-40,s*.6)}
const wob=t<.68?.05*sin(8*T*t):0
c.save();c.translate(tip[0],tip[1]-lift);c.rotate(.55+wob);c.lineWidth=6
c.fillStyle=CR;c.beginPath();c.moveTo(-3,-2);c.quadraticCurveTo(0,1,3,-2);c.lineTo(12,-28);c.lineTo(-12,-28);c.closePath();c.fill();c.stroke()
c.beginPath();c.roundRect(-15,-112,30,86,[4,4,3,3]);c.fill();c.stroke()
c.fillStyle=CL;c.beginPath();c.rect(-15,-96,30,58);c.fill();c.stroke()
c.lineWidth=4;for(const y of[-88,-46])c.beginPath(),c.moveTo(-15,y),c.lineTo(15,y),c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*6,-72,3.2,0,T),c.fill()
c.lineWidth=3;c.beginPath();c.arc(0,-66,4.5,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(d*10,-63,3.5,2,0,0,T),c.fill()
c.fillStyle=CD;c.lineWidth=5;c.beginPath();c.roundRect(-15,-118,30,10,4);c.fill();c.stroke()
c.restore()
}
+25
View File
@@ -0,0 +1,25 @@
// golden crown bobbing on a velvet cap, its jewels glinting one after another with little sparkles popping off
const{sin,cos,PI,max,round}=Math,T=2*PI
export const frames=20,delay=80
const G='#ffd43b',GD='#fab005',VEL='#9775fa',PE='#fff'
const J=[[86,92,7,'#f783ac'],[76,172,10,'#4dabf7'],[128,172,14,'#ff6b6b'],[180,172,10,'#69db7c'],[170,92,7,'#4dabf7']]
const crown=c=>{c.beginPath();for(const[x,y]of[[52,156],[42,78],[66,118],[86,60],[107,110],[128,40],[149,110],[170,60],[190,118],[214,78],[204,156]])c.lineTo(x,y);c.closePath()}
const spark=(c,x,y,s)=>{c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
export default(c,t)=>{
const b=round(4*sin(t*T)),g=t*5|0,gp=t*5%1
c.translate(0,b)
c.fillStyle=VEL;c.beginPath();c.ellipse(128,124,74,58,0,PI,0);c.lineTo(202,150);c.lineTo(54,150);c.closePath();c.fill();c.stroke()
crown(c);c.fillStyle=G;c.fill()
c.save();crown(c);c.clip();c.fillStyle=GD;for(const[x,y]of[[42,78],[128,40],[214,78]])c.beginPath(),c.moveTo(x,y),c.lineTo(x+22,y+80),c.lineTo(x+6,y+90),c.closePath(),c.fill();c.restore()
crown(c);c.stroke()
c.lineWidth=5;c.fillStyle=PE;for(const[x,y]of[[42,78],[86,60],[128,40],[170,60],[214,78]])c.beginPath(),c.arc(x,y-4,9,0,T),c.fill(),c.stroke()
c.lineWidth=7;c.fillStyle=GD;c.beginPath();c.roundRect(38,146,180,48,12);c.fill();c.stroke()
c.fillStyle=G;c.beginPath();c.roundRect(44,152,168,10,5);c.fill()
for(const[k,[x,y,s,col]]of J.entries()){
c.lineWidth=5;c.fillStyle=col;c.beginPath()
if(k==2)c.ellipse(x,y,s,s*.8,0,0,T);else c.moveTo(x,y-s),c.lineTo(x+s*.8,y),c.lineTo(x,y+s),c.lineTo(x-s*.8,y),c.closePath()
c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.ellipse(x-s*.3,y-s*.3,s*.22,s*.3,-.5,0,T);c.fill()
if(k==g){const s2=4+12*sin(PI*gp);c.fillStyle='#fff';c.lineWidth=4;spark(c,x+s*.6,y-s*.7,s2)}
}
}
+26
View File
@@ -0,0 +1,26 @@
// cupcake with a swirl of pink frosting, sprinkles raining down onto it and the cherry on top wobbling
const{sin,cos,PI}=Math,T=2*PI
export const frames=20,delay=70
const WR='#38d9a9',WD='#0ca678',FR='#faa2c1',CH='#ff6b6b',STEM='#a9744f',BL='#ffa8a8',SP=['#ffd43b','#4dabf7','#fff','#69db7c','#b197fc','#ff922b']
const rod=(c,x,y,a,col)=>{for(const[w,s]of[[9,'#111'],[4,col]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x-cos(a)*6,y-sin(a)*6),c.lineTo(x+cos(a)*6,y+sin(a)*6),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
export default(c,t)=>{
c.fillStyle=WR;c.beginPath();c.moveTo(58,150);c.lineTo(198,150);c.lineTo(178,234);c.lineTo(78,234);c.closePath();c.fill();c.stroke()
c.strokeStyle=WD;c.lineWidth=5;for(let k=1;k<6;k++)c.beginPath(),c.moveTo(58+k*140/6,158),c.lineTo(78+k*100/6,228),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24,190,10,0,T),c.fill()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24,190,6,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24-2,188,2.2,0,T),c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(128,200,9,.15*PI,.85*PI);c.closePath();c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,204,9,5,0,0,T),c.fill()
c.fillStyle=FR;for(const[y,rx,ry]of[[144,84,24],[116,66,22],[90,46,20]])c.beginPath(),c.ellipse(128,y,rx,ry,0,0,T),c.fill(),c.stroke()
c.beginPath();c.moveTo(108,84);c.quadraticCurveTo(116,58,132,60);c.quadraticCurveTo(150,66,148,86);c.closePath();c.fill();c.stroke()
c.strokeStyle='#fff';c.lineWidth=5;for(const[y,rx,ry]of[[144,72,14],[116,54,13],[90,34,11]])c.beginPath(),c.ellipse(128,y-2,rx,ry,0,PI*1.12,PI*1.32),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
;[[90,154,.5],[118,162,-.6],[152,158,.9],[176,146,-.3],[74,140,1.2],[104,128,-1],[142,130,.4],[166,118,-.8],[112,104,.7],[140,100,-.2],[92,112,.2]].forEach(([x,y,a],k)=>rod(c,x,y,a,SP[k%6]))
c.save();c.translate(128,66);c.rotate(.3*sin(2*T*t))
for(const[w,s]of[[9,'#111'],[4,STEM]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(0,-24),c.quadraticCurveTo(2,-42,12,-50),c.stroke()
c.lineWidth=7;c.strokeStyle='#111';c.fillStyle=CH;c.beginPath();c.arc(0,-12,15,0,T);c.fill();c.stroke()
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(0,-12,8,PI*1.1,PI*1.45);c.stroke()
c.restore();c.strokeStyle='#111';c.lineWidth=7
;[[84,128],[104,104],[152,102],[176,126],[120,86],[164,112]].forEach(([x,land],k)=>{const p=(t+k/6)%1;rod(c,x+4*sin(p*T+k),12+p*(land-12),k+p*9,SP[(k+2)%6])})
}
+35
View File
@@ -0,0 +1,35 @@
// mirror disco ball spinning on its chain, tiles catching the light, colored light spots blinking around it
// the tile colors repeat every 4 columns and the ball turns 4 columns per loop, so the loop is seamless
const{sin,cos,PI,max,min,floor}=Math,T=2*PI
export const frames=16,delay=90
const TL=['#e7f5ff','#d0bfff','#c5f6fa','#ffdeeb'],TD=['#a5d8ff','#b197fc','#66d9e8','#faa2c1'],SP=['#ff6b6b','#ffd43b','#69db7c','#4dabf7','#b197fc','#f783ac']
const star=(c,x,y,s,col)=>{c.fillStyle=col;c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
export default(c,t,i)=>{
const cx=128,cy=140,R=76,M=16,N=8,cw=T/M,rot=T*t/4
for(let k=0;k<8;k++){
const a=k*T/8+.39,r=104,on=(floor(t*8)+k*3)%4!=0
if(on){c.lineWidth=4;star(c,cx+cos(a)*r,cy+sin(a)*r*.92,k%2?9:12,SP[k%6])}
}
for(const[w,st]of[[11,'#111'],[4,'#ced4da']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(cx,0),c.lineTo(cx,58),c.stroke()
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#ced4da';c.beginPath();c.roundRect(cx-15,52,30,14,4);c.fill();c.stroke()
c.fillStyle=TD[0];c.lineWidth=7;c.beginPath();c.arc(cx,cy,R,0,T);c.fill()
c.save();c.beginPath();c.arc(cx,cy,R,0,T);c.clip()
const P=(lo,la)=>[cx+R*cos(la)*sin(lo),cy+R*sin(la)]
c.lineWidth=1.5;c.strokeStyle='#495057'
for(let l=0;l<N;l++){
const la0=-PI/2+l*PI/N,la1=la0+PI/N,lm=(la0+la1)/2
for(let j=0;j<M;j++){
let lo0=j*cw+rot-PI,lo1=lo0+cw
lo0=((lo0+PI)%T+T)%T-PI;lo1=lo0+cw
if(lo1<-PI/2||lo0>PI/2)continue
const a0=max(lo0,-PI/2),a1=min(lo1,PI/2),lm2=(a0+a1)/2
const lit=cos(lm2)*cos(lm)*.7-sin(lm2)*cos(lm)*.35-sin(lm)*.45,ci=(j+l*2)%4
c.fillStyle=lit>.62?'#fff':lit>.25?TL[ci]:TD[ci]
c.beginPath();for(const[lo,la]of[[a0,la0],[a1,la0],[a1,la1],[a0,la1]])c.lineTo(...P(lo,la));c.closePath();c.fill();c.stroke()
}
}
c.restore();c.strokeStyle='#111'
c.lineWidth=7;c.beginPath();c.arc(cx,cy,R,0,T);c.stroke()
c.lineWidth=3
for(const[x,y,p]of[[100,112,0],[156,154,.33],[116,178,.66]]){const s=13*max(0,sin(T*(2*t+p)));if(s>2)star(c,x,y,s,'#fff')}
}
+27
View File
@@ -0,0 +1,27 @@
// ferris wheel slowly turning, its candy-colored gondolas hanging upright, a smiling hub
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=100
const G=['#ff6b6b','#ffd43b','#4dabf7','#69db7c'],RIM='#b197fc',LEG='#f783ac',BASE='#a9744f',HUB='#ffd43b',BL='#ffa8a8'
const CX=128,CY=104,R=78
export default(c,t)=>{
// turning by half a revolution per loop lines gondola k up with gondola k+4, which has the same color
const a0=t*PI
for(const[w,s]of[[16,'#111'],[8,LEG]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(72,232),c.lineTo(CX,CY),c.lineTo(184,232),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
c.fillStyle=BASE;c.beginPath();c.roundRect(44,226,168,16,8);c.fill();c.stroke()
for(let k=0;k<8;k++){const a=a0+k*PI/4;for(const[w,s]of[[9,'#111'],[3,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(CX,CY),c.lineTo(CX+R*cos(a),CY+R*sin(a)),c.stroke()}
for(const[w,s]of[[16,'#111'],[8,RIM]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(CX,CY,R,0,T),c.stroke()
c.strokeStyle='#111'
for(let k=0;k<8;k++){
const a=a0+k*PI/4,x=CX+R*cos(a),y=CY+R*sin(a)
c.save();c.translate(x,y)
c.lineWidth=4;c.beginPath();c.moveTo(0,0);c.lineTo(0,9);c.stroke()
c.lineWidth=5;c.fillStyle=G[k%4];c.beginPath();c.roundRect(-14,8,28,22,7);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.roundRect(-9,13,18,8,3);c.fill();c.lineWidth=3;c.stroke()
c.restore()
}
c.lineWidth=6;c.fillStyle=HUB;c.beginPath();c.arc(CX,CY,17,0,T);c.fill();c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(CX+d*6,CY-3,2.5,0,T),c.fill()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(CX+d*10,CY+4,3.5,2.5,0,0,T),c.fill()
c.lineWidth=3;c.beginPath();c.arc(CX,CY+2,4,.2*PI,.8*PI);c.stroke()
}
+39
View File
@@ -0,0 +1,39 @@
// fire truck rumbling along with its siren flashing red and blue, ladder bobbing, wheels turning, road dashes rolling past
const{sin,cos,PI,abs,round}=Math,T=2*PI
export const frames=16,delay=70
const RD='#ff6b6b',RDD='#e03131',WIN='#a5d8ff',TIRE='#343a40',HUB='#ced4da',LAD='#dee2e6',SB='#4dabf7',SR='#fa5252',G='#ffd43b',BL='#ffa8a8'
export default(c,t,i)=>{
const b=-round(3*abs(sin(2*T*t))),rot=t*T,red=(i>>1)%2==0,la=-.05-.04*sin(2*T*t+1)
c.fillStyle='#868e96';c.beginPath();c.roundRect(4,214,248,22,11);c.fill();c.stroke()
c.save();c.beginPath();c.rect(10,214,236,22);c.clip()
for(let k=-1;k<5;k++){const x=k*64-((t*128)%64)+20;c.fillStyle='#fff';c.beginPath();c.roundRect(x,222,30,6,3);c.fill()}
c.restore()
c.translate(0,b)
c.save();c.translate(34,104);c.rotate(la)
for(const[w,s]of[[11,'#111'],[4,LAD]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(0,0),c.lineTo(136,0),c.moveTo(0,-14),c.lineTo(136,-14),c.stroke()
c.lineWidth=4;c.strokeStyle='#111';c.beginPath();for(let x=12;x<136;x+=16)c.moveTo(x,0),c.lineTo(x,-14);c.stroke()
c.restore();c.lineWidth=7
c.fillStyle=RD;c.beginPath();c.roundRect(24,108,144,76,8);c.fill();c.stroke()
c.beginPath();c.moveTo(160,184);c.lineTo(160,96);c.quadraticCurveTo(160,86,170,86);c.lineTo(204,86);c.quadraticCurveTo(214,86,220,96);c.lineTo(232,128);c.lineTo(232,184);c.closePath();c.fill();c.stroke()
c.fillStyle=WIN;c.lineWidth=5;c.beginPath();c.moveTo(174,98);c.lineTo(206,98);c.lineTo(218,126);c.lineTo(174,126);c.closePath();c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.ellipse(184,108,3,7,.4,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.rect(24,150,208,10);c.fill();c.beginPath();c.moveTo(24,150);c.lineTo(232,150);c.moveTo(24,160);c.lineTo(232,160);c.stroke()
c.lineWidth=5;c.beginPath();for(const x of[72,120])c.moveTo(x,116),c.lineTo(x,146);c.moveTo(166,134);c.lineTo(166,180);c.stroke()
c.fillStyle=G;c.beginPath();c.arc(48,130,13,0,T);c.fill();c.stroke();c.beginPath();c.arc(48,130,5,0,T);c.stroke()
c.fillStyle=G;c.lineWidth=5;c.beginPath();c.roundRect(222,132,14,12,4);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(206,140,4,0,T);c.fill()
c.fillStyle=BL;c.beginPath();c.ellipse(198,170,7,4,0,0,T);c.fill()
c.lineWidth=4;c.beginPath();c.arc(214,166,6,.2*PI,.8*PI);c.stroke()
c.lineWidth=5;c.fillStyle='#ced4da';c.beginPath();c.roundRect(176,76,32,10,4);c.fill();c.stroke()
for(const[d,on,col,dim]of[[-1,red,SR,'#c92a2a'],[1,!red,SB,'#1c7ed6']]){
c.fillStyle=on?col:dim;c.beginPath();c.moveTo(192,76);c.arc(192,76,14,d<0?PI:PI*1.5,d<0?PI*1.5:0);c.closePath();c.fill();c.stroke()
if(on)for(const a of[-.5,-.15,.2].map(v=>d<0?PI+v+.35:-v-.35))for(const[w,s]of[[9,'#111'],[4,col]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(192+cos(a)*22,76+sin(a)*22),c.lineTo(192+cos(a)*34,76+sin(a)*34),c.stroke()
c.strokeStyle='#111';c.lineWidth=5
}
c.fillStyle='#fff';c.beginPath();c.ellipse(187,69,2.5,4,-.5,0,T);c.fill()
for(const x of[64,112,196]){
c.lineWidth=7;c.fillStyle=TIRE;c.beginPath();c.arc(x,190,22,0,T);c.fill();c.stroke()
c.fillStyle=HUB;c.beginPath();c.arc(x,190,10,0,T);c.fill();c.lineWidth=4;c.stroke()
for(let k=0;k<4;k++){const a=rot+k*PI/2;c.beginPath();c.moveTo(x+cos(a)*4,190+sin(a)*4);c.lineTo(x+cos(a)*9,190+sin(a)*9);c.stroke()}
}
}
+29
View File
@@ -0,0 +1,29 @@
// flamingo standing on one leg, wobbling to keep its balance while its neck sways the other way
const{sin,cos,PI}=Math,T=2*PI
export const frames=20,delay=80
const PK='#f783ac',PL='#faa2c1',PD='#e64980',LG='#ff8fab',WA='#74c0fc',WL='#d0ebff',BL='#ff6b6b'
export default(c,t)=>{
const a=.07*sin(T*t),n=-.12*sin(T*t-.5)
const D=(wd,col,f)=>{for(const[l,s]of[[wd,'#111'],[wd-10,col]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
c.fillStyle=WA;c.beginPath();c.ellipse(128,234,70,14,0,0,T);c.fill();c.stroke()
c.strokeStyle=WL;c.lineWidth=4;c.beginPath();c.ellipse(128,234,30+6*sin(2*T*t),6,0,0,T);c.stroke();c.strokeStyle='#111';c.lineWidth=7
c.save();c.translate(128,232);c.rotate(a);c.translate(-128,-232)
D(17,LG,()=>{c.moveTo(128,232);c.lineTo(126,186);c.lineTo(122,150)})
D(17,LG,()=>{c.moveTo(134,150);c.lineTo(156,180);c.lineTo(126,186)})
c.fillStyle=LG;c.beginPath();c.ellipse(132,234,18,6,0,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7
c.fillStyle=PK;c.beginPath();c.moveTo(64,118);c.lineTo(38,108);c.lineTo(50,128);c.lineTo(36,138);c.lineTo(66,136);c.closePath();c.fill();c.stroke()
c.beginPath();c.ellipse(112,128,56,34,-.12,0,T);c.fill();c.stroke()
c.fillStyle=PD;c.beginPath();c.moveTo(78,116);c.quadraticCurveTo(116,96,146,118);c.quadraticCurveTo(118,148,78,116);c.fill();c.lineWidth=5;c.stroke()
c.beginPath();c.moveTo(96,120);c.quadraticCurveTo(112,112,128,122);c.stroke();c.lineWidth=7
c.save();c.translate(150,112);c.rotate(n)
D(26,PK,()=>{c.moveTo(0,0);c.bezierCurveTo(30,-18,-18,-50,2,-74)})
c.translate(2,-80)
c.fillStyle=PK;c.beginPath();c.arc(0,0,20,0,T);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.moveTo(14,-8);c.quadraticCurveTo(36,-6,40,8);c.lineTo(30,22);c.quadraticCurveTo(26,8,12,8);c.closePath();c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.moveTo(36,2);c.quadraticCurveTo(44,12,32,30);c.quadraticCurveTo(30,20,28,16);c.closePath();c.fill();c.lineWidth=4;c.strokeStyle='#fff';c.stroke();c.strokeStyle='#111';c.lineWidth=7
c.fillStyle='#111';c.beginPath();c.arc(2,-4,4.5,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(0.5,-5.5,1.6,0,T);c.fill()
c.fillStyle=BL;c.beginPath();c.ellipse(-4,8,6,4,0,0,T);c.fill()
c.restore()
c.restore()
}
+27
View File
@@ -0,0 +1,27 @@
// french fries in a red carton bobbing and wiggling one after another like a wave, salt specks drifting down
const{sin,PI,max}=Math,T=2*PI
export const frames=20,delay=70
const FY='#ffd43b',FD='#f59f00',CA='#ff6b6b',CB='#e03131',BL='#ffc9c9',PK='#ff8fab'
const FR=[[90,48],[118,40],[146,42],[172,52],[104,64],[132,58],[160,66]]
export default(c,t)=>{
;[[96,0],[138,.33],[164,.66]].forEach(([x,o])=>{const p=(t+o)%1;c.fillStyle='#fff';c.lineWidth=2.5;c.save();c.translate(x+4*sin(T*p*2),8+p*56);c.rotate(p*6);c.beginPath();c.rect(-3,-3,6,6);c.fill();c.stroke();c.restore()})
c.lineWidth=7
c.fillStyle=CB;c.beginPath();c.moveTo(60,128);c.quadraticCurveTo(128,100,196,128);c.lineTo(196,150);c.lineTo(60,150);c.closePath();c.fill();c.stroke()
FR.forEach(([x,top],k)=>{
const ph=T*t*2-k*.9,dy=-14*max(0,sin(ph)),r=(x-128)/420+.1*sin(ph)
c.save();c.translate(x,150+dy);c.rotate(r);const L=150-top
c.fillStyle=FY;c.beginPath();c.roundRect(-9,-L,18,L+30,4);c.fill()
c.fillStyle=FD;c.fillRect(3,-L+4,6,L+24);c.beginPath();c.roundRect(-9,-L,18,8,4);c.fill()
c.beginPath();c.roundRect(-9,-L,18,L+30,4);c.stroke()
c.fillStyle='#fff';for(const[sx,sy]of[[-4,-L+18],[0,-L+34]])c.fillRect(sx,sy+(k%3)*4,3,3)
c.restore()
})
c.fillStyle=CA;c.beginPath();c.moveTo(54,124);c.quadraticCurveTo(128,156,202,124);c.lineTo(180,236);c.lineTo(76,236);c.closePath();c.fill();c.stroke()
c.strokeStyle=CB;c.lineWidth=5;c.beginPath();c.moveTo(68,150);c.quadraticCurveTo(128,176,188,150);c.stroke();c.strokeStyle='#111';c.lineWidth=7
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24,186,6,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24-2,184,2.2,0,T),c.fill()
const mo=.55+.45*max(0,sin(T*t*4)),mouth=()=>{c.beginPath();c.moveTo(116,196);c.lineTo(140,196);c.bezierCurveTo(140,196+18*mo,116,196+18*mo,116,196);c.closePath()}
c.fillStyle='#111';mouth();c.fill()
c.save();mouth();c.clip();c.fillStyle=PK;c.beginPath();c.ellipse(128,196+14*mo,7,5,0,0,T);c.fill();c.restore()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,198,9,5,0,0,T),c.fill()
}
+25
View File
@@ -0,0 +1,25 @@
// spiral galaxy turning on a round night-sky sticker, two candy-striped arms around a smiley core, stars twinkling
// both arms match, so half a turn per loop is seamless; the dark sky gets a white rim for dark backgrounds
const{sin,cos,PI,exp,max,min,hypot}=Math,T=2*PI
export const frames=20,delay=90
const SKY='#2b3a7a',ARM='#9775fa',STRIPE='#faa2c1',BL='#ff8787',SQ=.64,M=9.4
const spk=(c,x,y,s)=>{c.lineWidth=3;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 disc=()=>{c.beginPath();c.arc(128,128,112,0,T)},ph=-t*PI
disc();c.lineWidth=18;c.strokeStyle='#fff';c.stroke();c.fillStyle=SKY;c.fill();c.strokeStyle='#111'
c.save();disc();c.clip()
for(const[x,y,o]of[[44,62,0],[206,50,.3],[218,190,.6],[50,198,.15],[128,30,.8],[136,230,.45]])spk(c,x,y,2+4*max(0,sin((t+o)*2*T)))
const P=(th,k)=>{const r=16*exp(.19*th),q=th+k*PI+ph;return[128+r*cos(q),128+r*sin(q)*SQ]}
for(const k of[0,1]){
const C=[];for(let th=0;th<=M;th+=.2)C.push([P(th,k),th])
const side=(f,g)=>C.map(([[x,y],th],j)=>{const[[x0,y0]]=C[max(0,j-1)],[[x1,y1]]=C[min(C.length-1,j+1)],l=hypot(x1-x0,y1-y0),w=f*(24*(1-th/M)+2);return[x-(y1-y0)/l*w*g,y+(x1-x0)/l*w*g]})
for(const[f,col,lw]of[[1,ARM,5],[.4,STRIPE,0]]){c.beginPath();for(const[x,y]of[...side(f,1),...side(f,-1).reverse()])c.lineTo(x,y);c.closePath();c.fillStyle=col;c.fill();if(lw)c.lineWidth=lw,c.stroke()}
c.fillStyle='#fff';for(const th of[3.4,5.2,7,8.4]){const[x,y]=P(th,k);c.beginPath();c.arc(x,y,2.6,0,T);c.fill()}
}
c.lineWidth=5;c.fillStyle='#fff3bf';c.beginPath();c.ellipse(128,128,36,28,0,0,T);c.fill();c.stroke()
c.fillStyle='#ffd43b';c.beginPath();c.ellipse(128,128,26,20,0,0,T);c.fill();c.lineWidth=4;c.stroke()
for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,127,4.5,PI*1.15,PI*1.85),c.stroke()
c.lineWidth=3.5;c.beginPath();c.arc(128,130,5,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*16,133,4,2.5,0,0,T),c.fill()
c.restore();c.lineWidth=7;disc();c.stroke()
}
+33
View File
@@ -0,0 +1,33 @@
// giraffe stretching its long neck up to a branch, munching leaves with its jaw working and an ear flicking
const{sin,cos,PI,abs}=Math,T=2*PI
export const frames=16,delay=80
const GY='#ffc078',SP='#a9744f',MZ='#ffe8cc',BR='#8d6346',LF='#69db7c',LD='#51cf66',BL='#ffa8a8'
export default(c,t,i)=>{
const ch=abs(sin(3*PI*t)),hx=-3*ch,fl=i==10||i==11?.7:i==9||i==12?.35:0
const D=(wd,col,f)=>{for(const[l,s]of[[wd,'#111'],[wd-10,col]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
D(20,BR,()=>{c.moveTo(118,24);c.quadraticCurveTo(190,18,248,38)})
c.fillStyle=LF;for(const[x,y,r]of[[150,34,.6],[176,40,.2],[236,52,-.4],[244,26,-1]])c.beginPath(),c.ellipse(x,y,14,8,r,0,T),c.fill(),c.stroke()
for(const[x,y]of[[54,196],[104,198]])D(18,GY,()=>{c.moveTo(x,y);c.lineTo(x-2,236)})
for(const[x,y]of[[70,198],[120,196]])D(18,GY,()=>{c.moveTo(x,y);c.lineTo(x+2,238)})
c.fillStyle=BR;for(const x of[52,72,102,122])c.beginPath(),c.roundRect(x-10,232,20,12,4),c.fill(),c.stroke()
D(10,GY,()=>{c.moveTo(42,170);c.quadraticCurveTo(26,184,30,206)})
c.fillStyle=BR;c.beginPath();c.ellipse(30,210,6,10,0,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7
for(const x of[188,200])D(10,GY,()=>{c.moveTo(x+hx,70);c.lineTo(x+hx-2,48)})
c.fillStyle=BR;for(const x of[186,198])c.beginPath(),c.arc(x+hx,46,7,0,T),c.fill(),c.stroke()
D(16,BR,()=>{for(let k=0;k<=8;k++){const u=k/8,o=k%2?-7:0;c.lineTo(96+u*(70+hx)+o,158-u*86+o)}})
const sil=()=>{c.beginPath();c.ellipse(86,180,50,28,-.08,0,T);c.moveTo(102,164);c.lineTo(174+hx,80);c.lineTo(196+hx,94);c.lineTo(130,176);c.closePath();c.moveTo(220+hx,80);c.ellipse(194+hx,80,27,18,-.35,0,T)}
sil();c.lineWidth=14;c.stroke();c.fillStyle=GY;sil();c.fill()
c.save();sil();c.clip();c.fillStyle=SP
for(const[x,y,r]of[[60,172,11],[86,186,12],[112,170,10],[78,160,8],[46,190,7],[122,190,8],[118,148,9],[136,128,9],[150,108,8],[132,150,6],[164,96,6]])c.beginPath(),c.ellipse(x,y,r,r*.8,.5,0,T),c.fill()
c.restore();c.lineWidth=7
c.save();c.translate(178+hx,70);c.rotate(-.9-fl);c.fillStyle=GY;c.beginPath();c.ellipse(-12,0,14,7,0,0,T);c.fill();c.stroke();c.fillStyle=BL;c.beginPath();c.ellipse(-12,0,7,3,0,0,T);c.fill();c.restore()
c.save();c.translate(212+hx,74);c.rotate(-.35)
c.fillStyle=MZ;c.beginPath();c.ellipse(4,-2,13,11,0,0,T);c.fill();c.lineWidth=5;c.stroke()
c.beginPath();c.moveTo(8,8+ch*2);c.quadraticCurveTo(0,12+ch*5,-8,8);c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(8,-4,2.5,2,0,0,T);c.fill()
c.restore()
D(10,LD,()=>{c.moveTo(224+hx,82+ch*2);c.quadraticCurveTo(232,70,236,56)})
c.fillStyle=LF;c.beginPath();c.ellipse(232+hx*.5,84+ch*3,10,6,.5+ch*.3,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7
c.lineWidth=5;c.beginPath();c.arc(190+hx,76,6,1.1*PI,1.9*PI);c.stroke();c.lineWidth=7
c.fillStyle=BL;c.beginPath();c.ellipse(196+hx,88,7,4,-.3,0,T);c.fill()
}
+33
View File
@@ -0,0 +1,33 @@
// cute helicopter hovering and gently tilting, main rotor whirring, tail rotor spinning, downwash puffs below
const{sin,cos,PI,abs,round}=Math,T=2*PI
export const frames=16,delay=70
const B='#ff6b6b',BD='#e03131',GL='#a5d8ff',SK='#868e96',RT='#dee2e6',BL='#ffa8a8'
const body=c=>{c.beginPath();c.ellipse(140,138,62,44,0,0,T)}
export default(c,t)=>{
const b=round(4*sin(t*T)),r=.05*sin(t*T+1),th=t*T*2
c.lineWidth=5
for(const d of[-1,1])for(let k=0;k<2;k++){const p=(t*2+k/2)%1,x=140+d*(30+64*p),y=226+8*p,l=12*sin(PI*p);if(l>2)for(const[w,s]of[[9,'#111'],[3,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x-d*l,y-l*.3),c.lineTo(x+d*l,y+l*.3),c.stroke()}
c.strokeStyle='#111'
c.save();c.translate(140,140+b);c.rotate(r);c.translate(-140,-140)
for(const[lw,st]of[[12,'#111'],[5,SK]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(118,176),c.lineTo(112,204),c.moveTo(162,176),c.lineTo(168,204),c.moveTo(80,204),c.lineTo(196,204),c.quadraticCurveTo(212,204,214,192),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
c.fillStyle=B;c.beginPath();c.moveTo(92,122);c.lineTo(34,112);c.lineTo(34,128);c.lineTo(92,152);c.closePath();c.fill();c.stroke()
c.fillStyle=BD;c.beginPath();c.moveTo(28,126);c.lineTo(18,90);c.lineTo(38,94);c.lineTo(44,120);c.closePath();c.fill();c.stroke()
const tp=t*T*6
for(const a of[tp,tp+PI/2])for(const[lw,st]of[[10,'#111'],[4,RT]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(26-14*cos(a),104-14*sin(a)),c.lineTo(26+14*cos(a),104+14*sin(a)),c.stroke()
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SK;c.beginPath();c.arc(26,104,5,0,T);c.fill();c.stroke()
c.lineWidth=7;c.fillStyle=SK;c.beginPath();c.roundRect(132,80,16,18,4);c.fill();c.stroke()
body(c);c.fillStyle=B;c.fill()
c.save();body(c);c.clip();c.fillStyle=BD;c.fillRect(70,160,140,30)
c.fillStyle=GL;c.beginPath();c.ellipse(170,124,40,30,0,0,T);c.fill();c.lineWidth=5;c.stroke();c.restore()
c.lineWidth=7;body(c);c.stroke()
c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(170,124,26,PI*1.1,PI*1.4);c.stroke();c.strokeStyle='#111'
c.fillStyle='#111';for(const x of[156,182])c.beginPath(),c.ellipse(x,124,5,7,0,0,T),c.fill()
c.fillStyle='#fff';for(const x of[154,180])c.beginPath(),c.arc(x,121,2.5,0,T),c.fill()
c.fillStyle=BL;for(const x of[142,196])c.beginPath(),c.ellipse(x,150,8,5,0,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.arc(169,146,7,.2*PI,.8*PI);c.stroke()
for(const[lw,st]of[[9,'#111'],[3,RT]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.ellipse(140,78,104,7,0,0,T),c.stroke()
for(const a of[th,th+PI/2])for(const[lw,st]of[[11,'#111'],[5,SK]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(140-100*cos(a),78-5*sin(a)),c.lineTo(140+100*cos(a),78+5*sin(a)),c.stroke()
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SK;c.beginPath();c.arc(140,78,8,0,T);c.fill();c.stroke()
c.restore()
}
+31
View File
@@ -0,0 +1,31 @@
// striped hot-air balloon with a happy face bobbing up and down, burner flame puffing, little clouds drifting by
const{sin,PI,round}=Math,T=2*PI
export const frames=20,delay=80
const ST=['#ff6b6b','#ffd43b','#4dabf7','#fff'],SK='#fa5252',BK='#a9744f',BKD='#7f5539',FO='#ff922b',FY='#ffd43b',BL='#ffa8a8'
const env=c=>{c.beginPath();c.moveTo(100,170);c.bezierCurveTo(30,128,36,16,128,16);c.bezierCurveTo(220,16,226,128,156,170);c.closePath()}
// union outline: stroke every puff wide in black, then fill them all white
const cloud=(c,x,y)=>{const b=[[-16,4,12],[2,-4,16],[20,4,11]];c.lineWidth=14;for(const[a,d,r]of b)c.beginPath(),c.arc(x+a,y+d,r,0,T),c.stroke();c.fillStyle='#fff';for(const[a,d,r]of b)c.beginPath(),c.arc(x+a,y+d,r,0,T),c.fill();c.lineWidth=7}
export default(c,t)=>{
const b=round(6*sin(t*T)),p=.5+.5*sin(4*T*t),fh=12+14*p
cloud(c,round(48+8*sin(t*T)),212);cloud(c,round(204-8*sin(t*T+1)),56)
c.translate(0,b)
for(const[w,s]of[[9,'#111'],[3,'#ffd8a8']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(106,180),c.lineTo(112,206),c.moveTo(150,180),c.lineTo(144,206),c.stroke()
c.strokeStyle='#111'
c.fillStyle=FO;c.lineWidth=5;c.beginPath();c.moveTo(118,204);c.quadraticCurveTo(114,196-fh*.4,128,196-fh);c.quadraticCurveTo(142,196-fh*.4,138,204);c.closePath();c.fill();c.stroke()
c.fillStyle=FY;c.beginPath();c.moveTo(123,203);c.quadraticCurveTo(121,199-fh*.3,128,199-fh*.6);c.quadraticCurveTo(135,199-fh*.3,133,203);c.closePath();c.fill()
c.lineWidth=7;env(c);c.fillStyle=ST[0];c.fill()
c.save();env(c);c.clip()
for(const[k,rx]of[[1,70],[2,46],[3,20]])c.fillStyle=ST[k],c.beginPath(),c.ellipse(128,92,rx,120,0,0,T),c.fill()
c.lineWidth=4;for(const rx of[70,46,20])c.beginPath(),c.ellipse(128,92,rx,120,0,0,T),c.stroke()
c.restore()
c.lineWidth=7;env(c);c.stroke()
c.strokeStyle='#fff';c.lineWidth=6;c.beginPath();c.arc(128,96,62,PI*1.12,PI*1.3);c.stroke();c.strokeStyle='#111'
c.fillStyle='#111';for(const x of[108,148])c.beginPath(),c.ellipse(x,108,6,8,0,0,T),c.fill()
c.fillStyle='#fff';for(const x of[106,146])c.beginPath(),c.arc(x,105,2.5,0,T),c.fill()
c.fillStyle=BL;for(const x of[92,164])c.beginPath(),c.ellipse(x,124,9,5,0,0,T),c.fill()
c.lineWidth=5;c.beginPath();c.arc(128,118,10,.15*PI,.85*PI);c.stroke()
c.lineWidth=7;c.fillStyle=SK;c.beginPath();c.moveTo(100,170);c.lineTo(156,170);c.lineTo(150,184);c.lineTo(106,184);c.closePath();c.fill();c.stroke()
c.fillStyle=BKD;c.beginPath();c.roundRect(102,200,52,10,5);c.fill();c.stroke()
c.fillStyle=BK;c.beginPath();c.roundRect(106,208,44,30,6);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();for(const y of[218,228])c.moveTo(108,y),c.lineTo(148,y);for(const x of[120,136])c.moveTo(x,210),c.lineTo(x,236);c.stroke()
}
+24
View File
@@ -0,0 +1,24 @@
// tiny smiling island with a palm tree swaying in the breeze, waves lapping all around it, coconuts in the fronds
const{sin,cos,PI,abs}=Math,T=2*PI
export const frames=12,delay=120
const SEA='#4dabf7',SL='#a5d8ff',SAND='#ffd8a8',SD='#ffc078',TR='#a9744f',TD='#7f5539',FR='#51cf66',FD='#2f9e44',CO='#8d6346',BL='#ff8787'
export default(c,t)=>{
const a=t*T,s=sin(a),f=4*sin(2*a)
c.beginPath();for(let k=0;k<=96;k++){const q=k/96*T,r=1+.03*sin(8*q-2*a);c.lineTo(128+114*r*cos(q),198+42*r*sin(q))}c.closePath();c.fillStyle=SEA;c.fill();c.stroke()
c.strokeStyle=SL;c.lineWidth=5;c.beginPath();c.ellipse(128,198,90+f,31+f*.4,0,0,T);c.stroke()
c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(128,194,74+f*.7,26+f*.3,0,0,T);c.stroke()
for(const[x,y,o]of[[38,204,0],[214,196,2],[96,228,4],[170,226,1]]){const b=2*sin(2*a+o);c.lineWidth=4;c.beginPath();c.arc(x,y+b,7,PI*1.1,PI*1.9);c.stroke();c.beginPath();c.arc(x+13,y+b,7,PI*1.1,PI*1.9);c.stroke()}
c.strokeStyle='#111';c.lineWidth=7;c.fillStyle=SAND;c.beginPath();c.ellipse(128,190,64,22,0,0,T);c.fill();c.stroke()
c.fillStyle=SD;c.beginPath();c.ellipse(96,194,14,4,0,0,T);c.fill()
const px=150+10*s,py=84,bx=116,by=186,cx=110,cy=132
for(const[w,col]of[[26,'#111'],[16,TR]])c.lineWidth=w,c.strokeStyle=col,c.beginPath(),c.moveTo(bx,by),c.quadraticCurveTo(cx,cy,px,py),c.stroke()
c.strokeStyle=TD;c.lineWidth=3
for(let u=.12;u<.9;u+=.13){const v=1-u,x=v*v*bx+2*u*v*cx+u*u*px,y=v*v*by+2*u*v*cy+u*u*py,tx=2*v*(cx-bx)+2*u*(px-cx),ty=2*v*(cy-by)+2*u*(py-cy),l=Math.hypot(tx,ty),nx=-ty/l,ny=tx/l;c.beginPath();c.moveTo(x-nx*6,y-ny*6);c.quadraticCurveTo(x+tx/l*3,y+ty/l*3,x+nx*6,y+ny*6);c.stroke()}
c.strokeStyle='#111'
const frond=(d,q,L,k)=>{c.save();c.translate(px,py);c.scale(d,1);c.rotate(q+.1*sin(a+k*.7));c.fillStyle=FR;c.lineWidth=6;c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(L*.5,-20,L,10);c.quadraticCurveTo(L*.5,4,0,0);c.fill();c.stroke();c.strokeStyle=FD;c.lineWidth=3;c.beginPath();c.moveTo(4,-2);c.quadraticCurveTo(L*.5,-10,L-6,8);c.stroke();c.restore();c.strokeStyle='#111'}
for(const[d,q,L,k]of[[-1,-.7,58,0],[1,-.75,56,1],[-1,.05,66,2],[1,.05,68,3],[-1,.62,52,4],[1,.6,54,5]])frond(d,q,L,k)
c.fillStyle=CO;c.lineWidth=5;for(const[x,y]of[[-9,10],[7,11],[-1,19]])c.beginPath(),c.arc(px+x,py+y,9,0,T),c.fill(),c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(146+d*12,196,4,0,T),c.fill()
c.lineWidth=3.5;c.beginPath();c.arc(146,200,6,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(146+d*22,202,5,3,0,0,T),c.fill()
}
+32
View File
@@ -0,0 +1,32 @@
// latte in a teal cup seen from above at an angle, a heart of foam blooming in the coffee and fading, steam curling up
const{sin,PI,min,max}=Math,T=2*PI
export const frames=24,delay=80
const CU='#38d9a9',CD='#0ca678',SA='#fff',CO='#a9744f',CR='#c99a6e',FO='#fff4e6',BL='#ffa8a8'
const heart=(c,s)=>{c.beginPath();c.moveTo(0,s*.95);c.bezierCurveTo(-s*1.5,0,-s*.9,-s*1.05,0,-s*.45);c.bezierCurveTo(s*.9,-s*1.05,s*1.5,0,0,s*.95);c.closePath()}
export default(c,t)=>{
c.fillStyle=SA;c.beginPath();c.ellipse(128,204,112,34,0,0,T);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.ellipse(128,200,74,20,0,0,T);c.stroke();c.lineWidth=7
for(const[w,s]of[[22,'#111'],[11,CU]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.ellipse(208,146,22,17,-.2,-1.2,1.6),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
c.fillStyle=CU;c.beginPath();c.moveTo(52,110);c.bezierCurveTo(56,230,200,230,204,110);c.closePath();c.fill();c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24,174,6,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24-2,172,2.2,0,T),c.fill()
c.lineWidth=5;c.beginPath();c.arc(128,180,8,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,184,9,5,0,0,T),c.fill()
c.lineWidth=7;c.fillStyle=CU;c.beginPath();c.ellipse(128,110,76,40,0,0,T);c.fill();c.stroke()
c.fillStyle=CO;c.beginPath();c.ellipse(128,112,62,31,0,0,T);c.fill();c.lineWidth=5;c.stroke()
c.fillStyle=CR;c.beginPath();c.ellipse(128,113,50,23,0,0,T);c.fill()
const p=t,g=min(1,p/.3),m=min(1,max(0,(p-.25)/.25)),f=p>.82?1-(p-.82)/.18:1
if(p>.02){
c.save();c.globalAlpha=f;c.translate(128,114);c.scale(1+.25*(1-f),.52*(1+.25*(1-f)));c.fillStyle=FO
const rc=22*g*(1-m);if(rc>.5)c.beginPath(),c.arc(0,4,rc,0,T),c.fill()
if(m>0){heart(c,30*m);c.fill();c.strokeStyle=CR;c.lineWidth=5;heart(c,17*m);c.stroke();c.fillStyle=FO;heart(c,11*m);c.fill()}
c.restore();c.strokeStyle='#111'
}
c.lineCap='round'
for(let k=0;k<3;k++){
const x0=102+k*26,pts=[];for(let s=0;s<=1.001;s+=.1)pts.push([x0+7*sin(T*(s*1.1-t)+k*2),64-44*s])
for(const[w,s]of[[9,'#111'],[4,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y])=>c.lineTo(x,y));c.stroke()}
}
c.strokeStyle='#111';c.lineWidth=7
}
+27
View File
@@ -0,0 +1,27 @@
// lava lamp with a happy base, pink blobs rising, merging and sinking through blue goo
const{sin,cos,PI,abs}=Math,T=2*PI
export const frames=24,delay=90
const CAP='#9775fa',CD='#7048e8',GOO='#74c0fc',BLOB='#f783ac',BH='#ffdeeb',BL='#faa2c1'
const glass=c=>{c.beginPath();c.moveTo(100,62);c.lineTo(156,62);c.lineTo(180,166);c.quadraticCurveTo(184,190,160,190);c.lineTo(96,190);c.quadraticCurveTo(72,190,76,166);c.closePath()}
export default(c,t)=>{
c.fillStyle=CAP;c.beginPath();c.moveTo(94,186);c.lineTo(162,186);c.lineTo(194,232);c.quadraticCurveTo(198,244,184,244);c.lineTo(72,244);c.quadraticCurveTo(58,244,62,232);c.closePath();c.fill();c.stroke()
c.fillStyle=CD;c.beginPath();c.roundRect(86,182,84,12,6);c.fill();c.lineWidth=5;c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*18,214,5,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(126+d*18,212,1.8,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.arc(128,219,7,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*34,224,8,4,0,0,T),c.fill()
c.lineWidth=7;glass(c);c.fillStyle=GOO;c.fill()
c.save();glass(c);c.clip()
const bl=[[0,124,20],[.3,110,16],[.55,146,14],[.8,132,11]].map(([p,x,r])=>{
const s=sin(T*(t+p)),y=126+54*cos(T*(t+p)),v=abs(s)
return[x+7*sin(2*T*(t+p)),y,r*(1-.12*v),r*(1+.3*v)]
})
const blobs=()=>{c.beginPath();c.ellipse(128,198,58,20,0,0,T);c.moveTo(158,58);c.ellipse(128,58,30,9,0,0,T);for(const[x,y,rx,ry]of bl)c.moveTo(x+rx,y),c.ellipse(x,y,rx,ry,0,0,T)}
c.lineWidth=10;blobs();c.stroke();c.fillStyle=BLOB;blobs();c.fill()
c.fillStyle=BH;for(const[x,y,rx,ry]of bl)c.beginPath(),c.ellipse(x-rx*.35,y-ry*.4,rx*.28,ry*.2,-.5,0,T),c.fill()
c.restore()
c.lineWidth=7;glass(c);c.stroke()
c.strokeStyle='#fff';c.lineWidth=6;c.beginPath();c.moveTo(108,78);c.lineTo(92,150);c.stroke();c.strokeStyle='#111'
c.lineWidth=7;c.fillStyle=CAP;c.beginPath();c.moveTo(96,64);c.lineTo(160,64);c.lineTo(146,22);c.quadraticCurveTo(144,16,138,16);c.lineTo(118,16);c.quadraticCurveTo(112,16,110,22);c.closePath();c.fill();c.stroke()
c.fillStyle=CD;c.beginPath();c.roundRect(92,58,72,10,5);c.fill();c.lineWidth=5;c.stroke()
}
+24
View File
@@ -0,0 +1,24 @@
// pink lotus on a lily pad opening into full bloom and closing back to a bud, ripples spreading on the pond
const{sin,cos,PI,max,min}=Math,T=2*PI
export const frames=16,delay=100
const W='#74c0fc',WL='#d0ebff',PAD='#51cf66',PD='#2f9e44',P1='#fcc2d7',P2='#f783ac',PV='#fff0f6',POD='#ffd43b',PDK='#f59f00'
const spk=(c,x,y,s)=>{c.lineWidth=3;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 o=min(1,max(0,(1-cos(t*T))/2*1.3-.12)),bx=128,by=166
const pond=()=>{c.beginPath();c.ellipse(128,174,118,48,0,0,T)}
pond();c.fillStyle=W;c.fill();c.stroke()
c.save();pond();c.clip()
for(let k=0;k<2;k++){const p=(t*2+k/2)%1,r=98+50*p;c.strokeStyle=WL;c.lineWidth=5*(1-p)+1;c.beginPath();c.ellipse(128,176,r,r*.36,0,0,T);c.stroke()}
c.restore();c.strokeStyle='#111';c.lineWidth=5
c.fillStyle=PAD;c.beginPath();c.ellipse(204,204,16,6,0,0,T);c.fill();c.stroke()
c.lineWidth=7;c.beginPath();c.moveTo(128,176);c.ellipse(128,176,96,32,0,.4,.4+T-.45);c.closePath();c.fill();c.stroke()
c.strokeStyle=PD;c.lineWidth=3;for(const q of[1.2,1.9,2.6,3.4,4.2,5,5.7])c.beginPath(),c.moveTo(128,176),c.lineTo(128+82*cos(q),176+26*sin(q)),c.stroke()
c.strokeStyle='#111'
const petal=(a,L,w,col)=>{c.save();c.translate(bx,by);c.rotate(a);c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(-w,-L*.5,0,-L);c.quadraticCurveTo(w,-L*.5,0,0);c.fillStyle=col;c.fill();c.lineWidth=7;c.stroke();c.strokeStyle=PV;c.lineWidth=4;c.beginPath();c.moveTo(0,-12);c.lineTo(0,-L*.68);c.stroke();c.restore();c.strokeStyle='#111'}
petal(0,117,55,P1);for(const d of[-1,1])petal(d*(.36+.95*o),107,55,P1)
c.fillStyle=POD;c.lineWidth=6;c.beginPath();c.ellipse(128,109,26,12,0,0,T);c.fill();c.stroke()
c.fillStyle=PDK;for(const x of[-13,0,13])c.beginPath(),c.arc(128+x,109,3,0,T),c.fill()
for(const d of[-1,1])petal(d*(.12+.72*o),96,49,P2)
petal(0,88*(1-.52*o),49,P2)
if(o>.7)for(const[x,y,q]of[[46,62,0],[210,54,.5]])spk(c,x,y,(o-.7)/.3*(5+5*max(0,sin((t+q)*4*T))))
}
+42
View File
@@ -0,0 +1,42 @@
// maneki-neko lucky cat beckoning with its raised paw, hugging a gold koban coin, collar bell swinging
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=16,delay=80
const W='#fff',OR='#ffa94d',DK='#495057',PK='#faa2c1',CO='#ff6b6b',G='#ffd43b',GD='#fab005',CU='#f783ac',CUD='#e64980'
export default(c,t)=>{
// the paw curls down twice per loop: its height squashes by cos of the curl and the toe beans tip out of view
const cu=.5-.5*cos(2*T*t),k=cos(cu*1.2),bell=.35*sin(2*T*t-.8)
c.fillStyle=CU;c.beginPath();c.roundRect(34,206,188,36,16);c.fill();c.stroke()
c.fillStyle=CUD;for(const x of[44,212])c.beginPath(),c.arc(x,238,7,0,T),c.fill(),c.stroke()
c.fillStyle=W;c.beginPath();c.ellipse(128,176,64,50,0,0,T);c.fill();c.stroke()
c.fillStyle=OR;c.save();c.beginPath();c.ellipse(128,176,64,50,0,0,T);c.clip();c.beginPath();c.ellipse(78,206,30,26,0,0,T);c.fill();c.restore()
c.beginPath();c.ellipse(128,176,64,50,0,0,T);c.stroke()
c.fillStyle=W;for(const x of[100,156])c.beginPath(),c.ellipse(x,220,18,10,0,0,T),c.fill(),c.stroke()
c.fillStyle=W;c.beginPath();c.moveTo(78,74);c.lineTo(84,30);c.lineTo(114,58);c.closePath();c.fill();c.stroke()
c.fillStyle=DK;c.beginPath();c.moveTo(142,58);c.lineTo(172,30);c.lineTo(178,74);c.closePath();c.fill();c.stroke()
c.fillStyle=PK;c.lineWidth=4;c.beginPath();c.moveTo(88,60);c.lineTo(89,42);c.lineTo(104,56);c.closePath();c.fill()
c.lineWidth=7;c.fillStyle=W;c.beginPath();c.ellipse(128,100,64,52,0,0,T);c.fill()
c.save();c.clip();c.fillStyle=OR;c.beginPath();c.ellipse(82,66,34,26,.3,0,T);c.fill();c.fillStyle=DK;c.beginPath();c.ellipse(170,60,22,16,-.3,0,T);c.fill();c.restore()
c.beginPath();c.ellipse(128,100,64,52,0,0,T);c.stroke()
c.lineWidth=5;for(const x of[104,152])c.beginPath(),c.arc(x,104,10,PI*1.15,PI*1.85),c.stroke()
c.fillStyle=PK;for(const x of[90,166])c.beginPath(),c.ellipse(x,118,9,5,0,0,T),c.fill()
c.fillStyle=CUD;c.lineWidth=3;c.beginPath();c.moveTo(123,112);c.lineTo(133,112);c.lineTo(128,118);c.closePath();c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.arc(122,120,6,.1*PI,.9*PI);c.arc(134,120,6,.1*PI,.9*PI);c.stroke()
for(const d of[-1,1])for(const[dy,ang]of[[-4,-.12],[6,.12]])for(const[w,s]of[[7,'#111'],[2,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(128+d*48,116+dy),c.lineTo(128+d*78,116+dy+ang*30),c.stroke()
c.strokeStyle='#111'
c.lineWidth=6;c.fillStyle=CO;c.beginPath();c.roundRect(84,144,88,14,7);c.fill();c.stroke()
c.save();c.translate(128,156);c.rotate(bell)
c.fillStyle=G;c.lineWidth=5;c.beginPath();c.arc(0,12,11,0,T);c.fill();c.stroke()
c.lineWidth=3;c.beginPath();c.moveTo(-10,10);c.lineTo(10,10);c.moveTo(0,14);c.lineTo(0,22);c.stroke();c.beginPath();c.arc(0,16,2.5,0,T);c.fillStyle='#111';c.fill()
c.restore()
c.lineWidth=6;c.fillStyle=G;c.beginPath();c.ellipse(96,188,20,30,0,0,T);c.fill();c.stroke()
c.fillStyle=GD;c.beginPath();c.ellipse(96,188,13,22,0,0,T);c.fill()
c.lineWidth=3;c.beginPath();for(const y of[176,184,192,200])c.moveTo(88,y),c.lineTo(104,y);c.stroke()
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.arc(96,188,16,PI*1.15,PI*1.45);c.stroke();c.strokeStyle='#111'
c.lineWidth=6;c.fillStyle=W;c.beginPath();c.ellipse(108,176,14,12,.3,0,T);c.fill();c.stroke()
for(const[w,s]of[[30,'#111'],[18,W]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(170,166),c.quadraticCurveTo(192,150,194,108),c.stroke()
c.strokeStyle='#111';c.lineWidth=6
const py=96-14*k
c.fillStyle=W;c.beginPath();c.ellipse(194,py+14*(1-k)*.4,18,18*max(.45,k),0,0,T);c.fill();c.stroke()
if(k>.5){c.fillStyle=PK;c.beginPath();c.ellipse(194,py+4,7,6*k,0,0,T);c.fill();for(const d of[-9,0,9])c.beginPath(),c.ellipse(194+d,py-9*k,3.5,3.5*k,0,0,T),c.fill()}
else{c.lineWidth=4;c.beginPath();for(const d of[-6,6])c.moveTo(194+d,py+4),c.lineTo(194+d,py+12);c.stroke()}
}
+31
View File
@@ -0,0 +1,31 @@
// big smiley maple leaf rocking side to side like a pendulum as it floats, two little leaves fluttering down beside it
const{sin,cos,PI,abs,min}=Math,T=2*PI
export const frames=20,delay=90
const LEAF=[[0,-1],[.16,-.58],[.42,-.74],[.36,-.36],[.84,-.48],[.6,-.1],[.76,.08],[.4,.14],[.52,.42],[.12,.28],[0,.44]]
const PTS=[...LEAF,...LEAF.slice(0,-1).reverse().map(([x,y])=>[-x,y])],TIPS=[[0,-1],[.42,-.74],[.84,-.48],[.76,.08],[.52,.42]]
const CL=[['#ff6b6b','#c92a2a'],['#ff922b','#d9480f'],['#ffd43b','#e67700'],['#fa5252','#a61e1e']]
const leaf=(c,x,y,S,rot,fx,[col,dk],lw,v0=0)=>{
const m=([px,py])=>{const X=px*S*fx,Y=py*S;return[x+X*cos(rot)-Y*sin(rot),y+X*sin(rot)+Y*cos(rot)]}
for(const[w,s]of[[lw*1.8,'#111'],[lw*.7,dk]]){const[a,b]=m([0,.4]),[e,f]=m([0,.95]);c.lineWidth=w;c.strokeStyle=s;c.beginPath();c.moveTo(a,b);c.lineTo(e,f);c.stroke()}
c.strokeStyle='#111';c.beginPath();for(const p of PTS)c.lineTo(...m(p));c.closePath();c.fillStyle=col;c.fill();c.lineWidth=lw;c.stroke()
c.strokeStyle=dk;c.lineWidth=lw*.5;const[ox,oy]=m([0,.3])
for(const[tx,ty]of TIPS)for(const d of tx?[-1,1]:[1]){const[ex,ey]=m([tx*d*.62,ty*.62]),[sx,sy]=v0?m([tx*d*v0,ty*v0]):[ox,oy];c.beginPath();c.moveTo(sx,sy);c.lineTo(ex,ey);c.stroke()}
c.strokeStyle='#111';return m
}
export default(c,t)=>{
const a=t*T,s=sin(a),x=128+38*s,y=126-16*s*s,rot=-.4*s
for(const[o,x0,k]of[[0,40,1],[.5,214,2]]){
const p=(t+o)%1,S=24*min(1,sin(PI*p)*2.5)
if(S>2)leaf(c,x0+12*sin(p*T*2),34+p*188,S,.4*sin(p*T*2+o*6),cos(p*T*2+o*3),CL[k],4)
}
const v=cos(a),d=v>0?1:-1
if(abs(v)>.5)for(const[dy,l]of[[-18,22],[6,30],[30,18]])for(const[w,st]of[[9,'#111'],[3.5,'#fff']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(x-d*68,y+dy),c.lineTo(x-d*(68+l*abs(v)),y+dy),c.stroke()
c.strokeStyle='#111'
const m=leaf(c,x,y,68,rot,1,CL[0],7,.38),lk=v*.03
for(const d of[-1,1]){
const[ex,ey]=m([d*.2,-.18]);c.fillStyle='#111';c.beginPath();c.ellipse(ex,ey,5.5,7,rot,0,T);c.fill()
const[hx,hy]=m([d*.2-.03+lk,-.23]);c.fillStyle='#fff';c.beginPath();c.arc(hx,hy,2.2,0,T);c.fill()
const[bx,by]=m([d*.38,-.04]);c.fillStyle='#ffa8a8';c.beginPath();c.ellipse(bx,by,7,4,rot,0,T);c.fill()
}
const[a0,b0]=m([-.13,-.04]),[a1,b1]=m([0,.1]),[a2,b2]=m([.13,-.04]);c.lineWidth=4;c.beginPath();c.moveTo(a0,b0);c.quadraticCurveTo(a1,b1,a2,b2);c.stroke()
}
+25
View File
@@ -0,0 +1,25 @@
// neon smiley heart sign on a dark board, glowing pink then flickering off and on with a little buzz
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=70
const BOARD='#343a40',SCREW='#5c636a',PK='#f783ac',PL='#fff0f6',OFF='#7a3352',WIRE='#ced4da',ZAP='#ffd43b'
const ON=[1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,1,1,1,1,1,1,1,0,1]
const heart=c=>{c.beginPath();c.moveTo(128,196);c.bezierCurveTo(42,140,62,58,128,100);c.bezierCurveTo(194,58,214,140,128,196)}
const face=c=>{c.beginPath();for(const x of[110,146])c.moveTo(x,122),c.lineTo(x,132);c.moveTo(112,146);c.quadraticCurveTo(128,160,144,146)}
export default(c,t,i)=>{
const on=ON[i],zap=ON[i]!=ON[(i+23)%24]
for(const[x0,x1]of[[58,74],[198,182]])for(const[w,st]of[[11,'#111'],[4,WIRE]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(x0,2),c.lineTo(x1,46),c.stroke()
const board=()=>{c.beginPath();c.roundRect(22,42,212,190,22)}
c.lineWidth=18;c.strokeStyle='#fff';board();c.stroke()
c.fillStyle=BOARD;board();c.fill();c.lineWidth=7;c.strokeStyle='#111';c.stroke()
c.fillStyle=SCREW;for(const[x,y]of[[40,60],[216,60],[40,214],[216,214]])c.beginPath(),c.arc(x,y,5,0,T),c.fill()
if(on){
c.save();board();c.clip();c.globalAlpha=.16;c.fillStyle=PK;c.beginPath();c.ellipse(128,140,100,82,0,0,T);c.fill()
c.strokeStyle=PK;for(const[w,al]of[[30,.22],[19,.4]]){c.globalAlpha=al;c.lineWidth=w;heart(c);c.stroke();face(c);c.stroke()}
c.restore()
}
c.strokeStyle='#111';c.lineWidth=13;heart(c);c.stroke();face(c);c.stroke()
c.strokeStyle=on?PK:OFF;c.lineWidth=7;heart(c);c.stroke();face(c);c.stroke()
if(on){c.strokeStyle=PL;c.lineWidth=2.5;heart(c);c.stroke();face(c);c.stroke()}
c.strokeStyle='#111'
if(zap)for(const[x,y,d]of[[190,84,1],[66,84,-1]])for(const[w,st]of[[9,'#111'],[3.5,ZAP]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(x,y),c.lineTo(x+d*8,y-6),c.lineTo(x+d*4,y-12),c.lineTo(x+d*14,y-20),c.stroke()
}
+38
View File
@@ -0,0 +1,38 @@
// otter floating on its back, banging a clam on the stone on its tummy while it bobs on the waves
const{sin,cos,PI,abs,max}=Math,T=2*PI
export const frames=16,delay=80
const BR='#a9744f',BD='#8d6346',BE='#ffd8a8',WA='#4dabf7',WL='#a5d8ff',ST='#ced4da',SD='#868e96',CL='#faa2c1',CD='#f06595',BL='#ffa8a8'
export default(c,t)=>{
const b=Math.round(4*sin(T*t)),r=.05*sin(T*t+.6),h=max(0,(abs(sin(T*t))-.4)/.6),bang=h==0
const wy=(x,o=0)=>186+5*sin(x/20-T*t+o)
const sea=(o,dy)=>{c.fillStyle=WA;c.beginPath();c.moveTo(22,244);c.quadraticCurveTo(8,244,8,230);for(let x=8;x<=248;x+=4)c.lineTo(x,wy(x,o)+dy);c.lineTo(248,230);c.quadraticCurveTo(248,244,234,244);c.closePath();c.fill();c.stroke()}
sea(2,-14)
c.save();c.translate(128,170+b);c.rotate(r);c.translate(-128,-170)
c.fillStyle=BR
c.beginPath();c.moveTo(48,166);c.quadraticCurveTo(14,170,10,148);c.quadraticCurveTo(26,156,50,152);c.fill();c.stroke()
for(const[x,a]of[[56,-.5],[76,-.2]])c.save(),c.translate(x,150),c.rotate(a),c.beginPath(),c.ellipse(0,-14,9,16,0,0,T),c.fill(),c.stroke(),c.restore()
c.beginPath();c.ellipse(116,168,76,30,0,0,T);c.fill();c.stroke()
c.fillStyle=BE;c.beginPath();c.ellipse(118,160,56,18,0,0,T);c.fill()
c.fillStyle=BR;c.beginPath();c.arc(194,140,36,0,T);c.fill();c.stroke()
c.fillStyle=BE;c.beginPath();c.ellipse(204,150,22,16,0,0,T);c.fill()
for(const[x,y]of[[176,108],[216,112]])c.fillStyle=BR,c.beginPath(),c.arc(x,y,8,0,T),c.fill(),c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(206,142,6,4,0,0,T);c.fill()
c.lineWidth=5;c.beginPath();c.moveTo(206,146);c.lineTo(206,152);c.moveTo(196,154);c.quadraticCurveTo(206,160,216,154);c.stroke()
for(const[x,y]of[[184,128],[220,130]])c.beginPath(),bang?(c.moveTo(x-6,y-4),c.lineTo(x+2,y),c.lineTo(x-6,y+4)):c.arc(x,y+2,6,1.15*PI,1.85*PI),c.stroke()
c.lineWidth=3;for(const d of[-1,1])c.beginPath(),c.moveTo(222,150+d*3),c.lineTo(236,148+d*6),c.stroke()
c.fillStyle=BL;c.beginPath();c.ellipse(180,148,7,4,0,0,T);c.fill()
c.lineWidth=7
c.fillStyle=ST;c.beginPath();c.ellipse(118,140,22,12,0,0,T);c.fill();c.stroke()
c.fillStyle=SD;c.beginPath();c.ellipse(112,136,6,3,0,0,T);c.fill()
const cy=118-26*h
c.save();c.translate(122,cy);c.rotate(-.2)
c.fillStyle=CL;c.beginPath();c.moveTo(-18,4);c.quadraticCurveTo(0,-26,18,4);c.quadraticCurveTo(0,12,-18,4);c.fill();c.stroke()
c.lineWidth=3;c.strokeStyle=CD;for(const a of[-.5,0,.5])c.beginPath(),c.moveTo(0,6),c.lineTo(sin(a)*14,-10+abs(a)*6),c.stroke()
c.restore();c.lineWidth=7;c.strokeStyle='#111'
c.fillStyle=BR;for(const d of[-1,1])c.beginPath(),c.moveTo(d<0?98:146,154),c.quadraticCurveTo(d<0?94:150,cy+16,122+d*20,cy+4),c.lineWidth=16,c.stroke(),c.strokeStyle=BR,c.lineWidth=6,c.stroke(),c.strokeStyle='#111',c.lineWidth=7,c.beginPath(),c.arc(122+d*20,cy+4,9,0,T),c.fill(),c.stroke()
if(bang){c.lineWidth=4;for(const[a,l]of[[-2.5,14],[-1.9,16],[-.6,14],[-1.25,16]])c.beginPath(),c.moveTo(122+cos(a)*30,130+sin(a)*22),c.lineTo(122+cos(a)*(30+l),130+sin(a)*(22+l)),c.stroke();c.lineWidth=7}
c.restore()
sea(0,6)
c.strokeStyle=WL;c.lineWidth=4;for(const[x,y,w]of[[60,224,18],[150,230,24],[210,222,14]])c.beginPath(),c.moveTo(x-w,y+2*sin(T*t+x)),c.lineTo(x+w,y+2*sin(T*t+x)),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
}
+29
View File
@@ -0,0 +1,29 @@
// stack of pancakes on a plate, the butter pat softening on top while blobs of syrup run down the sides
const{sin,PI,min,max,sqrt}=Math,T=2*PI
export const frames=20,delay=80
const PC='#ffc078',PT='#ffd8a8',SY='#b5651d',SL='#e8a35a',BU='#ffe066',BS='#fcc419',PL='#e7f5ff',BL='#ffa8a8'
const DR=[[70,60],[88,64],[170,64],[190,56]]
export default(c,t)=>{
c.fillStyle=PL;c.beginPath();c.ellipse(128,214,112,24,0,0,T);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.ellipse(128,212,88,15,0,0,T);c.stroke();c.lineWidth=7
c.fillStyle=SY;c.beginPath();c.ellipse(128,206,100,15,0,0,T);c.fill();c.stroke()
for(let k=0;k<3;k++){const y=200-k*28;c.fillStyle=PC;c.beginPath();c.ellipse(128,y,86,18,0,0,PI);c.lineTo(42,y-22);c.ellipse(128,y-22,86,18,0,PI,T);c.closePath();c.fill();c.stroke()}
c.fillStyle=PT;c.beginPath();c.ellipse(128,122,86,18,0,0,T);c.fill();c.stroke()
c.fillStyle=SY;c.beginPath();for(let a=0;a<=T+.01;a+=.1)c.lineTo(128+Math.cos(a)*(64+5*sin(a*5)),122+sin(a)*(12+2*sin(a*5)));c.closePath();c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7
const m=(1+sin(T*t))/2
c.fillStyle='#fff3bf';c.beginPath();c.ellipse(128,118,30+6*m,8+2*m,0,0,T);c.fill()
c.fillStyle=BS;c.beginPath();c.moveTo(104,116);c.lineTo(128,128);c.lineTo(152,116);c.lineTo(152,116-10+3*m);c.lineTo(128,128-10+3*m);c.lineTo(104,116-10+3*m);c.closePath();c.fill();c.lineWidth=5;c.stroke()
const top=116-10+3*m;c.fillStyle=BU;c.beginPath();c.moveTo(104,top);c.quadraticCurveTo(116,top-14,128,top-13);c.quadraticCurveTo(142,top-14,152,top);c.lineTo(128,top+12);c.closePath();c.fill();c.stroke()
c.strokeStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(116,top-4);c.lineTo(126,top-8);c.stroke();c.strokeStyle='#111'
DR.forEach(([x,L],k)=>{
const p=(t+k*.27)%1,y0=122+18*sqrt(1-((x-128)/86)**2)-6,yb=y0+L*min(1,p/.6),ya=y0+L*max(0,(p-.6)/.4)
for(const[w,s]of[[15,'#111'],[8,SY]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x,ya),c.lineTo(x,yb),c.stroke()
c.fillStyle=SY;c.lineWidth=5;c.strokeStyle='#111';c.beginPath();c.arc(x,yb+2,8,0,PI);c.stroke();c.beginPath();c.arc(x,yb+1,6.5,0,T);c.fill()
c.strokeStyle=SL;c.lineWidth=3;c.beginPath();c.moveTo(x-2,ya+3);c.lineTo(x-2,max(ya+3,yb-6));c.stroke();c.strokeStyle='#111'
})
c.lineWidth=7
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*20,151,5.5,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*20-2,149,2,0,T),c.fill()
c.lineWidth=4.5;c.beginPath();c.arc(128,154,7,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*29,158,6,3.5,0,0,T),c.fill()
}
+34
View File
@@ -0,0 +1,34 @@
// peacock fanning its tail open into a ring of eye feathers, shimmying, then folding it back up
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=12,delay=100
const TL='#38d9a9',TD='#12b886',GO='#ffd43b',EB='#4dabf7',ED='#1864ab',BD='#4dabf7',BK='#1c7ed6',OR='#ffa94d',BL='#ffa8a8'
const ease=x=>x<0?0:x>1?1:x*x*(3-2*x)
export default(c,t)=>{
const s=ease(t/.25)-ease((t-.7)/.25),sh=sin(4*T*t+.8)*s**6,N=9
// all feather outlines first, so a folded bunch doesn't turn into a black mass, then fills with a thin teal seam
const fe=[...Array(N)].map((_,k)=>{const u=(k-(N-1)/2)/((N-1)/2);return[u*(.45+.95*s)+.05*sh*u,84+32*s]})
const go=(a,f)=>{c.save();c.translate(128,170);c.rotate(a);f();c.restore()}
for(const[a,L]of fe)go(a,()=>{c.lineWidth=14;c.beginPath();c.ellipse(0,-L*.55,15+5*s,L*.55,0,0,T);c.stroke()})
for(const[a,L]of fe)go(a,()=>{
c.fillStyle=TL;c.strokeStyle=TD;c.lineWidth=3;c.beginPath();c.ellipse(0,-L*.55,15+5*s,L*.55,0,0,T);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.moveTo(0,-8);c.lineTo(0,-L+20);c.stroke();c.strokeStyle='#111'
c.fillStyle=GO;c.lineWidth=5;c.beginPath();c.ellipse(0,-L+16,12,15,0,0,T);c.fill();c.stroke()
c.fillStyle=EB;c.beginPath();c.ellipse(0,-L+17,7,9,0,0,T);c.fill()
c.fillStyle=ED;c.beginPath();c.ellipse(0,-L+18,3.5,5,0,0,T);c.fill()
})
c.lineWidth=7
c.fillStyle=OR;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*14,228,12,6,0,0,T),c.fill(),c.stroke()
c.lineWidth=12;c.beginPath();for(const d of[-1,1])c.moveTo(128+d*12,206),c.lineTo(128+d*14,224);c.stroke()
c.strokeStyle=OR;c.lineWidth=5;c.beginPath();for(const d of[-1,1])c.moveTo(128+d*12,206),c.lineTo(128+d*14,224);c.stroke();c.strokeStyle='#111';c.lineWidth=7
for(const[x,y]of[[116,52],[128,46],[140,52]])c.lineWidth=4,c.beginPath(),c.moveTo(128,78),c.lineTo(x,y),c.stroke(),c.lineWidth=5,c.fillStyle=BD,c.beginPath(),c.arc(x,y,6,0,T),c.fill(),c.stroke()
c.lineWidth=7
const sil=()=>{c.beginPath();c.ellipse(128,176,34,40,0,0,T);c.moveTo(110,152);c.lineTo(114,104);c.lineTo(142,104);c.lineTo(146,152);c.closePath();c.moveTo(152,98);c.arc(128,98,24,0,T)}
sil();c.lineWidth=14;c.stroke();c.fillStyle=BD;sil();c.fill();c.lineWidth=7
c.fillStyle=BK;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*26,180,10,26,-d*.2,0,T),c.fill(),c.lineWidth=5,c.stroke()
c.lineWidth=7
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*11,94,8,10,0,0,T),c.fill()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*11,95,4.5,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(127+d*11,93,1.6,0,T),c.fill()
c.fillStyle=GO;c.lineWidth=4;c.beginPath();c.moveTo(122,104);c.lineTo(134,104);c.lineTo(128,114);c.closePath();c.fill();c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*17,108,5,3,0,0,T),c.fill()
}
+56
View File
@@ -0,0 +1,56 @@
// striped donkey piñata swinging on its rope, bonked by a stick from below, candy popping out and falling
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=16,delay=90
const ST=['#f783ac','#ffd43b','#4dabf7','#69db7c'],PINK='#faa2c1',SN='#ffd8a8',ROPE='#e0a96d',STICK='#a9744f',CANDY=['#ff6b6b','#b197fc','#ffd43b','#38d9a9','#ffa94d','#4dabf7']
const parts=[
c=>c.roundRect(-54,0,94,56,16),
c=>{c.moveTo(12,10);c.lineTo(24,-30);c.lineTo(46,-26);c.lineTo(38,16);c.closePath()},
c=>c.roundRect(22,-54,64,34,14),
c=>c.ellipse(36,-62,6,16,-.25,0,T),
c=>{c.moveTo(56,-62);c.ellipse(50,-62,6,16,.25,0,T)},
...[-46,-26,4,24].map(x=>c=>c.roundRect(x,48,14,28,4))
]
const burst=(c,x,y,s)=>{c.fillStyle='#fff9db';c.lineWidth=5;c.beginPath();for(let k=0;k<16;k++){const r=k%2?s*.45:s,a=k*PI/8;c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.closePath();c.fill();c.stroke()}
export default(c,t)=>{
const a=.18*cos(T*t),w=(t+.12)%1,hit=t<.14
c.save();c.translate(132,-40);c.rotate(a)
for(const[lw,st]of[[11,'#111'],[5,ROPE]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(0,0),c.lineTo(0,152),c.stroke()
c.strokeStyle='#111';c.translate(0,150);c.rotate(-a*.4);c.scale(.9,.9)
for(const[x,col]of[[-58,ST[0]],[-62,ST[1]],[-66,ST[2]]])for(const[lw,st]of[[9,'#111'],[4,col]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(-52,12),c.lineTo(x,40+(x+58)*-2),c.stroke()
c.strokeStyle='#111'
c.lineWidth=14;for(const p of parts)c.beginPath(),p(c),c.stroke()
c.fillStyle=PINK;for(const p of parts)c.beginPath(),p(c),c.fill()
c.save();c.beginPath();parts[0](c);parts[1](c);c.clip()
for(let k=0;k<8;k++){c.fillStyle=ST[k%4];c.beginPath();c.moveTo(-60,-34+k*12);for(let x=-60;x<=50;x+=10)c.lineTo(x+5,-34+k*12+6),c.lineTo(x+10,-34+k*12);c.lineTo(50,-34+k*12+14);c.lineTo(-60,-34+k*12+14);c.fill()}
c.restore()
c.save();c.beginPath();parts[2](c);c.clip();c.fillStyle=ST[1];c.beginPath();c.moveTo(22,-54);for(let x=22;x<=60;x+=8)c.lineTo(x+4,-42),c.lineTo(x+8,-54);c.fill()
c.fillStyle=SN;c.beginPath();c.ellipse(80,-36,14,15,0,0,T);c.fill();c.restore()
;[-46,-26,4,24].forEach((x,k)=>{c.fillStyle=ST[(k+2)%4];c.beginPath();c.roundRect(x,48,14,28,4);c.fill()})
c.lineWidth=4;for(const p of[parts[0],...parts.slice(5)])c.beginPath(),p(c),c.stroke()
c.fillStyle='#111';c.lineWidth=3.5
if(hit){c.beginPath();c.moveTo(50,-44);c.lineTo(58,-39);c.lineTo(50,-34);c.stroke()}
else c.beginPath(),c.arc(54,-40,4,0,T),c.fill()
c.beginPath();c.arc(84,-38,2.2,0,T);c.fill()
c.beginPath();if(hit)c.ellipse(72,-26,4,3,0,0,T),c.fill();else c.arc(72,-30,5,.2*PI,.8*PI),c.stroke()
c.restore()
const sw=w<.12?sin(PI/2*w/.12):w<.3?cos(PI/2*(w-.12)/.18):0
if(sw>0){
const ph=-.08-1.356*sw
c.save();c.translate(40,300);c.rotate(ph);c.lineWidth=7
c.fillStyle=STICK;c.beginPath();c.moveTo(0,-7);c.lineTo(150,-11);c.quadraticCurveTo(158,0,150,11);c.lineTo(0,7);c.closePath();c.fill();c.stroke()
c.lineWidth=4;for(const x of[96,116,136])c.beginPath(),c.moveTo(x,-10),c.lineTo(x-6,10),c.stroke()
c.restore()
}
if(t<.16)burst(c,60,152,22*sin(PI*t/.16)+8)
if(t<.62){
const u=t
for(let k=0;k<6;k++){
const vx=-30+k*32,x=93+vx*u,y=157-110*u+380*u*u,s=u<.45?1:(.62-u)/.17
if(y>270||s<=0)continue
c.save();c.translate(x,y);c.rotate(k*1.3+u*9);c.scale(s,s);c.fillStyle=CANDY[k];c.lineWidth=4
for(const d of[-1,1])c.beginPath(),c.moveTo(d*8,0),c.lineTo(d*17,-7),c.lineTo(d*17,7),c.closePath(),c.fill(),c.stroke()
c.beginPath();c.ellipse(0,0,10,8,0,0,T);c.fill();c.stroke()
c.restore()
}
}
}
+29
View File
@@ -0,0 +1,29 @@
// pineapple in sunglasses grooving side to side to the beat, crown leaves swaying behind it and music notes popping up
const{sin,cos,PI,abs}=Math,T=2*PI
export const frames=14,delay=90
const PA='#ffd43b',PD='#f59f00',LF='#69db7c',LD='#40c057',BL='#ffa8a8',NC=['#9775fa','#4dabf7']
const note=(c,x,y,s,col)=>{
c.save();c.translate(x,y);c.scale(s,s);c.rotate(-.2)
for(const[w,st]of[[10,'#111'],[4,col]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(8,4),c.lineTo(8,-26),c.quadraticCurveTo(18,-20,20,-10),c.stroke()
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=col;c.beginPath();c.ellipse(0,4,10,7,-.4,0,T);c.fill();c.stroke();c.restore()
}
export default(c,t)=>{
const r=.13*sin(T*t),b=-5*abs(sin(2*T*t))
;[0,1].forEach(k=>{const p=(t+k*.5)%1;note(c,k?54:202,104-64*p,sin(PI*p),NC[k])})
c.strokeStyle='#111'
c.save();c.translate(128,234+b);c.rotate(r);c.translate(-128,-234)
;[[-1.05,52],[1.05,52],[-.6,64],[.6,64],[-.25,76],[.25,76],[0,66]].forEach(([a,L],k)=>{
c.save();c.translate(128,96);c.rotate(a+.14*sin(T*t-1.1-k*.2));c.fillStyle=k%2?LD:LF
c.beginPath();c.moveTo(-11,6);c.quadraticCurveTo(-9,-L*.6,0,-L);c.quadraticCurveTo(9,-L*.6,11,6);c.closePath();c.fill();c.lineWidth=6;c.stroke();c.restore()
})
c.lineWidth=7;c.fillStyle=PA;c.beginPath();c.ellipse(128,164,62,70,0,0,T);c.fill()
c.save();c.clip();c.strokeStyle=PD;c.lineWidth=4
for(let k=-8;k<9;k++)c.beginPath(),c.moveTo(128+k*24-90,90),c.lineTo(128+k*24+90,240),c.stroke(),c.beginPath(),c.moveTo(128+k*24+90,90),c.lineTo(128+k*24-90,240),c.stroke()
c.restore();c.strokeStyle='#111';c.beginPath();c.ellipse(128,164,62,70,0,0,T);c.stroke()
c.fillStyle='#343a40';for(const d of[-1,1])c.beginPath(),c.roundRect(128+d*24-21,128,42,28,[6,6,14,14]),c.fill(),c.stroke()
c.lineWidth=6;c.beginPath();c.moveTo(118,134);c.quadraticCurveTo(128,128,138,134);c.stroke()
c.strokeStyle='#fff';c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*24-12,148),c.lineTo(128+d*24-2,136),c.stroke()
c.strokeStyle='#111';c.fillStyle='#111';c.beginPath();c.moveTo(112,176);c.quadraticCurveTo(128,196,146,174);c.quadraticCurveTo(128,182,112,176);c.closePath();c.fill();c.lineWidth=4;c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,174,9,5,0,0,T),c.fill()
c.restore()
}
+26
View File
@@ -0,0 +1,26 @@
// two-tone popsicle slowly melting, drips swelling on its bottom edge and falling into a little puddle
// the body and its drips are stroked wide first and filled after, so they read as one outline
const{sin,PI,min}=Math,T=2*PI
export const frames=24,delay=70
const PK='#f783ac',YE='#ffd43b',STK='#e0a96d',BL='#ffa8a8',SW='#a5d8ff',DX=[98,154]
const body=c=>{c.beginPath();c.moveTo(178,170);c.lineTo(178,64);c.bezierCurveTo(178,20,78,20,78,64);c.lineTo(78,170);for(let k=0;k<5;k++)c.quadraticCurveTo(88+k*20,k%2?166:180,98+k*20,172);c.closePath()}
export default(c,t)=>{
const dr=DX.map((x,k)=>{const p=(t+k*.5)%1,g=min(1,p/.65);return{x,p,L:p<.65?6+30*g:6,r:p<.65?5+4.5*g:5}})
const tongue=({x,L,r})=>{c.beginPath();c.moveTo(x-12,164);c.quadraticCurveTo(x-7,172+L*.5,x-r,172+L);c.arc(x,172+L,r,PI,0,true);c.quadraticCurveTo(x+7,172+L*.5,x+12,164);c.closePath()}
c.fillStyle=YE;c.beginPath();c.ellipse(128,230,78,12,0,0,T);c.fill();c.stroke()
c.lineWidth=4;for(const d of dr)if(d.p>.8){const q=(d.p-.8)/.2;c.beginPath();c.ellipse(d.x,230,6+20*q,2+4*q,0,0,T);c.stroke()}
c.lineWidth=7
c.fillStyle=STK;c.beginPath();c.roundRect(114,150,28,82,[0,0,12,12]);c.fill();c.stroke()
c.lineWidth=14;body(c);c.stroke();for(const d of dr)tongue(d),c.stroke()
c.save();body(c);c.clip();c.fillStyle=YE;c.fillRect(70,20,116,170);c.fillStyle=PK;c.beginPath();c.moveTo(70,20);c.lineTo(186,20);for(let x=186;x>=70;x-=4)c.lineTo(x,104+6*sin(x/11));c.closePath();c.fill()
c.strokeStyle='#fff';c.lineWidth=8;c.beginPath();c.moveTo(94,56);c.lineTo(94,86);c.stroke();c.restore()
c.fillStyle=YE;for(const d of dr)tongue(d),c.fill()
c.lineWidth=7;c.strokeStyle='#111'
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*22,128,6,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*22-2,126,2.2,0,T),c.fill()
c.lineWidth=4.5;c.beginPath();c.moveTo(116,144);for(let k=1;k<=4;k++)c.lineTo(116+k*6,k%2?140:144);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*38,142,8,4.5,0,0,T),c.fill()
c.fillStyle=SW;c.lineWidth=4;c.beginPath();c.moveTo(164,74);c.quadraticCurveTo(172,90,164,94);c.quadraticCurveTo(156,90,164,74);c.fill();c.stroke()
c.lineWidth=6
for(const d of dr)if(d.p>=.65&&d.p<.8){const y=200+(d.p-.65)/.15*26;c.fillStyle=YE;c.beginPath();c.moveTo(d.x,y-12);c.quadraticCurveTo(d.x+7,y-2,d.x+6,y+2);c.arc(d.x,y+2,6,0,PI);c.quadraticCurveTo(d.x-7,y-2,d.x,y-12);c.fill();c.stroke()}
}
+27
View File
@@ -0,0 +1,27 @@
// coaster car with a cheering rider racing around a loop-the-loop on a track held up by striped supports
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=70
const RA='#ff6b6b',SU='#4dabf7',CA='#ffd43b',CD='#fab005',SKIN='#ffd8a8',HAIR='#a9744f',GR='#69db7c',BL='#ffa8a8'
const CX=128,CY=106,R=68,Y0=CY+R,X0=-44,X1=300,L1=CX-X0,L2=T*R,L3=X1-CX
// position and heading along the track by distance: in from the left, once round the loop, out to the right
const at=s=>s<L1?[X0+s,Y0,0]:s<L1+L2?(a=>[CX+R*cos(a),CY+R*sin(a),PI/2-a])(PI/2-(s-L1)/R):[CX+s-L1-L2,Y0,0]
export default(c,t)=>{
c.fillStyle=GR;c.beginPath();c.roundRect(10,230,236,14,7);c.fill();c.stroke()
for(const[x,y]of[[30,Y0],[CX-46,CY+50],[CX,Y0],[CX+46,CY+50],[226,Y0]]){for(const[w,s]of[[13,'#111'],[5,SU]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x,y),c.lineTo(x,231),c.stroke()}
for(const[w,s]of[[15,'#111'],[7,RA]]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();c.arc(CX,CY,R,0,T);c.stroke();c.beginPath();c.moveTo(4,Y0);c.lineTo(252,Y0);c.stroke()}
c.strokeStyle='#111'
const[x,y,a]=at((t*(L1+L2+L3)+90)%(L1+L2+L3))
c.save();c.translate(x,y);c.rotate(-a)
for(const[w,s]of[[9,'#111'],[3,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(-30,-14),c.lineTo(-46,-14),c.moveTo(-30,-24),c.lineTo(-42,-24),c.stroke()
c.strokeStyle='#111'
for(const[w,s]of[[9,'#111'],[4,SKIN]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(-10,-34),c.lineTo(-18,-50),c.moveTo(2,-34),c.lineTo(8,-50),c.stroke()
c.strokeStyle='#111';c.lineWidth=5
c.fillStyle=HAIR;c.beginPath();c.arc(-4,-38,10,0,T);c.fill();c.stroke()
c.fillStyle=SKIN;c.beginPath();c.arc(-3,-36,8,0,PI);c.fill()
c.lineWidth=6;c.fillStyle=CA;c.beginPath();c.roundRect(-24,-30,48,22,7);c.fill();c.stroke()
c.fillStyle=CD;c.beginPath();c.roundRect(-24,-14,48,6,3);c.fill()
c.fillStyle='#111';c.beginPath();c.arc(12,-21,3,0,T);c.fill()
c.lineWidth=3;c.beginPath();c.arc(16,-18,4,.2*PI,.9*PI);c.stroke()
c.fillStyle='#495057';c.lineWidth=4;for(const wx of[-13,13])c.beginPath(),c.arc(wx,-6,5,0,T),c.fill(),c.stroke()
c.restore()
}
+37
View File
@@ -0,0 +1,37 @@
// snow globe gets a shake, snow swirls around a tiny house and pine inside, then drifts down and settles
// each flake lifts off its resting spot, swirls, and lands back on the same spot, so the loop is seamless
const{sin,cos,PI,abs,min}=Math,T=2*PI
export const frames=24,delay=70
const GL='#d0ebff',BS='#a9744f',BAND='#ffd43b',RF='#ff6b6b',WL='#ffd8a8',WIN='#ffd43b',PN='#51cf66',BR='#8d6346'
const F=[...Array(14)].map((_,j)=>({gx:62+j*9.6,gy:178-(j%3)*5,r:24+(j*37)%56,q:j*2.4}))
const sm=x=>x<0?0:x>1?1:x*x*(3-2*x)
export default(c,t)=>{
const u=t/.32,sh=t<.32?.15*sin(PI*u)*sin(3*T*u):0,cx=128,cy=108,R=90
if(abs(sh)>.05){const d=sh>0?1:-1;for(const r of[104,118])for(const[w,s]of[[10,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(128,140,r,d>0?-.3:PI-.3,d>0?.3:PI+.3),c.stroke()}
c.strokeStyle='#111';c.save();c.translate(128,240);c.rotate(sh);c.translate(-128,-240)
const glob=()=>{c.beginPath();c.arc(cx,cy,R,0,T)}
glob();c.fillStyle=GL;c.fill()
c.save();glob();c.clip()
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.ellipse(128,192,100,32,0,0,T);c.fill();c.stroke()
c.fillStyle=BR;c.lineWidth=5;c.beginPath();c.rect(162,150,10,14);c.fill();c.stroke()
for(const[w,y0,h]of[[28,154,28],[22,134,26],[16,114,24]]){c.fillStyle=PN;c.beginPath();c.moveTo(167-w,y0);c.lineTo(167,y0-h);c.lineTo(167+w,y0);c.closePath();c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.moveTo(167-w*.4,y0-h*.6);c.lineTo(167,y0-h);c.lineTo(167+w*.4,y0-h*.6);c.quadraticCurveTo(167,y0-h*.45,167-w*.4,y0-h*.6);c.fill()}
c.fillStyle=WL;c.beginPath();c.rect(80,128,46,36);c.fill();c.stroke()
c.fillStyle=BR;c.beginPath();c.rect(110,96,10,20);c.fill();c.stroke()
c.fillStyle=RF;c.beginPath();c.moveTo(72,132);c.lineTo(103,102);c.lineTo(134,132);c.closePath();c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.moveTo(84,120);c.lineTo(103,102);c.lineTo(122,120);c.quadraticCurveTo(103,112,84,120);c.fill();c.stroke()
c.fillStyle=WIN;c.beginPath();c.rect(86,138,14,12);c.fill();c.stroke();c.fillStyle=BR;c.beginPath();c.rect(106,144,12,20);c.fill();c.stroke()
for(const f of F){
const sw=k=>[cx+f.r*cos(f.q+k*T*1.6),cy-8+f.r*.85*sin(f.q+k*T*1.6)]
let x,y,rr
if(t<.12){const b=sm(t/.12),[sx,sy]=sw(t);x=f.gx+(sx-f.gx)*b;y=f.gy+(sy-f.gy)*b;rr=min(1,b*4)}
else if(t<.4){[x,y]=sw(t);rr=1}
else{const e=sm((t-.4)/.6),[sx,sy]=sw(.4);x=sx+(f.gx-sx)*e+8*(1-e)*sin(e*T*2+f.q);y=sy+(f.gy-sy)*e;rr=min(1,(1-e)*5)}
if(rr>.05){c.fillStyle='#fff';c.lineWidth=2;c.beginPath();c.arc(x,y,3.8*rr,0,T);c.fill();c.stroke()}
}
c.restore()
c.strokeStyle='#fff';c.lineWidth=8;c.beginPath();c.arc(cx,cy,R-16,PI*1.08,PI*1.38);c.stroke();c.lineWidth=6;c.beginPath();c.arc(cx,cy,R-16,PI*1.45,PI*1.53);c.stroke()
c.strokeStyle='#111';c.lineWidth=7;glob();c.stroke()
c.fillStyle=BS;c.beginPath();c.moveTo(64,184);c.lineTo(192,184);c.lineTo(210,240);c.lineTo(46,240);c.closePath();c.fill();c.stroke()
c.fillStyle=BAND;c.beginPath();c.moveTo(58,202);c.lineTo(198,202);c.lineTo(203,218);c.lineTo(53,218);c.closePath();c.fill();c.lineWidth=5;c.stroke()
c.restore()
}
+39
View File
@@ -0,0 +1,39 @@
// soccer ball kicked spinning into the goal, the net bulging back around it, then a fresh ball bounces in to start again
const{sin,cos,PI,abs,max,min,exp,hypot}=Math,T=2*PI
export const frames=24,delay=70
const GR='#69db7c',GRD='#51cf66',NET='#adb5bd'
const ball=(c,x,y,r,a)=>{
if(r<1)return
c.save();c.translate(x,y);c.rotate(a)
c.fillStyle='#fff';c.beginPath();c.arc(0,0,r,0,T);c.fill()
c.save();c.clip();c.fillStyle='#111'
const pent=(px,py,s)=>{c.beginPath();for(let k=0;k<5;k++){const b=k*T/5-PI/2;c.lineTo(px+cos(b)*s,py+sin(b)*s)}c.closePath();c.fill()}
pent(0,0,r*.34);c.lineWidth=max(2,r*.08);c.strokeStyle='#111'
for(let k=0;k<5;k++){const b=k*T/5-PI/2;pent(cos(b)*r*.98,sin(b)*r*.98,r*.32);c.beginPath();c.moveTo(cos(b)*r*.34,sin(b)*r*.34);c.lineTo(cos(b)*r*.7,sin(b)*r*.7);c.stroke()}
c.restore();c.lineWidth=max(3,r*.2);c.beginPath();c.arc(0,0,r,0,T);c.stroke();c.restore()
}
const lerp=(a,b,p)=>a+(b-a)*p
export default(c,t)=>{
// 0-.4 flight, .4-.7 net bulge and drop, .7-.82 ball pops away, .82-1 a fresh ball bounces in at the kick spot
const S=[50,200,30],H=[178,124,17]
let x,y,r,a=0,m=0,pop=0
if(t<.4){const p=t/.4;x=lerp(S[0],H[0],p);y=lerp(S[1],H[1],p)-46*sin(PI*p);r=lerp(S[2],H[2],p);a=-p*T*1.5}
else if(t<.7){const p=(t-.4)/.3;m=sin(PI*min(1,p*1.6))*(1-p*.3);x=H[0];y=lerp(H[1],178,p*p);r=H[2];a=-T*1.5-p*2}
else if(t<.82){const p=(t-.7)/.12;x=H[0];y=178;r=H[2]*(1-p);a=0;pop=p}
else{const p=(t-.82)/.18;x=S[0];y=S[1]-24*abs(sin(PI*p))*(1-p);r=S[2]*min(1,p*1.6)*(1+.15*sin(PI*min(1,p*1.6)));a=0}
c.lineWidth=7;c.fillStyle=GR;c.beginPath();c.roundRect(8,192,240,52,14);c.fill();c.stroke()
c.save();c.beginPath();c.roundRect(8,192,240,52,14);c.clip();c.fillStyle=GRD;for(const sx of[40,120,200])c.fillRect(sx,192,40,52);c.restore()
c.beginPath();c.roundRect(8,192,240,52,14);c.stroke()
// net: grid on the back panel, each point pulled toward the impact spot while the ball pushes in
const P=(px,py)=>{const d=hypot(px-H[0],py-H[1]),k=m*.45*exp(-d*d/1800);return[px+(H[0]-px)*k,py+(H[1]-py)*k]}
c.strokeStyle=NET;c.lineWidth=3
for(let gx=100;gx<=228;gx+=16){c.beginPath();for(let gy=84;gy<=194;gy+=4)c.lineTo(...P(gx,gy));c.stroke()}
for(let gy=84;gy<=194;gy+=14){c.beginPath();for(let gx=100;gx<=228;gx+=4)c.lineTo(...P(gx,gy));c.stroke()}
c.beginPath();for(const[ax,ay,bx,by]of[[84,70,100,84],[244,70,228,84]])c.moveTo(ax,ay),c.lineTo(bx,by);c.stroke()
c.strokeStyle='#111'
if(t>=.4&&t<.82)ball(c,x,y,r,a)
if(pop>0){const s=8+18*pop;for(let k=0;k<6;k++){const b=k*T/6;for(const[w,st]of[[9,'#111'],[4,'#ffd43b']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(x+cos(b)*s,y+sin(b)*s),c.lineTo(x+cos(b)*(s+10*(1-pop)+2),y+sin(b)*(s+10*(1-pop)+2)),c.stroke()}c.strokeStyle='#111'}
for(const[w,st]of[[16,'#111'],[8,'#fff']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(84,196),c.lineTo(84,70),c.lineTo(244,70),c.lineTo(244,196),c.stroke()
c.strokeStyle='#111'
if(t<.4||t>=.82){c.fillStyle='#2f9e44';c.beginPath();c.ellipse(x,t<.4?lerp(226,196,t/.4):226,r*.8,r*.22,0,0,T);c.fill();ball(c,x,y,r,a)}
}
+24
View File
@@ -0,0 +1,24 @@
// strawberry bouncing twice a loop with squash and stretch, its seeds jiggling and its leafy cap flapping behind
const{sin,PI,abs,max,min}=Math,T=2*PI
export const frames=20,delay=60
const RD='#ff6b6b',SD='#ffe066',LF='#69db7c',LD='#40c057',BL='#ffc9c9',SH='#ced4da'
export default(c,t)=>{
const u=(t*2)%1,H=54,h=H*4*u*(1-u),q=max(0,1-min(u,1-u)/.1),s=.1*abs(1-2*u)*(1-q)
const sx=1+.18*q-.5*s,sy=1-.22*q+s,lag=.22*sin(2*T*t*2-1.2)
c.fillStyle=SH;c.beginPath();c.ellipse(128,232,48*(1-.4*h/H)*(1+.2*q),7,0,0,T);c.fill()
c.save();c.translate(128,228-h);c.scale(sx*.88,sy*.88);c.translate(0,-76)
c.fillStyle=RD;c.beginPath();c.moveTo(0,-44);c.bezierCurveTo(46,-62,80,-28,70,8);c.bezierCurveTo(60,44,22,72,0,76);c.bezierCurveTo(-22,72,-60,44,-70,8);c.bezierCurveTo(-80,-28,-46,-62,0,-44);c.closePath();c.fill();c.stroke()
c.lineWidth=2.5;c.fillStyle=SD
;[[-46,-20],[-20,-30],[22,-30],[48,-18],[-56,6],[56,6],[-40,34],[40,34],[-18,52],[18,52],[0,64],[-26,-2],[28,0]].forEach(([x,y],k)=>{const j=1.6*sin(2*T*t*2+k*1.7);c.beginPath();c.ellipse(x+j,y,3,4.5,x/90,0,T);c.fill();c.stroke()})
c.lineWidth=7
for(const[a,L]of[[-1.25,38],[1.25,38],[-.65,42],[.65,42],[0,40]]){
c.save();c.translate(0,-48);c.rotate(PI+a+lag*(a?Math.sign(a):1)*.6);c.fillStyle=a?LF:LD;c.beginPath();c.moveTo(-10,0);c.quadraticCurveTo(-11,L*.6,0,L);c.quadraticCurveTo(11,L*.6,10,0);c.closePath();c.fill();c.stroke();c.restore()
}
for(const[w,st]of[[13,'#111'],[6,LD]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(0,-52),c.quadraticCurveTo(2,-66,10+lag*12,-74),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*22,6,6.5,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*22-2,4,2.4,0,T),c.fill()
c.lineWidth=5;c.beginPath();c.arc(0,14,9,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(d*40,20,9,5,0,0,T),c.fill()
c.restore()
}
+33
View File
@@ -0,0 +1,33 @@
// yellow submarine bobbing underwater, periscope turning, propeller spinning, bubbles rising past swaying seaweed
const{sin,cos,PI,abs,min,max,round}=Math,T=2*PI
export const frames=20,delay=80
const Y='#ffd43b',YD='#fab005',GL='#a5d8ff',PR='#ced4da',SW='#51cf66',SA='#ffd8a8',BU='#d0ebff',BL='#ffa8a8'
export default(c,t)=>{
const b=round(5*sin(t*T)),th=t*T*4,pa=t*T,w=20*cos(pa)
for(const[x,h,o]of[[34,74,0],[222,58,1.7]]){const s=8*sin(t*T+o);for(const[lw,st]of[[16,'#111'],[8,SW]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(x,238),c.quadraticCurveTo(x-12,238-h*.5,x+s,238-h),c.stroke()}
c.strokeStyle='#111';c.lineWidth=7
c.fillStyle=SA;c.beginPath();c.moveTo(8,246);c.quadraticCurveTo(128,206,248,246);c.closePath();c.fill();c.stroke()
c.fillStyle='#fff';c.lineWidth=4
for(let k=0;k<3;k++){const p=(t+k/3)%1,s=3+5*p;c.fillStyle=BU;c.beginPath();c.arc(36+8*sin(p*T*2+k),146+b-p*118,s,0,T);c.fill();c.stroke()}
c.save();c.translate(0,b)
c.lineWidth=7
c.fillStyle=YD;c.beginPath();c.moveTo(56,124);c.lineTo(40,100);c.lineTo(66,104);c.lineTo(84,122);c.closePath();c.fill();c.stroke()
for(const[lw,st]of[[12,'#111'],[5,PR]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(144,100),c.lineTo(144,60),c.stroke()
c.strokeStyle='#111';c.lineWidth=5
c.fillStyle=YD;c.beginPath();c.roundRect(144+min(0,w)-7,50,abs(w)+14,15,7);c.fill();c.stroke()
c.fillStyle=GL
if(abs(w)>5)c.beginPath(),c.ellipse(144+w,57.5,3,6,0,0,T),c.fill(),c.stroke()
else if(sin(pa)>0)c.beginPath(),c.arc(144,57.5,5,0,T),c.fill(),c.stroke()
c.lineWidth=7;c.fillStyle=YD;c.beginPath();c.roundRect(100,96,56,36,10);c.fill();c.stroke()
c.fillStyle=PR;c.lineWidth=5;c.beginPath();c.roundRect(20,140,22,22,6);c.fill();c.stroke()
for(const L of[22*cos(th),-22*cos(th),22*sin(th),-22*sin(th)])if(abs(L)>2)c.beginPath(),c.ellipse(18,151+L/2,5,abs(L/2),0,0,T),c.fill(),c.stroke()
c.lineWidth=7;c.fillStyle=Y;c.beginPath();c.roundRect(36,118,188,66,33);c.fill();c.stroke()
c.save();c.beginPath();c.roundRect(36,118,188,66,33);c.clip();c.fillStyle=YD;c.fillRect(30,168,200,20);c.restore()
c.beginPath();c.roundRect(36,118,188,66,33);c.stroke()
c.lineWidth=5;for(const x of[78,112,146]){c.fillStyle=GL;c.beginPath();c.arc(x,148,11,0,T);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.arc(x-4,144,3,0,T);c.fill()}
c.fillStyle='#111';c.beginPath();c.ellipse(194,142,5,7,0,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(192,139,2.5,0,T);c.fill()
c.fillStyle=BL;c.beginPath();c.ellipse(182,160,8,5,0,0,T);c.fill()
c.lineWidth=4;c.beginPath();c.arc(202,154,7,.2*PI,.8*PI);c.stroke()
c.restore()
}
+30
View File
@@ -0,0 +1,30 @@
// potted sunflower turning its smiley face to follow a little sun swinging across the sky, petals swaying
const{sin,cos,PI,abs}=Math,T=2*PI
export const frames=16,delay=100
const PT='#ff922b',PD='#e8590c',ST='#51cf66',SD='#2f9e44',PE=['#fcc419','#ffd43b'],CE='#a9744f',CD='#7f5539',BL='#ff8787'
const leaf=(c,x,y,a,L)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle=ST;c.lineWidth=6;c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(L*.5,-L*.38,L,0);c.quadraticCurveTo(L*.5,L*.38,0,0);c.fill();c.stroke();c.strokeStyle=SD;c.lineWidth=3;c.beginPath();c.moveTo(4,0);c.lineTo(L*.8,0);c.stroke();c.restore();c.strokeStyle='#111'}
export default(c,t)=>{
const s=sin(t*T),sx=128+94*s,sy=32+14*s*s,hx=128+12*s,hy=104,sq=1-.16*abs(s),w=.08*sin(2*t*T)
c.save();c.translate(sx,sy);c.rotate(t*PI/4)
for(let k=0;k<8;k++){const q=k*PI/4;for(const[lw,col]of[[10,'#111'],[4,'#ffd43b']])c.lineWidth=lw,c.strokeStyle=col,c.beginPath(),c.moveTo(cos(q)*22,sin(q)*22),c.lineTo(cos(q)*28,sin(q)*28),c.stroke()}
c.restore();c.strokeStyle='#111';c.lineWidth=6;c.fillStyle='#ffd43b';c.beginPath();c.arc(sx,sy,16,0,T);c.fill();c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(sx+d*5,sy-1,2.2,0,T),c.fill()
c.lineWidth=2.5;c.beginPath();c.arc(sx,sy+3,4,.15*PI,.85*PI);c.stroke()
for(const[lw,col]of[[20,'#111'],[11,ST]])c.lineWidth=lw,c.strokeStyle=col,c.beginPath(),c.moveTo(128,200),c.quadraticCurveTo(128-6*s,160,hx,hy+20),c.stroke()
c.strokeStyle='#111'
leaf(c,127,176,-.5+w,44);leaf(c,127,160,PI+.45-w,40)
c.lineWidth=7;c.fillStyle=PT;c.beginPath();c.moveTo(92,200);c.lineTo(164,200);c.lineTo(154,244);c.lineTo(102,244);c.closePath();c.fill();c.stroke()
c.fillStyle=PD;c.beginPath();c.roundRect(84,190,88,20,8);c.fill();c.stroke()
c.save();c.translate(hx,hy);c.rotate(.2*s)
c.lineWidth=5
for(const[r,o,col]of[[44,PI/14,PE[0]],[40,0,PE[1]]])for(let k=0;k<14;k++){const q=k*T/14+o+.05*sin(2*t*T+k);c.fillStyle=col;c.beginPath();c.ellipse(cos(q)*r*sq,sin(q)*r,15,6.5,q,0,T);c.fill();c.stroke()}
const fx=9*s
c.lineWidth=7;c.fillStyle=CE;c.beginPath();c.ellipse(0,0,31*sq,31,0,0,T);c.fill();c.stroke()
c.save();c.clip();c.fillStyle=CD;for(let y=-28;y<30;y+=9)for(let x=-28;x<30;x+=9)c.beginPath(),c.arc(x+(y/9&1)*4.5,y,1.8,0,T),c.fill();c.restore()
c.fillStyle='#fff3bf';c.beginPath();c.ellipse(fx,6,20*sq,15,0,0,T);c.fill()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(fx+d*9*sq,1,4,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(fx+d*9*sq-1,-.5,1.5,0,T),c.fill()
c.lineWidth=3.5;c.beginPath();c.arc(fx,7,5,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(fx+d*15*sq,9,4,2.5,0,0,T),c.fill()
c.restore()
}
+28
View File
@@ -0,0 +1,28 @@
// sun sinks into the sea inside a round sticker while the sky warms to dusk, sparkles on the water, then it rises again
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=16,delay=90
const DAY=['#ffa94d','#ffc078','#ffd8a8','#fff3bf'],DUSK=['#7048e8','#9775fa','#f783ac','#ffa94d'],E=[0,60,98,130,156],BL='#ff8787'
const mix=(a,b,k)=>'#'+[1,3,5].map(i=>Math.round(parseInt(a.slice(i,i+2),16)*(1-k)+parseInt(b.slice(i,i+2),16)*k).toString(16).padStart(2,'0')).join('')
const spk=(c,x,y,s)=>{c.lineWidth=3;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 k=(1-cos(t*T))/2,y=100+100*k,H=156,disc=()=>{c.beginPath();c.arc(128,128,112,0,T)}
c.save();disc();c.clip()
DAY.forEach((d,j)=>{c.fillStyle=mix(d,DUSK[j],k);c.fillRect(0,E[j],256,E[j+1]-E[j])})
c.fillStyle=mix('#fff','#fcc2d7',k);c.lineWidth=5
for(const[x,cy,w]of[[70,62,30],[186,86,24]])c.beginPath(),c.ellipse(x,cy,w,7,0,0,T),c.fill(),c.stroke()
c.lineWidth=7;c.fillStyle=mix('#ffd43b','#ff922b',k);c.beginPath();c.arc(128,y,36,0,T);c.fill();c.stroke()
c.fillStyle='#111'
if(k<.5)for(const d of[-1,1])c.beginPath(),c.arc(128+d*13,y-4,5,0,T),c.fill()
else{c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.arc(128+d*13,y-7,6,.15*PI,.85*PI),c.stroke()}
c.lineWidth=4;c.beginPath();c.arc(128,y+6,8,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*24,y+8,6,3.5,0,0,T),c.fill()
c.fillStyle='#4dabf7';c.fillRect(0,H,256,100);c.fillStyle='#339af0';c.fillRect(0,H+46,256,60)
c.lineWidth=7;c.beginPath();c.moveTo(0,H);c.lineTo(256,H);c.stroke()
const L=(1-k)*42+12,sun=mix('#ffe066','#ff922b',k)
for(const[j,dy]of[[0,14],[1,28],[2,42]]){const w=L*(1-j*.25),x=128+4*sin(2*t*T+j*2);for(const[lw,s]of[[11,'#111'],[5,sun]])c.lineWidth=lw,c.strokeStyle=s,c.beginPath(),c.moveTo(x-w,H+dy),c.lineTo(x+w,H+dy),c.stroke()}
c.strokeStyle='#d0ebff';c.lineWidth=4
for(const[x,cy]of[[42,184],[196,180],[70,218],[170,224]])for(const dx of[0,14])c.beginPath(),c.arc(x+dx,cy,7,PI*1.1,PI*1.9),c.stroke()
c.strokeStyle='#111'
for(const[x,cy,o]of[[60,200,0],[198,206,.5]])spk(c,x,cy,3+5*max(0,sin((t+o)*2*T)))
c.restore();c.lineWidth=7;disc();c.stroke()
}
+30
View File
@@ -0,0 +1,30 @@
// salmon nigiri hopping on its wooden board, squishing on landing while a little wasabi buddy watches it go up and down
const{sin,PI,abs}=Math,T=2*PI
export const frames=20,delay=60
const SA='#ffa94d',SS='#fff4e6',WA='#8ce99a',BD='#d4a373',BDD='#a9744f',BL='#ffa8a8'
const salmon=c=>{c.beginPath();c.moveTo(-76,-50);c.bezierCurveTo(-72,-98,58,-106,82,-68);c.quadraticCurveTo(88,-50,72,-46);c.bezierCurveTo(32,-56,-34,-40,-66,-38);c.quadraticCurveTo(-80,-38,-76,-50);c.closePath()}
export default(c,t)=>{
const air=t<.7,h=air?72*sin(PI*t/.7):0,q=air?0:sin(PI*(t-.7)/.3),st=air?.07*abs(sin(2*PI*t/.7)):0
const sx=1+.16*q-st,sy=1-.2*q+st
c.fillStyle=BD;c.beginPath();c.roundRect(16,204,224,24,10);c.fill();c.stroke()
c.fillStyle=BDD;c.beginPath();c.ellipse(112,216,62*sx*(1-h/72*.45),5,0,0,T);c.fill()
c.lineWidth=4;c.strokeStyle=BDD;for(const[x,w]of[[34,26],[176,18]])c.beginPath(),c.moveTo(x,220),c.lineTo(x+w,220),c.stroke()
c.strokeStyle='#111';c.lineWidth=5
c.fillStyle=WA;c.beginPath();c.moveTo(190,207);c.bezierCurveTo(188,184,200,178,210,170);c.quadraticCurveTo(206,162,214,154);c.quadraticCurveTo(220,166,222,174);c.bezierCurveTo(234,184,236,200,234,207);c.closePath();c.fill();c.stroke()
const ly=-2.5*h/72
for(const x of[205,220]){c.fillStyle='#111';c.beginPath();c.arc(x-1.5,192+ly,3.6,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-2.5,190.5+ly,1.3,0,T);c.fill()}
c.lineWidth=3.5;c.beginPath();c.arc(212,198,4,.15*PI,.85*PI);c.stroke()
c.lineWidth=7
c.save();c.translate(112,210-h);c.scale(sx,sy)
c.fillStyle='#fff';c.beginPath();c.roundRect(-62,-66,124,66,[26,26,14,14]);c.fill();c.stroke()
c.lineWidth=3;for(const[x,y,a]of[[-46,-14,0],[46,-12,1],[-30,-6,1.5],[30,-5,2.5]])c.beginPath(),c.arc(x,y,5,a,a+1.6),c.stroke()
c.lineWidth=7
c.fillStyle=SA;salmon(c);c.fill()
c.save();salmon(c);c.clip();c.strokeStyle=SS;c.lineWidth=7;for(let k=0;k<5;k++)c.beginPath(),c.moveTo(-70+k*36,-36),c.quadraticCurveTo(-58+k*36,-70,-42+k*36,-104),c.stroke();c.restore()
salmon(c);c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*20,-30,6,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*20-2,-32,2.2,0,T),c.fill()
c.lineWidth=5;c.beginPath();c.arc(0,-24,8,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(d*38,-18,8,4.5,0,0,T),c.fill()
c.restore()
}
+39
View File
@@ -0,0 +1,39 @@
// toucan perched on a branch, bobbing its huge colorful beak up and down and blinking
// the black body gets a white rim so it doesn't vanish on dark backgrounds
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=80
const BR='#a9744f',LF='#69db7c',BIB='#fff9db',OR='#ff922b',YE='#ffd43b',RD='#ff6b6b',TP='#e8590c',EB='#4dabf7',SK='#ffa94d'
export default(c,t,i)=>{
const a=.1*sin(2*T*t),b=Math.round(2*sin(2*T*t)),shut=i==6||i==7
const D=(wd,cl,f)=>{for(const[l,s]of[[wd,'#111'],[wd-10,cl]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
const dark=f=>{c.beginPath();f();c.lineWidth=16;c.strokeStyle='#fff';c.stroke();c.fillStyle='#111';c.fill();c.lineWidth=7;c.strokeStyle='#111';c.stroke()}
dark(()=>{c.moveTo(70,184);c.lineTo(52,240);c.lineTo(82,240);c.lineTo(94,190);c.closePath()})
D(20,BR,()=>{c.moveTo(16,212);c.quadraticCurveTo(128,204,240,214)})
c.fillStyle=LF;c.beginPath();c.ellipse(222,228,16,8,.5,0,T);c.fill();c.stroke()
c.save();c.translate(0,b)
// body and head rimmed together so there is no white seam between them
const bd=()=>{c.beginPath();c.ellipse(92,150,44,60,-.15,0,T)},hd=()=>{c.save();c.translate(104,120);c.rotate(a);c.beginPath();c.arc(0,-30,34,0,T);c.restore()}
c.lineWidth=16;c.strokeStyle='#fff';bd();c.stroke();hd();c.stroke();c.fillStyle='#111';bd();c.fill();hd();c.fill();c.lineWidth=7;c.strokeStyle='#111'
c.fillStyle=RD;c.beginPath();c.ellipse(96,198,18,10,-.2,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7
c.save();c.translate(104,120);c.rotate(a)
c.fillStyle=BIB;c.beginPath();c.ellipse(8,4,26,30,-.1,0,T);c.fill();c.stroke()
c.fillStyle=YE;c.beginPath();c.ellipse(10,24,18,8,0,0,T);c.fill()
c.fillStyle=SK;c.beginPath();c.ellipse(12,-36,17,15,0,0,T);c.fill();c.lineWidth=5;c.stroke()
c.fillStyle=EB;c.beginPath();c.arc(14,-36,10,0,T);c.fill();c.stroke()
if(shut){c.beginPath();c.arc(14,-38,7,.15*PI,.85*PI);c.stroke()}
else{c.fillStyle='#111';c.beginPath();c.arc(15,-36,5.5,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(13,-38,2,0,T);c.fill()}
c.lineWidth=7
const beak=()=>{c.beginPath();c.moveTo(26,-44);c.bezierCurveTo(80,-66,128,-48,138,-14);c.quadraticCurveTo(136,-2,124,-6);c.quadraticCurveTo(80,-4,28,-8);c.closePath()}
c.fillStyle=OR;beak();c.fill()
c.save();beak();c.clip()
c.fillStyle=YE;c.beginPath();c.moveTo(20,-80);c.quadraticCurveTo(80,-60,140,-40);c.lineTo(140,-80);c.closePath();c.fill()
c.fillStyle=TP;c.beginPath();c.arc(136,-14,22,0,T);c.fill()
c.fillStyle=RD;c.fillRect(20,-60,10,60)
c.restore()
c.lineWidth=5;c.beginPath();c.moveTo(30,-24);c.quadraticCurveTo(80,-26,132,-14);c.stroke();c.lineWidth=7
beak();c.stroke()
c.restore()
c.restore()
c.fillStyle=EB;for(const x of[84,104])c.lineWidth=5,c.beginPath(),c.ellipse(x,210,8,6,0,0,T),c.fill(),c.stroke()
c.lineWidth=7
}
+33
View File
@@ -0,0 +1,33 @@
// traffic light cycling green, yellow, red, the lit lamp has a face that grins, panics, then glares
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=90
const HOUSE='#495057',HD='#343a40',POLE='#adb5bd',L=[['#ff6b6b','#6b3434'],['#ffd43b','#6b5f2c'],['#51cf66','#2c5a37']],DROP='#a5d8ff'
export default(c,t,i)=>{
const k=t<.42?2:t<.58?1:0
c.fillStyle=POLE;c.beginPath();c.roundRect(114,200,28,52,6);c.fill();c.stroke()
const h=()=>{c.beginPath();c.roundRect(70,8,116,206,30)}
c.lineWidth=18;c.strokeStyle='#fff';h();c.stroke();c.fillStyle=HOUSE;h();c.fill();c.lineWidth=7;c.strokeStyle='#111';c.stroke()
for(let j=0;j<3;j++){
const y=46+j*65,on=j==k,[lc,dc]=L[j]
c.fillStyle=HD;c.beginPath();c.arc(128,y,31,0,T);c.fill()
if(on)for(const a of[-.5,0,.5,PI-.5,PI,PI+.5]){for(const[w,st]of[[9,'#111'],[3.5,lc]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(128+cos(a)*34,y+sin(a)*34),c.lineTo(128+cos(a)*42,y+sin(a)*42),c.stroke()}
c.strokeStyle='#111';c.fillStyle=on?lc:dc;c.lineWidth=5;c.beginPath();c.arc(128,y,25,0,T);c.fill();c.stroke()
c.fillStyle=on?'#fff':'#868e96';c.beginPath();c.ellipse(118,y-12,6,3.5,-.6,0,T);c.fill()
if(!on)continue
c.fillStyle='#111';c.lineWidth=3.5
if(j==2){
for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,y-2,5,PI*1.1,PI*1.9),c.stroke()
c.beginPath();c.moveTo(118,y+5);c.quadraticCurveTo(128,y+20,138,y+5);c.closePath();c.fill()
c.fillStyle='#ff8787';c.beginPath();c.ellipse(128,y+12,4,2.5,0,0,T);c.fill()
}else if(j==1){
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,y-3,6,0,T),c.fill(),c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,y-3+(i%2),2.5,0,T),c.fill()
c.beginPath();c.ellipse(128,y+11,3.5,4.5,0,0,T);c.fill()
c.fillStyle=DROP;c.lineWidth=3;const dy=(i%3)*2;c.beginPath();c.moveTo(150,y-16+dy);c.quadraticCurveTo(157,y-6+dy,150,y-4+dy);c.quadraticCurveTo(143,y-6+dy,150,y-16+dy);c.fill();c.stroke()
}else{
c.lineWidth=3.5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*15,y-14),c.lineTo(128+d*5,y-9),c.stroke()
for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,y-3,3.5,0,T),c.fill()
c.beginPath();c.arc(128,y+14,7,PI*1.15,PI*1.85);c.stroke()
}
}
}
+38
View File
@@ -0,0 +1,38 @@
// unicorn prancing in place, front hooves lifting in turn, rainbow mane and tail flowing, a sparkle off the horn
const{sin,cos,PI,abs,max}=Math,T=2*PI
export const frames=16,delay=80
const RB=['#ff6b6b','#ffa94d','#ffd43b','#69db7c','#4dabf7','#b197fc'],HN='#ffd43b',HO='#b197fc',FAR='#e9ecef',PK='#faa2c1'
export default(c,t)=>{
const b=-5*abs(sin(T*t)),l1=max(0,sin(T*t)),l2=max(0,-sin(T*t)),w=sin(T*t)
const D=(wd,col,f)=>{for(const[l,s]of[[wd,'#111'],[wd-12,col]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
const leg=(x,y,l,col)=>{
const kx=x+16*l,ky=y+30-6*l,fx=x+4*l,fy=226-30*l
D(26,col,()=>{c.moveTo(x,y);c.lineTo(kx,ky);c.lineTo(fx,fy)})
c.fillStyle=HO;c.beginPath();c.roundRect(fx-12,fy-4,24,14,5);c.fill();c.stroke()
}
// rainbow bundle: every strand stroked black and wide first, then the colors on top, so it reads as one outlined ribbon
const bundle=(f)=>{
for(let k=0;k<6;k++)c.lineWidth=20,c.beginPath(),f(k),c.stroke()
for(let k=0;k<6;k++)c.lineWidth=8,c.strokeStyle=RB[k],c.beginPath(),f(k),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
}
c.translate(0,b)
leg(98,160,0,FAR);leg(164,160,l2,FAR)
bundle(k=>{const o=k*6-15;c.moveTo(64,138+o*.4);c.bezierCurveTo(30+o,140+o*.3,26+8*w+o*.6,186,46+12*w-o*.3,214+o*.2)})
leg(80,164,0,'#fff');leg(150,164,l1,'#fff')
c.fillStyle='#fff';c.beginPath();c.moveTo(170,68);c.lineTo(176,40);c.lineTo(190,62);c.closePath();c.fill();c.stroke()
const sil=()=>{c.beginPath();c.ellipse(118,146,62,36,0,0,T);c.moveTo(144,152);c.lineTo(160,96);c.lineTo(196,92);c.lineTo(182,150);c.closePath();c.moveTo(232,96);c.ellipse(198,86,34,24,.45,0,T)}
sil();c.lineWidth=14;c.stroke();c.fillStyle='#fff';sil();c.fill();c.lineWidth=7
c.fillStyle=PK;c.beginPath();c.ellipse(222,104,4,3,.4,0,T);c.fill()
c.fillStyle=PK;c.beginPath();c.ellipse(196,100,9,5,.3,0,T);c.fill()
c.lineWidth=5;c.beginPath();c.arc(188,82,8,.15*PI,.85*PI);c.stroke()
c.beginPath();c.moveTo(181,86);c.lineTo(176,90);c.moveTo(184,89);c.lineTo(181,94);c.stroke()
c.beginPath();c.moveTo(212,110);c.quadraticCurveTo(218,114,224,110);c.stroke();c.lineWidth=7
bundle(k=>{const o=k*5-12;c.moveTo(184-o*.4,60+o*.6);c.bezierCurveTo(160-o*.8,64+o*.4,150+5*w-o*.9,96,140+7*w-o,136+o*.3)})
c.save();c.translate(200,66);c.rotate(.5)
c.fillStyle=HN;c.beginPath();c.moveTo(-11,0);c.lineTo(0,-54);c.lineTo(11,0);c.closePath();c.fill();c.stroke()
c.lineWidth=4;for(const y of[-14,-28,-40])c.beginPath(),c.moveTo(-9-y*.2,y+4),c.lineTo(9+y*.2,y-4),c.stroke()
c.restore();c.lineWidth=7
const s=abs(sin(2*T*t+.6)),sx=236,sy=24
if(s>.25){c.fillStyle=HN;c.lineWidth=4;c.beginPath();for(let k=0;k<8;k++){const r=k%2?6*s:20*s,a=k*PI/4-PI/2;c.lineTo(sx+cos(a)*r,sy+sin(a)*r)}c.closePath();c.fill();c.stroke()}
}
+34
View File
@@ -0,0 +1,34 @@
// record player spinning a vinyl whose label has a little face, tonearm riding the grooves, notes floating up
const{sin,cos,PI}=Math,T=2*PI
export const frames=20,delay=80
const BASE='#ffa94d',BD='#e8590c',REC='#343a40',GR='#5c636a',SH='#868e96',LAB='#ff6b6b',ARM='#dee2e6',HEAD='#ffd43b',NOTE=['#4dabf7','#b197fc']
const note=(c,x,y,s,col)=>{
c.save();c.translate(x,y);c.scale(s,s);c.fillStyle=col
for(const[w,st]of[[9,'#111'],[3,col]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(5,4),c.lineTo(5,-24),c.lineTo(17,-18),c.stroke()
c.strokeStyle='#111';c.lineWidth=4;c.beginPath();c.ellipse(0,5,8,6,-.4,0,T);c.fill();c.stroke();c.restore()
}
export default(c,t)=>{
const cx=110,cy=150
c.fillStyle=BASE;c.beginPath();c.roundRect(22,64,212,168,22);c.fill();c.stroke()
c.fillStyle=BD;c.lineWidth=5;c.beginPath();c.roundRect(186,206,30,12,6);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.arc(202,184,10,0,T);c.fill();c.stroke();c.beginPath();c.moveTo(202,184);c.lineTo(202,176);c.stroke()
c.lineWidth=7;c.fillStyle=REC;c.beginPath();c.arc(cx,cy,76,0,T);c.fill();c.stroke()
c.strokeStyle=GR;c.lineWidth=2;for(const r of[66,58,50,42])c.beginPath(),c.arc(cx,cy,r,0,T),c.stroke()
c.strokeStyle=SH;c.lineWidth=5;for(const[r,a0]of[[62,-2.5],[54,.65]])c.beginPath(),c.arc(cx,cy,r,a0,a0+.5),c.stroke()
c.strokeStyle='#111'
c.save();c.translate(cx,cy);c.rotate(T*t)
c.fillStyle=LAB;c.lineWidth=5;c.beginPath();c.arc(0,0,28,0,T);c.fill();c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*10,-9,3.5,0,T),c.fill()
c.lineWidth=3.5;c.beginPath();c.arc(0,8,7,.15*PI,.85*PI);c.stroke()
c.fillStyle='#ffc9c9';for(const d of[-1,1])c.beginPath(),c.ellipse(d*17,2,4,2.5,0,0,T),c.fill()
c.restore()
c.fillStyle=ARM;c.lineWidth=3;c.beginPath();c.arc(cx,cy,4,0,T);c.fill();c.stroke()
const hx=166,hy=196
for(const[w,st]of[[13,'#111'],[6,ARM]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(206,92),c.lineTo(208,160),c.lineTo(hx,hy),c.stroke()
c.strokeStyle='#111';c.lineWidth=5
c.save();c.translate(hx,hy);c.rotate(.75);c.fillStyle=HEAD;c.beginPath();c.roundRect(-9,-6,22,14,4);c.fill();c.stroke();c.restore()
c.fillStyle=ARM;c.beginPath();c.roundRect(196,64,20,16,4);c.fill();c.stroke()
c.lineWidth=7;c.beginPath();c.arc(206,92,14,0,T);c.fill();c.stroke()
c.fillStyle='#868e96';c.beginPath();c.arc(206,92,5,0,T);c.fill()
for(let k=0;k<2;k++){const p=(t+k/2)%1,s=p<.2?p*5:p<.8?1:(1-p)*5;note(c,108+(k?10:-12)+6*sin(p*T),112-p*94,s,NOTE[k])}
}
+45
View File
@@ -0,0 +1,45 @@
// watering can with a face tips forward and sprinkles a droopy sprout in a pot, which perks up, then gets thirsty again
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=24,delay=80
const CAN='#4dabf7',CD='#1c7ed6',POT='#ff922b',PD='#e8590c',SOIL='#a9744f',LF='#69db7c',LD='#2f9e44',DROP='#a5d8ff',BL='#faa2c1'
const sm=x=>x<0?0:x>1?1:x*x*(3-2*x)
const sparkle=(c,x,y,s)=>{c.fillStyle='#fff9db';c.lineWidth=3.5;c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
export default(c,t)=>{
const tip=sm((t-.04)/.14)-sm((t-.6)/.14),a=.5*tip,perk=sm((t-.34)/.2)-sm((t-.8)/.18)
const PX=74,PY=112,rose=[PX+86*cos(a)+34*sin(a),PY+86*sin(a)-34*cos(a)]
c.fillStyle=SOIL;c.beginPath();c.ellipse(190,190,38,7,0,0,T);c.fill();c.stroke()
const tx=214-24*perk,ty=170-34*perk,qx=204-14*perk,qy=176-24*perk
for(const[w,st]of[[11,'#111'],[5,LD]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(190,190),c.quadraticCurveTo(qx,qy,tx,ty),c.stroke()
c.strokeStyle='#111'
for(const[up,dn]of[[3.78,2.2],[-.6,.9]]){
const th=dn+(up-dn)*perk,x=tx+15*cos(th),y=ty+15*sin(th)
c.fillStyle=LF;c.lineWidth=5;c.beginPath();c.ellipse(x,y,15,8,th,0,T);c.fill();c.stroke()
c.lineWidth=2.5;c.beginPath();c.moveTo(tx,ty);c.lineTo(x+8*cos(th),y+8*sin(th));c.stroke()
}
if(perk>.7){const s=10*(perk-.7)/.3;sparkle(c,166,132,s);sparkle(c,226,142,s*.7)}
c.lineWidth=7;c.fillStyle=POT;c.beginPath();c.moveTo(154,204);c.lineTo(226,204);c.lineTo(216,246);c.lineTo(164,246);c.closePath();c.fill();c.stroke()
c.fillStyle=PD;c.beginPath();c.roundRect(146,188,88,18,6);c.fill();c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(190+d*13,222,4,0,T),c.fill()
c.lineWidth=3.5;c.beginPath();if(perk>.5)c.arc(190,227,6,.15*PI,.85*PI);else c.moveTo(184,232),c.quadraticCurveTo(190,227,196,232);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(190+d*24,230,5,3,0,0,T),c.fill()
if(t>.18&&t<.74)for(let j=0;j<12;j++){
const s0=.18+j*.04,u=(t-s0)/.12;if(u<0||u>1||s0>.62)continue
const off=((j*5)%3-1)*6,x=rose[0]+off+22*u,y=rose[1]+4+(188-rose[1]-4)*u
c.fillStyle=DROP;c.lineWidth=3;c.beginPath();c.moveTo(x,y-7);c.quadraticCurveTo(x+5,y,x,y+3);c.quadraticCurveTo(x-5,y,x,y-7);c.fill();c.stroke()
}
c.save();c.translate(PX,PY);c.rotate(a);c.lineWidth=7
for(const[w,st]of[[15,'#111'],[7,CD]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.arc(-18,-30,22,PI*1.05,PI*1.95),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
c.fillStyle=CAN;c.beginPath();c.moveTo(30,4);c.lineTo(80,-30);c.lineTo(88,-20);c.lineTo(34,24);c.closePath();c.fill();c.stroke()
c.save();c.translate(86,-32);c.rotate(-.6);c.fillStyle=CD;c.beginPath();c.roundRect(-6,-14,14,28,5);c.fill();c.stroke()
c.fillStyle='#e7f5ff';for(const y of[-6,0,6])c.beginPath(),c.arc(8,y,1.8,0,T),c.fill();c.restore()
c.fillStyle=CAN;c.beginPath();c.roundRect(-50,-32,86,72,16);c.fill();c.stroke()
c.fillStyle=CD;c.beginPath();c.roundRect(-50,-32,86,14,[16,16,0,0]);c.fill();c.lineWidth=5;c.stroke()
const happy=tip>.5
c.fillStyle='#111';c.lineWidth=4
if(happy)for(const d of[-1,1])c.beginPath(),c.arc(-7+d*14,4,5,PI*1.1,PI*1.9),c.stroke()
else for(const d of[-1,1])c.beginPath(),c.arc(-7+d*14,2,4.5,0,T),c.fill()
c.beginPath();c.arc(-7,12,7,.15*PI,.85*PI);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(-7+d*26,16,5,3,0,0,T),c.fill()
c.restore()
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Some files were not shown because too many files have changed in this diff Show More