mirror of
https://github.com/direct-img/vibegif.direct-img.link.git
synced 2026-10-29 11:44:51 +00:00
Double the anime and manga sections to 100 each
Anime, 50 more colored girls: daily moments (yawn, hiccup, toothbrush, hair dryer, blanket burrito, pocky), hobbies (baker, barista, DJ, piano, violin, polaroid, knitting, origami, pottery), sports (swimming, tennis, badminton, skating, ballet, jump rope, hula hoop, bicycle, campfire, surfing), holidays (halloween, christmas, valentine, mochi, birthday, easter, tsukimi, suikawari, taiko, teru teru bozu) and fantasy (fairy, bunny hood, slime, cyber, magical girl, alien, jiangshi, tanuki, yuki-onna, little devil). Manga, 50 more black-and-white girls: reactions (doki, stare, ufufu, denial, fired up, panic, sigh, confused, shiver, poka poka), sports and martial arts (boxing, fencing, volleyball, sprint, gymnast, baseball, naginata, nunchaku, parkour, motorbike), school (library, flute, calligraphy, science, hanko, transfer student, tug of war, sweeping, note passing, yakisoba bread), home and town (claw machine, cooking, ironing, bus stop, matcha, sewing, boxes, stray cat, foggy window, piggy bank) and yokai and fantasy (tengu, kappa, rokurokubi, temari, alchemist, mummy, wolf girl, medusa, genie, bard). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
// lilac-skinned alien girl with bobbling antennae waving hello, a soft UFO plushie tucked under her arm, stars twinkling
|
||||
const{sin,cos,PI,round,max}=Math,T=2*PI
|
||||
export const frames=12,delay=80
|
||||
const SKIN='#e5dbff',SK2='#d0bfff',HAIR='#22b8cf',HD='#0c8599',HL='#c5f6fa',EYE='#ff6b6b',ED='#c92a2a',SUIT='#e9ecef',SD='#adb5bd',GO='#ffd43b',UF='#91a7ff',UD='#5c7cfa',DOME='#faa2c1',BL='#f783ac'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,15,18,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,12.5,16,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,14,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,12.5,16,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,6,8,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2.5,0,T);c.fill();c.beginPath();c.arc(x+6,y-3,1.5,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const spark=(c,x,y,r)=>{const q=r*.2;c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x+q,y-q,x+r,y);c.quadraticCurveTo(x+q,y+q,x,y+r);c.quadraticCurveTo(x-q,y+q,x-r,y);c.quadraticCurveTo(x-q,y-q,x,y-r);c.fill();c.stroke()}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
export default(c,t)=>{
|
||||
const wv=sin(2*T*t),bob=sin(T*t)
|
||||
for(const[x,y,o]of[[54,40,0],[202,40,.5]]){const r=round(9*max(0,sin(T*(t+o))));if(r>2){c.fillStyle=GO;c.lineWidth=3;spark(c,x,y,r)}}
|
||||
c.save();c.translate(128,262);c.scale(.82,.82);c.translate(-128,-262)
|
||||
for(const d of[-1,1]){const a=d*.32+.16*sin(T*t*2+d)+.06*bob,L=70,bx=128+d*24,by=50,ex=bx+L*sin(a),ey=by-L*cos(a)
|
||||
line(c,[[bx,by],[(bx+ex)/2+d*8,(by+ey)/2],[ex,ey]],4,SK2)
|
||||
c.fillStyle=d<0?GO:DOME;c.lineWidth=6;c.beginPath();c.arc(ex,ey,13,0,T);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.arc(ex-4,ey-4,4,0,T);c.fill()}
|
||||
c.lineWidth=6;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(198,36,212,98,202,150);c.quadraticCurveTo(218,170,214,180);c.quadraticCurveTo(196,176,184,170);c.lineTo(72,170);c.quadraticCurveTo(60,176,42,180);c.quadraticCurveTo(38,170,54,150);c.bezierCurveTo(44,98,58,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=SUIT;c.beginPath();c.moveTo(98,190);c.quadraticCurveTo(56,198,46,262);c.lineTo(210,262);c.quadraticCurveTo(200,198,158,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=UF;c.beginPath();c.ellipse(128,192,36,12,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=GO;c.lineWidth=5;c.beginPath();for(let k=0;k<10;k++){const r=k%2?6:14,a=k*PI/5-PI/2;c.lineTo(150+cos(a)*r,226+sin(a)*r)}c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=SD;c.lineWidth=4;c.beginPath();c.moveTo(70,226);c.quadraticCurveTo(80,240,76,262);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=SKIN;c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1);eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,163,12,6,0,0,T),c.fill()
|
||||
c.lineWidth=4;c.fillStyle='#c92a2a';c.beginPath();c.moveTo(116,164);c.quadraticCurveTo(128,182,140,164);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(60,150)
|
||||
c.bezierCurveTo(46,74,88,36,128,36);c.bezierCurveTo(168,36,210,74,196,150)
|
||||
c.quadraticCurveTo(192,120,182,106);c.quadraticCurveTo(172,96,166,108);c.quadraticCurveTo(156,90,146,92);c.quadraticCurveTo(140,106,130,110)
|
||||
c.quadraticCurveTo(124,94,112,90);c.quadraticCurveTo(108,106,98,110);c.quadraticCurveTo(92,96,84,98);c.quadraticCurveTo(80,110,76,114)
|
||||
c.quadraticCurveTo(66,122,60,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.save();c.translate(76,236);c.scale(.8,.8);c.rotate(-.12+.04*bob)
|
||||
c.fillStyle=DOME;c.lineWidth=5;c.beginPath();c.ellipse(0,-14,24,22,0,PI,T);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(-9,-24,6,4,-.5,0,T);c.fill()
|
||||
c.fillStyle=UF;c.beginPath();c.ellipse(0,-6,52,18,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=UD;c.beginPath();c.ellipse(0,4,32,8,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*13,-8,3.5,0,T),c.fill()
|
||||
c.lineWidth=3;c.beginPath();c.arc(0,-7,5,.15*PI,.85*PI);c.stroke()
|
||||
c.fillStyle=GO;c.lineWidth=3;for(const x of[-38,38])c.beginPath(),c.arc(x,-6,5,0,T),c.fill(),c.stroke()
|
||||
c.restore()
|
||||
c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(100,224,11,9,-.4,0,T);c.fill();c.stroke()
|
||||
const sx=176,sy=214,ea=-.7,ex=sx+40*cos(ea),ey=sy+40*sin(ea),fa=-1.5+.45*wv,hx=ex+36*cos(fa),hy=ey+36*sin(fa)
|
||||
line(c,[[sx,sy],[ex,ey],[hx,hy]],11,SUIT)
|
||||
c.save();c.translate(hx,hy);c.rotate(fa+PI/2);c.fillStyle=SKIN;c.lineWidth=5
|
||||
c.beginPath();c.moveTo(-11,4);c.quadraticCurveTo(-14,-22,-6,-26);c.quadraticCurveTo(0,-30,6,-26);c.quadraticCurveTo(14,-22,11,4);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const k of[-4,2])c.moveTo(k,-25),c.lineTo(k+.5,-15);c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.ellipse(-13,-6,4.5,8,-.7,0,T);c.fill();c.stroke();c.restore()
|
||||
c.lineWidth=4;for(const s of[1,-1])c.beginPath(),c.arc(hx,hy,26,fa+s*.6-.2,fa+s*.6+.2),c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// girl with a scarlet bob and sweatband leaps and smashes a falling shuttlecock, which rockets off with speed lines
|
||||
const{sin,cos,PI,max,min,hypot,sqrt,atan2}=Math,T=2*PI
|
||||
export const frames=16,delay=70
|
||||
const SKIN='#ffe3d1',HAIR='#f03e3e',HD='#c92a2a',HL='#ffc9c9',EYE='#fab005',ED='#e67700',JER='#9775fa',JD='#7048e8',SH='#364fc7',BAND='#fff',RQ='#fcc419',BL='#ffa8a8'
|
||||
const eye=(c,x,y,d,lx,ly)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip();const X=x+lx,Y=y+ly
|
||||
c.beginPath();c.ellipse(X,Y+2,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(X,Y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(X,Y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(X+1,Y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(X-3,Y-5,4.5,0,T);c.fill();c.beginPath();c.arc(X+6,Y+8,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const ik=(hx,hy,fx,fy,L,s=1)=>{const dx=fx-hx,dy=fy-hy,d=hypot(dx,dy),k=sqrt(max(0,L*L-d*d/4));return[hx+dx/2+s*dy/d*k,hy+dy/2-s*dx/d*k]}
|
||||
const ss=u=>u*u*(3-2*u)
|
||||
const key=(K,t)=>{let k=0;while(k<K.length-2&&t>=K[k+1][0])k++;const[t0,...a]=K[k],[t1,...b]=K[k+1],u=ss(min(1,max(0,(t-t0)/(t1-t0))));return a.map((v,j)=>v+(b[j]-v)*u)}
|
||||
const RK=[[0,24,22,-.9],[.14,24,22,-.9],[.36,-8,-26,1.9],[.44,-4,-36,3.6],[.48,30,-34,5.35],[.6,30,24,7.1],[.82,24,22,5.38],[1,24,22,5.38]]
|
||||
const cock=(c,x,y,a,s=1)=>{c.save();c.translate(x,y);c.rotate(a);c.scale(s,s);c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(-4,-6);c.lineTo(-24,-13);c.quadraticCurveTo(-28,0,-24,13);c.lineTo(-4,6);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const k of[-7,0,7])c.beginPath(),c.moveTo(-5,k*.5),c.lineTo(-25,k*1.4),c.stroke();c.lineWidth=3;c.beginPath();c.moveTo(-15,-9);c.quadraticCurveTo(-17,0,-15,9);c.stroke()
|
||||
c.fillStyle='#ff6b6b';c.lineWidth=4;c.beginPath();c.arc(0,0,7,-PI/2,PI/2);c.closePath();c.fill();c.stroke();c.restore()}
|
||||
export default(c,t,i)=>{
|
||||
const h=t>.28&&t<.7?32*sin(PI*(t-.28)/.42):0,cr=t>.16&&t<.28?7*sin(PI*(t-.16)/.12):t>.7&&t<.84?8*sin(PI*(t-.7)/.14):0,oy=cr-h
|
||||
const sh=[112,138+oy],[rdx,rdy,ra]=key(RK,t),hd=[sh[0]+rdx,sh[1]+rdy],air=h>6
|
||||
const up=t>.18&&t<.5,hit=t>.46&&t<.56
|
||||
c.translate(128,244);c.scale(.9,.9);c.translate(-128,-244)
|
||||
c.fillStyle='#dee2e6';c.beginPath();c.ellipse(108,234,40*(1-h/90),6*(1-h/90),0,0,T);c.fill()
|
||||
const oa=up?[sh[0]+38,sh[1]-6]:[sh[0]+18,sh[1]+24]
|
||||
line(c,[[sh[0]-4,sh[1]+2],[(sh[0]+oa[0])/2+4,(sh[1]+oa[1])/2+6],oa],7,SKIN)
|
||||
c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.arc(...oa,6,0,T);c.fill();c.stroke()
|
||||
const hip=[104,180+oy]
|
||||
for(const[d,s]of[[-1,1],[1,-1]]){const f=air?[hip[0]+(d<0?-22:10),hip[1]+(d<0?30:38)]:[hip[0]+d*14,224],k=ik(...hip,f[0],f[1]-4,27,d<0?-1:1);line(c,[hip,k,[f[0],f[1]-4]],8,SKIN)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(f[0]+4,f[1],12,7,air&&d<0?.5:0,0,T);c.fill();c.stroke();c.strokeStyle='#ff6b6b';c.lineWidth=3;c.beginPath();c.moveTo(f[0]-2,f[1]-2);c.lineTo(f[0]+10,f[1]-2);c.stroke();c.strokeStyle='#111'}
|
||||
c.fillStyle=SH;c.lineWidth=5;c.beginPath();c.moveTo(90,168+oy);c.lineTo(120,168+oy);c.lineTo(124,190+oy);c.lineTo(86,190+oy);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(106,124+oy,12,10);c.fill()
|
||||
c.fillStyle=JER;c.lineWidth=5;c.beginPath();c.moveTo(98,130+oy);c.lineTo(124,130+oy);c.lineTo(132,142+oy);c.lineTo(126,172+oy);c.lineTo(88,172+oy);c.lineTo(90,140+oy);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(92,156+oy);c.lineTo(128,152+oy);c.stroke();c.strokeStyle='#111'
|
||||
const bk=t>.2&&t<.47,arm=()=>{
|
||||
c.save();c.translate(...hd);c.rotate(ra)
|
||||
line(c,[[0,0],[40,0]],3,'#e9ecef');c.fillStyle='#fff';c.beginPath();c.ellipse(58,0,18,13,0,0,T);c.fill();c.save();c.clip();c.strokeStyle='#ced4da';c.lineWidth=1.5;for(let k=-18;k<=18;k+=5)c.beginPath(),c.moveTo(40,k),c.lineTo(76,k),c.moveTo(58+k,-14),c.lineTo(58+k,14),c.stroke();c.restore()
|
||||
c.strokeStyle='#111';c.lineWidth=9;c.beginPath();c.ellipse(58,0,18,13,0,0,T);c.stroke();c.strokeStyle=RQ;c.lineWidth=4;c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=JD;c.lineWidth=4;c.beginPath();c.roundRect(-8,-4,16,8,3);c.fill();c.stroke();c.restore()
|
||||
line(c,[sh,[(sh[0]+hd[0])/2+(rdy<0?-10:8),(sh[1]+hd[1])/2+(rdy<0?2:6)],hd],8,SKIN)
|
||||
c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.arc(...hd,6.5,0,T);c.fill();c.stroke()
|
||||
}
|
||||
if(bk)arm()
|
||||
const head=g=>{c.save();c.translate(114,92+oy);c.rotate(up?-.12:0);c.scale(.56,.56);c.translate(-128,-120);g();c.restore()}
|
||||
head(()=>{
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(126,34);c.bezierCurveTo(196,34,210,96,196,170);c.quadraticCurveTo(178,178,166,166);c.lineTo(70,166);c.quadraticCurveTo(54,178,46,160);c.bezierCurveTo(40,96,56,34,126,34);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(78,96);c.bezierCurveTo(74,152,104,184,132,186);c.bezierCurveTo(160,184,190,152,186,96);c.bezierCurveTo(186,44,78,44,78,96);c.fill();c.stroke()
|
||||
if(hit){c.lineWidth=8;for(const[x,d]of[[108,1],[158,-1]])c.beginPath(),c.moveTo(x-12*d,128),c.lineTo(x+10*d,140),c.lineTo(x-12*d,152),c.stroke()}else eye(c,108,140,-1,4,up?-5:0),eye(c,158,140,1,4,up?-5:0)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(134+d*42,162,12,6,0,0,T),c.fill()
|
||||
c.fillStyle='#c92a2a';c.lineWidth=6;c.beginPath()
|
||||
if(hit||air)c.moveTo(120,164),c.quadraticCurveTo(138,190,152,164),c.closePath(),c.fill();else c.moveTo(126,168),c.quadraticCurveTo(136,174,146,166)
|
||||
c.stroke()
|
||||
c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.moveTo(70,150);c.bezierCurveTo(56,74,92,40,132,40);c.bezierCurveTo(176,40,206,78,192,150)
|
||||
c.lineTo(184,112);c.lineTo(174,118);c.lineTo(166,104);c.lineTo(156,116);c.lineTo(146,100);c.lineTo(136,116);c.lineTo(124,100);c.lineTo(114,116);c.lineTo(102,102);c.lineTo(92,118);c.lineTo(80,112);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=8;for(const[a0,a1]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(132,104,56,PI*a0,PI*a1),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=BAND;c.lineWidth=8;c.beginPath();c.moveTo(64,80);c.quadraticCurveTo(132,50,200,80);c.lineTo(200,96);c.quadraticCurveTo(132,66,64,96);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#ff6b6b';c.lineWidth=5;c.beginPath();c.moveTo(70,86);c.quadraticCurveTo(132,60,194,86);c.stroke();c.strokeStyle='#111'
|
||||
for(const k of[0,1]){c.save();c.translate(62,90);c.rotate(2.4+k*.5+.25*sin(T*t+k));c.fillStyle=BAND;c.lineWidth=7;c.beginPath();c.moveTo(0,-7);c.quadraticCurveTo(24,-10,40,0);c.quadraticCurveTo(24,10,0,7);c.closePath();c.fill();c.stroke();c.restore()}
|
||||
})
|
||||
if(!bk)arm()
|
||||
const C=[176,28]
|
||||
if(t<.47||t>=.84){const u=((t+.16)%1)/.63,x=226+(C[0]-226)*u+6*sin(u*PI*3),y=4+(C[1]-4)*u;cock(c,x,y,PI/2+.25*sin(u*PI*3),t>=.84?min(1,(t-.84)/.06):1)}
|
||||
else if(t<.66){const u=(t-.47)/.19,x=C[0]+u*130,y=C[1]+u*230,a=atan2(230,130)
|
||||
for(const[k,l]of[[-9,40],[0,64],[9,40]])line(c,[[x-l*cos(a)+k*sin(a),y-l*sin(a)-k*cos(a)],[x-16*cos(a)+k*sin(a),y-16*sin(a)-k*cos(a)]],3,'#fff')
|
||||
cock(c,x,y,a)}
|
||||
if(hit){c.fillStyle='#fff3bf';c.lineWidth=4;c.beginPath();for(let k=0;k<16;k++){const r=k%2?9:22,b=k*PI/8+.2;c.lineTo(C[0]+4+r*cos(b),C[1]+4+r*sin(b))}c.closePath();c.fill();c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// baker girl in a polka-dot kerchief kneading a ball of dough on a floured board, pushing it flat and rolling it back, flour puffing up
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#e0a458',HD='#b8782e',HL='#f7dcae',EYE='#82c91e',ED='#5c940d',BL='#ffa8a8',KER='#ff6b6b',APR='#a5d8ff',WOOD='#e0a96d',WD='#b5803f',DOUGH='#ffe8cc',DS='#f5d0a0'
|
||||
const ds=(c,a,b,s,f)=>{for(const[l,k]of[[a,'#111'],[b,s]])c.lineWidth=l,c.strokeStyle=k,c.beginPath(),f(),c.stroke();c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d,ly)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x,y+2+ly,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8+ly,13,10,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3+ly,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5+ly,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8+ly,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const puff=(c,x,y,r)=>{c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();for(const[a,b,q]of[[-.7,.2,.7],[0,-.4,.9],[.7,.1,.75]])c.moveTo(x+a*r+q*r,y+b*r),c.arc(x+a*r,y+b*r,q*r,0,T);c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const k=(1-cos(2*T*t))/2,u=(2*t)%1
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(102,150);c.quadraticCurveTo(62,160,52,240);c.lineTo(204,240);c.quadraticCurveTo(194,160,154,150);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=APR;c.lineWidth=5;c.beginPath();c.moveTo(106,172);c.lineTo(150,172);c.lineTo(156,240);c.lineTo(100,240);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(108,172);c.lineTo(114,154);c.moveTo(148,172);c.lineTo(142,154);c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(118,138);c.lineTo(118,156);c.quadraticCurveTo(128,164,138,156);c.lineTo(138,138);c.fill();c.beginPath();c.moveTo(118,138);c.lineTo(118,154);c.moveTo(138,138);c.lineTo(138,154);c.stroke()
|
||||
c.save();c.translate(128,100);c.scale(.72,.72);c.translate(-128,-120)
|
||||
c.lineWidth=7;c.fillStyle=HD;c.beginPath();c.moveTo(128,38);c.bezierCurveTo(198,38,212,110,202,196);c.quadraticCurveTo(190,212,176,202);c.lineTo(80,202);c.quadraticCurveTo(66,212,54,196);c.bezierCurveTo(44,110,58,38,128,38);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1,4);eye(c,153,140,1,4)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,163,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const x of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+x-2,166),c.lineTo(128+d*44+x+2,160),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#c92a2a';c.beginPath();c.moveTo(118,166);c.quadraticCurveTo(128,180,138,166);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#ff8fab';c.lineWidth=3;c.beginPath();c.ellipse(134,170,4,3,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(68,150)
|
||||
c.bezierCurveTo(54,90,80,60,128,60);c.bezierCurveTo(176,60,202,90,188,150)
|
||||
c.quadraticCurveTo(186,122,178,106);c.quadraticCurveTo(170,110,162,116);c.quadraticCurveTo(160,100,148,92)
|
||||
c.quadraticCurveTo(138,108,124,112);c.quadraticCurveTo(126,100,120,92);c.quadraticCurveTo(108,106,92,112)
|
||||
c.quadraticCurveTo(92,102,86,98);c.quadraticCurveTo(70,118,68,150);c.closePath();c.fill();c.stroke()
|
||||
for(const d of[-1,1]){c.beginPath();c.moveTo(128+d*58,108);c.quadraticCurveTo(128+d*72,160,128+d*62,200);c.quadraticCurveTo(128+d*52,196,128+d*50,176);c.quadraticCurveTo(128+d*52,140,128+d*46,112);c.closePath();c.fill();c.stroke()}
|
||||
c.strokeStyle=HL;c.lineWidth=5;c.beginPath();c.arc(128,110,52,PI*1.3,PI*1.42);c.stroke();c.beginPath();c.arc(128,110,52,PI*1.58,PI*1.7);c.stroke();c.strokeStyle='#111'
|
||||
const ker=()=>{c.moveTo(56,96);c.bezierCurveTo(50,22,206,22,200,96);c.quadraticCurveTo(128,70,56,96);c.closePath()}
|
||||
c.lineWidth=7;c.fillStyle=KER;c.beginPath();ker();c.fill();c.save();c.clip();c.fillStyle='#fff';for(const[x,y]of[[84,58],[112,44],[146,46],[176,60],[98,78],[130,66],[162,80],[70,82],[190,84]])c.beginPath(),c.arc(x,y,6,0,T),c.fill();c.restore();c.beginPath();ker();c.stroke()
|
||||
c.fillStyle=KER;for(const a of[-.5,.7])c.beginPath(),c.ellipse(204+cos(a)*12,92+sin(a)*12,13,8,a,0,T),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(202,94,7,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
c.lineWidth=6;c.fillStyle=WOOD;c.beginPath();c.roundRect(12,212,232,36,12);c.fill();c.stroke()
|
||||
c.fillStyle=WD;c.beginPath();c.roundRect(12,236,232,12,6);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';for(const[x,y]of[[40,222],[52,228],[66,220],[192,224],[206,219],[218,228],[180,230]])c.beginPath(),c.arc(x,y,2.5,0,T),c.fill()
|
||||
const rx=36+18*k,ry=22-8*k,cy=230-ry
|
||||
c.fillStyle=DOUGH;c.lineWidth=6;c.beginPath();c.ellipse(128,cy,rx,ry,0,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle=DS;c.lineWidth=4;c.beginPath();c.ellipse(128,cy+ry*.2,rx*.6,ry*.45,0,.15*PI,.85*PI);c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(128-rx*.35,cy-ry*.25);c.quadraticCurveTo(128,cy-ry*(.5-.4*k),128+rx*.35,cy-ry*.25);c.stroke()
|
||||
if(u>.25&&u<.8){const p=(u-.25)/.55,r=11*sin(PI*p);for(const d of[-1,1])puff(c,128+d*(rx+10+26*p),cy-4-26*p,r)}
|
||||
for(const d of[-1,1]){
|
||||
const hx=128+d*(16+14*k),hy=cy-ry+6+4*k
|
||||
ds(c,20,11,'#fff',()=>{c.moveTo(128+d*30,166);c.lineTo(128+d*54,190)})
|
||||
ds(c,18,9,SKIN,()=>{c.moveTo(128+d*54,190);c.lineTo(hx+d*6,hy+2)})
|
||||
c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(hx,hy,13,10,d*.3,0,T);c.fill();c.stroke()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// ballerina with a coral bun rises on pointe and pirouettes twice, spotting her head front, tutu bouncing, then lands in plié
|
||||
const{sin,cos,PI,max,min,abs,round}=Math,T=2*PI
|
||||
export const frames=20,delay=75
|
||||
const SKIN='#ffe3d1',HAIR='#ff7f6e',HD='#f0564a',HL='#ffd6cf',EYE='#40c057',ED='#2b8a3e',LEO='#b197fc',LD='#7950f2',TU='#e5dbff',TD='#b197fc',SHOE='#faa2c1',FL='#ffd43b',BL='#ffa8a8',WOOD='#e0a96d',WD='#b07d4f',SPOT='#fff3bf'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const ss=u=>u*u*(3-2*u),cl=u=>min(1,max(0,u))
|
||||
export default(c,t,i)=>{
|
||||
const up=ss(cl((t-.18)/.08))*(1-ss(cl((t-.78)/.07))),pl=t<.18?4*sin(PI*t/.18):t>.85?4*sin(PI*(t-.85)/.15):0
|
||||
const q=4*PI*ss(cl((t-.26)/.52)),cq=cos(q),sq=sin(q),m=q%T,hq=m<4.4?0:(m-4.4)/(T-4.4)*T,ch=cos(hq),sh=sin(hq)
|
||||
const oy=pl-9*up,bounce=2.5*sin(T*t*5)*up
|
||||
c.fillStyle=WOOD;c.lineWidth=6;c.beginPath();c.ellipse(128,226,94,20,0,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle=WD;c.lineWidth=3;for(const x of[78,112,146,180])c.beginPath(),c.moveTo(x,209+abs(x-128)*.06),c.lineTo(x-6,243-abs(x-128)*.06),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=SPOT;c.beginPath();c.ellipse(128,226,50,10,0,0,T);c.fill()
|
||||
c.lineWidth=3.5;for(const[x,y,p]of[[50,64,0],[206,84,.4],[40,150,.7],[218,170,.2]]){const s=5+4*sin(T*(t+p)*2);if(s>2){c.fillStyle=FL;c.beginPath();for(let k=0;k<8;k++){const r=k%2?s*.4:s,b=k*PI/4;c.lineTo(x+r*cos(b),y+r*sin(b))}c.closePath();c.fill();c.stroke()}}
|
||||
const pk=up*(1)
|
||||
const lx=128+3*cq
|
||||
line(c,[[lx-2,184+oy],[lx-1,206+oy+pl*.5],[lx,218+oy*.15-2]],8,SKIN)
|
||||
if(pk>.3){const kx=128+20*cq,ky=192+oy;line(c,[[134,184+oy],[kx,ky],[128+2*cq,206+oy]],8,SKIN);c.fillStyle=SHOE;c.lineWidth=4;c.beginPath();c.ellipse(128+3*cq,208+oy,6,8,0,0,T);c.fill();c.stroke()}
|
||||
else line(c,[[136,184+oy],[140,206+oy],[142,220]],8,SKIN)
|
||||
const shoe=(x,y,pt)=>{c.fillStyle=SHOE;c.lineWidth=4;c.beginPath();pt?c.ellipse(x,y,6,9,0,0,T):c.ellipse(x+3,y,11,6,0,0,T);c.fill();c.stroke();c.strokeStyle='#f06595';c.lineWidth=2.5;c.beginPath();c.moveTo(x-5,y-12);c.lineTo(x+5,y-6);c.moveTo(x+5,y-12);c.lineTo(x-5,y-6);c.stroke();c.strokeStyle='#111'}
|
||||
shoe(lx,222+oy*.15-(up>.5?6:0),up>.5);if(pk<=.3)shoe(142,222,0)
|
||||
const tut=(back)=>{const ry=17+2*bounce,rx=60,y=172+oy;c.save();c.beginPath();back?c.rect(0,0,256,y):c.rect(0,y,256,256);c.clip()
|
||||
for(const[r,dy,col]of[[1,5,'#fff'],[.94,0,TU]]){c.fillStyle=col;c.lineWidth=4;c.beginPath();for(let k=0;k<=32;k++){const a=k*T/32+q*.15,rr=(k%2?.95:1)*r;c.lineTo(128+rx*rr*cos(a),y+dy+ry*rr*sin(a))}c.closePath();c.fill();c.stroke()}
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.ellipse(128,y,rx*.7,ry*.62,0,0,T);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=TD;for(let k=0;k<6;k++){const a=q+k*T/6;c.beginPath();c.arc(128+42*cos(a),y+11*sin(a),3,0,T);c.fill()}c.restore()}
|
||||
tut(1)
|
||||
c.fillStyle=LEO;c.lineWidth=5;c.beginPath();c.moveTo(114,128+oy);c.lineTo(142,128+oy);c.quadraticCurveTo(148,150,144,174+oy);c.lineTo(112,174+oy);c.quadraticCurveTo(108,150,114,128+oy);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=LD;c.lineWidth=3;c.beginPath();c.moveTo(118,140+oy);c.quadraticCurveTo(128+6*sq,150+oy,138,140+oy);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=FL;c.lineWidth=3.5;c.beginPath();c.arc(128+10*sq,134+oy,4,0,T);c.fill();c.stroke()
|
||||
tut(0)
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(122,120+oy,12,10);c.fill()
|
||||
const head=g=>{c.save();c.translate(128,90+oy);c.scale(.56,.56);c.translate(-128,-120);g();c.restore()}
|
||||
const front=ch>-.2,ex=34*sh,es=25*ch
|
||||
head(()=>{
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(128,38);c.bezierCurveTo(194,38,200,96,190,138);c.lineTo(66,138);c.bezierCurveTo(56,96,62,38,128,38);c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(128+6*sh,28,30,24,0,0,T);c.fill();c.stroke();c.strokeStyle=HD;c.lineWidth=5;c.beginPath();c.ellipse(128+6*sh,28,18,13,0,PI,T);c.stroke();c.strokeStyle='#111'
|
||||
if(front){
|
||||
c.fillStyle=SKIN;c.lineWidth=9;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(ch>.9&&up<.5){eye(c,103,140,-1);eye(c,153,140,1)}
|
||||
else{c.lineWidth=8;for(const d of[-1,1]){const x=128+ex+d*es;if(abs(es)>8||d*sh>0)c.beginPath(),c.arc(x,146,13,PI*1.15,PI*1.85),c.stroke(),c.beginPath(),c.moveTo(x+d*12,138),c.lineTo(x+d*19,133),c.stroke()}}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+ex+d*42*ch,162,11*max(.4,abs(ch)),6,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.arc(128+ex,164,7,.15*PI,.85*PI);c.stroke()
|
||||
c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.moveTo(70,130);c.bezierCurveTo(56,70,92,42,128,42);c.bezierCurveTo(166,42,200,70,186,130)
|
||||
c.quadraticCurveTo(180,104,162,92);c.quadraticCurveTo(146,88,132,96);c.quadraticCurveTo(128,84,124,96);c.quadraticCurveTo(110,88,94,92);c.quadraticCurveTo(76,104,70,130);c.closePath();c.fill();c.stroke()
|
||||
}else{
|
||||
c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.moveTo(72,100);c.bezierCurveTo(68,160,100,186,128,186);c.bezierCurveTo(156,186,188,160,184,100);c.bezierCurveTo(184,44,72,44,72,100);c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*14,60),c.quadraticCurveTo(128+d*34,120,128+d*14,170),c.stroke();c.strokeStyle='#111'
|
||||
}
|
||||
c.strokeStyle=HL;c.lineWidth=8;c.beginPath();c.arc(128,104,56,PI*1.3,PI*1.44);c.stroke();c.strokeStyle='#111'
|
||||
c.save();c.translate(128+46*sh+30*ch*(front?1:-1),36);c.fillStyle=FL;c.lineWidth=6;for(let k=0;k<5;k++){const b=k*T/5;c.beginPath();c.arc(12*cos(b),12*sin(b),9,0,T);c.fill();c.stroke()}c.fillStyle='#ff6b6b';c.beginPath();c.arc(0,0,7,0,T);c.fill();c.stroke();c.restore()
|
||||
})
|
||||
const op=1-up
|
||||
for(const d of[-1,1]){const s=[128+d*14,136+oy],e=[128+d*(22+30*op),148+oy-24*op],h=[128+d*(8+50*op),158+oy-34*op];line(c,[s,e,h],7,SKIN);c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.arc(...h,5.5,0,T);c.fill();c.stroke()}
|
||||
if(up>.8)for(const[y,r]of[[176,72],[112,56]])for(const d of[-1,1]){const a0=d>0?.15:PI+.15;for(const[lw,s]of[[9,'#111'],[3.5,'#fff']])c.lineWidth=lw,c.strokeStyle=s,c.beginPath(),c.ellipse(128,y+oy,r,r*.2,0,a0,a0+.8),c.stroke()}
|
||||
c.strokeStyle='#111'
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// barista girl with an espresso-brown bob pouring steamed milk from a steel pitcher into a latte, wiggling the pour, ripples and steam
|
||||
// the dark back hair gets a white rim so it doesn't vanish on black
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#6b4a3a',HD='#4a3329',HL='#b0907e',EYE='#f59f00',ED='#d9480f',BL='#ffa8a8',APR='#2f9e44',APD='#2b8a3e',CNT='#c08552',CND='#8d5a33',STEEL='#ced4da',STD='#868e96',COF='#c08552',FOAM='#fff4e6',CUP='#fff'
|
||||
const ds=(c,a,b,s,f)=>{for(const[l,k]of[[a,'#111'],[b,s]])c.lineWidth=l,c.strokeStyle=k,c.beginPath(),f(),c.stroke();c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x+4,y+6,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x+4,y-4,13,10,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+4,y+7,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x,y,4.5,0,T);c.fill();c.beginPath();c.arc(x+9,y+12,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const w=sin(4*T*t),blink=t>=.55&&t<.65
|
||||
c.save();c.translate(-18,0)
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(90,152);c.quadraticCurveTo(52,160,42,236);c.lineTo(178,236);c.quadraticCurveTo(168,160,130,152);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=APR;c.lineWidth=6;c.beginPath();c.moveTo(84,176);c.lineTo(136,176);c.lineTo(146,236);c.lineTo(74,236);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(86,176);c.lineTo(94,156);c.moveTo(134,176);c.lineTo(126,156);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(110,200,11,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=COF;c.beginPath();c.ellipse(110,200,4.5,7,.5,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.lineWidth=6;c.beginPath();c.moveTo(100,140);c.lineTo(100,158);c.quadraticCurveTo(110,166,120,158);c.lineTo(120,140);c.fill();c.beginPath();c.moveTo(100,140);c.lineTo(100,156);c.moveTo(120,140);c.lineTo(120,156);c.stroke()
|
||||
c.save();c.translate(110,100);c.scale(.72,.72);c.translate(-128,-120)
|
||||
const back=()=>{c.moveTo(128,36);c.bezierCurveTo(200,36,214,104,204,178);c.quadraticCurveTo(196,188,184,182);c.lineTo(72,182);c.quadraticCurveTo(60,188,52,178);c.bezierCurveTo(42,104,56,36,128,36)}
|
||||
c.strokeStyle='#fff';c.lineWidth=20;c.beginPath();back();c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=7;c.fillStyle=HD;c.beginPath();back();c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(blink){c.lineWidth=6;for(const x of[103,153])c.beginPath(),c.arc(x,138,14,.15*PI,.85*PI),c.stroke()}else eye(c,103,140,-1),eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(118,169);c.quadraticCurveTo(128,176,138,168);c.stroke()
|
||||
c.fillStyle='#ff8fab';c.lineWidth=3.5;c.beginPath();c.moveTo(130,172);c.quadraticCurveTo(138,180,140,170);c.fill();c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(62,176)
|
||||
c.bezierCurveTo(44,80,84,40,128,40);c.bezierCurveTo(172,40,212,80,194,176)
|
||||
c.quadraticCurveTo(186,182,180,174);c.quadraticCurveTo(186,130,176,104);c.quadraticCurveTo(160,96,150,86)
|
||||
c.quadraticCurveTo(140,110,104,118);c.quadraticCurveTo(116,108,118,94);c.quadraticCurveTo(100,112,78,116)
|
||||
c.quadraticCurveTo(70,150,76,174);c.quadraticCurveTo(70,182,62,176);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[s,e]of[[1.28,1.4],[1.5,1.58],[1.66,1.76]])c.beginPath(),c.arc(128,108,58,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=COF;c.lineWidth=5;c.save();c.translate(170,84);c.rotate(.6);c.beginPath();c.ellipse(0,0,11,15,0,0,T);c.fill();c.stroke();c.beginPath();c.moveTo(0,-14);c.quadraticCurveTo(-5,0,0,14);c.stroke();c.restore()
|
||||
c.restore();c.restore()
|
||||
c.lineWidth=6;c.fillStyle=CNT;c.beginPath();c.roundRect(6,214,244,38,10);c.fill();c.stroke()
|
||||
c.fillStyle=CND;c.beginPath();c.roundRect(6,214,244,12,6);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.lineWidth=5;c.beginPath();c.ellipse(52,212,14,10,-.2,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=CUP;c.lineWidth=6;c.beginPath();c.ellipse(200,215,40,8,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(228,194);c.bezierCurveTo(250,192,250,214,226,210);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(170,189);c.lineTo(174,208);c.quadraticCurveTo(200,218,226,208);c.lineTo(230,189);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=COF;c.beginPath();c.ellipse(200,189,30,8,0,0,T);c.fill();c.stroke()
|
||||
c.save();c.beginPath();c.ellipse(200,189,30,8,0,0,T);c.clip()
|
||||
c.fillStyle=FOAM;c.beginPath();c.ellipse(198+2*w,190,18,5,0,0,T);c.fill()
|
||||
c.strokeStyle=COF;c.lineWidth=2.5;for(const o of[0,.5]){const p=(2*t+o)%1;c.beginPath();c.ellipse(198+2*w,190,6+16*p,2+4*p,0,0,T);c.stroke()}
|
||||
c.restore();c.strokeStyle='#111'
|
||||
for(const[x,o]of[[222,0],[238,.5]]){const p=(t+o)%1,s=sin(PI*p);if(s>.15)ds(c,7,3,'#fff',()=>{const y=176-34*p;c.moveTo(x,y+10);c.bezierCurveTo(x-7*s,y+4,x+7*s,y-4,x+2,y-12)})}
|
||||
const sx=194+2*w,sy=172,a=.8+.05*w,Z=.85
|
||||
c.save();c.beginPath();c.rect(0,0,256,189);c.clip()
|
||||
ds(c,13,7,FOAM,()=>{c.moveTo(sx,sy);c.quadraticCurveTo(sx+3+2*w,sy+10,198+2*w,190)})
|
||||
c.restore()
|
||||
const hx=sx+Z*(-70*cos(a)-16*sin(a)),hy=sy+Z*(-70*sin(a)+16*cos(a))
|
||||
ds(c,20,11,'#fff',()=>{c.moveTo(110,168);c.lineTo(128,196);c.lineTo(hx,hy+6)})
|
||||
c.save();c.translate(sx,sy);c.rotate(a);c.scale(Z,Z)
|
||||
c.fillStyle=STEEL;c.lineWidth=7;c.beginPath();c.moveTo(-6,-6);c.lineTo(-46,-14);c.quadraticCurveTo(-56,-8,-56,0);c.lineTo(-56,30);c.quadraticCurveTo(-56,40,-46,40);c.lineTo(-14,40);c.quadraticCurveTo(-4,40,-4,30);c.lineTo(-4,4);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-46,-2);c.lineTo(-46,30);c.stroke();c.strokeStyle=STD;c.beginPath();c.moveTo(-14,4);c.lineTo(-14,32);c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(-56,4);c.bezierCurveTo(-78,4,-78,30,-56,30);c.stroke()
|
||||
c.restore()
|
||||
c.fillStyle=SKIN;c.lineWidth=5;c.beginPath();c.ellipse(hx,hy,11,12,0,0,T);c.fill();c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// girl with a tangerine bob pedals a bicycle with a puppy riding in the front basket, wheels spinning, scarf streaming behind
|
||||
const{sin,cos,PI,max,min,hypot,sqrt}=Math,T=2*PI
|
||||
export const frames=12,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#fd7e14',HD='#e8590c',HL='#ffe8cc',EYE='#339af0',ED='#1864ab',TOP='#fff',STR='#20c997',DEN='#4dabf7',DD='#1c7ed6',SCARF='#20c997',SCD='#0ca678',BIKE='#ff6b6b',BD='#e03131',BAS='#e0a96d',BSD='#b07d4f',PUP='#fff4e6',EAR='#a9744f',SHOE='#fa5252',BL='#ffa8a8'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x+3,y+2,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x+3,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x+3,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+5,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+9,y+8,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const ik=(hx,hy,fx,fy,L)=>{const dx=fx-hx,dy=fy-hy,d=hypot(dx,dy),k=sqrt(max(0,L*L-d*d/4));return[hx+dx/2-dy/d*k,hy+dy/2+dx/d*k]}
|
||||
export default(c,t,i)=>{
|
||||
const f=T*t,bob=1.2*sin(2*f),RH=[70,198],FH=[192,198],BB=[128,200],R=33
|
||||
c.fillStyle='#ced4da';c.lineWidth=5;c.beginPath();c.roundRect(14,226,228,16,8);c.fill();c.stroke()
|
||||
c.save();c.beginPath();c.roundRect(14,226,228,16,8);c.clip();c.strokeStyle='#fff';c.lineWidth=4;for(let k=0;k<4;k++){const x=((k*64-t*256)%256+256)%256;c.beginPath();c.moveTo(x,234);c.lineTo(x+28,234);c.stroke()}c.restore()
|
||||
for(const[k,y]of[[0,132],[1,164]]){const x=((k*20-t*48)%48+48)%48-6;line(c,[[x,y],[x+20,y]],3,'#fff')}
|
||||
const pd=k=>[BB[0]+15*cos(f+k*PI),BB[1]+15*sin(f+k*PI)],hip=[112,146+bob]
|
||||
const leg=(k,col)=>{const p=pd(k),kn=ik(...hip,p[0],p[1]-3,37);line(c,[hip,kn,[p[0],p[1]-3]],9,col);line(c,[kn,[(kn[0]+p[0])/2,(kn[1]+p[1])/2]],9,SKIN);c.fillStyle=SHOE;c.lineWidth=5;c.beginPath();c.ellipse(p[0]+4,p[1]-1,11,6,0,0,T);c.fill();c.stroke()}
|
||||
leg(1,DD)
|
||||
const wheel=([x,y])=>{c.strokeStyle='#fff';c.lineWidth=16;c.beginPath();c.arc(x,y,R,0,T);c.stroke();c.strokeStyle='#343a40';c.lineWidth=9;c.stroke();c.strokeStyle='#111';c.lineWidth=2.5;c.beginPath();c.arc(x,y,R+5,0,T);c.stroke();c.beginPath();c.arc(x,y,R-5,0,T);c.stroke()
|
||||
c.strokeStyle='#adb5bd';c.lineWidth=2.5;for(let k=0;k<6;k++){const a=f+k*PI/3;c.beginPath();c.moveTo(x,y);c.lineTo(x+(R-5)*cos(a),y+(R-5)*sin(a));c.stroke()}c.strokeStyle='#111';c.fillStyle='#e9ecef';c.lineWidth=4;c.beginPath();c.arc(x,y,5,0,T);c.fill();c.stroke()}
|
||||
wheel(RH);wheel(FH)
|
||||
const seat=[112,152],ht=[172,150]
|
||||
line(c,[RH,BB,seat,RH],6,BIKE);line(c,[seat,ht],6,BIKE);line(c,[BB,[170,162]],6,BIKE);line(c,[[176,140],FH],6,BD);line(c,[[176,140],[176,126],[162,124]],5,'#adb5bd')
|
||||
c.fillStyle='#495057';c.lineWidth=5;c.beginPath();c.ellipse(108,148,16,6,0,0,T);c.fill();c.strokeStyle='#fff';c.lineWidth=9;c.stroke();c.strokeStyle='#111';c.lineWidth=5;c.stroke()
|
||||
c.fillStyle=BIKE;c.lineWidth=4;c.beginPath();c.arc(...BB,8,0,T);c.fill();c.stroke()
|
||||
const scarf=Array.from({length:7},(_,k)=>[128-15*k,112+6*sin(k*.9-T*t*2)*k/6+k*2.5+bob])
|
||||
line(c,scarf,10,SCARF);c.strokeStyle=SCD;c.lineWidth=3;const e=scarf[6];for(const d of[-5,0,5])c.beginPath(),c.moveTo(e[0],e[1]+d),c.lineTo(e[0]-9,e[1]+d+2),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=DEN;c.lineWidth=5;c.beginPath();c.moveTo(100,150+bob);c.lineTo(108,124+bob);c.lineTo(132,122+bob);c.lineTo(132,152+bob);c.quadraticCurveTo(116,160,100,150+bob);c.closePath();c.fill();c.stroke()
|
||||
leg(0,DEN)
|
||||
c.fillStyle=TOP;c.lineWidth=5;c.beginPath();c.moveTo(112,104+bob);c.lineTo(134,100+bob);c.lineTo(140,128+bob);c.lineTo(110,132+bob);c.closePath();c.fill();c.save();c.clip();c.strokeStyle=STR;c.lineWidth=4;for(const y of[108,116,124])c.beginPath(),c.moveTo(100,y+bob+2),c.lineTo(150,y+bob-3),c.stroke();c.restore();c.stroke()
|
||||
c.fillStyle=DEN;c.lineWidth=4;c.beginPath();c.moveTo(116,118+bob);c.lineTo(134,114+bob);c.lineTo(136,128+bob);c.lineTo(114,132+bob);c.closePath();c.fill();c.stroke()
|
||||
const sh=[130,108+bob],gp=[164,124]
|
||||
line(c,[sh,[148,122+bob/2],gp],8,SKIN);c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.arc(...gp,6.5,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=SCARF;c.lineWidth=5;c.beginPath();c.ellipse(128,104+bob,16,8,-.2,0,T);c.fill();c.stroke()
|
||||
c.save();c.translate(132,72+bob);c.rotate(.05);c.scale(.54,.54);c.translate(-128,-120)
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(124,34);c.bezierCurveTo(192,34,206,96,196,160);c.quadraticCurveTo(180,170,168,160);c.lineTo(70,160);c.quadraticCurveTo(46,168,44,140);c.bezierCurveTo(26,124,30,104,46,110);c.bezierCurveTo(46,60,70,34,124,34);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(78,96);c.bezierCurveTo(74,152,104,184,132,186);c.bezierCurveTo(160,184,190,152,186,96);c.bezierCurveTo(186,44,78,44,78,96);c.fill();c.stroke()
|
||||
eye(c,110,140,-1);eye(c,160,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(136+d*42,162,12,6,0,0,T),c.fill()
|
||||
c.fillStyle='#c92a2a';c.lineWidth=6;c.beginPath();c.moveTo(126,164);c.quadraticCurveTo(140,184,154,164);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.moveTo(72,148);c.bezierCurveTo(56,74,94,40,134,40);c.bezierCurveTo(178,40,208,78,192,148)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(170,102,164,112);c.quadraticCurveTo(158,98,148,94);c.quadraticCurveTo(142,106,134,112)
|
||||
c.quadraticCurveTo(128,98,120,94);c.quadraticCurveTo(114,106,106,112);c.quadraticCurveTo(100,98,92,98);c.quadraticCurveTo(86,106,82,112);c.quadraticCurveTo(74,124,72,148);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=8;for(const[a0,a1]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(132,104,56,PI*a0,PI*a1),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#ffd43b';c.lineWidth=7;c.beginPath();c.roundRect(150,70,34,13,6);c.fill();c.stroke()
|
||||
c.restore()
|
||||
const bx=178,by=118
|
||||
c.save();c.translate(bx+19,by-6+bob*.5);c.scale(1.25,1.25)
|
||||
for(const d of[-1,1]){c.save();c.translate(d*11,-8);c.rotate(d*(.5+.35*sin(T*t*3+d)));c.fillStyle=EAR;c.lineWidth=4;c.beginPath();c.ellipse(0,9,6,11,0,0,T);c.fill();c.stroke();c.restore()}
|
||||
c.fillStyle=PUP;c.lineWidth=5;c.beginPath();c.ellipse(0,0,15,13,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=EAR;c.beginPath();c.ellipse(-6,-6,6,5,0,0,T);c.fill()
|
||||
c.fillStyle='#111';for(const x of[-5,6])c.beginPath(),c.arc(x,-1,2.6,0,T),c.fill()
|
||||
c.beginPath();c.ellipse(1,4,3.5,2.5,0,0,T);c.fill()
|
||||
c.fillStyle='#ff8fab';c.lineWidth=2.5;c.beginPath();c.moveTo(-2,8);c.quadraticCurveTo(1,16+2*sin(T*t*3),5,8);c.closePath();c.fill();c.stroke();c.restore()
|
||||
c.fillStyle=BAS;c.lineWidth=5;c.beginPath();c.moveTo(bx,by+8);c.lineTo(bx+38,by+8);c.lineTo(bx+34,by+32);c.lineTo(bx+4,by+32);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=BSD;c.lineWidth=3;for(const y of[16,24])c.beginPath(),c.moveTo(bx+2,by+y),c.lineTo(bx+36,by+y),c.stroke();for(const x of[10,19,28])c.beginPath(),c.moveTo(bx+x,by+10),c.lineTo(bx+x-1,by+30),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=PUP;c.lineWidth=3.5;for(const x of[8,28])c.beginPath(),c.ellipse(bx+x,by+8,5,4,0,0,T),c.fill(),c.stroke()
|
||||
c.fillStyle='#ffd43b';c.lineWidth=3.5;c.beginPath();for(let k=0;k<10;k++){const r=k%2?3:7,b=k*PI/5-PI/2;c.lineTo(bx+19+r*cos(b),by+21+r*sin(b))}c.closePath();c.fill();c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// birthday girl in a party hat behind her cake: makes a wish, puffs her cheeks and blows the candles out one by one, confetti pops, the candles spark back on
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=90
|
||||
const SKIN='#ffe3d1',HAIR='#8f1d4a',HD='#6a1236',HL='#f3a6c4',EYE='#4dabf7',ED='#1864ab',DRESS='#ffd43b',DD='#fab005',HAT='#4dabf7',HS='#fff',POM='#ff6b6b',CAKE='#fff4e6',CR='#f783ac',CRD='#e64980',BERRY='#fa5252',CAN=['#69db7c','#b197fc','#ffa94d'],FL='#ffd43b',FO='#ff922b',BL='#ffa8a8',MO='#c92a2a',CONF=['#ff6b6b','#4dabf7','#ffd43b','#69db7c','#b197fc']
|
||||
const cl=x=>max(0,min(1,x))
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const wish=t<.16,puff=t>=.16&&t<.26,blow=t>=.26&&t<.56,happy=t>=.56&&t<.84,out=[.32,.4,.48],relit=t>=.84
|
||||
c.save();c.translate(128,-12);c.scale(.86,.86);c.translate(-128,0)
|
||||
// braid over her shoulder, then back hair
|
||||
c.lineWidth=6;c.fillStyle=HAIR;for(let k=0;k<5;k++){const y=150+k*18;c.beginPath();c.ellipse(186+k*2,y,13-k,11,.2,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle='#69db7c';c.beginPath();c.arc(196,240,7,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,40);c.bezierCurveTo(196,40,206,100,196,156);c.lineTo(60,156);c.bezierCurveTo(50,100,60,40,128,40);c.fill();c.stroke()
|
||||
// dress shoulders
|
||||
c.fillStyle=DRESS;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(60,198,50,262);c.lineTo(206,262);c.quadraticCurveTo(196,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(128,206),c.quadraticCurveTo(128+d*22,190,128+d*30,194),c.quadraticCurveTo(128+d*24,210,128,206),c.fill(),c.stroke()
|
||||
c.fillStyle=SKIN;c.lineWidth=6;c.beginPath();c.rect(116,170,24,26);c.fill();c.beginPath();c.moveTo(116,170);c.lineTo(116,192);c.moveTo(140,170);c.lineTo(140,192);c.stroke()
|
||||
// face
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(puff||blow){const pf=puff?1.25:1.1;c.fillStyle=SKIN;for(const d of[-1,1])c.beginPath(),c.arc(128+d*42,158,14*pf,d<0?PI*.5:-PI*.5,d<0?PI*1.5:PI*.5),c.fill(),c.stroke()}
|
||||
if(wish||happy||puff){c.lineWidth=6;for(const x of[103,153])c.beginPath(),wish||puff?c.arc(x,140,12,PI*.15,PI*.85):c.arc(x,146,13,PI*1.15,PI*1.85),c.stroke()}
|
||||
else{eye(c,103,140,-1);eye(c,153,140,1)}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,161,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=3.5;c.fillStyle=MO;c.beginPath()
|
||||
if(blow)c.ellipse(128,170,5,6,0,0,T),c.fill(),c.stroke()
|
||||
else if(puff)c.moveTo(122,170),c.lineTo(134,170),c.stroke()
|
||||
else if(happy)c.moveTo(114,164),c.quadraticCurveTo(128,186,142,164),c.closePath(),c.fill(),c.stroke()
|
||||
else c.arc(128,166,7,.15*PI,.85*PI),c.stroke()
|
||||
// bangs
|
||||
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,146);c.bezierCurveTo(50,74,88,40,128,40);c.bezierCurveTo(168,40,206,74,190,146)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(170,102,164,110);c.quadraticCurveTo(160,96,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,122,90);c.quadraticCurveTo(116,104,106,108);c.quadraticCurveTo(102,96,92,96);c.quadraticCurveTo(86,106,80,112);c.quadraticCurveTo(70,124,66,146);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
// party hat
|
||||
c.save();c.translate(82,74);c.rotate(-.55)
|
||||
c.lineWidth=6;c.fillStyle=HAT;c.beginPath();c.moveTo(-24,6);c.lineTo(0,-52);c.lineTo(24,6);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.strokeStyle=HS;c.lineWidth=7;for(const y of[-36,-14])c.beginPath(),c.moveTo(-30,y+12),c.lineTo(30,y-4),c.stroke();c.restore()
|
||||
c.beginPath();c.moveTo(-24,6);c.lineTo(0,-52);c.lineTo(24,6);c.closePath();c.stroke()
|
||||
c.fillStyle=POM;c.beginPath();c.arc(0,-54,9,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
c.restore()
|
||||
// wind lines while blowing
|
||||
if(blow)for(const[dx,o]of[[-14,0],[0,.33],[14,.66]]){const p=(t*8+o)%1;for(const[w,s]of[[7,'#111'],[3,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(128+dx*.6,142+10*p),c.lineTo(128+dx*(1+.5*p),152+10*p),c.stroke()}
|
||||
c.strokeStyle='#111'
|
||||
// cake
|
||||
c.lineWidth=6;c.fillStyle=CAKE;c.beginPath();c.moveTo(52,206);c.lineTo(52,246);c.quadraticCurveTo(128,262,204,246);c.lineTo(204,206);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=CR;c.lineWidth=5;c.beginPath();c.moveTo(56,230);c.quadraticCurveTo(128,244,200,230);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=CR;c.beginPath();c.moveTo(50,206);for(let k=0;k<=8;k++){const x=50+k*19.5;c.quadraticCurveTo(x-10,222+(k%2)*6,x,210)}c.lineTo(206,206);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(128,204,78,16,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=BERRY;c.lineWidth=4;for(const x of[78,178])c.beginPath(),c.moveTo(x,190),c.quadraticCurveTo(x+11,196,x,212),c.quadraticCurveTo(x-11,196,x,190),c.fill(),c.stroke()
|
||||
// candles and flames
|
||||
for(const[k,x]of[[0,100],[1,128],[2,156]]){
|
||||
c.fillStyle=CAN[k];c.lineWidth=5;c.beginPath();c.roundRect(x-6,178,12,24,3);c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;for(const y of[186,196])c.beginPath(),c.moveTo(x-5,y+3),c.lineTo(x+5,y-3),c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(x,178);c.lineTo(x,172);c.stroke()
|
||||
const lit=t<out[k]||relit,lean=blow&&lit?6*cl((t-.26)/.06):0
|
||||
if(lit){const f=1+.12*sin(4*T*t+k*2),pop=relit?cl((t-.84)/.06):1
|
||||
c.save();c.translate(x,171);c.scale(pop,pop);c.fillStyle=FO;c.lineWidth=4;c.beginPath();c.moveTo(0,0);c.bezierCurveTo(-9,-2,-6,-14,lean,-22*f);c.bezierCurveTo(6,-14,9,-2,0,0);c.fill();c.stroke()
|
||||
c.fillStyle=FL;c.beginPath();c.moveTo(0,-3);c.quadraticCurveTo(-4,-8,lean*.6,-14*f);c.quadraticCurveTo(4,-8,0,-3);c.fill();c.restore()
|
||||
if(relit&&t<.92){c.lineWidth=3;for(let a=0;a<4;a++)c.beginPath(),c.moveTo(x+12*cos(a*PI/2+.7),160+12*sin(a*PI/2+.7)),c.lineTo(x+18*cos(a*PI/2+.7),160+18*sin(a*PI/2+.7)),c.stroke()}}
|
||||
else{const p=cl((t-out[k])/.3);if(p<1){c.globalAlpha=1-p;for(const[w,s]of[[7,'#111'],[3,'#dee2e6']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x,170),c.bezierCurveTo(x-8,158-20*p,x+8,148-20*p,x,136-20*p),c.stroke();c.globalAlpha=1;c.strokeStyle='#111'}}
|
||||
}
|
||||
// confetti burst
|
||||
if(happy){const p=cl((t-.56)/.28);for(let k=0;k<10;k++){const a=-PI/2+(k-4.5)*.3,r=40+70*p,x=128+r*cos(a)*1.4,y=130+r*sin(a)+90*p*p;c.save();c.translate(x,y);c.rotate(k+p*6);c.fillStyle=CONF[k%5];c.lineWidth=3;c.beginPath();c.rect(-5,-3,10,6);c.fill();c.stroke();c.restore()}}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// peach-haired girl rolled up in a starry blanket burrito, only her sleepy face peeking out, wobbling side to side with a slow blink and a drifting z
|
||||
const{sin,cos,PI}=Math,T=2*PI
|
||||
export const frames=12,delay=110
|
||||
const SKIN='#ffe3d1',HAIR='#ffcfa8',HD='#f2a26b',HL='#fff0e4',EYE='#51cf66',ED='#2b8a3e',BLK='#91a7ff',BKD='#5c7cfa',LIN='#fcc2d7',SOCK='#fff',SKS='#ff8787',BL='#ffa8a8',MO='#c92a2a',ZC='#d0bfff'
|
||||
const eye=(c,x,y,d,shut)=>{
|
||||
if(shut){c.lineWidth=6;c.beginPath();c.arc(x,y-2,13,PI*.15,PI*.85);c.stroke();return}
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+4,5.5,7.5,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-4,y+6,3,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(x-19,y+3);c.quadraticCurveTo(x,y-10,x+19,y+3);c.lineTo(x+19,y-24);c.lineTo(x-19,y-24);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y+3);c.quadraticCurveTo(x,y-9,x+16,y+3);c.moveTo(x+d*15,y+2);c.lineTo(x+d*20,y-2);c.stroke()
|
||||
}
|
||||
const star=(c,x,y,r)=>{c.beginPath();for(let k=0;k<10;k++){const rr=k%2?r*.45:r,a=k*PI/5-PI/2;c.lineTo(x+cos(a)*rr,y+sin(a)*rr)}c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t,i)=>{
|
||||
const a=.12*sin(T*t),shut=i==7||i==8
|
||||
c.fillStyle='rgba(17,17,17,.15)';c.beginPath();c.ellipse(128,246,56,6,0,0,T);c.fill()
|
||||
c.save();c.translate(128,244);c.rotate(a);c.translate(-128,-244)
|
||||
// socks peeking out the bottom
|
||||
c.lineWidth=5;for(const d of[-1,1]){c.fillStyle=SOCK;c.beginPath();c.ellipse(128+d*18,238,13,8,0,0,T);c.fill();c.stroke();c.strokeStyle=SKS;c.lineWidth=3;c.beginPath();c.moveTo(128+d*18-10,236);c.lineTo(128+d*18+10,236);c.stroke();c.strokeStyle='#111';c.lineWidth=5}
|
||||
// the burrito
|
||||
const body=()=>{c.beginPath();c.moveTo(128,32);c.bezierCurveTo(206,32,214,120,204,190);c.quadraticCurveTo(196,236,128,236);c.quadraticCurveTo(60,236,52,190);c.bezierCurveTo(42,120,50,32,128,32);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle=BLK;body();c.fill()
|
||||
c.save();body();c.clip()
|
||||
c.fillStyle='#fff';c.lineWidth=2.5;for(const[x,y]of[[70,170],[176,150],[110,214],[160,206],[66,96],[192,90],[92,140]])star(c,x,y,6)
|
||||
// wrapped edge spiralling round the front, with the lining showing
|
||||
c.lineWidth=6;c.fillStyle=LIN;c.beginPath();c.moveTo(40,180);c.quadraticCurveTo(140,176,220,226);c.lineTo(220,240);c.quadraticCurveTo(140,192,40,196);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=BLK;c.beginPath();c.moveTo(40,196);c.quadraticCurveTo(140,192,220,240);c.lineTo(220,260);c.lineTo(40,260);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=2.5;for(const[x,y]of[[82,216],[140,224]])star(c,x,y,6)
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(40,196);c.quadraticCurveTo(140,192,220,240);c.stroke()
|
||||
c.restore()
|
||||
c.lineWidth=6;body();c.stroke()
|
||||
// face peeking out of the opening, under a folded hood of blanket
|
||||
c.fillStyle=LIN;c.beginPath();c.ellipse(128,108,70,62,0,0,T);c.fill();c.stroke()
|
||||
c.save();c.beginPath();c.ellipse(128,110,60,53,0,0,T);c.clip()
|
||||
c.fillStyle=HD;c.fillRect(60,50,140,130)
|
||||
c.translate(128,118);c.scale(.82,.82);c.translate(-128,-132)
|
||||
c.lineWidth=7;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,142,-1,shut);eye(c,153,142,1,shut)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,14,7,0,0,T),c.fill()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+k-2,167),c.lineTo(128+d*44+k+2,161),c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(120,172);c.quadraticCurveTo(124,176,128,173);c.quadraticCurveTo(132,176,136,172);c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(60,160);c.bezierCurveTo(46,74,88,36,128,36);c.bezierCurveTo(168,36,210,74,196,160)
|
||||
c.quadraticCurveTo(186,124,180,108);c.quadraticCurveTo(170,100,162,114);c.quadraticCurveTo(154,96,140,94);c.quadraticCurveTo(136,108,128,114)
|
||||
c.quadraticCurveTo(120,98,106,96);c.quadraticCurveTo(102,108,94,114);c.quadraticCurveTo(86,100,76,108);c.quadraticCurveTo(68,128,60,160);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[a0,e]of[[1.3,1.4],[1.46,1.56],[1.62,1.7]])c.beginPath(),c.arc(128,110,56,PI*a0,PI*e),c.stroke()
|
||||
c.restore()
|
||||
c.strokeStyle='#111';c.lineWidth=6;c.beginPath();c.ellipse(128,110,60,53,0,0,T);c.stroke()
|
||||
c.restore()
|
||||
// z drifting up and fading
|
||||
const p=(t*2)%1,s=.6+.6*p
|
||||
c.save();c.globalAlpha=p<.8?1:1-(p-.8)/.2;c.translate(214+8*p,44-28*p);c.scale(s,s);c.rotate(-.2)
|
||||
for(const[l,st]of[[9,'#111'],[4,ZC]])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(-8,-8),c.lineTo(8,-8),c.lineTo(-8,8),c.lineTo(8,8),c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// close-up of a girl with long wavy ash-blonde hair breathing in a bouquet with her eyes shut, then sighing dreamily, petals drifting by
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=90
|
||||
const SKIN='#ffe3d1',HAIR='#d8cbb0',HD='#a99c80',HL='#f4efe3',EYE='#b197fc',ED='#7048e8',BL='#ffa8a8',DR='#b2d8a8',WR='#faa2c1',WRD='#f06595',ROSE='#ff8787',RD='#e03131',DAI='#fff',YEL='#ffd43b',LEAF='#69db7c',PET='#ffc9de'
|
||||
const ds=(c,a,b,s,f)=>{for(const[l,k]of[[a,'#111'],[b,s]])c.lineWidth=l,c.strokeStyle=k,c.beginPath(),f(),c.stroke();c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d,lid)=>{
|
||||
const u=-lid*.7
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x+1,y+2+u,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x+1,y-8+u,13,10,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+1,y+3+u,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-3,y-2+u,5.5,0,T);c.fill();c.beginPath();c.arc(x+6,y+8+u,2.5,0,T);c.fill()
|
||||
c.lineWidth=1.5;c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,q=k%2?1.6:4.5;c.lineTo(x+5+cos(a)*q,y+2+u+sin(a)*q)}c.closePath();c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const shut=(c,x,y,d)=>{c.lineWidth=6;c.beginPath();c.arc(x,y-6,15,.12*PI,.88*PI);c.stroke();c.lineWidth=4;c.beginPath();for(const a of[.25,.5,.75])c.moveTo(x+15*cos(a*PI)*.9,y-6+15*sin(a*PI)),c.lineTo(x+19*cos(a*PI)*.9,y-6+19*sin(a*PI)+2);c.moveTo(x+d*14,y-1);c.lineTo(x+d*21,y+2);c.stroke()}
|
||||
const petal=(c,x,y,a)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle=PET;c.lineWidth=3.5;c.beginPath();c.moveTo(0,-9);c.quadraticCurveTo(9,-2,0,9);c.quadraticCurveTo(-9,-2,0,-9);c.fill();c.stroke();c.restore()}
|
||||
const rose=(c,x,y,r)=>{c.fillStyle=ROSE;c.lineWidth=5;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke();c.strokeStyle=RD;c.lineWidth=3;c.beginPath();for(let a=0;a<3*T;a+=.2){const q=r*.75*(1-a/(3*T));a?c.lineTo(x+q*cos(a),y+q*sin(a)):c.moveTo(x+q,y)}c.stroke();c.strokeStyle='#111'}
|
||||
const daisy=(c,x,y,r)=>{c.fillStyle=DAI;c.lineWidth=3.5;for(let k=0;k<8;k++){const a=k*PI/4;c.beginPath();c.ellipse(x+cos(a)*r*.62,y+sin(a)*r*.62,r*.42,r*.22,a,0,T);c.fill();c.stroke()}c.fillStyle=YEL;c.beginPath();c.arc(x,y,r*.32,0,T);c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const sn=t<.5,b=sn?sin(PI*t/.5):0,lid=sn?0:6+2*sin(2*T*t),g=sn?0:sin(PI*(t-.5)/.5)
|
||||
c.lineWidth=7;c.fillStyle=HD;c.beginPath();c.moveTo(128,30)
|
||||
c.bezierCurveTo(214,30,228,110,222,178);for(let k=0;k<4;k++)c.quadraticCurveTo(236-(k%2)*20,190+k*16,226-(k%2)*8,204+k*16)
|
||||
c.lineTo(30,254);for(let k=3;k>=0;k--)c.quadraticCurveTo(20+(k%2)*20,198+k*16,30+(k%2)*8,186+k*16)
|
||||
c.bezierCurveTo(28,110,42,30,128,30);c.fill();c.stroke()
|
||||
c.fillStyle=DR;c.beginPath();c.moveTo(100,196);c.quadraticCurveTo(64,204,58,256);c.lineTo(198,256);c.quadraticCurveTo(192,204,156,196);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(128,210),c.quadraticCurveTo(128+d*16,196,128+d*30,198),c.quadraticCurveTo(128+d*26,214,128,214),c.fill(),c.stroke()
|
||||
c.fillStyle=SKIN;c.lineWidth=6;c.beginPath();c.moveTo(114,170);c.lineTo(114,198);c.quadraticCurveTo(128,212,142,198);c.lineTo(142,170);c.fill();c.beginPath();c.moveTo(114,170);c.lineTo(114,196);c.moveTo(142,170);c.lineTo(142,196);c.stroke()
|
||||
c.save();c.translate(128,110);c.scale(.92,.92);c.translate(-128,-120)
|
||||
c.lineWidth=7;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(t>.06&&sn)shut(c,103,140,-1),shut(c,153,140,1);else eye(c,103,140,-1,lid),eye(c,153,140,1,lid)
|
||||
const bl=1+.25*g
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,163,13*bl,7*bl,0,0,T),c.fill()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const x of[-6,0,6])c.beginPath(),c.moveTo(128+d*44+x-2,167),c.lineTo(128+d*44+x+2,159),c.stroke()
|
||||
c.beginPath();c.moveTo(129,152);c.lineTo(127,157);c.stroke()
|
||||
c.lineWidth=4.5;c.beginPath()
|
||||
if(sn)c.moveTo(121,168),c.quadraticCurveTo(128,173,135,168),c.stroke();else c.fillStyle='#c92a2a',c.moveTo(121,167),c.quadraticCurveTo(129,176+4*g,137,167),c.closePath(),c.fill(),c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,170)
|
||||
c.bezierCurveTo(46,80,86,40,128,40);c.bezierCurveTo(170,40,212,80,190,170)
|
||||
c.quadraticCurveTo(190,130,182,108);c.quadraticCurveTo(170,96,152,92);c.quadraticCurveTo(150,104,144,110)
|
||||
c.quadraticCurveTo(128,96,108,90);c.quadraticCurveTo(112,104,106,112);c.quadraticCurveTo(92,104,84,96)
|
||||
c.quadraticCurveTo(86,108,80,118);c.quadraticCurveTo(70,140,66,170);c.closePath();c.fill();c.stroke()
|
||||
for(const d of[-1,1]){c.beginPath();c.moveTo(128+d*56,110);c.bezierCurveTo(128+d*70,140,128+d*58,170,128+d*70,210);c.quadraticCurveTo(128+d*56,206,128+d*50,186);c.bezierCurveTo(128+d*44,160,128+d*54,140,128+d*46,116);c.closePath();c.fill();c.stroke()}
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[s,e]of[[1.26,1.38],[1.46,1.54],[1.62,1.72]])c.beginPath(),c.arc(128,108,58,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=YEL;c.lineWidth=5;c.save();c.translate(170,74);c.rotate(.4);c.beginPath();c.roundRect(-14,-5,28,10,5);c.fill();c.stroke();c.restore()
|
||||
c.restore()
|
||||
if(sn&&b>.4)for(const k of[0,1,2])ds(c,6,2.5,'#fff',()=>{const x=146+k*8,y=150-k*3;c.moveTo(x,y);c.quadraticCurveTo(x+4,y-4,x+8,y)})
|
||||
if(g>.05){const s=g,x=170+36*(t-.5),y=168-40*(t-.5);c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();for(const[a,q,r]of[[0,0,9],[10,-5,7],[18,2,6]])c.moveTo(x+a*s+r*s,y+q*s),c.arc(x+a*s,y+q*s,r*s,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#f06595';c.lineWidth=3.5;for(const[x0,o]of[[204,0],[222,.25]]){const p=min(1,max(0,(t-.5-o*.4)/.4)),h=sin(PI*p);if(h>.1){const x=x0+4*sin(p*T),y=130-50*p;c.save();c.translate(x,y);c.scale(h,h);c.beginPath();c.moveTo(0,8);c.bezierCurveTo(-13,-1,-9,-12,0,-5);c.bezierCurveTo(9,-12,13,-1,0,8);c.restore();c.fill();c.stroke()}}}
|
||||
const bx=82+8*b,by=208-10*b,Z=.86
|
||||
c.save();c.translate(bx,by);c.rotate(-.35);c.scale(Z,Z)
|
||||
c.fillStyle=LEAF;c.lineWidth=5;for(const[a,l]of[[-1.2,30],[-2,30],[-.3,28]])c.beginPath(),c.ellipse(cos(a)*l,sin(a)*l,14,7,a,0,T),c.fill(),c.stroke()
|
||||
rose(c,-14,-22,15);daisy(c,16,-24,15);rose(c,4,-42,13);daisy(c,-30,-44,12);c.fillStyle=YEL;c.lineWidth=4;c.beginPath();c.arc(28,-50,8,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=WR;c.lineWidth=6;c.beginPath();c.moveTo(-34,-14);c.lineTo(34,-14);c.lineTo(6,60);c.lineTo(-6,60);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=WRD;c.lineWidth=3;c.beginPath();c.moveTo(-14,-10);c.lineTo(0,52);c.moveTo(14,-10);c.lineTo(2,52);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=YEL;c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.ellipse(d*10,18,10,6,d*.5,0,T),c.fill(),c.stroke()
|
||||
c.restore()
|
||||
c.fillStyle=SKIN;c.lineWidth=5;for(const[x,y]of[[-10,22],[12,30]]){const a=-.35,X=bx+Z*(x*cos(a)-y*sin(a)),Y=by+Z*(x*sin(a)+y*cos(a));c.beginPath();c.ellipse(X,Y,13,11,0,0,T);c.fill();c.stroke()}
|
||||
for(const[o,y0]of[[0,40],[.5,70]]){const p=(t+o)%1;petal(c,250-200*p,y0+80*p+10*sin(p*T*2),p*T*1.5+o*4)}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// lilac-haired girl with two braids blowing soap bubbles through a wand, rainbow-sheen bubbles growing, drifting up and popping
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#d0bfff',HD='#9775fa',HL='#f3f0ff',EYE='#4c6ef5',ED='#364fc7',DR='#ffc9c9',DD='#ff8787',COL='#fff',BOW='#69db7c',WAND='#ffd43b',BUB='#f8f9fa',BL='#ffa8a8',MO='#c92a2a'
|
||||
const ease=x=>x<=0?0:x>=1?1:x*x*(3-2*x)
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+4,y+1,5.5,7.5,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x,y-6,4.5,0,T);c.fill();c.beginPath();c.arc(x+8,y+6,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
// soap bubble with a rainbow sheen and a shine
|
||||
const bubble=(c,x,y,r)=>{
|
||||
c.fillStyle=BUB;c.lineWidth=3.5;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=max(2,r*.16);for(const[a,col]of[[0,'#faa2c1'],[1,'#a5d8ff'],[2,'#b2f2bb']]){c.strokeStyle=col;c.beginPath();c.arc(x,y,r*(.72-a*.13),PI*.15,PI*.6);c.stroke()}
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.beginPath();c.ellipse(x-r*.4,y-r*.4,r*.2,r*.13,-.7,0,T);c.fill()
|
||||
}
|
||||
const pop=(c,x,y,r)=>{for(const[l,s]of[[6,'#111'],[2.5,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();for(let k=0;k<8;k++){const a=k*T/8;c.moveTo(x+cos(a)*r*.55,y+sin(a)*r*.55);c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.stroke()}c.strokeStyle='#111'}
|
||||
export default(c,t)=>{
|
||||
const blow=sin(PI*((t*2)%1)),puff=blow>.3
|
||||
c.lineWidth=6
|
||||
c.save();c.translate(100,256);c.scale(.94,.94);c.translate(-128,-256)
|
||||
// braids hang down either side, a tapered plait with chevrons
|
||||
for(const d of[-1,1]){
|
||||
const x=128+d*64
|
||||
c.fillStyle=HAIR;c.beginPath();c.moveTo(x-14,138);c.quadraticCurveTo(x-16,190,x-8,228);c.lineTo(x+8,228);c.quadraticCurveTo(x+16,190,x+14,138);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();for(let y=150;y<224;y+=14)c.moveTo(x-12+(y-150)/14,y),c.quadraticCurveTo(x,y+9,x+12-(y-150)/14,y);c.stroke();c.lineWidth=6
|
||||
c.fillStyle=BOW;c.beginPath();c.moveTo(x,232);c.lineTo(x-12,222);c.lineTo(x-12,242);c.closePath();c.moveTo(x,232);c.lineTo(x+12,222);c.lineTo(x+12,242);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.arc(x,232,4,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=HAIR;c.beginPath();c.moveTo(x-8,236);c.lineTo(x+8,236);c.lineTo(x,254);c.closePath();c.fill();c.stroke()
|
||||
}
|
||||
c.fillStyle=DR;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,256);c.lineTo(202,256);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=COL;for(const d of[-1,1])c.beginPath(),c.moveTo(128,200),c.quadraticCurveTo(128+d*30,186,128+d*36,204),c.quadraticCurveTo(128+d*20,214,128,204),c.closePath(),c.fill(),c.stroke()
|
||||
c.fillStyle=DD;for(const[x,y]of[[86,236],[170,232],[128,246]])c.beginPath(),c.arc(x,y,5,0,T),c.fill()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,168);c.lineTo(116,194);c.quadraticCurveTo(128,204,140,194);c.lineTo(140,168);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,192);c.moveTo(140,168);c.lineTo(140,192);c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1);eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,160,13+3*blow*(d>0),6,0,0,T),c.fill()
|
||||
// pursed lips blowing, cheeks puffing
|
||||
c.lineWidth=3.5;c.fillStyle=MO;c.beginPath();c.ellipse(140,170,4+2*blow,4+2*blow,0,0,T);c.fill();c.stroke()
|
||||
if(puff){c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.arc(166,164,8+5*blow,-PI*.5,PI*.5);c.fill();c.stroke()}
|
||||
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(68,150)
|
||||
c.bezierCurveTo(52,74,88,36,128,36);c.bezierCurveTo(168,36,204,74,188,150)
|
||||
c.quadraticCurveTo(184,124,180,108);c.quadraticCurveTo(160,90,150,112);c.quadraticCurveTo(146,92,126,90);c.quadraticCurveTo(120,104,112,110)
|
||||
c.quadraticCurveTo(108,94,94,96);c.quadraticCurveTo(86,108,78,112);c.quadraticCurveTo(70,128,68,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,102,56,PI*a,PI*e),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// wand held up to her lips
|
||||
for(const[l,s]of[[24,'#111'],[15,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(196,262),c.lineTo(186,214),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
for(const[l,s]of[[11,'#111'],[5,WAND]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(186,214),c.lineTo(166,180),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(188,214,12,13,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(178,210);c.lineTo(192,210);c.moveTo(178,218);c.lineTo(192,218);c.stroke()
|
||||
for(const[l,s]of[[11,'#111'],[5,WAND]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.arc(160,170,11,0,T),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
c.restore()
|
||||
// ring position in screen space after the girl's transform
|
||||
const rx=100+(160-128)*.94,ry=256+(170-256)*.94
|
||||
// a bubble swelling on the wand, then the stream of released bubbles drifting up and popping
|
||||
const g=(t*2)%1
|
||||
if(g<.5)bubble(c,rx+ease(g/.5)*8,ry-ease(g/.5)*4,3+11*ease(g/.5))
|
||||
for(const[o,dx,r]of[[0,4,15],[.25,-14,11],[.5,0,19],[.75,-22,10]]){
|
||||
const p=(t+o)%1,x=rx+16+80*min(1,p*1.6)+dx*p+6*sin(T*p*2+o*7),y=ry-4-118*p
|
||||
if(p<.86)bubble(c,x,y,r*min(1,.4+p*3))
|
||||
else pop(c,x,y,r*1.4)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// chibi in a bunny-ear hoodie crouching and hopping, the floppy ears trailing behind the jump and flopping on landing
|
||||
const{sin,cos,PI,max,round}=Math,T=2*PI
|
||||
export const frames=16,delay=60
|
||||
const SKIN='#ffe3d1',HAIR='#f8f0e3',HD='#d8c8a8',HL='#fff',EYE='#e64980',ED='#a61e4d',HOOD='#fff',HP='#faa2c1',HPD='#f06595',LEG='#b197fc',BL='#ffa8a8'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const ear=(c,x,y,a,b)=>{
|
||||
c.save();c.translate(x,y);c.rotate(a)
|
||||
const p=(s,o)=>{c.beginPath();c.moveTo(-15*s,o);c.bezierCurveTo(-26*s,-44*s+o,(-14+b)*s,-76*s+o,b*s,-88*s+o);c.bezierCurveTo((16+b)*s,-76*s+o,26*s,-44*s+o,15*s,o);c.closePath()}
|
||||
c.lineWidth=9;c.fillStyle=HOOD;p(1,0);c.fill();c.stroke()
|
||||
c.fillStyle=HP;p(.55,-14);c.fill();c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
let sq=0,h=0,v=0
|
||||
if(t<.2)sq=.8*sin(PI*t/.2)
|
||||
else if(t<.8){const p=(t-.2)/.6;h=round(32*sin(PI*p));v=cos(PI*p);sq=-.25*v*v}
|
||||
else sq=.6*sin(PI*(t-.8)/.2)
|
||||
const happy=sq>.3,arm=t>=.2&&t<.8?1:0
|
||||
c.fillStyle='#e9ecef';c.beginPath();c.ellipse(128,237,46-h*.4,7-h*.06,0,0,T);c.fill()
|
||||
c.save();c.translate(128,233-h);c.scale(.86*(1+sq*.14),.86*(1-sq*.16));c.translate(-128,-230)
|
||||
for(const x of[116,140]){line(c,[[x,196],[x,222]],9,LEG);c.lineWidth=5;c.fillStyle=HP;c.beginPath();c.ellipse(x+(x<128?-3:3),226,11,7,0,0,T);c.fill();c.stroke()}
|
||||
for(const d of[-1,1]){const a=arm?2.3+.3*v:.4+.3*sq,sx=128+d*20,sy=146,hx=sx+d*26*sin(a),hy=sy+26*cos(a)
|
||||
line(c,[[sx,sy],[hx,hy]],9,HOOD);c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.arc(hx,hy,7,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle=HOOD;c.lineWidth=5;c.beginPath();c.moveTo(110,136);c.lineTo(146,136);c.quadraticCurveTo(156,166,154,198);c.lineTo(102,198);c.quadraticCurveTo(100,166,110,136);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=HP;c.beginPath();c.roundRect(112,172,32,16,6);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(122,140);c.lineTo(120,160);c.moveTo(134,140);c.lineTo(136,160);c.stroke()
|
||||
c.fillStyle=HP;c.lineWidth=3;for(const x of[120,136])c.beginPath(),c.arc(x,162,4,0,T),c.fill(),c.stroke()
|
||||
c.save();c.translate(128,96);c.scale(.62,.62);c.translate(-128,-128)
|
||||
for(const d of[-1,1])ear(c,128+d*36,46,d*(arm?.95+.3*max(0,v):.28+.5*sq),d*(arm?10+24*v:14-20*sq))
|
||||
c.lineWidth=9;c.fillStyle=HOOD;c.beginPath();c.moveTo(128,26);c.bezierCurveTo(214,26,226,110,212,170);c.quadraticCurveTo(206,200,176,204);c.lineTo(80,204);c.quadraticCurveTo(50,200,44,170);c.bezierCurveTo(30,110,42,26,128,26);c.fill();c.stroke()
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(70,170);c.bezierCurveTo(56,90,90,52,128,52);c.bezierCurveTo(166,52,200,90,186,170);c.closePath();c.fill()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(happy){c.lineWidth=8;for(const x of[103,153])c.beginPath(),c.moveTo(x-15,144),c.quadraticCurveTo(x,128,x+15,144),c.stroke()}else eye(c,103,140,-1),eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,162,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=6;if(arm||happy){c.fillStyle='#c92a2a';c.beginPath();c.moveTo(116,164);c.quadraticCurveTo(128,184,140,164);c.closePath();c.fill();c.stroke()}else{c.beginPath();c.moveTo(118,166);c.quadraticCurveTo(123,172,128,166);c.quadraticCurveTo(133,172,138,166);c.stroke()}
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(72,136)
|
||||
c.bezierCurveTo(64,84,94,58,128,58);c.bezierCurveTo(162,58,192,84,184,136)
|
||||
c.quadraticCurveTo(180,112,172,102);c.quadraticCurveTo(164,108,158,114);c.quadraticCurveTo(152,98,142,94);c.quadraticCurveTo(136,106,128,110)
|
||||
c.quadraticCurveTo(120,98,110,94);c.quadraticCurveTo(104,108,96,112);c.quadraticCurveTo(90,104,84,104);c.quadraticCurveTo(76,114,72,136);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=5;c.beginPath();for(const x of[110,146])c.moveTo(x,72),c.quadraticCurveTo(x+(x<128?-6:6),86,x+(x<128?-4:4),100);c.stroke();c.strokeStyle='#111'
|
||||
for(const[lw,col]of[[30,'#111'],[18,HOOD]])c.lineWidth=lw,c.strokeStyle=col,c.beginPath(),c.moveTo(56,196),c.bezierCurveTo(40,100,80,46,128,46),c.bezierCurveTo(176,46,216,100,200,196),c.stroke()
|
||||
c.strokeStyle='#111';c.restore()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// girl with emerald braids and a knit beanie roasts a marshmallow on a stick over a crackling campfire, it puffs up golden
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#12b886',HD='#087f5b',HL='#c3fae8',EYE='#a9744f',ED='#5c3d2e',BEAN='#ff922b',BND='#e8590c',JK='#e64980',JD='#a61e4d',LOG='#a9744f',LD='#7c5236',F1='#ff6b6b',F2='#ffa94d',F3='#ffe066',ST='#ced4da',MAL='#fff',TOAST='#f4b860',BL='#ff8787'
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const flame=(c,x,y,w,h,col,lw)=>{c.fillStyle=col;c.lineWidth=lw;c.beginPath();c.moveTo(x-w,y);c.bezierCurveTo(x-w*1.1,y-h*.5,x-w*.2,y-h*.6,x+w*.1,y-h);c.bezierCurveTo(x+w*.3,y-h*.55,x+w*1.2,y-h*.45,x+w,y);c.quadraticCurveTo(x,y+w*.5,x-w,y);c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t,i)=>{
|
||||
const p=T*t,glow=.5+.5*sin(p*4),puff=t<.6?min(1,t/.6):1-(t-.6)/.4*0,ms=1+.35*min(1,t/.7)*(t<.85?1:1-(t-.85)/.15),gold=min(1,t/.75)*(t<.9?1:1-(t-.9)/.1)
|
||||
const fx=172,fy=208
|
||||
c.fillStyle='#fff3bf';c.globalAlpha=.6+.2*glow;c.beginPath();c.ellipse(fx,fy+4,64,14,0,0,T);c.fill();c.globalAlpha=1
|
||||
for(const[x,a]of[[-30,.28],[30,-.28]]){c.save();c.translate(fx+x*.4,fy+2);c.rotate(a);c.fillStyle=LOG;c.lineWidth=5;c.beginPath();c.roundRect(-34,-8,68,16,8);c.fill();c.stroke();c.fillStyle='#ffd8a8';c.beginPath();c.ellipse(x>0?30:-30,0,6,8,0,0,T);c.fill();c.stroke();c.strokeStyle=LD;c.lineWidth=3;c.beginPath();c.moveTo(-18,-2);c.lineTo(10,-2);c.stroke();c.strokeStyle='#111';c.restore()}
|
||||
for(const[k,x]of[[0,-50],[1,-30],[2,32],[3,52]]){c.fillStyle=ST;c.lineWidth=4;c.beginPath();c.ellipse(fx+x,fy+12-(k%3)*2,11,7,0,0,T);c.fill();c.stroke()}
|
||||
const fl=[[-14,26,58,F1,1.3],[12,22,52,F1,2.1],[0,30,74,F1,0]]
|
||||
for(const[x,w,h,col,o]of fl)flame(c,fx+x+3*sin(p*3+o),fy,w,h*(1+.12*sin(p*4+o)),col,5)
|
||||
for(const[x,w,h,o]of[[-6,18,48,.7],[8,16,42,2.4],[0,20,56,1.5]])flame(c,fx+x+2*sin(p*3+o),fy-2,w,h*(1+.14*sin(p*4+o)),F2,0)
|
||||
flame(c,fx+2*sin(p*4),fy-4,12,34*(1+.12*sin(p*4+.3)),F3,0)
|
||||
c.lineWidth=3.5;for(let k=0;k<3;k++){const u=(t*2+k/3)%1,x=fx-20+k*20+10*sin(u*PI*2+k),y=fy-70-u*70,r=4*(1-u);if(r>.8){c.fillStyle=k%2?F3:F2;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke()}}
|
||||
const head=g=>{c.save();c.translate(76,94);c.rotate(-.05);c.scale(.56,.56);c.translate(-128,-120);g();c.restore()}
|
||||
head(()=>{
|
||||
for(const d of[-1,1]){c.save();c.translate(128+d*56,148);c.rotate(-d*.08);c.fillStyle=HAIR;c.lineWidth=9;for(let k=0;k<4;k++){c.beginPath();c.ellipse(0,k*24,15-k*1.5,16,0,0,T);c.fill();c.stroke()}c.fillStyle=BEAN;c.beginPath();c.ellipse(0,92,10,7,0,0,T);c.fill();c.stroke();c.restore()}
|
||||
c.lineWidth=9;c.fillStyle=HD;c.beginPath();c.moveTo(128,40);c.bezierCurveTo(194,40,200,96,192,150);c.lineTo(64,150);c.bezierCurveTo(56,96,62,40,128,40);c.fill();c.stroke()
|
||||
})
|
||||
c.fillStyle=JK;c.lineWidth=5;c.beginPath();c.moveTo(56,132);c.lineTo(96,132);c.quadraticCurveTo(116,160,112,200);c.lineTo(40,200);c.quadraticCurveTo(36,160,56,132);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=JD;c.lineWidth=4;c.beginPath();c.moveTo(76,138);c.lineTo(76,198);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(60,132);c.quadraticCurveTo(76,146,92,132);c.quadraticCurveTo(76,126,60,132);c.fill();c.stroke()
|
||||
c.fillStyle=LOG;c.lineWidth=5;c.beginPath();c.roundRect(24,196,104,22,10);c.fill();c.stroke();c.fillStyle='#ffd8a8';c.beginPath();c.ellipse(28,207,7,10,0,0,T);c.fill();c.stroke();c.strokeStyle=LD;c.lineWidth=3;c.beginPath();c.moveTo(50,204);c.lineTo(110,204);c.moveTo(64,212);c.lineTo(100,212);c.stroke();c.strokeStyle='#111'
|
||||
for(const x of[56,90]){c.fillStyle=JD;c.lineWidth=5;c.beginPath();c.roundRect(x-10,192,26,16,8);c.fill();c.stroke()}
|
||||
for(const x of[64,96]){c.fillStyle='#868e96';c.lineWidth=5;c.beginPath();c.roundRect(x-4,214,16,20,6);c.fill();c.stroke();c.fillStyle=BND;c.beginPath();c.ellipse(x+6,236,13,7,0,0,T);c.fill();c.stroke()}
|
||||
const tip=[fx-6+3*sin(p),fy-86+2*cos(p)],hand=[110,164]
|
||||
line(c,[hand,tip],4,'#d4a373')
|
||||
line(c,[[92,144],[106,154],hand],8,JK);c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.arc(...hand,7,0,T);c.fill();c.stroke()
|
||||
c.save();c.translate(...tip);c.rotate(-.5);c.scale(ms,ms)
|
||||
c.fillStyle=MAL;c.lineWidth=4;c.beginPath();c.roundRect(-11,-9,22,18,8);c.fill()
|
||||
c.save();c.clip();c.globalAlpha=gold;c.fillStyle=TOAST;c.beginPath();c.ellipse(4,6,16,9,0,0,T);c.fill();c.globalAlpha=1;c.restore()
|
||||
c.beginPath();c.roundRect(-11,-9,22,18,8);c.stroke()
|
||||
if(gold>.6){c.fillStyle='#111';for(const x of[-4,4])c.beginPath(),c.arc(x,-1,1.6,0,T),c.fill();c.lineWidth=2;c.beginPath();c.arc(0,2,2.5,.2*PI,.8*PI);c.stroke()}
|
||||
c.restore()
|
||||
if(gold>.85){c.fillStyle=F3;c.lineWidth=3;for(const[dx,dy,s]of[[-20,-16,5],[18,-20,4]]){const x=tip[0]+dx,y=tip[1]+dy;c.beginPath();for(let k=0;k<8;k++){const r=k%2?s*.4:s,b=k*PI/4;c.lineTo(x+r*cos(b),y+r*sin(b))}c.closePath();c.fill();c.stroke()}}
|
||||
head(()=>{
|
||||
c.lineWidth=9;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
for(const x of[108,158]){c.fillStyle='#fff';c.beginPath();c.ellipse(x,140,14,17,0,0,T);c.fill();c.save();c.clip();c.beginPath();c.ellipse(x+5,142,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x+5,132,13,10,0,0,T);c.fill();c.restore();c.lineWidth=2.5;c.beginPath();c.ellipse(x+5,142,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+7,143,5.5,7.5,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x+2,135,4.5,0,T);c.fill();c.fillStyle=F3;c.globalAlpha=.5+.5*glow;c.beginPath();c.arc(x+10,148,3,0,T);c.fill();c.globalAlpha=1;c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,134);c.quadraticCurveTo(x,120,x+16,134);c.stroke()}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(133+d*42,162,13,7,0,0,T),c.fill()
|
||||
c.fillStyle='#c92a2a';c.lineWidth=6;c.beginPath();c.moveTo(124,164);c.quadraticCurveTo(138,182,152,164);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.moveTo(70,140);c.bezierCurveTo(54,90,80,70,128,70);c.bezierCurveTo(176,70,202,90,186,140)
|
||||
c.quadraticCurveTo(184,118,176,106);c.quadraticCurveTo(168,104,162,112);c.quadraticCurveTo(156,100,146,98);c.quadraticCurveTo(140,108,132,112)
|
||||
c.quadraticCurveTo(126,100,118,98);c.quadraticCurveTo(112,108,104,112);c.quadraticCurveTo(98,100,90,100);c.quadraticCurveTo(84,108,80,112);c.quadraticCurveTo(72,120,70,140);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=BEAN;c.beginPath();c.moveTo(62,92);c.bezierCurveTo(56,20,200,20,194,92);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=BND;c.lineWidth=5;for(const x of[96,128,160])c.beginPath(),c.moveTo(x,36+(x==128?-6:0)),c.lineTo(x+(x-128)*.1,88),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=BND;c.lineWidth=9;c.beginPath();c.roundRect(56,80,144,22,11);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(128,22,16,0,T);c.fill();c.stroke()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// girl peeking over a huge heart chocolate box she thrusts toward you, eyes squeezed shut, blushing hard, box trembling, hearts rising
|
||||
const{sin,cos,PI}=Math,T=2*PI
|
||||
export const frames=12,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#ffc9de',HD='#f783ac',HL='#fff0f6',SLV='#9775fa',SD='#7048e8',BOX='#e03131',BXD='#a61e1e',BXL='#ff8787',RIB='#faa2c1',RD='#e64980',BOW='#e64980',BL='#ff6b81',HT='#f06595'
|
||||
const heart=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.beginPath();c.moveTo(0,8);c.bezierCurveTo(-13,-1,-9,-12,0,-5);c.bezierCurveTo(9,-12,13,-1,0,8);c.restore()}
|
||||
export default(c,t)=>{
|
||||
const tr=1.8*sin(6*T*t),pu=1+.05*Math.max(0,sin(2*T*t)),bp=1+.15*sin(2*T*t)
|
||||
c.save();c.translate(0,-28)
|
||||
// long hair behind
|
||||
c.lineWidth=7;c.fillStyle=HD;c.beginPath();c.moveTo(128,40);c.bezierCurveTo(206,40,216,120,216,200);c.quadraticCurveTo(222,250,200,272);c.lineTo(56,272);c.quadraticCurveTo(34,250,40,200);c.bezierCurveTo(40,120,50,40,128,40);c.fill();c.stroke()
|
||||
// big bow on top
|
||||
c.fillStyle=BOW;c.lineWidth=6;for(const d of[-1,1])c.beginPath(),c.moveTo(184,62),c.quadraticCurveTo(184+d*30,30,184+d*30,70),c.closePath(),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(184,62,8,0,T);c.fill();c.stroke()
|
||||
// face
|
||||
c.fillStyle=SKIN;c.lineWidth=7;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
c.lineWidth=7;for(const d of[-1,1]){const x=128+d*25;c.beginPath();c.moveTo(x+d*12,128);c.lineTo(x-d*8,138);c.lineTo(x+d*12,148);c.stroke()}
|
||||
c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*14,112),c.quadraticCurveTo(128+d*26,104,128+d*38,112),c.stroke()
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*38,160,18*bp,9*bp,0,0,T),c.fill()
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-9,-3,3,9])c.beginPath(),c.moveTo(128+d*38+k-2,165),c.lineTo(128+d*38+k+2,155),c.stroke()
|
||||
// bangs
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,150)
|
||||
c.bezierCurveTo(50,74,88,38,128,38);c.bezierCurveTo(168,38,206,74,190,150)
|
||||
c.quadraticCurveTo(186,120,178,106);c.quadraticCurveTo(170,104,164,112);c.quadraticCurveTo(160,96,150,94);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,122,92);c.quadraticCurveTo(116,104,106,108);c.quadraticCurveTo(102,96,92,98);c.quadraticCurveTo(86,106,80,112)
|
||||
c.quadraticCurveTo(70,126,66,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,58,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
// tremble lines and a sweat drop by her head
|
||||
c.lineWidth=4;for(const d of[-1,1])for(const y of[78,94])c.beginPath(),c.moveTo(128+d*(92+tr),y),c.lineTo(128+d*(102+tr),y-4),c.stroke()
|
||||
c.fillStyle='#a5d8ff';c.beginPath();c.moveTo(190,64);c.quadraticCurveTo(198,78,190,84);c.quadraticCurveTo(182,78,190,64);c.fill();c.stroke()
|
||||
// sleeves from the bottom corners
|
||||
for(const d of[-1,1])for(const[l,s]of[[38,'#111'],[27,SLV]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128+d*112,262),c.quadraticCurveTo(128+d*96,222,128+d*(84+tr),198),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// the big heart box, pushed forward
|
||||
c.save();c.translate(128+tr,196);c.scale(pu,pu)
|
||||
c.fillStyle=BXD;c.lineWidth=7;heart(c,0,3,7);c.fill();c.stroke()
|
||||
c.fillStyle=BOX;heart(c,0,-2,6.8);c.fill();c.stroke()
|
||||
c.strokeStyle=BXL;c.lineWidth=6;c.beginPath();c.arc(-38,-36,16,PI*1.1,PI*1.6);c.stroke();c.strokeStyle='#111'
|
||||
c.save();heart(c,0,-2,6.8);c.clip()
|
||||
for(const[l,s]of[[22,'#111'],[13,RIB]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(-90,-40),c.lineTo(90,40),c.stroke()
|
||||
c.restore();c.strokeStyle='#111'
|
||||
c.fillStyle=RIB;c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(6,4),c.quadraticCurveTo(6+d*30,-22,6+d*26,16),c.closePath(),c.fill(),c.stroke()
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(6,8),c.lineTo(6+d*14,34),c.lineTo(6+d*4,30),c.closePath(),c.fill(),c.stroke()
|
||||
c.fillStyle=RD;c.beginPath();c.arc(6,6,7,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
// hands gripping the sides
|
||||
c.fillStyle=SKIN;c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*(78+tr*d),196,13,15,d*.3,0,T),c.fill(),c.stroke()
|
||||
// hearts rising beside her
|
||||
c.fillStyle=HT;c.lineWidth=4
|
||||
for(const[x0,o]of[[30,0],[226,.5]]){const p=(t+o)%1,s=1.4*sin(PI*p);if(s>.15)heart(c,x0+6*sin(2*T*p),150-p*120,s),c.fill(),c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// cyber girl with split cyan and pink hair and a glowing visor tapping holographic screens that pop up and slide around her
|
||||
const{sin,cos,PI,max,min,round}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HC='#66d9e8',HCD='#1098ad',HP='#f783ac',HPD='#c2255c',EYE='#9775fa',ED='#5f3dc4',JK='#f8f9fa',JD='#495057',CY='#3bc9db',GL='#c5f6fa',BL='#ffa8a8'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const glow=(c,f,w=3)=>{for(const[lw,s]of[[w+5,CY],[w,'#fff']]){c.lineWidth=lw;c.strokeStyle=s;f();c.stroke()}c.strokeStyle='#111'}
|
||||
const panel=(c,x,y,w,h,s,k,flash)=>{
|
||||
if(s<=.02)return
|
||||
c.save();c.translate(x,y);c.scale(s,s)
|
||||
const r=()=>{c.beginPath();c.roundRect(-w/2,-h/2,w,h,6)}
|
||||
c.globalAlpha=.85;c.fillStyle=flash?'#fff':GL;r();c.fill();c.globalAlpha=1;glow(c,r)
|
||||
c.strokeStyle=HCD;c.lineWidth=3;c.beginPath()
|
||||
if(k==0)for(let j=0;j<3;j++)c.moveTo(-w/2+8,-h/2+10+j*9),c.lineTo(-w/2+8+(w-16)*[.9,.6,.75][j],-h/2+10+j*9)
|
||||
else if(k==1){c.moveTo(-w/2+8,h/2-8);for(let j=0;j<5;j++)c.lineTo(-w/2+8+j*(w-16)/4,h/2-10-[6,14,9,18,12][j]*h/40)}
|
||||
else{c.arc(0,0,min(w,h)*.28,0,T);c.moveTo(0,0);c.lineTo(min(w,h)*.28,0)}
|
||||
c.stroke();c.strokeStyle='#111';c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tap=(t*2)%1,push=tap<.25?sin(PI*tap/.25):0,fl=tap<.12&&tap>.0
|
||||
const sA=min(1,max(0,(t<.5?t:1-t)*6)),yA=round(4*sin(T*t)),sC=min(1,max(0,sin(T*(t+.25))*3))
|
||||
const g=(a,b)=>{const q=c.createLinearGradient(60,0,196,0);q.addColorStop(.5,a);q.addColorStop(.5,b);return q}
|
||||
c.lineWidth=6;c.fillStyle=g(HCD,HPD);c.beginPath();c.moveTo(128,36);c.bezierCurveTo(198,36,212,98,204,150);c.lineTo(210,208);c.lineTo(192,198);c.lineTo(184,212);c.lineTo(72,212);c.lineTo(64,198);c.lineTo(46,208);c.lineTo(52,150);c.bezierCurveTo(44,98,58,36,128,36);c.fill();c.stroke()
|
||||
panel(c,46,70+yA,58,42,1,0,false)
|
||||
c.fillStyle=JK;c.beginPath();c.moveTo(98,186);c.quadraticCurveTo(58,194,50,256);c.lineTo(206,256);c.quadraticCurveTo(198,194,158,186);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=JD;c.beginPath();c.moveTo(104,188);c.lineTo(128,214);c.lineTo(152,188);c.lineTo(150,176);c.lineTo(106,176);c.closePath();c.fill();c.stroke()
|
||||
glow(c,()=>{c.beginPath();c.moveTo(96,210);c.lineTo(84,256);c.moveTo(160,210);c.lineTo(172,256)},2)
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1);eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,163,12,6,0,0,T),c.fill()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(118,167);c.quadraticCurveTo(128,174,140,165);c.stroke()
|
||||
const vz=()=>{c.beginPath();c.moveTo(68,124);c.quadraticCurveTo(128,112,188,124);c.lineTo(184,152);c.quadraticCurveTo(128,162,72,152);c.closePath()}
|
||||
c.globalAlpha=.55;c.fillStyle=CY;vz();c.fill();c.globalAlpha=1;c.lineWidth=5;vz();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(82,128);c.lineTo(110,123);c.moveTo(150,124);c.lineTo(160,126);c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=6
|
||||
c.fillStyle=g(HC,HP);c.beginPath();c.moveTo(62,150)
|
||||
c.bezierCurveTo(48,74,88,36,128,36);c.bezierCurveTo(168,36,208,74,194,150)
|
||||
c.quadraticCurveTo(190,118,180,104);c.quadraticCurveTo(170,104,164,114);c.quadraticCurveTo(158,98,146,92);c.quadraticCurveTo(140,104,132,112)
|
||||
c.quadraticCurveTo(126,96,114,88);c.quadraticCurveTo(108,104,98,110);c.quadraticCurveTo(94,98,86,98);c.quadraticCurveTo(80,108,78,116)
|
||||
c.quadraticCurveTo(68,124,62,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(128,104,56,PI*1.25,PI*1.36);c.moveTo(150,52);c.arc(128,104,56,PI*1.6,PI*1.72);c.stroke();c.strokeStyle='#111'
|
||||
for(const d of[-1,1]){c.fillStyle=JD;c.lineWidth=5;c.beginPath();c.roundRect(128+d*58-10,108,20,30,8);c.fill();c.stroke();c.fillStyle=(t*4|0)%2&&d>0?'#fff':CY;c.beginPath();c.arc(128+d*58,123,4,0,T);c.fill()}
|
||||
panel(c,214,80,52,40,sC,2,false)
|
||||
const px=196,py=168
|
||||
panel(c,px,py,74,56,1,1,fl)
|
||||
if(tap<.4){const r=8+tap*90;c.globalAlpha=max(0,1-tap/.4);glow(c,()=>{c.beginPath();c.arc(px-14,py+4,r,0,T)},2);c.globalAlpha=1}
|
||||
const hx=px-14-6*push,hy=py+4+10*(1-push)
|
||||
for(const[lw,s]of[[17,'#111'],[9,JK]])c.lineWidth=lw,c.strokeStyle=s,c.beginPath(),c.moveTo(184,256),c.quadraticCurveTo(186,214,hx+6,hy+18),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=4;c.fillStyle=SKIN;c.beginPath();c.ellipse(hx+6,hy+16,10,9,0,0,T);c.fill();c.stroke()
|
||||
for(const[lw,s]of[[11,'#111'],[5,SKIN]])c.lineWidth=lw,c.strokeStyle=s,c.beginPath(),c.moveTo(hx+4,hy+10),c.lineTo(hx,hy),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// little devil with crimson hair and tiny horns hovering on flapping bat wings, spade tail swishing, poking her toy trident with a sly fanged grin
|
||||
const{sin,cos,PI,round,max}=Math,T=2*PI
|
||||
export const frames=12,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#e03131',HD='#a51111',HL='#ffc9c9',EYE='#ff922b',ED='#d9480f',HORN='#495057',WG='#9c36b5',WL='#e599f7',DR='#862e9c',DD='#5f3dc4',GO='#ffd43b',GD='#f59f00',TL='#c92a2a',BL='#ffa8a8'
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const sly=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+4,14,12,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+4,14,12,0,0,T);c.clip();c.fillStyle=EYE;c.beginPath();c.ellipse(x-d*3,y+8,11,13,0,0,T);c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x-d*3,y,12,8,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x-d*3,y+8,3,7,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-d*3-4,y+3,3.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-17,y-6+d*3);c.lineTo(x+17,y-6-d*3);c.moveTo(x+d*16,y-6-d*3);c.lineTo(x+d*22,y-11-d*3);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-12,y-22+d*6);c.lineTo(x+12,y-22-d*2);c.stroke()
|
||||
}
|
||||
const wing=(c,x,y,d,a)=>{
|
||||
c.save();c.translate(x,y);c.scale(d,1);c.rotate(-a)
|
||||
const p=()=>{c.beginPath();c.moveTo(0,0);c.lineTo(30,-34);c.lineTo(76,-46);c.quadraticCurveTo(70,-24,82,-8);c.quadraticCurveTo(62,-12,56,6);c.quadraticCurveTo(40,-2,30,16);c.quadraticCurveTo(18,4,0,14);c.closePath()}
|
||||
c.lineWidth=14;c.strokeStyle='#fff';p();c.stroke();c.fillStyle=WG;p();c.fill();c.strokeStyle=WL;c.lineWidth=3;c.beginPath();for(const[ex,ey]of[[76,-46],[56,6],[30,16]])c.moveTo(30,-34),c.lineTo(ex,ey);c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;p();c.stroke();c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const by=round(4*sin(T*t)),a=.34*sin(2*T*t),sw=sin(T*t+1),pk=max(0,sin(2*T*t-1))
|
||||
c.translate(0,by)
|
||||
const tp=[[124,190]];for(let k=1;k<=10;k++){const s=k/10;tp.push([124-60*s+10*sin(s*PI*2+sw),196+28*sin(s*PI)-34*s*s+14*sw*s])}
|
||||
line(c,tp,4,TL)
|
||||
const[tx,ty]=tp[10],ta=.6+.5*sw
|
||||
c.save();c.translate(tx,ty);c.rotate(ta);c.fillStyle=TL;c.lineWidth=5;c.beginPath();c.moveTo(0,4);c.bezierCurveTo(-18,-2,-14,-22,0,-26);c.bezierCurveTo(14,-22,18,-2,0,4);c.closePath();c.fill();c.stroke();c.restore()
|
||||
for(const d of[-1,1])wing(c,128+d*14,138,d,a)
|
||||
for(const[x,s]of[[118,-1],[138,1]]){line(c,[[x,196],[x+s*3,218]],7,SKIN);c.lineWidth=5;c.fillStyle=DD;c.beginPath();c.ellipse(x+s*5,222,9,7,s*.3,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle=DR;c.lineWidth=5;c.beginPath();c.moveTo(112,136);c.lineTo(144,136);c.bezierCurveTo(156,160,164,180,170,200);c.lineTo(158,194);c.lineTo(148,204);c.lineTo(138,196);c.lineTo(128,206);c.lineTo(118,196);c.lineTo(108,204);c.lineTo(98,194);c.lineTo(86,200);c.bezierCurveTo(92,180,100,160,112,136);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.moveTo(128,148),c.lineTo(128+d*16,136),c.lineTo(128+d*18,146),c.closePath(),c.fill(),c.stroke()
|
||||
c.fillStyle=GO;c.lineWidth=4;c.beginPath();c.moveTo(128,154);c.bezierCurveTo(118,146,120,138,128,144);c.bezierCurveTo(136,138,138,146,128,154);c.fill();c.stroke()
|
||||
line(c,[[114,142],[98,160],[104,172]],7,SKIN)
|
||||
const hx=168+6*pk,hy=156-8*pk,ang=.45
|
||||
c.save();c.translate(hx,hy);c.rotate(ang)
|
||||
for(const[lw,col]of[[11,'#111'],[5,GO]])c.lineWidth=lw,c.strokeStyle=col,c.beginPath(),c.moveTo(0,30),c.lineTo(0,-64),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle=GO;c.lineWidth=5;c.beginPath();c.moveTo(-20,-62);c.quadraticCurveTo(-20,-74,-22,-88);c.lineTo(-14,-80);c.quadraticCurveTo(-12,-70,-6,-66);c.lineTo(-5,-86);c.lineTo(0,-100);c.lineTo(5,-86);c.lineTo(6,-66);c.quadraticCurveTo(12,-70,14,-80);c.lineTo(22,-88);c.quadraticCurveTo(20,-74,20,-62);c.quadraticCurveTo(0,-54,-20,-62);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#ff6b6b';c.beginPath();c.arc(0,-60,5,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
line(c,[[142,142],[158,152],[hx,hy]],7,SKIN)
|
||||
c.lineWidth=4;c.fillStyle=SKIN;c.beginPath();c.arc(hx,hy,7,0,T);c.fill();c.stroke()
|
||||
if(pk>.6){c.lineWidth=4;const ex=hx+cos(ang-PI/2)*104,ey=hy+sin(ang-PI/2)*104;for(const b of[-.7,0,.7]){const q=ang-PI/2+b;c.beginPath();c.moveTo(ex+cos(q)*12,ey+sin(q)*12);c.lineTo(ex+cos(q)*22,ey+sin(q)*22);c.stroke()}}
|
||||
c.save();c.translate(128,92);c.scale(.6,.6);c.translate(-128,-128)
|
||||
c.lineWidth=9;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(200,36,212,98,202,150);c.quadraticCurveTo(190,166,176,160);c.lineTo(80,160);c.quadraticCurveTo(66,166,54,150);c.bezierCurveTo(44,98,56,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
sly(c,103,140,-1);sly(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=6;c.fillStyle='#c92a2a';c.beginPath();c.moveTo(108,162);c.quadraticCurveTo(128,186,150,160);c.quadraticCurveTo(128,170,108,162);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(134,167);c.lineTo(138,176);c.lineTo(142,165);c.fill();c.stroke()
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,142)
|
||||
c.bezierCurveTo(52,70,90,36,128,36);c.bezierCurveTo(166,36,204,70,190,142)
|
||||
c.quadraticCurveTo(186,112,176,100);c.quadraticCurveTo(168,104,162,114);c.quadraticCurveTo(156,96,146,90);c.quadraticCurveTo(140,104,132,110)
|
||||
c.quadraticCurveTo(126,96,116,90);c.quadraticCurveTo(110,104,100,112);c.quadraticCurveTo(96,98,88,96);c.quadraticCurveTo(82,108,80,114)
|
||||
c.quadraticCurveTo(70,114,66,142);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=7;for(const[a0,e]of[[1.26,1.38],[1.46,1.54],[1.62,1.72]])c.beginPath(),c.arc(128,104,56,PI*a0,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
for(const d of[-1,1]){c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.arc(128+d*56,52,22,0,T);c.fill();c.stroke()
|
||||
const hp=()=>{c.beginPath();c.moveTo(128+d*24,52);c.quadraticCurveTo(128+d*22,22,128+d*38,6);c.quadraticCurveTo(128+d*36,30,128+d*44,48);c.closePath()}
|
||||
c.lineWidth=20;c.strokeStyle='#fff';hp();c.stroke();c.fillStyle=HORN;hp();c.fill();c.lineWidth=8;c.strokeStyle='#111';hp();c.stroke()
|
||||
c.strokeStyle='#adb5bd';c.lineWidth=4;c.beginPath();c.moveTo(128+d*29,40);c.quadraticCurveTo(128+d*29,26,128+d*35,16);c.stroke();c.strokeStyle='#111'}
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// dj girl with copper hair and big headphones scratching a record back and forth, nodding to the beat, light beams behind her
|
||||
const{sin,cos,PI,abs}=Math,T=2*PI
|
||||
export const frames=12,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#e07a3a',HD='#b0561e',HL='#f8c9a0',EYE='#15aabf',ED='#0b7285',BL='#ffa8a8',HOOD='#9775fa',HOD='#7048e8',PH='#38d9a9',PHD='#0ca678',LED='#f783ac',DECK='#e9ecef',DKD='#adb5bd',REC='#495057',LAB='#ffd43b',B1='#faa2c1',B2='#74c0fc'
|
||||
const ds=(c,a,b,s,f)=>{for(const[l,k]of[[a,'#111'],[b,s]])c.lineWidth=l,c.strokeStyle=k,c.beginPath(),f(),c.stroke();c.strokeStyle='#111'}
|
||||
const note=(c,x,y,s,col)=>{ds(c,8,3,col,()=>{c.moveTo(x+6*s,y);c.lineTo(x+6*s,y-22*s);c.lineTo(x+16*s,y-18*s)});c.fillStyle=col;c.lineWidth=3.5;c.beginPath();c.ellipse(x,y,7*s,5*s,-.4,0,T);c.fill();c.stroke()}
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x-2,y+6,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x-2,y-4,13,10,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x-2,y+7,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-6,y,4.5,0,T);c.fill();c.beginPath();c.arc(x+3,y+12,2,0,T);c.fill()
|
||||
c.fillStyle=SKIN;c.fillRect(x-16,y-20,32,9);c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-9);c.quadraticCurveTo(x,y-16,x+16,y-9);c.moveTo(x+d*15,y-9);c.lineTo(x+d*21,y-14);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const nod=abs(sin(2*PI*t)),th=1.1*sin(2*T*t),cf=10*sin(2*T*t)
|
||||
for(const[x,d,col]of[[50,-1,B1],[206,1,B2]]){c.save();c.translate(x,206);c.rotate(d*.45);c.fillStyle=col;c.lineWidth=5;c.beginPath();c.moveTo(-6,0);c.lineTo(-30,-190);c.lineTo(30,-190);c.lineTo(6,0);c.closePath();c.fill();c.stroke();c.restore()}
|
||||
for(const[x,o,col]of[[40,0,'#ffd43b'],[212,.5,'#f783ac']]){const p=(t+o)%1,s=sin(PI*p);if(s>.2)note(c,x+5*sin(p*T),120-60*p,.5+.4*s,col)}
|
||||
c.lineWidth=7;c.fillStyle=HOOD;c.beginPath();c.moveTo(100,150);c.quadraticCurveTo(58,160,50,230);c.lineTo(206,230);c.quadraticCurveTo(198,160,156,150);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=HOD;c.lineWidth=5;c.beginPath();c.moveTo(102,152);c.quadraticCurveTo(128,176,154,152);c.quadraticCurveTo(150,168,128,170);c.quadraticCurveTo(106,168,102,152);c.fill();c.stroke()
|
||||
ds(c,7,3,'#fff',()=>{c.moveTo(118,166);c.lineTo(116,190);c.moveTo(138,166);c.lineTo(140,190)})
|
||||
c.fillStyle=SKIN;c.lineWidth=6;c.beginPath();c.moveTo(118,136);c.lineTo(118,154);c.quadraticCurveTo(128,162,138,154);c.lineTo(138,136);c.fill();c.beginPath();c.moveTo(118,136);c.lineTo(118,152);c.moveTo(138,136);c.lineTo(138,152);c.stroke()
|
||||
c.save();c.translate(128,96+Math.round(3*nod));c.scale(.7,.7);c.translate(-128,-120)
|
||||
c.lineWidth=7;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(198,36,210,100,200,166);c.lineTo(56,166);c.bezierCurveTo(46,100,58,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1);eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.fillStyle='#c92a2a';c.beginPath();c.moveTo(112,164);c.quadraticCurveTo(128,184,144,164);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(114,165);c.lineTo(142,165);c.lineTo(140,170);c.lineTo(116,170);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(64,150)
|
||||
c.bezierCurveTo(48,76,86,38,128,38);c.bezierCurveTo(170,38,208,76,192,150)
|
||||
c.lineTo(186,120);c.lineTo(178,126);c.lineTo(174,102);c.lineTo(160,110);c.quadraticCurveTo(150,94,134,86)
|
||||
c.quadraticCurveTo(120,106,90,118);c.quadraticCurveTo(100,104,98,94);c.lineTo(80,110);c.lineTo(76,126);c.lineTo(70,122);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[s,e]of[[1.3,1.42],[1.52,1.6],[1.68,1.78]])c.beginPath(),c.arc(128,108,58,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
ds(c,24,14,PH,()=>{c.arc(128,118,78,PI*1.04,PI*1.96)})
|
||||
for(const d of[-1,1]){c.fillStyle=PH;c.lineWidth=7;c.beginPath();c.ellipse(128+d*76,130,22,32,0,0,T);c.fill();c.stroke();c.fillStyle=PHD;c.beginPath();c.ellipse(128+d*82,130,10,20,0,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle=nod>.5?LED:'#fff';c.lineWidth=5;c.beginPath();c.ellipse(128+d*76,130,15,24,0,0,T);c.stroke();c.strokeStyle='#111'}
|
||||
c.restore()
|
||||
c.lineWidth=7;c.fillStyle=DECK;c.beginPath();c.moveTo(18,192);c.lineTo(238,192);c.lineTo(250,250);c.lineTo(6,250);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=DKD;c.lineWidth=6;c.beginPath();c.moveTo(6,238);c.lineTo(250,238);c.lineTo(250,250);c.lineTo(6,250);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=14;c.strokeStyle='#fff';c.beginPath();c.ellipse(82,216,56,20,0,0,T);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=REC;c.lineWidth=6;c.beginPath();c.ellipse(82,216,56,20,0,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle=DKD;c.lineWidth=2;for(const r of[.5,.72,.9])c.beginPath(),c.ellipse(82,216,56*r,20*r,0,0,T),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=LAB;c.lineWidth=4;c.beginPath();c.ellipse(82,216,18,7,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(82,216);c.lineTo(82+16*cos(th),216+6*sin(th));c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(82,216,3,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;for(const[x,y]of[[164,206],[182,206],[220,206]])c.beginPath(),c.arc(x,y,6,0,T),c.fill(),c.stroke()
|
||||
c.fillStyle=DKD;c.lineWidth=4;c.beginPath();c.roundRect(160,222,64,8,4);c.fill();c.stroke()
|
||||
const fx=192+cf,a=th+2.4,rx=82+44*cos(a),ry=216+15*sin(a)
|
||||
ds(c,20,11,HOOD,()=>{c.moveTo(104,172);c.lineTo(rx+6,ry-10)})
|
||||
ds(c,20,11,HOOD,()=>{c.moveTo(152,172);c.lineTo(fx-4,212)})
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(fx-7,218,14,16,4);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.lineWidth=5;for(const[x,y]of[[rx,ry],[fx,218]])c.beginPath(),c.ellipse(x,y,12,10,0,0,T),c.fill(),c.stroke()
|
||||
for(const d of[-1,1])if(abs(cos(2*T*t))<.6)ds(c,6,2.5,'#fff',()=>{c.moveTo(rx+d*18,ry-6);c.lineTo(rx+d*26,ry-10)})
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// easter chibi with bunny-ear headband and a basket: spots a painted egg in the grass, pulls it out, holds it up sparkling, drops it in her basket
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=20,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#ffe066',HD='#f59f00',HL='#fff9db',EYE='#f06595',ED='#a61e4d',DRESS='#a5d8ff',DD='#4dabf7',EAR='#fff',PINK='#faa2c1',BASK='#c08a5e',BD='#8d6346',GRASS='#69db7c',GD='#2f9e44',SHOE='#f783ac',BL='#ffa8a8',MO='#c92a2a',EGG=[['#b197fc','#fff'],['#f783ac','#ffd43b'],['#4dabf7','#fff'],['#ffd43b','#69db7c']]
|
||||
const cl=x=>max(0,min(1,x)),ease=x=>x*x*(3-2*x),lerp=(a,b,k)=>[a[0]+(b[0]-a[0])*k,a[1]+(b[1]-a[1])*k]
|
||||
const eye=(c,x,y,d,dx,dy)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip()
|
||||
c.fillStyle=EYE;c.beginPath();c.ellipse(x+dx,y+2+dy,11.5,15,0,0,T);c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x+dx,y-8+dy,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+dx,y+3+dy,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4+dx,y-5+dy,4.5,0,T);c.fill();c.beginPath();c.arc(x+5+dx,y+8+dy,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const egg=(c,x,y,s,k,a=0)=>{const[f,p]=EGG[k];c.save();c.translate(x,y);c.rotate(a);c.scale(s,s)
|
||||
const sh=()=>{c.beginPath();c.moveTo(0,-15);c.bezierCurveTo(11,-15,13,4,12,7);c.bezierCurveTo(10,15,-10,15,-12,7);c.bezierCurveTo(-13,4,-11,-15,0,-15)}
|
||||
sh();c.fillStyle=f;c.lineWidth=4/s;c.fill();c.save();c.clip();c.strokeStyle=p;c.lineWidth=3.5/s;c.beginPath();for(let i=0;i<=6;i++)c.lineTo(-14+i*4.7,(i%2?-1:3));c.stroke()
|
||||
c.fillStyle=p;for(const[px,py]of[[-5,-8],[5,-8],[0,9]])c.beginPath(),c.arc(px,py,2.2,0,T),c.fill();c.restore();sh();c.stroke();c.restore()}
|
||||
const spark=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r);for(const[px,py,qx,qy]of[[r,0,r*.18,-r*.18],[0,r,r*.18,r*.18],[-r,0,-r*.18,r*.18],[0,-r,-r*.18,-r*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const BK=[78,198],GR=[192,222],UP=[170,64]
|
||||
const H=t<.15?lerp(BK,GR,ease(t/.15)):t<.3?GR:t<.5?lerp(GR,UP,ease((t-.3)/.2)):t<.78?UP:t<.86?lerp(UP,[150,192],ease((t-.78)/.08)):t<.95?lerp([150,192],BK,ease((t-.86)/.09)):BK
|
||||
const held=t>=.3&&t<.95,inGrass=t<.3||t>=.9,up=t>=.5&&t<.78,Z=.62,look=t<.3?[4,6]:t<.5?[3,-2]:t<.78?[3,-6]:[-3,4],pop=t>=.9?ease(cl((t-.9)/.06)):1
|
||||
// grass tuft with an egg peeking out
|
||||
const blades=(x0,y0,n,hs,sw)=>{c.fillStyle=GRASS;c.lineWidth=4;for(let k=0;k<n;k++){const x=x0+k*13,h=hs[k%hs.length],b=sw*sin(T*t+k);c.beginPath();c.moveTo(x-8,y0);c.quadraticCurveTo(x-2,y0-h*.6,x+b,y0-h);c.quadraticCurveTo(x+3,y0-h*.5,x+8,y0);c.closePath();c.fill();c.stroke()}}
|
||||
blades(162,242,6,[30,38,26],3)
|
||||
if(inGrass)egg(c,GR[0],228,.95*pop,0,-.2)
|
||||
blades(168,248,6,[18,24,14],1.5)
|
||||
if(t>=.9&&t<.98){c.fillStyle='#ffd43b';c.lineWidth=3;spark(c,GR[0]+18,206,8*sin(PI*(t-.9)/.08))}
|
||||
// legs and shoes
|
||||
for(const d of[-1,1])for(const[l,s]of[[15,'#111'],[8,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(112+d*12,212),c.lineTo(112+d*12,234),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SHOE;for(const d of[-1,1])c.beginPath(),c.ellipse(112+d*14,240,11,6.5,0,0,T),c.fill(),c.stroke()
|
||||
// dress
|
||||
c.lineWidth=5;c.fillStyle=DRESS;c.beginPath();c.moveTo(96,166);c.quadraticCurveTo(86,170,84,184);c.lineTo(72,220);c.quadraticCurveTo(112,230,152,220);c.lineTo(140,184);c.quadraticCurveTo(138,170,128,166);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=DD;c.lineWidth=3;c.beginPath();c.moveTo(76,214);c.quadraticCurveTo(112,222,148,214);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(112,170),c.quadraticCurveTo(112+d*14,166,112+d*16,176),c.quadraticCurveTo(112+d*8,184,112,176),c.fill(),c.stroke()
|
||||
// basket arm and basket
|
||||
for(const[l,s]of[[15,'#111'],[8,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(94,178),c.lineTo(70,196),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=9;c.beginPath();c.arc(60,206,22,PI*1.05,PI*1.95);c.stroke();c.strokeStyle=BASK;c.lineWidth=4;c.stroke();c.strokeStyle='#111'
|
||||
egg(c,50,200,.75,1,-.3);egg(c,68,198,.75,2,.25);egg(c,59,195,.7,3,0)
|
||||
c.lineWidth=5;c.fillStyle=BASK;c.beginPath();c.moveTo(34,204);c.lineTo(86,204);c.lineTo(80,230);c.quadraticCurveTo(60,236,40,230);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=BD;c.lineWidth=3;for(const y of[212,220])c.beginPath(),c.moveTo(38,y),c.lineTo(83,y),c.stroke();for(const x of[48,60,72])c.beginPath(),c.moveTo(x,206),c.lineTo(x,230),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=SKIN;c.lineWidth=5;c.beginPath();c.arc(70,198,7,0,T);c.fill();c.stroke()
|
||||
// head
|
||||
c.save();c.translate(112,72);c.scale(Z,Z);c.translate(-128,-40)
|
||||
c.lineWidth=8;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,206,96,198,150);c.quadraticCurveTo(186,162,170,152);c.lineTo(86,152);c.quadraticCurveTo(70,162,58,150);c.bezierCurveTo(50,96,60,36,128,36);c.fill();c.stroke()
|
||||
// bunny-ear headband
|
||||
for(const d of[-1,1]){c.save();c.translate(128+d*30,44);c.rotate(d*(.18+(d>0?.5+.12*sin(2*T*t):0)))
|
||||
c.fillStyle=EAR;c.lineWidth=8;c.beginPath();c.ellipse(0,-44,15,44,0,0,T);c.fill();c.stroke();c.fillStyle=PINK;c.beginPath();c.ellipse(0,-42,7,32,0,0,T);c.fill();c.restore()}
|
||||
c.fillStyle=SKIN;c.lineWidth=8;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(up){c.lineWidth=8;for(const x of[103,153])c.beginPath(),c.arc(x,146,13,PI*1.15,PI*1.85),c.stroke()}
|
||||
else eye(c,103,140,-1,...look),eye(c,153,140,1,...look)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,162,14,7,0,0,T),c.fill()
|
||||
c.lineWidth=4.5;c.fillStyle=MO;c.beginPath()
|
||||
if(up||t>=.3&&t<.5)c.moveTo(114,164),c.quadraticCurveTo(128,186,142,164),c.closePath(),c.fill(),c.stroke();else c.ellipse(128,170,6,7,0,0,T),c.fill(),c.stroke()
|
||||
c.lineWidth=8;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,146);c.bezierCurveTo(50,74,88,38,128,38);c.bezierCurveTo(168,38,206,74,190,146)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(170,102,164,110);c.quadraticCurveTo(160,96,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,122,90);c.quadraticCurveTo(116,104,106,108);c.quadraticCurveTo(102,96,92,96);c.quadraticCurveTo(86,106,80,112);c.quadraticCurveTo(70,124,66,146);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,58,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=PINK;c.lineWidth=8;c.beginPath();c.roundRect(76,52,104,12,6);c.fill();c.stroke()
|
||||
c.restore()
|
||||
// reaching arm with the egg
|
||||
const E=[150+.3*(H[0]-150),Math.min(176,H[1]+40)]
|
||||
for(const[l,s]of[[15,'#111'],[8,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(130,178),c.quadraticCurveTo(E[0]+14,E[1],H[0],H[1]),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
if(held)egg(c,H[0],H[1]-12,1.05,0,0)
|
||||
c.fillStyle=SKIN;c.lineWidth=5;c.beginPath();c.arc(H[0],H[1],8,0,T);c.fill();c.stroke()
|
||||
if(up){c.fillStyle='#ffd43b';c.lineWidth=3;for(const[dx,dy,o]of[[-22,-26,0],[24,-30,.5],[26,4,.25]]){const s=10*sin(PI*((((t-.5)/.28)*2+o)%1));if(s>1.5)spark(c,H[0]+dx,H[1]+dy-12,s)}}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// tiny emerald-haired fairy hovering on buzzing dragonfly wings, waving her star wand so glitter sprinkles down
|
||||
const{sin,cos,PI,round,abs}=Math,T=2*PI
|
||||
export const frames=12,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#12b886',HD='#087f5b',HL='#c3fae8',EYE='#fab005',ED='#e67700',DR='#faa2c1',DD='#f06595',WG='#e3fafc',WV='#66d9e8',BL='#ffa8a8',FL='#fff3bf',GL=['#ffd43b','#f783ac','#66d9e8','#b197fc']
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const spark=(c,x,y,r)=>{const q=r*.2;c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x+q,y-q,x+r,y);c.quadraticCurveTo(x+q,y+q,x,y+r);c.quadraticCurveTo(x-q,y+q,x-r,y);c.quadraticCurveTo(x-q,y-q,x,y-r);c.fill();c.stroke()}
|
||||
const star=(c,x,y,r,a)=>{c.save();c.translate(x,y);c.rotate(a);c.beginPath();for(let k=0;k<10;k++){const q=k%2?r*.45:r,b=k*PI/5-PI/2;c.lineTo(cos(b)*q,sin(b)*q)}c.closePath();c.restore();c.fill();c.stroke()}
|
||||
const wing=(c,a,len,w)=>{c.save();c.rotate(a);c.fillStyle=WG;c.lineWidth=4;c.beginPath();c.ellipse(len/2+4,0,len/2,w,0,0,T);c.fill();c.stroke();c.strokeStyle=WV;c.lineWidth=2.5;c.beginPath();c.moveTo(10,0);c.lineTo(len-6,0);for(const k of[.35,.6,.82])c.moveTo(len*k,0),c.lineTo(len*k+8,-w*.6),c.moveTo(len*k,0),c.lineTo(len*k+8,w*.6);c.stroke();c.restore()}
|
||||
export default(c,t)=>{
|
||||
const by=round(5*sin(T*t)),fl=abs(sin(3*T*t)),wa=.3*sin(2*T*t)
|
||||
for(let k=0;k<5;k++){const p=(t*2+k/5)%1,x=204+18*p+7*sin(T*(p+k*.3)),y=92+by+70*p*p+50*p,r=12*(1-p)+3
|
||||
c.fillStyle=GL[k%4];c.lineWidth=3;spark(c,x,y,r)}
|
||||
c.translate(0,by)
|
||||
c.save();c.translate(128,142)
|
||||
for(const d of[-1,1]){c.save();c.scale(d,1);wing(c,-.55-.18*fl,74,14+5*fl);wing(c,.35+.12*fl,56,11+4*fl);c.restore()}
|
||||
c.restore()
|
||||
for(const x of[118,138]){line(c,[[x,186],[x+(x<128?-2:2),212]],6,SKIN);c.lineWidth=4;c.fillStyle=DD;c.beginPath();c.moveTo(x-7,210);c.quadraticCurveTo(x+(x<128?-14:14),214,x+(x<128?-16:16),206);c.quadraticCurveTo(x+(x<128?-8:8),220,x-6,218);c.closePath();c.fill();c.stroke()}
|
||||
line(c,[[112,140],[98,162],[104,176]],6,SKIN)
|
||||
c.fillStyle=DR;c.lineWidth=5;c.beginPath();c.moveTo(114,128);c.lineTo(142,128);c.bezierCurveTo(150,150,158,170,168,186);for(let k=0;k<5;k++)c.quadraticCurveTo(160-k*16,198-(k%2)*4,152-k*16,190);c.bezierCurveTo(98,170,106,150,114,128);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=DD;c.lineWidth=3;c.beginPath();for(const x of[120,136])c.moveTo(x,150),c.quadraticCurveTo(x+(x-128)*.6,168,x+(x-128),186);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=HAIR;c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*8,132,8,5,d*.5,0,T),c.fill(),c.stroke()
|
||||
const sx=146,sy=134,ex=184+6*cos(wa),ey=116-10*sin(wa)
|
||||
c.save();c.translate(ex,ey);c.rotate(.5+wa)
|
||||
for(const[lw,col]of[[9,'#111'],[4,FL]])c.lineWidth=lw,c.strokeStyle=col,c.beginPath(),c.moveTo(0,4),c.lineTo(0,-34),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle=GL[0];c.lineWidth=4;star(c,0,-42,13,wa*2);c.restore()
|
||||
line(c,[[sx,sy],[164,132],[ex,ey]],6,SKIN)
|
||||
c.lineWidth=4;c.fillStyle=SKIN;c.beginPath();c.arc(ex,ey,6,0,T);c.fill();c.stroke()
|
||||
c.save();c.translate(128,92);c.scale(.55,.55);c.translate(-128,-128)
|
||||
c.lineWidth=9;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(198,36,210,100,198,156);c.quadraticCurveTo(184,170,170,156);c.lineTo(86,156);c.quadraticCurveTo(72,170,58,156);c.bezierCurveTo(46,100,58,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*50,108),c.quadraticCurveTo(128+d*86,82,128+d*96,70),c.quadraticCurveTo(128+d*84,118,128+d*54,138),c.closePath(),c.fill(),c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1);eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,162,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=6;c.fillStyle='#c92a2a';c.beginPath();c.moveTo(116,164);c.quadraticCurveTo(128,182,140,164);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(64,140)
|
||||
c.bezierCurveTo(52,70,90,38,128,38);c.bezierCurveTo(166,38,204,70,192,140)
|
||||
c.quadraticCurveTo(186,112,176,100);c.quadraticCurveTo(168,106,162,114);c.quadraticCurveTo(156,98,146,92);c.quadraticCurveTo(140,104,132,110)
|
||||
c.quadraticCurveTo(126,96,116,92);c.quadraticCurveTo(110,106,100,110);c.quadraticCurveTo(96,98,88,98);c.quadraticCurveTo(82,108,80,114)
|
||||
c.quadraticCurveTo(70,110,64,140);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=7;for(const[a,e]of[[1.26,1.38],[1.46,1.54],[1.62,1.72]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=6;c.fillStyle=DR;for(let k=0;k<5;k++){const a=k*T/5-PI/2;c.beginPath();c.ellipse(78+16*cos(a),66+16*sin(a),11,9,a,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle='#ffd43b';c.beginPath();c.arc(78,66,8,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// butter-blonde girl in a bathrobe blow-drying her hair, long locks whooshing and fluttering in the air stream, eyes squinting, droplets flying off
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=12,delay=90
|
||||
const SKIN='#ffe3d1',HAIR='#ffe066',HD='#f2b705',HL='#fff9db',ROBE='#ffa8a8',RD='#fa5252',DRY='#9775fa',DD='#7048e8',BL='#ff8fab',MO='#c92a2a',DROP='#a5d8ff'
|
||||
// a tapered lock streaming left from (x,y), rippling with phase o
|
||||
const lock=(c,x,y,len,w0,o,t)=>{
|
||||
const P=[...Array(11)].map((_,k)=>{const f=k/10;return[x-len*f,y+f*f*10+9*f*sin(T*t*2-f*5+o)]}),L=[],R=[]
|
||||
P.forEach(([px,py],k)=>{
|
||||
const[ax,ay]=P[max(0,k-1)],[bx,by]=P[Math.min(10,k+1)],dx=bx-ax,dy=by-ay,n=Math.hypot(dx,dy),w=w0*(1-k/10*.9)/2
|
||||
L.push([px-dy/n*w,py+dx/n*w]);R.push([px+dy/n*w,py-dx/n*w])
|
||||
})
|
||||
c.beginPath();c.moveTo(...L[0]);for(const p of L)c.lineTo(...p);for(const p of R.reverse())c.lineTo(...p);c.closePath();c.fill();c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
c.lineWidth=6
|
||||
// air stream: dashes rushing left past her head
|
||||
c.save();c.setLineDash([20,14]);c.lineDashOffset=-68*t
|
||||
for(const[l,s]of[[9,'#111'],[3.5,'#fff']]){c.lineWidth=l;c.strokeStyle=s;for(const[y,o]of[[42,0],[60,17],[30,9]])c.beginPath(),c.moveTo(196,y+40),c.bezierCurveTo(160,y,90,y+4,10,y+14+o/4),c.stroke()}
|
||||
c.restore();c.strokeStyle='#111';c.lineWidth=6
|
||||
// streaming locks behind the head
|
||||
c.fillStyle=HD;for(const[x,y,len,w,o]of[[86,74,92,30,0],[78,108,104,34,1.3],[76,144,100,32,2.6],[84,178,86,28,3.9]])lock(c,x,y,len,w,o,t)
|
||||
c.fillStyle=HAIR;for(const[x,y,len,w,o]of[[96,92,78,26,.7],[90,128,94,30,2],[90,162,86,26,3.3]])lock(c,x,y,len,w,o,t)
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,200,90,194,150);c.quadraticCurveTo(190,178,176,188);c.lineTo(80,188);c.bezierCurveTo(60,150,56,36,128,36);c.fill();c.stroke()
|
||||
// robe with a wide white collar
|
||||
c.fillStyle=ROBE;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,256);c.lineTo(202,256);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(102,190);c.lineTo(128,240);c.lineTo(154,190);c.lineTo(166,196);c.lineTo(132,256);c.lineTo(122,256);c.lineTo(90,196);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,168);c.lineTo(116,194);c.lineTo(128,226);c.lineTo(140,194);c.lineTo(140,168);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,194);c.moveTo(140,168);c.lineTo(140,194);c.stroke()
|
||||
// face, squinting against the wind
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
c.lineWidth=6;for(const d of[-1,1]){const x=128+d*25;c.beginPath();c.moveTo(x+d*13,130);c.lineTo(x-d*9,141);c.lineTo(x+d*13,151);c.stroke()}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,162,13,6,0,0,T),c.fill()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+k-2,165),c.lineTo(128+d*44+k+2,159),c.stroke()
|
||||
c.lineWidth=3.5;c.fillStyle=MO;c.beginPath();c.moveTo(118,168);c.quadraticCurveTo(128,162,138,168);c.quadraticCurveTo(136,178,128,178);c.quadraticCurveTo(120,178,118,168);c.fill();c.stroke()
|
||||
// bangs swept left and fluttering
|
||||
const f=k=>4*sin(T*t*2+k)
|
||||
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(70,148)
|
||||
c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,196,70,184,120);c.quadraticCurveTo(182,104,176,98)
|
||||
c.quadraticCurveTo(160,104,150+f(0),118);c.quadraticCurveTo(140,98,124,96);c.quadraticCurveTo(112,100,98+f(1),118)
|
||||
c.quadraticCurveTo(96,100,84,98);c.quadraticCurveTo(68,104,50+f(2),112+f(3));c.quadraticCurveTo(66,96,72,90);c.quadraticCurveTo(68,120,70,148);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,100,56,PI*a,PI*e),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// droplets flung off to the left
|
||||
c.fillStyle=DROP;c.lineWidth=3
|
||||
for(const[y,o]of[[70,0],[120,.33],[170,.66],[96,.5]]){const p=(t+o)%1,x=40-40*p,yy=y-8*p;if(p<.8){c.beginPath();c.ellipse(x,yy,6,4,-.2,0,T);c.fill();c.stroke()}}
|
||||
// hair dryer from the right, held by its handle
|
||||
for(const[l,s]of[[26,'#111'],[17,ROBE]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(196,256),c.quadraticCurveTo(222,214,228,172),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=DRY
|
||||
c.beginPath();c.roundRect(214,112,22,52,8);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(194,84);c.lineTo(212,78);c.lineTo(212,118);c.lineTo(194,112);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.roundRect(208,70,42,56,22);c.fill();c.stroke()
|
||||
c.fillStyle=DD;c.beginPath();c.arc(232,98,12,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(let k=0;k<3;k++){const a=k*T/3+t*T*3;c.moveTo(232,98);c.lineTo(232+cos(a)*10,98+sin(a)*10)}c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=2.5;c.beginPath();c.roundRect(218,128,6,14,3);c.fill();c.stroke()
|
||||
c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(226,150,13,14,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(216,146);c.lineTo(228,146);c.moveTo(216,154);c.lineTo(228,154);c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// lavender-gray haired girl with a scrunchie in her teeth gathering her long hair up, tying a high ponytail that swishes, then pulling it loose again
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=20,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#b9b8d6',HD='#8d8ab8',HL='#f1f0fa',EYE='#f06595',ED='#a61e4d',HOOD='#74c0fc',HOD='#4dabf7',SCR='#ff8787',SCD='#e03131',BL='#ffa8a8',MO='#c92a2a'
|
||||
const ease=x=>x<=0?0:x>=1?1:x*x*(3-2*x)
|
||||
const MOUTH=[128,174],TIE=[128,40]
|
||||
const eye=(c,x,y,d,u)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3-u,5.5,7.5,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5-u,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8-u,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
// puffy scrunchie ring
|
||||
const scrunchie=(c,x,y,sx,sy)=>{
|
||||
c.fillStyle=SCR;c.lineWidth=4;c.beginPath();for(let k=0;k<=24;k++){const a=k*T/24,r=1+.14*(k%2);c.lineTo(x+cos(a)*sx*r,y+sin(a)*sy*r)}c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=SCD;c.beginPath();c.ellipse(x,y,sx*.45,sy*.45,0,0,T);c.fill();c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const g=ease((t-.12)/.2)-ease((t-.84)/.1),tied=t>=.42&&t<.84
|
||||
const al=ease((t-.12)/.18)-ease((t-.42)/.1),ar=max(al,ease((t-.78)/.06)-ease((t-.88)/.06))
|
||||
// where the scrunchie is: teeth, carried round the side of her head, on the ponytail, then back to her teeth
|
||||
const side=s=>{const u=1-s;return[u*u*MOUTH[0]+2*u*s*226+s*s*TIE[0]+12*s*(1-s),u*u*MOUTH[1]+2*u*s*110+s*s*TIE[1]]}
|
||||
const sp=t<.32?MOUTH:t<.42?side(Math.sqrt((t-.32)/.1)):t<.82?TIE:t<.94?side(1-((t-.82)/.12)**2):MOUTH
|
||||
const carry=t>=.32&&t<.42||t>=.82&&t<.94,sw=.16*sin(T*(t-.42)/.42*2)
|
||||
c.lineWidth=6
|
||||
// ponytail sits behind the head, swinging from the tie
|
||||
if(tied){
|
||||
c.save();c.translate(...TIE);c.rotate(sw)
|
||||
c.fillStyle=HAIR;c.beginPath();c.moveTo(-12,4);c.bezierCurveTo(-10,-30,46,-40,70,-6);c.bezierCurveTo(94,30,86,90,96,134);c.bezierCurveTo(70,108,66,60,56,20);c.bezierCurveTo(44,-6,14,-6,12,6);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=3;c.beginPath();c.moveTo(30,-16);c.bezierCurveTo(64,-14,76,40,84,100);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
}
|
||||
// back hair: long and loose, shrinking into the gathered shape
|
||||
const by=250-112*g,bw=86-14*g
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(128+bw,34,128+bw+8,110,128+bw+2,by-20);c.quadraticCurveTo(128+bw-4,by+4,128+bw-24,by)
|
||||
c.lineTo(128-bw+24,by);c.quadraticCurveTo(128-bw+4,by+4,128-bw-2,by-20);c.bezierCurveTo(128-bw-8,110,128-bw,34,128,34);c.fill();c.stroke()
|
||||
// the bundle she's holding up before it's tied
|
||||
if(g>.05&&!tied){c.fillStyle=HAIR;c.beginPath();c.moveTo(108,44);c.quadraticCurveTo(112,44-30*g,128,40-36*g);c.quadraticCurveTo(144,44-30*g,148,44);c.closePath();c.fill();c.stroke()}
|
||||
c.fillStyle=HOOD;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,256);c.lineTo(202,256);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=HOD;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*6,194),c.quadraticCurveTo(128+d*44,190,128+d*52,214),c.quadraticCurveTo(128+d*30,206,128+d*6,214),c.closePath(),c.fill(),c.stroke()
|
||||
c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*12,212),c.lineTo(128+d*14,240),c.stroke()
|
||||
c.fillStyle=SKIN;c.lineWidth=6;c.beginPath();c.moveTo(116,168);c.lineTo(116,194);c.lineTo(140,194);c.lineTo(140,168);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,192);c.moveTo(140,168);c.lineTo(140,192);c.stroke()
|
||||
// arms up behind her head
|
||||
const hands=[]
|
||||
for(const[d,a]of[[-1,al],[1,ar]]){
|
||||
if(a<.02)continue
|
||||
// elbows and hands swing out wide on the way up and down instead of crossing the chest
|
||||
const q=(p0,p1,p2)=>[0,1].map(i=>(1-a)*(1-a)*p0[i]+2*a*(1-a)*p1[i]+a*a*p2[i])
|
||||
const hp=d>0&&carry?sp:[128+d*60,58],e=q([128+d*70,282],[128+d*126,214],[128+d*110,116]),h=q([128+d*64,310],[128+d*134,170],hp)
|
||||
for(const[l,s]of[[28,'#111'],[18,HOOD]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128+d*46,216),c.lineTo(...e),c.lineTo(...h),c.stroke()
|
||||
hands.push(h)
|
||||
}
|
||||
c.strokeStyle='#111';c.lineWidth=6
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(tied){c.lineWidth=6;for(const x of[103,153])c.beginPath(),c.arc(x,146,13,PI*1.15,PI*1.85),c.stroke()}
|
||||
else{const u=g>.3?6:0;eye(c,103,140,-1,u);eye(c,153,140,1,u)}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,160,13,6,0,0,T),c.fill()
|
||||
c.lineWidth=3.5;c.fillStyle=MO
|
||||
if(tied){c.beginPath();c.moveTo(116,164);c.quadraticCurveTo(128,184,140,164);c.closePath();c.fill();c.stroke()}
|
||||
else{c.beginPath();c.ellipse(128,172,9,6,0,0,T);c.fill();c.stroke()}
|
||||
// side locks framing the face, short once the hair is gathered
|
||||
const sy=232-84*g
|
||||
c.lineWidth=6;c.fillStyle=HAIR
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*52,100),c.quadraticCurveTo(128+d*64,sy-40,128+d*56,sy),c.quadraticCurveTo(128+d*48,sy-30,128+d*40,120),c.closePath(),c.fill(),c.stroke()
|
||||
c.beginPath();c.moveTo(70,140);c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,140)
|
||||
c.quadraticCurveTo(184,112,178,100);c.quadraticCurveTo(168,100,162,114);c.quadraticCurveTo(156,98,144,94);c.quadraticCurveTo(140,106,130,112)
|
||||
c.quadraticCurveTo(124,98,112,94);c.quadraticCurveTo(108,106,98,112);c.quadraticCurveTo(92,98,84,98);c.quadraticCurveTo(78,110,78,104)
|
||||
c.quadraticCurveTo(70,120,70,140);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,102,56,PI*a,PI*e),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// scrunchie: squashed in her teeth, upright on the ponytail
|
||||
const m=(sp[1]-TIE[1])/(MOUTH[1]-TIE[1])
|
||||
scrunchie(c,...sp,13-3*m,8+6*(1-m)*(tied?1:.6))
|
||||
c.lineWidth=5;c.fillStyle=SKIN;for(const[x,y]of hands)c.beginPath(),c.ellipse(x,y+6,11,12,0,0,T),c.fill(),c.stroke()
|
||||
if(tied&&t<.56){c.fillStyle='#ffd43b';c.lineWidth=3.5;const r=12*sin(PI*(t-.42)/.14);for(const[x,y]of[[96,24],[176,44]]){c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x+r*.2,y-r*.2,x+r,y);c.quadraticCurveTo(x+r*.2,y+r*.2,x,y+r);c.quadraticCurveTo(x-r*.2,y+r*.2,x-r,y);c.quadraticCurveTo(x-r*.2,y-r*.2,x,y-r);c.fill();c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// full-body chibi with apricot pigtails hiccuping twice per loop: a little hop, wide eyes, a hand over her mouth and a tiny hic bubble popping out
|
||||
const{sin,PI,max,min,exp}=Math,T=2*PI
|
||||
export const frames=20,delay=70
|
||||
const SKIN='#ffe3d1',HAIR='#ffb87a',HD='#f0883e',HL='#ffe8cc',EYE='#f59f00',ED='#b35900',DR='#38d9a9',DRD='#0ca678',SHOE='#8d6346',BOB='#ffd43b',BL='#ffa8a8',HIC=[.1,.6]
|
||||
const eye=(c,x,y,d,wide)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip()
|
||||
if(wide){c.fillStyle=EYE;c.beginPath();c.arc(x,y+2,6.5,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.arc(x,y+2,3,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-2,y,1.6,0,T);c.fill()}
|
||||
else{c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()}
|
||||
c.restore();c.lineWidth=3.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
// u is time since the latest hiccup, so everything below reacts to it
|
||||
const u=min(...HIC.map(h=>(t-h+1)%1)),hop=u<.16?-18*sin(PI*u/.16):0,jolt=exp(-u*14),wide=u<.22,Z=.66
|
||||
const dy=Math.round(hop)
|
||||
c.fillStyle='rgba(17,17,17,.18)';c.beginPath();c.ellipse(128,248,32*(1+hop/60),5,0,0,T);c.fill()
|
||||
c.translate(128,250);c.scale(1.1,1.1);c.translate(-128,-250+dy)
|
||||
for(const d of[-1,1])for(const[l,s]of[[15,'#111'],[8,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128+d*12,212),c.lineTo(128+d*12-d*3*(hop<0?1:0),234),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.roundRect(128+d*12-6,224,12,10,3),c.fill(),c.stroke()
|
||||
c.fillStyle=SHOE;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*14,240,11,6.5,0,0,T),c.fill(),c.stroke()
|
||||
// left arm hangs, flapping out a little on each hiccup
|
||||
for(const[l,s]of[[15,'#111'],[8,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(104,180),c.lineTo(90-8*jolt,204-6*jolt),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.arc(89-8*jolt,207-6*jolt,6,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(112,164);c.lineTo(144,164);c.lineTo(146,184);c.lineTo(110,184);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=DR;c.beginPath();c.moveTo(108,178);c.lineTo(148,178);c.lineTo(166,218);c.quadraticCurveTo(128,228,90,218);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*16,178),c.lineTo(128+d*14,166),c.stroke()
|
||||
c.fillStyle=BOB;for(const d of[-1,1])c.beginPath(),c.arc(128+d*15,182,3.5,0,T),c.fill(),c.stroke()
|
||||
c.strokeStyle=DRD;c.lineWidth=3;c.beginPath();c.moveTo(94,212);c.quadraticCurveTo(128,220,162,212);c.stroke();c.strokeStyle='#111'
|
||||
c.save();c.translate(128,72-4*jolt);c.scale(Z,Z);c.translate(-128,-40)
|
||||
// pigtails swing out with each hop
|
||||
c.lineWidth=8;c.fillStyle=HAIR
|
||||
for(const d of[-1,1]){
|
||||
c.save();c.translate(128+d*70,148);c.scale(d,1);c.rotate(-(.2+.3*jolt))
|
||||
c.beginPath();c.moveTo(-10,-4);c.bezierCurveTo(-12,30,-4,64,14,84);c.bezierCurveTo(22,70,18,62,12,58);c.bezierCurveTo(18,36,16,14,10,-4);c.closePath();c.fill();c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,206,96,196,150);c.lineTo(60,150);c.bezierCurveTo(50,96,60,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=BOB;for(const d of[-1,1])c.beginPath(),c.arc(128+d*70,150,10,0,T),c.fill(),c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1,wide);eye(c,153,140,1,wide)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,14,7,0,0,T),c.fill()
|
||||
c.lineWidth=8;c.fillStyle=HAIR;c.beginPath();c.moveTo(68,146)
|
||||
c.bezierCurveTo(52,74,88,38,128,38);c.bezierCurveTo(168,38,204,74,188,146)
|
||||
c.quadraticCurveTo(186,118,180,104);c.lineTo(166,110);c.quadraticCurveTo(160,96,150,94);c.lineTo(142,110)
|
||||
c.quadraticCurveTo(132,96,122,94);c.lineTo(114,110);c.quadraticCurveTo(106,96,96,96);c.lineTo(88,110)
|
||||
c.lineTo(76,104);c.quadraticCurveTo(70,126,68,146);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,58,PI*a,PI*e),c.stroke()
|
||||
// worried brows, raised on the hiccup
|
||||
c.strokeStyle='#111';c.lineWidth=6;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*14,116-6*jolt),c.lineTo(128+d*38,110-10*jolt),c.stroke()
|
||||
c.restore()
|
||||
// right hand clapped over her mouth
|
||||
for(const[l,s]of[[15,'#111'],[8,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(150,180),c.lineTo(168,184),c.lineTo(142,158-4*jolt),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(136,157-4*jolt,13,9,-.3,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(147,151-4*jolt,4.5,6.5,-.5,0,T);c.fill();c.stroke()
|
||||
// jolt ticks beside her head
|
||||
if(jolt>.25)for(const[l,st]of[[7,'#111'],[2.5,'#fff']]){c.lineWidth=l;c.strokeStyle=st;for(const d of[-1,1])for(const k of[-1,0,1])c.beginPath(),c.moveTo(128+d*(56+4*k*k),66+k*16),c.lineTo(128+d*(68+4*k*k),62+k*20),c.stroke()}
|
||||
c.strokeStyle='#111'
|
||||
// the hic bubble pops out and floats away, in screen space so the hop doesn't push it off the top
|
||||
if(u<.4){
|
||||
const p=u/.4,sc=min(1,u/.05),x=204,y=48-14*p
|
||||
c.setTransform(1,0,0,1,0,0);c.save();c.globalAlpha=p>.7?1-(p-.7)/.3:1;c.translate(x,y);c.scale(sc,sc)
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(-27,-17,54,32,14);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(-20,10);c.lineTo(-30,26);c.lineTo(-8,14);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(-24,-14,48,26,12);c.fill()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(-16,-8);c.lineTo(-16,8);c.moveTo(-16,0);c.quadraticCurveTo(-10,-6,-7,0);c.lineTo(-7,8);c.moveTo(1,-1);c.lineTo(1,8);c.stroke()
|
||||
c.beginPath();c.arc(14,3,5.5,.25*PI,1.75*PI);c.stroke();c.fillStyle='#111';c.beginPath();c.arc(1,-7,2.2,0,T);c.fill()
|
||||
c.restore()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// girl with long magenta hair twirling a striped hula hoop around her waist, hips swaying against it, arms up
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=70
|
||||
const SKIN='#ffe3d1',HAIR='#da3ea0',HD='#a61e7a',HL='#fcc2e7',EYE='#15aabf',ED='#0b7285',TEE='#ffd43b',TD='#f59f00',SHO='#20c997',SD='#0ca678',SHOE='#f783ac',BL='#ffa8a8',HOOP=['#ff6b6b','#ffd43b','#69db7c','#4dabf7','#b197fc','#ff922b']
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
export default(c,t,i)=>{
|
||||
const a=T*2*t,hx=128-6*cos(a),wy=168,sx=128-2*cos(a),bob=1.5*sin(2*a)
|
||||
const hc=[hx+14*cos(a),wy+2+4*sin(a)],R=[64,15]
|
||||
const hoop=(a0,a1)=>{
|
||||
for(const[lw,s]of[[15,'#111']])c.lineWidth=lw,c.strokeStyle=s,c.beginPath(),c.ellipse(...hc,...R,0,a0,a1),c.stroke()
|
||||
const n=12;for(let k=0;k<n;k++){const s0=((k*T/n-a*1.5)%T+T)%T;for(let j=-1;j<=1;j++){const u0=max(a0,s0+j*T),u1=min(a1,s0+T/n+j*T);if(u1>u0)c.lineWidth=8,c.strokeStyle=HOOP[k%6],c.beginPath(),c.ellipse(...hc,...R,0,u0,u1),c.stroke()}}
|
||||
c.strokeStyle='#111'
|
||||
}
|
||||
c.translate(128,150);c.scale(1.07,1.07);c.translate(-128,-150)
|
||||
c.fillStyle='#dee2e6';c.beginPath();c.ellipse(128,234,46,6,0,0,T);c.fill()
|
||||
hoop(PI,T)
|
||||
const head=g=>{c.save();c.translate(sx,90+bob);c.rotate(-.04*cos(a));c.scale(.56,.56);c.translate(-128,-120);g();c.restore()}
|
||||
head(()=>{
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(200,36,212,110,208,190);c.quadraticCurveTo(216+10*cos(a),236,200+14*cos(a),262);c.quadraticCurveTo(186,252,176,264+4*sin(a));c.lineTo(80,264-4*sin(a));c.quadraticCurveTo(70,252,56+14*cos(a),262);c.quadraticCurveTo(40+10*cos(a),236,48,190);c.bezierCurveTo(44,110,56,36,128,36);c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*62,150),c.quadraticCurveTo(128+d*70+8*cos(a),210,128+d*64+10*cos(a),248),c.stroke();c.strokeStyle='#111'
|
||||
})
|
||||
for(const[x,d]of[[hx-10,-1],[hx+10,1]]){const kx=(x+128+d*12)/2+d*2;line(c,[[x,186],[kx,206],[128+d*12,226]],8,SKIN)}
|
||||
for(const d of[-1,1]){c.fillStyle=SHOE;c.lineWidth=5;c.beginPath();c.ellipse(128+d*15,229,12,7,0,0,T);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.ellipse(128+d*15,233,12,3,0,0,PI);c.fill()}
|
||||
c.fillStyle=SHO;c.lineWidth=5;c.beginPath();c.moveTo(hx-20,168);c.lineTo(hx+20,168);c.lineTo(hx+25,192);c.lineTo(hx+3,192);c.lineTo(hx,186);c.lineTo(hx-3,192);c.lineTo(hx-25,192);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(sx-6,124+bob,12,10);c.fill()
|
||||
c.fillStyle=TEE;c.lineWidth=5;c.beginPath();c.moveTo(sx-14,130+bob);c.lineTo(sx+14,130+bob);c.lineTo(sx+28,138+bob);c.lineTo(sx+30,148+bob);c.lineTo(sx+20,146+bob);c.lineTo(hx+21,172);c.lineTo(hx-21,172);c.lineTo(sx-20,146+bob);c.lineTo(sx-30,148+bob);c.lineTo(sx-28,138+bob);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(sx-9,131+bob);c.quadraticCurveTo(sx,139+bob,sx+9,131+bob);c.stroke()
|
||||
c.fillStyle='#ff6b6b';c.lineWidth=3.5;c.beginPath();const px=(sx+hx)/2,py=154+bob/2;c.moveTo(px,py+7);c.bezierCurveTo(px-11,py-1,px-7,py-10,px,py-4);c.bezierCurveTo(px+7,py-10,px+11,py-1,px,py+7);c.fill();c.stroke()
|
||||
head(()=>{
|
||||
c.lineWidth=9;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
c.lineWidth=8;for(const x of[103,153])c.beginPath(),c.arc(x,148,14,PI*1.15,PI*1.85),c.stroke()
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,160,13,7,0,0,T),c.fill()
|
||||
c.fillStyle='#c92a2a';c.lineWidth=6;c.beginPath();c.moveTo(114,162);c.quadraticCurveTo(128,186,142,162);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle='#ff8fab';c.beginPath();c.ellipse(128,178,9,6,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.moveTo(66,150);c.bezierCurveTo(50,74,90,36,132,36);c.bezierCurveTo(176,36,206,80,188,150)
|
||||
c.quadraticCurveTo(184,118,174,100);c.quadraticCurveTo(160,104,150,118);c.quadraticCurveTo(146,98,134,88);c.quadraticCurveTo(116,110,92,116);c.quadraticCurveTo(100,104,102,92);c.quadraticCurveTo(84,104,76,118);c.quadraticCurveTo(68,130,66,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=8;for(const[a0,a1]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a0,PI*a1),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#ffd43b';c.lineWidth=6;c.beginPath();for(let k=0;k<10;k++){const r=k%2?6:14,b=k*PI/5-PI/2;c.lineTo(84+r*cos(b),74+r*sin(b))}c.closePath();c.fill();c.stroke()
|
||||
})
|
||||
for(const d of[-1,1]){const s=[sx+d*20,140+bob],w=sin(a+(d>0?PI:0)),e=[sx+d*42,118+bob],h=[sx+d*(54+4*w),78+bob-3*w];line(c,[s,e,h],8,SKIN);c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.ellipse(h[0],h[1]-3,7,9,d*.3,0,T);c.fill();c.stroke()}
|
||||
hoop(0,PI)
|
||||
c.lineWidth=4;for(const d of[-1,1]){const x=hc[0]+d*78,y=hc[1]-2;c.beginPath();c.arc(x-d*14,y,16,d>0?-.5:PI-.5,d>0?.5:PI+.5);c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// figure skater with a sky-blue bun spinning on the ice, arms joined overhead, skirt flaring, a sparkle trail circling her
|
||||
const{sin,cos,PI,max,min,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=70
|
||||
const SKIN='#ffe3d1',HAIR='#8ec5fc',HD='#4dabf7',HL='#e7f5ff',DR='#cc5de8',DD='#9c36b5',SK=['#e599f7','#f783ac'],RIB='#f783ac',BL='#ffa8a8',ICE='#e7f5ff',IL='#a5d8ff'
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const star=(c,x,y,r,col)=>{c.fillStyle=col;c.lineWidth=3.5;c.beginPath();for(let k=0;k<8;k++){const q=k%2?r*.38:r,b=k*PI/4;c.lineTo(x+q*cos(b),y+q*sin(b))}c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t,i)=>{
|
||||
const q=T*2*t,cq=cos(q),sq=sin(q),w=abs(cq),bob=1.5*sin(2*q)
|
||||
c.fillStyle=ICE;c.lineWidth=6;c.beginPath();c.ellipse(128,226,92,20,0,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle=IL;c.lineWidth=3;c.beginPath();c.ellipse(128,226,30,7,0,0,T);c.stroke();c.beginPath();c.ellipse(128,226,58,12,0,-2.6,-.4);c.stroke();c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(56,222);c.lineTo(74,218);c.moveTo(178,232);c.lineTo(198,229);c.stroke();c.strokeStyle='#111'
|
||||
const orb=f=>{for(let k=0;k<3;k++){const a=q*.5+k*T/3+1,s=sin(a);if(f(s))star(c,128+84*cos(a),174+18*s,5+3*(s+1),['#ffd43b','#fff','#f783ac'][k])}}
|
||||
orb(s=>s<0)
|
||||
const arcs=d=>{for(const[y,r]of[[176,70],[116,58]]){const a0=d>0?.1:PI+.1;for(const[lw,s]of[[9,'#111'],[3.5,'#fff']])c.lineWidth=lw,c.strokeStyle=s,c.beginPath(),c.ellipse(128,y,r,r*.22,0,a0,a0+.9),c.stroke()}c.strokeStyle='#111'}
|
||||
arcs(-1)
|
||||
const sx=16*cq,fl=sq
|
||||
line(c,[[128-4,184],[128-2,214]],8,SKIN)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.roundRect(118,206,18,14,5);c.fill();c.stroke();c.lineWidth=4;c.beginPath();c.moveTo(114,224);c.lineTo(142,224);c.stroke()
|
||||
const kx=128+18*fl,fx=128+6*fl;line(c,[[132,184],[kx,198],[fx,206]],8,SKIN)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(fx+4*fl,207,9,6,0,0,T);c.fill();c.stroke()
|
||||
const skirt=()=>{c.beginPath();c.moveTo(104,160+bob);c.lineTo(152,160+bob);c.quadraticCurveTo(178,174,188,190);c.quadraticCurveTo(128,202,68,190);c.quadraticCurveTo(78,174,104,160+bob);c.closePath()}
|
||||
c.fillStyle=SK[0];c.lineWidth=5;skirt();c.fill();c.save();c.clip()
|
||||
for(let k=0;k<8;k++){const a=q+k*T/8;if(sin(a)>-.2&&k%2){const x=128+64*cos(a),wd=10+10*sin(a);c.fillStyle=SK[1];c.beginPath();c.moveTo(128+22*cos(a)-wd*.3,160);c.lineTo(128+22*cos(a)+wd*.3,160);c.lineTo(x+wd,200);c.lineTo(x-wd,200);c.closePath();c.fill()}}
|
||||
c.restore();skirt();c.stroke()
|
||||
c.fillStyle=DR;c.lineWidth=5;c.beginPath();c.moveTo(112,128+bob);c.lineTo(144,128+bob);c.quadraticCurveTo(152,146,150,164+bob);c.lineTo(106,164+bob);c.quadraticCurveTo(104,146,112,128+bob);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';for(let k=0;k<6;k++){const a=q+k*T/6;if(sin(a)>0){c.beginPath();c.arc(128+18*cos(a),146+bob+6*(k%2),2.4,0,T);c.fill()}}
|
||||
for(const d of[-1,1]){const s=[128+d*14,134+bob],e=[128+d*(56+4*w),86+bob],h=[128+d*6,14+bob];line(c,[s,e,h],8,SKIN)}
|
||||
c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.ellipse(128,13+bob,9,7,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(122,120+bob,12,10);c.fill()
|
||||
const head=g=>{c.save();c.translate(128,96+bob);c.scale(.56,.56);c.translate(-128,-120);g();c.restore()}
|
||||
const front=cq>-.25,ex=36*sq,es=26*cq
|
||||
head(()=>{
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,206,96,198,150);c.lineTo(58,150);c.bezierCurveTo(50,96,60,36,128,36);c.fill();c.stroke()
|
||||
c.beginPath();c.arc(128,30,30,0,T);c.fill();c.stroke();c.strokeStyle=HD;c.lineWidth=5;c.beginPath();c.arc(128,30,18,PI*.9,PI*2.1);c.stroke();c.strokeStyle='#111'
|
||||
if(front){
|
||||
c.fillStyle=SKIN;c.lineWidth=9;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
c.save();c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.clip()
|
||||
c.lineWidth=8;for(const d of[-1,1]){const x=128+ex+d*es;if(abs(es)>8||d*sq>0)c.beginPath(),c.arc(x,146,13,PI*1.15,PI*1.85),c.stroke(),c.beginPath(),c.moveTo(x+d*12*sign(cq),138),c.lineTo(x+d*19*sign(cq),134),c.stroke()}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+ex*1.1+d*42*cq,162,11*max(.4,w),6,0,0,T),c.fill()
|
||||
c.fillStyle='#c92a2a';c.lineWidth=5;c.beginPath();c.arc(128+ex*1.1,166,7,.1*PI,.9*PI);c.closePath();c.fill();c.stroke();c.restore()
|
||||
c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.moveTo(70,140);c.bezierCurveTo(54,74,88,40,128,40);c.bezierCurveTo(168,40,202,74,186,140)
|
||||
c.quadraticCurveTo(182,112,170,96);c.quadraticCurveTo(160,104,152,112);c.quadraticCurveTo(150,96,138,88);c.quadraticCurveTo(128,104,112,110);c.quadraticCurveTo(116,98,112,88);c.quadraticCurveTo(96,104,86,112);c.quadraticCurveTo(74,120,70,140);c.closePath();c.fill();c.stroke()
|
||||
}else{
|
||||
c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.moveTo(72,100);c.bezierCurveTo(68,160,100,186,128,186);c.bezierCurveTo(156,186,188,160,184,100);c.bezierCurveTo(184,44,72,44,72,100);c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=5;for(const d of[-1,0,1])c.beginPath(),c.moveTo(128+d*20,70),c.quadraticCurveTo(128+d*30,130,128+d*22,176),c.stroke();c.strokeStyle='#111'
|
||||
}
|
||||
c.strokeStyle=HL;c.lineWidth=8;c.beginPath();c.arc(128,104,56,PI*1.28,PI*1.42);c.stroke();c.beginPath();c.arc(128,30,22,PI*1.2,PI*1.5);c.stroke();c.strokeStyle='#111'
|
||||
c.save();c.translate(128+34*sq,28);c.fillStyle=RIB;c.lineWidth=7;for(const d of[-1,1])c.beginPath(),c.moveTo(0,0),c.lineTo(d*24*max(.35,w),-12),c.lineTo(d*24*max(.35,w),12),c.closePath(),c.fill(),c.stroke();c.beginPath();c.arc(0,0,8,0,T);c.fill();c.stroke();c.restore()
|
||||
})
|
||||
arcs(1)
|
||||
orb(s=>s>=0)
|
||||
}
|
||||
const sign=x=>x<0?-1:1
|
||||
@@ -0,0 +1,51 @@
|
||||
// chibi jiangshi with dark violet hair and a qing hat hopping with stiff arms held out, long sleeves swinging, the paper talisman fluttering up on each fall
|
||||
const{sin,cos,PI,max,round}=Math,T=2*PI
|
||||
export const frames=12,delay=80
|
||||
const SKIN='#e9f5e1',HAIR='#5f3dc4',HD='#3b2a8c',HL='#b197fc',EYE='#fcc419',ED='#e67700',ROBE='#3bc9db',RD='#1098ad',GO='#ffd43b',HAT='#7048e8',HTD='#4c2fb0',RED='#fa5252',PAP='#ffe066',SH='#845ef7',BL='#ffc9c9'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y+1,4,0,T);c.fill()
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(x-18,y-22,36,18);c.fill()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-4);c.quadraticCurveTo(x,y-8,x+16,y-4);c.moveTo(x+d*15,y-5);c.lineTo(x+d*21,y-9);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
export default(c,t)=>{
|
||||
let sq=0,h=0,v=0
|
||||
if(t<.2)sq=.7*sin(PI*t/.2)
|
||||
else if(t<.8){const p=(t-.2)/.6;h=round(30*sin(PI*p));v=cos(PI*p)}
|
||||
else sq=.5*sin(PI*(t-.8)/.2)
|
||||
const fl=max(0,-v)
|
||||
c.fillStyle='#e9ecef';c.beginPath();c.ellipse(128,240,44-h*.4,7-h*.08,0,0,T);c.fill()
|
||||
c.save();c.translate(128,236-h);c.scale(.95*(1+sq*.1),.95*(1-sq*.12));c.translate(-128,-236)
|
||||
for(const x of[120,136]){line(c,[[x,202],[x,224]],8,SKIN);c.lineWidth=5;c.fillStyle=SH;c.beginPath();c.ellipse(x+(x<128?-3:3),228,11,7,0,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle=ROBE;c.lineWidth=5;c.beginPath();c.moveTo(110,134);c.lineTo(146,134);c.lineTo(158,208);c.lineTo(98,208);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=GO;c.beginPath();c.rect(100,196,56,12);c.fill();c.stroke()
|
||||
c.fillStyle=RD;c.beginPath();c.moveTo(128,136);c.lineTo(128,196);c.stroke()
|
||||
c.fillStyle=GO;c.lineWidth=4;c.beginPath();c.arc(128,170,9,0,T);c.fill();c.stroke();c.beginPath();c.moveTo(122,170);c.lineTo(134,170);c.moveTo(128,164);c.lineTo(128,176);c.stroke()
|
||||
const ay=146-6*v,dr=6+10*fl
|
||||
for(const d of[-1,1]){
|
||||
c.fillStyle=ROBE;c.lineWidth=5;c.beginPath();c.moveTo(128+d*14,136);c.lineTo(128+d*64,ay-8);c.lineTo(128+d*70,ay+12+dr);c.lineTo(128+d*50,ay+18+dr*.6);c.lineTo(128+d*16,156);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=GO;c.beginPath();c.moveTo(128+d*64,ay-8);c.lineTo(128+d*72,ay-6);c.lineTo(128+d*78,ay+14+dr);c.lineTo(128+d*70,ay+12+dr);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.ellipse(128+d*84,ay+8+dr*.5,7,10,-d*.3,0,T);c.fill();c.stroke()}
|
||||
c.save();c.translate(128,92);c.scale(.6,.6);c.translate(-128,-128)
|
||||
c.lineWidth=9;c.fillStyle=HD;c.beginPath();c.moveTo(128,44);c.bezierCurveTo(200,44,212,104,202,162);c.quadraticCurveTo(190,176,176,168);c.lineTo(80,168);c.quadraticCurveTo(66,176,54,162);c.bezierCurveTo(44,104,56,44,128,44);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,101,142,-1);eye(c,155,142,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(114,168);c.quadraticCurveTo(128,178,142,168);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(132,171);c.lineTo(136,180);c.lineTo(140,170);c.fill();c.stroke()
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(68,150)
|
||||
c.bezierCurveTo(56,90,90,64,128,64);c.bezierCurveTo(166,64,200,90,188,150)
|
||||
c.quadraticCurveTo(184,118,174,106);c.lineTo(160,114);c.lineTo(150,100);c.lineTo(138,112);c.lineTo(118,112);c.lineTo(106,100);c.lineTo(96,114);c.lineTo(82,106);c.quadraticCurveTo(72,118,68,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=7;c.beginPath();c.moveTo(80,92);c.quadraticCurveTo(86,80,96,76);c.moveTo(168,86);c.quadraticCurveTo(174,96,176,104);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=HAT;c.beginPath();c.moveTo(70,76);c.bezierCurveTo(70,20,186,20,186,76);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=HTD;c.beginPath();c.ellipse(128,78,76,16,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=RED;c.beginPath();c.arc(128,22,13,0,T);c.fill();c.stroke()
|
||||
c.save();c.translate(128,82);c.transform(1,0,-.25*fl,1,0,0);c.scale(1,1-.55*fl)
|
||||
c.fillStyle=PAP;c.lineWidth=6;c.beginPath();c.rect(-15,0,30,80);c.fill();c.stroke()
|
||||
c.strokeStyle=RED;c.lineWidth=4;c.beginPath();c.moveTo(-6,10);c.lineTo(6,10);c.moveTo(0,10);c.lineTo(0,30);c.moveTo(-6,22);c.quadraticCurveTo(8,26,-4,36);c.quadraticCurveTo(8,44,-6,52);c.moveTo(-6,64);c.lineTo(6,64);c.moveTo(0,58);c.lineTo(0,72);c.stroke()
|
||||
c.restore();c.restore();c.restore()
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// girl with lime pigtails skipping rope, the rope sweeping over her head and under her hopping feet, pigtails bouncing
|
||||
const{sin,cos,PI,max,min,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=70
|
||||
const SKIN='#ffe3d1',HAIR='#94d82d',HD='#66a80f',HL='#e9fac8',EYE='#f06595',ED='#a61e4d',TEE='#ff922b',TD='#e8590c',JEAN='#4dabf7',JD='#1c7ed6',ROPE='#38d9a9',GRIP='#9775fa',TIE='#f783ac',BL='#ffa8a8'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
export default(c,t,i)=>{
|
||||
const f=T*2*t,h=26*max(0,-cos(f)),cr=7*max(0,(cos(f)-.55)/.45),oy=cr-h,sw=sin(f)
|
||||
const hy=162+oy,hl=[84-4*sw,hy-5*cos(f)],hr=[172+4*sw,hy-5*cos(f)],by=hy-(cos(f)>0?134:100)*cos(f)
|
||||
const rope=()=>{const q=[2*128-(hl[0]+hr[0])/2,2*by-(hl[1]+hr[1])/2];for(const[lw,s]of[[10,'#111'],[5,ROPE]])c.lineWidth=lw,c.strokeStyle=s,c.beginPath(),c.moveTo(...hl),c.quadraticCurveTo(...q,...hr),c.stroke();c.strokeStyle='#111'}
|
||||
c.translate(128,134);c.scale(1.06,1.06);c.translate(-128,-134)
|
||||
c.fillStyle='#dee2e6';c.beginPath();c.ellipse(128,232,44*(1-h/70),6*(1-h/70),0,0,T);c.fill()
|
||||
if(sw<=0)rope()
|
||||
const head=g=>{c.save();c.translate(128,88+oy);c.scale(.56,.56);c.translate(-128,-120);g();c.restore()}
|
||||
head(()=>{
|
||||
for(const d of[-1,1]){
|
||||
c.save();c.translate(128+d*60,78);c.rotate(-d*(.3-.32*sin(f-.6)))
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(0,-6);c.bezierCurveTo(d*44,0,d*50,70,d*22,118);c.quadraticCurveTo(d*14,128,d*6,116);c.bezierCurveTo(d*18,70,d*6,30,0,-6);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=5;c.beginPath();c.moveTo(d*12,14);c.bezierCurveTo(d*28,40,d*30,74,d*20,100);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
}
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(194,36,204,96,196,150);c.lineTo(60,150);c.bezierCurveTo(52,96,62,36,128,36);c.fill();c.stroke()
|
||||
})
|
||||
const lg=[[118,-1],[138,1]],fy=224-h
|
||||
for(const[x,d]of lg){const air=h>4?min(1,h/14):0,kx=x+d*(3+cr*.8)-d*air*2,ky=(186+oy+fy)/2-air*4;line(c,[[x,184+oy],[kx,ky],[x+d*2-d*air*6*0,fy-6]],8,SKIN)}
|
||||
for(const[x,d]of lg){c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(x+d*5,fy-3,11,7,0,0,T);c.fill();c.stroke();c.strokeStyle=TD;c.lineWidth=3;c.beginPath();c.moveTo(x+d*5-7,fy-5);c.lineTo(x+d*5+7,fy-5);c.stroke();c.strokeStyle='#111'}
|
||||
c.fillStyle=JEAN;c.lineWidth=5;c.beginPath();c.moveTo(106,166+oy);c.lineTo(150,166+oy);c.lineTo(154,190+oy);c.lineTo(131,190+oy);c.lineTo(128,182+oy);c.lineTo(125,190+oy);c.lineTo(102,190+oy);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=JD;c.lineWidth=3;c.beginPath();c.moveTo(128,168+oy);c.lineTo(128,180+oy);c.stroke();c.strokeStyle='#111'
|
||||
for(const[s,hd,d]of[[[110,136+oy],hl,-1],[[146,136+oy],hr,1]]){const e=[s[0]+d*12,(s[1]+hd[1])/2+4];line(c,[s,e,hd],8,SKIN)}
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(122,122+oy,12,10);c.fill()
|
||||
c.fillStyle=TEE;c.lineWidth=5;c.beginPath();c.moveTo(114,128+oy);c.lineTo(142,128+oy);c.lineTo(158,138+oy);c.lineTo(152,150+oy);c.lineTo(146,146+oy);c.lineTo(150,172+oy);c.lineTo(106,172+oy);c.lineTo(110,146+oy);c.lineTo(104,150+oy);c.lineTo(98,138+oy);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(118,129+oy);c.quadraticCurveTo(128,138+oy,138,129+oy);c.stroke()
|
||||
c.fillStyle='#ffd43b';c.lineWidth=3.5;c.beginPath();for(let k=0;k<10;k++){const r=k%2?3.5:8,a=k*PI/5-PI/2;c.lineTo(128+r*cos(a),154+oy+r*sin(a))}c.closePath();c.fill();c.stroke()
|
||||
for(const hd of[hl,hr]){c.save();c.translate(...hd);c.rotate(hd==hl?-.3:.3);c.fillStyle=GRIP;c.lineWidth=4;c.beginPath();c.roundRect(-5,-12,10,22,5);c.fill();c.stroke();c.restore();c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.arc(hd[0],hd[1],6.5,0,T);c.fill();c.stroke()}
|
||||
head(()=>{
|
||||
c.lineWidth=9;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(i==11){c.lineWidth=8;for(const x of[103,153])c.beginPath(),c.moveTo(x-15,142),c.quadraticCurveTo(x,150,x+15,142),c.stroke()}else eye(c,103,140,-1),eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,162,12,6,0,0,T),c.fill()
|
||||
c.fillStyle='#c92a2a';c.lineWidth=6;c.beginPath();c.moveTo(116,164);c.quadraticCurveTo(128,186,140,164);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.moveTo(70,140);c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,140)
|
||||
c.quadraticCurveTo(184,116,176,102);c.quadraticCurveTo(168,100,162,110);c.quadraticCurveTo(156,96,146,92);c.quadraticCurveTo(140,104,132,110)
|
||||
c.quadraticCurveTo(126,96,118,92);c.quadraticCurveTo(112,104,104,110);c.quadraticCurveTo(98,96,90,98);c.quadraticCurveTo(84,104,80,110);c.quadraticCurveTo(72,118,70,140);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=8;for(const[a,e]of[[1.26,1.38],[1.46,1.54],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=TIE;c.lineWidth=7;for(const d of[-1,1])c.beginPath(),c.arc(128+d*60,76,11,0,T),c.fill(),c.stroke()
|
||||
})
|
||||
if(sw>0)rope()
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// cozy chibi with a messy sandy bun and round glasses knitting cross-legged, needles clicking, the striped scarf creeping down, yarn ball rolling
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=12,delay=100
|
||||
const SKIN='#ffe3d1',HAIR='#e2c08d',HD='#b8955e',HL='#f7e8cc',EYE='#4dabf7',ED='#1864ab',BL='#ffa8a8',SW='#f783ac',SWD='#e64980',PANT='#748ffc',SOCK='#fff',CUSH='#ffc078',CUD='#fd7e14',Y1='#38d9a9',Y2='#fff4e6',YD='#0ca678',ND='#e0a96d'
|
||||
const ds=(c,a,b,s,f)=>{for(const[l,k]of[[a,'#111'],[b,s]])c.lineWidth=l,c.strokeStyle=k,c.beginPath(),f(),c.stroke();c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x,y+7,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-3,13,10,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+8,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y+2,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+12,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const k=sin(3*T*t),rot=-t*T
|
||||
c.translate(122,250);c.scale(1.08,1.08);c.translate(-122,-250)
|
||||
c.lineWidth=6;c.fillStyle=CUSH;c.beginPath();c.ellipse(112,226,80,18,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=CUD;for(const a of[-1,1])c.beginPath(),c.arc(112+a*62,224,5,0,T),c.fill(),c.stroke()
|
||||
const bx=206+3*sin(T*t),by=216-Math.abs(2*sin(2*T*t))
|
||||
ds(c,7,3,Y1,()=>{c.moveTo(bx-14,by-14);c.bezierCurveTo(176,190+6*k,150,160,124,178)})
|
||||
c.save();c.translate(bx,by);c.rotate(rot);c.fillStyle=Y1;c.lineWidth=6;c.beginPath();c.arc(0,0,22,0,T);c.fill();c.stroke()
|
||||
c.save();c.clip();c.strokeStyle=YD;c.lineWidth=3;for(const a of[-.6,0,.6])c.beginPath(),c.ellipse(0,0,24,10,a,0,T),c.stroke();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.arc(0,0,22,0,T);c.stroke();c.restore()
|
||||
c.lineWidth=6;c.fillStyle=PANT;c.beginPath();c.moveTo(60,214);c.quadraticCurveTo(70,188,112,190);c.quadraticCurveTo(156,188,166,214);c.quadraticCurveTo(112,226,60,214);c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(112,192);c.quadraticCurveTo(108,206,96,214);c.stroke()
|
||||
c.fillStyle=SOCK;c.lineWidth=5;for(const[x,a]of[[62,-.3],[164,.3]])c.beginPath(),c.ellipse(x,212,11,8,a,0,T),c.fill(),c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=SW;c.beginPath();c.moveTo(94,124);c.quadraticCurveTo(70,132,72,196);c.lineTo(152,196);c.quadraticCurveTo(154,132,130,124);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=SWD;c.lineWidth=3;c.beginPath();for(let y=140;y<192;y+=10)c.moveTo(80,y),c.lineTo(84,y+4),c.moveTo(144,y),c.lineTo(140,y+4);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=SWD;c.lineWidth=5;c.beginPath();c.moveTo(96,124);c.quadraticCurveTo(112,140,128,124);c.lineTo(126,132);c.quadraticCurveTo(112,146,98,132);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.beginPath();c.rect(98,176,28,56);c.clip()
|
||||
c.fillStyle=Y1;c.fillRect(98,176,28,56);c.fillStyle=Y2;const o=16*t*3%16;for(let y=150+o;y<240;y+=16)c.fillRect(98,y,28,8)
|
||||
c.strokeStyle=YD;c.lineWidth=2;c.beginPath();for(let y=150+o;y<240;y+=8)for(const x of[104,112,120])c.moveTo(x-3,y),c.lineTo(x,y+4),c.lineTo(x+3,y);c.stroke();c.restore()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(98,176);c.lineTo(98,230);c.quadraticCurveTo(112,234,126,230);c.lineTo(126,176);c.stroke()
|
||||
c.save();c.translate(112,106);c.scale(.6,.6);c.translate(-128,-120)
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.arc(128,24,30,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=4;c.beginPath();c.arc(128,24,18,.5,3.6);c.stroke();c.strokeStyle='#111'
|
||||
for(const[a,b]of[[[104,10],[90,-6]],[[150,6],[166,-8]]])ds(c,7,3,HAIR,()=>{c.moveTo(...a);c.quadraticCurveTo(a[0],b[1]+4,...b)})
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,40);c.bezierCurveTo(194,40,206,100,196,160);c.lineTo(60,160);c.bezierCurveTo(50,100,62,40,128,40);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1);eye(c,153,140,1)
|
||||
c.lineWidth=5;c.strokeStyle='#495057';for(const x of[103,153])c.beginPath(),c.arc(x,141,23,0,T),c.stroke();c.beginPath();c.moveTo(126,138);c.quadraticCurveTo(128,132,130,138);c.stroke();c.strokeStyle='#111'
|
||||
c.strokeStyle='#fff';c.lineWidth=4;for(const x of[94,144])c.beginPath(),c.moveTo(x,128),c.lineTo(x+8,124),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*48,168,11,6,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(118,172);c.quadraticCurveTo(128,180,138,172);c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(68,150)
|
||||
c.bezierCurveTo(52,80,90,44,128,44);c.bezierCurveTo(166,44,204,80,188,150)
|
||||
c.quadraticCurveTo(186,120,176,108);c.quadraticCurveTo(168,96,154,100);c.quadraticCurveTo(148,110,140,112)
|
||||
c.quadraticCurveTo(132,98,118,98);c.quadraticCurveTo(116,108,104,112);c.quadraticCurveTo(96,100,86,104)
|
||||
c.quadraticCurveTo(72,122,68,150);c.closePath();c.fill();c.stroke()
|
||||
for(const d of[-1,1])ds(c,7,3,HAIR,()=>{c.moveTo(128+d*58,120);c.quadraticCurveTo(128+d*70,150,128+d*60,176)})
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[s,e]of[[1.28,1.4],[1.5,1.58],[1.66,1.76]])c.beginPath(),c.arc(128,110,56,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
const hl=[100,178+2*k],hr=[124,178-2*k]
|
||||
ds(c,18,10,SW,()=>{c.moveTo(90,134);c.lineTo(80,168);c.lineTo(hl[0]-4,hl[1]+2)})
|
||||
ds(c,18,10,SW,()=>{c.moveTo(134,134);c.lineTo(144,168);c.lineTo(hr[0]+4,hr[1]+2)})
|
||||
for(const[[x,y],a]of[[hl,-.42+.14*k],[hr,PI+.42+.14*k]]){ds(c,8,4,ND,()=>{c.moveTo(x-cos(a)*8,y-sin(a)*8);c.lineTo(x+cos(a)*42,y+sin(a)*42)});c.fillStyle='#f06595';c.lineWidth=3;c.beginPath();c.arc(x-cos(a)*10,y-sin(a)*10,5,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle=SKIN;c.lineWidth=5;for(const[x,y]of[hl,hr])c.beginPath(),c.ellipse(x,y,9,9,0,0,T),c.fill(),c.stroke()
|
||||
if(Math.abs(k)>.8)for(const d of[-1,1])ds(c,6,2.5,'#fff',()=>{c.moveTo(112+d*20,158);c.lineTo(112+d*26,150)})
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// magical girl with violet twin tails raising her heart wand while a pink ribbon spirals up around her skirt, sparkles popping
|
||||
const{sin,cos,PI,max,round}=Math,T=2*PI
|
||||
export const frames=12,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#9775fa',HD='#7048e8',HL='#e5dbff',EYE='#f783ac',ED='#c2255c',DR='#fff',PK='#faa2c1',PD='#f06595',GO='#ffd43b',GD='#f59f00',BL='#ffa8a8'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<10;k++){const r=k%2?3:8,a=k*PI/5-PI/2;c.lineTo(x+cos(a)*r,y+3+sin(a)*r)}c.closePath();c.fill()
|
||||
c.beginPath();c.arc(x+6,y+10,2.5,0,T);c.fill()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const heart=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.beginPath();c.moveTo(0,8);c.bezierCurveTo(-13,-1,-9,-12,0,-5);c.bezierCurveTo(9,-12,13,-1,0,8);c.restore();c.fill();c.stroke()}
|
||||
const spark=(c,x,y,r)=>{const q=r*.2;c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x+q,y-q,x+r,y);c.quadraticCurveTo(x+q,y+q,x,y+r);c.quadraticCurveTo(x-q,y+q,x-r,y);c.quadraticCurveTo(x-q,y-q,x,y-r);c.fill();c.stroke()}
|
||||
const ribbon=(c,ph,col,front)=>{
|
||||
const P=[];for(let k=0;k<=36;k++){const s=k/36,a=T*1.2*s+ph,r=86-22*s;P.push([128+r*cos(a),230-104*s+12*sin(a),sin(a)>0])}
|
||||
for(const[lw,st]of[[13,'#111'],[6,col]]){c.lineWidth=lw;c.strokeStyle=st;c.beginPath();let on=false
|
||||
for(let k=1;k<P.length;k++){const f=P[k][2]&&P[k-1][2];if(f==front){if(!on)c.moveTo(P[k-1][0],P[k-1][1]),on=true;c.lineTo(P[k][0],P[k][1])}else on=false}c.stroke()}
|
||||
c.strokeStyle='#111'
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const ph=-T*t,sw=.12*sin(T*t)
|
||||
ribbon(c,ph,PD,false)
|
||||
c.save();c.translate(128,90);c.scale(.6,.6);c.translate(-128,-128)
|
||||
for(const d of[-1,1]){c.save();c.translate(128+d*66,64);c.rotate(d*(.15+sw*d))
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(0,0);c.bezierCurveTo(d*70,10,d*84,110,d*44,170);c.bezierCurveTo(d*20,206,d*60,236,d*74,214);c.bezierCurveTo(d*70,250,d*10,236,d*14,190);c.bezierCurveTo(d*20,130,d*24,60,0,20);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=5;c.beginPath();c.moveTo(d*18,30);c.bezierCurveTo(d*50,70,d*54,120,d*30,170);c.stroke();c.restore()}
|
||||
c.restore()
|
||||
for(const x of[118,138]){line(c,[[x,192],[x+(x<128?-2:2),214]],8,SKIN);c.lineWidth=5;c.fillStyle=PD;c.beginPath();c.roundRect(x-9+(x<128?-2:2),208,18,20,6);c.fill();c.stroke()}
|
||||
c.fillStyle=PK;c.lineWidth=5;c.beginPath();c.moveTo(110,148);c.lineTo(146,148);c.bezierCurveTo(166,160,176,180,182,196);for(let k=0;k<6;k++)c.quadraticCurveTo(174-k*18,206,164-k*18,198);c.bezierCurveTo(80,180,90,160,110,148);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=DR;c.beginPath();c.moveTo(112,148);c.lineTo(144,148);c.bezierCurveTo(158,160,164,172,166,184);for(let k=0;k<5;k++)c.quadraticCurveTo(160-k*16,192,154-k*16,186);c.bezierCurveTo(92,172,98,160,112,148);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=DR;c.beginPath();c.moveTo(112,124);c.lineTo(144,124);c.lineTo(146,150);c.lineTo(110,150);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=PD;for(const d of[-1,1])c.beginPath(),c.moveTo(128,132),c.quadraticCurveTo(128+d*22,118,128+d*24,136),c.quadraticCurveTo(128+d*20,148,128,134),c.fill(),c.stroke()
|
||||
c.fillStyle=GO;heart(c,128,133,.7)
|
||||
line(c,[[112,130],[96,146],[94,160]],7,SKIN)
|
||||
c.lineWidth=4;c.fillStyle=SKIN;c.beginPath();c.arc(94,162,6,0,T);c.fill();c.stroke()
|
||||
const wx=190,wy=40+round(3*sin(2*T*t))
|
||||
line(c,[[144,128],[172,112],[wx,wy+30]],7,SKIN)
|
||||
for(const[lw,col]of[[11,'#111'],[5,GO]])c.lineWidth=lw,c.strokeStyle=col,c.beginPath(),c.moveTo(wx-2,wy+44),c.lineTo(wx,wy),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=4;c.fillStyle=SKIN;c.beginPath();c.arc(wx,wy+30,7,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=5;c.fillStyle=PD;heart(c,wx,wy-6,1.7);c.fillStyle='#fff';c.lineWidth=0;c.beginPath();c.ellipse(wx-6,wy-10,3,2,-.5,0,T);c.fill()
|
||||
c.fillStyle=GO;c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(wx+d*10,wy-2),c.lineTo(wx+d*24,wy-12),c.lineTo(wx+d*20,wy+2),c.closePath(),c.fill(),c.stroke()
|
||||
c.save();c.translate(128,90);c.scale(.6,.6);c.translate(-128,-128)
|
||||
c.lineWidth=9;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1);eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,163,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=6;c.fillStyle='#c92a2a';c.beginPath();c.moveTo(114,164);c.quadraticCurveTo(128,184,142,164);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,140)
|
||||
c.bezierCurveTo(52,70,90,36,128,36);c.bezierCurveTo(166,36,204,70,190,140)
|
||||
c.quadraticCurveTo(186,112,176,100);c.quadraticCurveTo(168,104,162,112);c.quadraticCurveTo(156,96,146,90);c.quadraticCurveTo(140,104,132,110)
|
||||
c.quadraticCurveTo(126,94,116,90);c.quadraticCurveTo(110,104,100,110);c.quadraticCurveTo(96,96,88,96);c.quadraticCurveTo(82,108,80,114)
|
||||
c.quadraticCurveTo(70,112,66,140);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=7;for(const[a,e]of[[1.26,1.38],[1.46,1.54],[1.62,1.72]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=7;c.fillStyle=GO;for(const d of[-1,1])heart(c,128+d*66,62,1.6)
|
||||
c.restore()
|
||||
ribbon(c,ph,PD,true)
|
||||
for(let k=0;k<4;k++){const p=(t+k/4)%1,a=k*1.7,r=round(10*sin(PI*p));if(r>2){c.fillStyle=[GO,PK,'#fff','#99e9f2'][k];c.lineWidth=3;spark(c,128+92*cos(a),120+86*sin(a)*.8,r)}}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// new year girl in a red furisode pulling a stretchy mochi out of her ozoni bowl with chopsticks, it stretches long, she bites it and chews happily
|
||||
const{sin,cos,PI,max,min,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=90
|
||||
const SKIN='#ffe3d1',HAIR='#364fc7',HD='#253789',HL='#91a7ff',EYE='#12b886',ED='#087f5b',KIM='#e03131',KD='#a61e1e',COL='#fff',COL2='#faa2c1',BOWL='#c92a2a',BI='#2b2b2b',SOUP='#ffe8cc',MOCHI='#fff',STICK='#a9744f',KAN='#ffd43b',BL='#ffa8a8',MO='#c92a2a'
|
||||
const cl=x=>max(0,min(1,x)),ease=x=>x*x*(3-2*x)
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const up=ease(cl(t/.38)),go=ease(cl((t-.38)/.12)),down=ease(cl((t-.72)/.26)),bite=t>=.48&&t<.72,chew=t>=.56&&t<.95,snap=t>=.56,u=snap?1-down:up
|
||||
// chopstick tip pulls up and out beside her face, swings to her mouth for the bite, then goes back to the bowl
|
||||
const [tx,ty]=snap?[136+10*down,170+40*down]:[146+44*up-54*go,210-92*up+52*go]
|
||||
// hair up in a bun
|
||||
c.lineWidth=6;c.fillStyle=HD;c.beginPath();c.arc(128,34,28,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle=HAIR;c.lineWidth=4;c.beginPath();c.arc(128,34,16,PI*1.1,PI*1.9);c.stroke();c.strokeStyle='#111';c.lineWidth=6
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,40);c.bezierCurveTo(196,40,206,100,196,150);c.lineTo(60,150);c.bezierCurveTo(50,100,60,40,128,40);c.fill();c.stroke()
|
||||
// furisode
|
||||
c.fillStyle=KIM;c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(58,198,48,262);c.lineTo(208,262);c.quadraticCurveTo(198,198,156,188);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3;for(const[x,y]of[[70,236],[188,226],[82,208]]){for(let k=0;k<5;k++){const a=k*T/5-PI/2;c.beginPath();c.arc(x+5*cos(a),y+5*sin(a),3.5,0,T);c.fill();c.stroke()}c.fillStyle='#ffd43b';c.beginPath();c.arc(x,y,2.5,0,T);c.fill();c.fillStyle='#fff'}
|
||||
c.lineWidth=5;c.fillStyle=COL;c.beginPath();c.moveTo(108,188);c.lineTo(128,226);c.lineTo(148,188);c.lineTo(140,188);c.lineTo(128,212);c.lineTo(116,188);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=COL2;c.beginPath();c.moveTo(100,190);c.lineTo(126,236);c.lineTo(114,236);c.lineTo(92,194);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(156,190);c.lineTo(130,236);c.lineTo(142,236);c.lineTo(164,194);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,168);c.lineTo(116,188);c.lineTo(128,208);c.lineTo(140,188);c.lineTo(140,168);c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,188);c.moveTo(140,168);c.lineTo(140,188);c.stroke()
|
||||
// face
|
||||
c.lineWidth=6;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(chew){const pf=1+.2*abs(sin(4*T*t));c.fillStyle=SKIN;for(const d of[-1,1])c.beginPath(),c.arc(128+d*40,162,13*pf,d<0?PI*.5:-PI*.5,d<0?PI*1.5:PI*.5),c.fill(),c.stroke()
|
||||
c.lineWidth=5;for(const x of[103,153])c.beginPath(),c.arc(x,144,12,PI*1.15,PI*1.85),c.stroke()}
|
||||
else{eye(c,103,140,-1);eye(c,153,140,1)}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,161,12,6,0,0,T),c.fill()
|
||||
c.lineWidth=3.5;c.fillStyle=MO;c.beginPath()
|
||||
if(bite&&!snap)c.ellipse(128,170,9,10,0,0,T),c.fill(),c.stroke()
|
||||
else if(chew)c.ellipse(128,170,4+2*abs(sin(4*T*t)),3,0,0,T),c.stroke()
|
||||
else c.moveTo(118,166),c.quadraticCurveTo(128,175,138,166),c.stroke()
|
||||
// swept bangs and thin side locks
|
||||
c.lineWidth=6;c.fillStyle=HAIR;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*52,118),c.quadraticCurveTo(128+d*62,150,128+d*56,178),c.quadraticCurveTo(128+d*50,160,128+d*44,124),c.closePath(),c.fill(),c.stroke()
|
||||
c.beginPath();c.moveTo(66,146);c.bezierCurveTo(50,74,88,40,128,40);c.bezierCurveTo(168,40,206,74,190,146)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(170,102,164,110);c.quadraticCurveTo(160,96,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,122,90);c.quadraticCurveTo(116,104,106,108);c.quadraticCurveTo(102,96,92,96);c.quadraticCurveTo(86,106,80,112);c.quadraticCurveTo(70,124,66,146);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=KAN;c.lineWidth=4;for(let k=0;k<5;k++){const a=k*T/5;c.beginPath();c.arc(160+8*cos(a),30+8*sin(a),6,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle=KIM;c.beginPath();c.arc(160,30,5,0,T);c.fill();c.stroke()
|
||||
const sw=4*sin(T*t);c.lineWidth=3;c.beginPath();c.moveTo(166,38);c.lineTo(178+sw,62);c.stroke();c.fillStyle=COL2;c.beginPath();c.arc(178+sw,66,5,0,T);c.fill();c.stroke()
|
||||
// ozoni bowl
|
||||
c.lineWidth=6;c.fillStyle=BOWL;c.beginPath();c.moveTo(84,214);c.quadraticCurveTo(88,254,128,254);c.quadraticCurveTo(168,254,172,214);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=SOUP;c.beginPath();c.ellipse(128,214,44,9,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#51cf66';c.lineWidth=3;c.beginPath();c.ellipse(108,214,8,3,.3,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#ffa8a8';c.beginPath();c.ellipse(112,216,5,3,0,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle='#ffd43b';c.lineWidth=4;c.beginPath();c.arc(128,238,10,PI*1.1,PI*1.9);c.stroke();c.strokeStyle='#111'
|
||||
// the mochi: a blob in the bowl stretching up to the chopstick tip, snapping back after the bite
|
||||
const st=snap?0:u,mh=8+6*(1-st),bx=146,by=212
|
||||
c.fillStyle=MOCHI;c.lineWidth=4
|
||||
if(st>.05){const w=max(4,14*(1-st*.7));c.beginPath();c.moveTo(bx-14,by);c.bezierCurveTo(bx-w,by-12,tx-w,ty+18,tx-10,ty+4);c.lineTo(tx+10,ty+4);c.bezierCurveTo(tx+w,ty+18,bx+w,by-12,bx+14,by);c.closePath();c.fill();c.stroke()}
|
||||
c.beginPath();c.ellipse(bx,by-2,18,mh*.6,0,0,T);c.fill();c.stroke()
|
||||
if(!snap&&u>.05){c.beginPath();c.ellipse(tx,ty+2,13,9,0,0,T);c.fill();c.stroke()}
|
||||
// steam
|
||||
c.strokeStyle='#fff';c.lineWidth=3;for(const x of[100,118]){const p=(t*2+(x-100)/36)%1;c.globalAlpha=sin(PI*p);for(const[w,s]of[[7,'#111'],[3,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x,206-12*p),c.quadraticCurveTo(x+6,200-12*p,x,192-12*p),c.stroke()}
|
||||
c.globalAlpha=1;c.strokeStyle='#111'
|
||||
// chopsticks in her hand
|
||||
const hx=min(tx+52,222),hy=ty+28
|
||||
for(const k of[0,1])for(const[l,s]of[[9,'#111'],[4,STICK]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(hx+8+k*4,hy-2+k*6),c.lineTo(tx+(k?2:-2),ty+(k?3:-1)),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(hx,hy,13,11,-.4,0,T);c.fill();c.stroke()
|
||||
for(const[l,s]of[[30,'#111'],[19,KIM]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(214,262),c.quadraticCurveTo(214,244,hx+10,hy+8),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
if(chew&&t<.9){c.fillStyle='#ffd43b';c.lineWidth=3;for(const[x,y,o]of[[52,110,0],[206,104,.5]]){const s=8*sin(PI*((t*4+o)%1));if(s>1.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()}}}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// girl with dark brown half-up hair folding a pink paper square, poof, it becomes a paper crane that flaps up out of her hands while she watches in delight
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=90
|
||||
const SKIN='#ffe3d1',HAIR='#6b4a35',HD='#4f3526',HL='#b8927a',EYE='#9c36b5',ED='#5f1f6e',BL='#ffa8a8',CAR='#96f2d7',CAD='#38d9a9',PAP='#faa2c1',PAD='#f06595',TIE='#fa5252',STK='#ffd43b'
|
||||
const ds=(c,a,b,s,f)=>{for(const[l,k]of[[a,'#111'],[b,s]])c.lineWidth=l,c.strokeStyle=k,c.beginPath(),f(),c.stroke();c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d,ly,lx=0)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x+lx,y+2+ly,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x+lx,y-8+ly,13,10,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+lx,y+3+ly,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+lx-4,y-5+ly,4.5,0,T);c.fill();c.beginPath();c.arc(x+lx+5,y+8+ly,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const P=(c,...q)=>{c.beginPath();q.forEach(([x,y],i)=>i?c.lineTo(x,y):c.moveTo(x,y));c.closePath()}
|
||||
const crane=(c,x,y,f)=>{
|
||||
c.save();c.translate(x,y);c.scale(-1.3,1.3);c.lineWidth=3.5;c.fillStyle=PAD
|
||||
P(c,[-14,-2],[-4,5],[-32,-28]);c.fill();c.stroke();P(c,[-32,-28],[-42,-20],[-29,-21]);c.fill();c.stroke()
|
||||
P(c,[14,-2],[5,5],[26,-20]);c.fill();c.stroke()
|
||||
c.fillStyle=PAP;P(c,[-16,0],[0,-8],[16,0],[0,11]);c.fill();c.stroke()
|
||||
c.fillStyle=PAD;P(c,[-9,-3],[9,-3],[6,-3-36*f]);c.fill();c.stroke()
|
||||
c.fillStyle=PAP;P(c,[-9,-3],[9,-3],[-4,-3-40*f]);c.fill();c.stroke()
|
||||
c.strokeStyle=PAD;c.lineWidth=2;c.beginPath();c.moveTo(0,-8);c.lineTo(0,11);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
}
|
||||
const poof=(c,x,y,r)=>{c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();for(let k=0;k<6;k++){const a=k*PI/3,X=x+cos(a)*r*.7,Y=y+sin(a)*r*.5;c.moveTo(X+r*.4,Y);c.arc(X,Y,r*.4,0,T)}c.fill();c.stroke();for(let k=0;k<4;k++){const a=k*PI/2+.4;ds(c,6,2.5,'#ffd43b',()=>{c.moveTo(x+cos(a)*r*1.1,y+sin(a)*r*.9);c.lineTo(x+cos(a)*r*1.5,y+sin(a)*r*1.2)})}}
|
||||
export default(c,t)=>{
|
||||
const fold=min(1,max(0,(t-.12)/.26)),fly=t>=.44&&t<.92,fp=fly?(t-.44)/.48:0,cy=198-92*sin(PI*fp),cx=128+70*sin(PI*fp),flap=.4+.6*Math.abs(sin(3*T*fp)),ly=fly?5-11*sin(PI*fp):5,lx=fly?5*sin(PI*fp):0,joy=fly&&fp>.15&&fp<.85
|
||||
c.lineWidth=7;c.fillStyle=CAR;c.beginPath();c.moveTo(102,150);c.quadraticCurveTo(62,160,54,250);c.lineTo(202,250);c.quadraticCurveTo(194,160,154,150);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(110,152);c.lineTo(128,176);c.lineTo(146,152);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=TIE;c.beginPath();c.moveTo(128,168);c.lineTo(120,190);c.lineTo(128,198);c.lineTo(136,190);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=CAD;c.lineWidth=3;c.beginPath();for(const y of[200,214,228])c.moveTo(66,y),c.lineTo(80,y),c.moveTo(176,y),c.lineTo(190,y);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=SKIN;c.lineWidth=6;c.beginPath();c.moveTo(118,134);c.lineTo(118,152);c.quadraticCurveTo(128,160,138,152);c.lineTo(138,134);c.fill();c.beginPath();c.moveTo(118,134);c.lineTo(118,150);c.moveTo(138,134);c.lineTo(138,150);c.stroke()
|
||||
c.save();c.translate(128,96);c.scale(.68,.68);c.translate(-128,-120)
|
||||
c.lineWidth=7;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(198,36,210,100,202,176);c.quadraticCurveTo(190,188,180,180);c.lineTo(76,180);c.quadraticCurveTo(66,188,54,176);c.bezierCurveTo(46,100,58,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=HAIR;c.beginPath();c.moveTo(150,40);c.bezierCurveTo(190,6,214,30,200,66);c.quadraticCurveTo(188,48,164,52);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=TIE;c.lineWidth=5;c.beginPath();c.ellipse(160,46,8,11,.6,0,T);c.fill();c.stroke()
|
||||
ds(c,9,4,STK,()=>{c.moveTo(138,30);c.lineTo(186,62)});c.fillStyle=STK;c.lineWidth=4;c.beginPath();c.arc(136,28,6,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(t>=.4&&t<.46){c.lineWidth=7;for(const x of[103,153])c.beginPath(),c.moveTo(x-14,132),c.lineTo(x+8,140),c.lineTo(x-14,148),c.stroke()}else eye(c,103,140,-1,ly,lx),eye(c,153,140,1,ly,lx)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.fillStyle='#c92a2a';c.beginPath()
|
||||
if(joy)c.moveTo(114,164),c.quadraticCurveTo(128,186,142,164),c.closePath(),c.fill(),c.stroke();else c.moveTo(120,168),c.quadraticCurveTo(128,174,136,168),c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(64,172)
|
||||
c.bezierCurveTo(46,80,86,38,128,38);c.bezierCurveTo(170,38,210,80,192,172)
|
||||
c.quadraticCurveTo(188,140,182,112);c.lineTo(168,114);c.lineTo(166,98);c.lineTo(150,112);c.lineTo(144,96);c.lineTo(128,112)
|
||||
c.lineTo(118,96);c.lineTo(106,112);c.lineTo(96,98);c.lineTo(88,114);c.lineTo(74,112);c.quadraticCurveTo(68,140,64,172);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[s,e]of[[1.26,1.38],[1.46,1.54],[1.62,1.72]])c.beginPath(),c.arc(128,106,58,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
const hy=fly?214:206
|
||||
ds(c,20,11,CAR,()=>{c.moveTo(98,168);c.lineTo(80,200);c.lineTo(104,hy)})
|
||||
ds(c,20,11,CAR,()=>{c.moveTo(158,168);c.lineTo(176,200);c.lineTo(152,hy)})
|
||||
if(!fly&&t<.4){const top=-36+52*fold,w=36-10*fold
|
||||
c.fillStyle=PAP;c.lineWidth=5;P(c,[128-w,196],[128,196+top],[128+w,196],[128,232-8*fold]);c.fill();c.stroke()
|
||||
c.strokeStyle=PAD;c.lineWidth=2.5;c.beginPath();c.moveTo(128-w,196);c.lineTo(128+w,196);c.moveTo(128,196+top);c.lineTo(128,232-8*fold);c.stroke();c.strokeStyle='#111'}
|
||||
if(fly||t>=.4&&t<.46||t>=.92)crane(c,cx,cy,fly?flap:.4)
|
||||
if(t>=.38&&t<.48)poof(c,128,196,30*sin(PI*(t-.38)/.1))
|
||||
if(t>=.92)poof(c,128,200,30*sin(PI*(t-.92)/.08))
|
||||
c.fillStyle=SKIN;c.lineWidth=5;for(const x of[104,152])c.beginPath(),c.ellipse(x,hy,12,10,0,0,T),c.fill(),c.stroke()
|
||||
if(joy)for(const[x,y,r]of[[cx-34,cy-34,7],[cx+30,cy+30,6]]){c.fillStyle='#ffd43b';c.lineWidth=3;c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,q=k%2?r*.35:r;c.lineTo(x+cos(a)*q,y+sin(a)*q)}c.closePath();c.fill();c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// side-view chibi with long black-brown hair and a white bow playing an upright piano, hands taking turns on the keys, swaying, notes floating up
|
||||
// the dark hair gets a white rim so it doesn't vanish on black
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=12,delay=110
|
||||
const SKIN='#ffe3d1',HAIR='#4d4549',HD='#363134',HL='#a5b1c4',EYE='#748ffc',ED='#3b5bdb',BL='#ffa8a8',DRS='#5c7cfa',DRD='#4263eb',WOOD='#a9744f',WDD='#7f5539',WDL='#c99a6e',BENCH='#e8590c',SOCK='#fff',SHOE='#c2255c'
|
||||
const ds=(c,a,b,s,f)=>{for(const[l,k]of[[a,'#111'],[b,s]])c.lineWidth=l,c.strokeStyle=k,c.beginPath(),f(),c.stroke();c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x+5,y+5,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x+5,y-5,13,10,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+6,y+6,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+1,y-1,4.5,0,T);c.fill();c.beginPath();c.arc(x+10,y+11,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const note=(c,x,y,s,col)=>{ds(c,8,3,col,()=>{c.moveTo(x+6*s,y);c.lineTo(x+6*s,y-22*s);c.quadraticCurveTo(x+18*s,y-16*s,x+14*s,y-6*s)});c.fillStyle=col;c.lineWidth=3.5;c.beginPath();c.ellipse(x,y,7*s,5*s,-.4,0,T);c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const sw=.03*sin(2*PI*t),p1=max(0,sin(4*T*t)),p2=max(0,sin(4*T*t+PI))
|
||||
for(const[x,o,col]of[[170,0,'#ffd43b'],[214,.33,'#f783ac'],[192,.66,'#74c0fc']]){const p=(t+o)%1,s=sin(PI*p);if(s>.2)note(c,x+6*sin(p*T),54-34*p,.45+.4*s,col)}
|
||||
const K=(f,d)=>[112+138*f+6*d,170-24*f+14*d],P=(...q)=>{c.beginPath();q.forEach(([x,y],i)=>i?c.lineTo(x,y):c.moveTo(x,y));c.closePath()}
|
||||
c.lineWidth=7;c.fillStyle=WOOD;P([118,184],[252,158],[252,246],[122,246]);c.fill();c.stroke()
|
||||
c.fillStyle=WDD;c.lineWidth=5;P([140,196],[236,178],[236,232],[140,236]);c.fill();c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=WOOD;P([112,170],[250,146],[250,70],[116,96]);c.fill();c.stroke()
|
||||
c.fillStyle=WDL;P([108,98],[254,70],[254,58],[108,86]);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;P([156,110],[206,101],[206,136],[156,145]);c.fill();c.stroke()
|
||||
c.lineWidth=2;c.beginPath();for(const y of[0,8,16])c.moveTo(162,116+y),c.lineTo(200,109+y);c.stroke()
|
||||
c.fillStyle='#111';for(const[x,y]of[[168,121],[180,127],[192,115]])c.beginPath(),c.ellipse(x,y,3,2.2,-.4,0,T),c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=6;P(K(0,0),K(1,0),K(1,1),K(0,1));c.fill();c.stroke()
|
||||
const n=16;c.fillStyle='#ced4da';for(const[f,p]of[[.2,p1],[.32,p2]])if(p>.3){const k=Math.floor(f*n);P(K(k/n,0),K((k+1)/n,0),K((k+1)/n,1),K(k/n,1));c.fill()}
|
||||
c.lineWidth=2;c.beginPath();for(let k=1;k<n;k++){const[a,b]=K(k/n,0),[x,y]=K(k/n,1);c.moveTo(a,b);c.lineTo(x,y)}c.stroke()
|
||||
c.fillStyle='#111';for(let k=1;k<n;k++)if(![3,7,10,14].includes(k)){const w=.018;P(K(k/n-w,0),K(k/n+w,0),K(k/n+w,.55),K(k/n-w,.55));c.fill()}
|
||||
c.lineWidth=6;P(K(0,0),K(1,0),K(1,1),K(0,1));c.stroke()
|
||||
c.lineWidth=6;c.fillStyle=BENCH;c.beginPath();c.roundRect(36,198,84,12,5);c.fill();c.stroke()
|
||||
for(const x of[44,110])ds(c,12,6,BENCH,()=>{c.moveTo(x,210);c.lineTo(x,244)})
|
||||
ds(c,14,7,SKIN,()=>{c.moveTo(112,204);c.lineTo(120,234)})
|
||||
c.fillStyle=SOCK;c.lineWidth=4;c.beginPath();c.roundRect(113,226,14,10,3);c.fill();c.stroke()
|
||||
c.fillStyle=SHOE;c.lineWidth=5;c.beginPath();c.ellipse(126,240,13,6,0,0,T);c.fill();c.stroke()
|
||||
c.save();c.translate(84,200);c.rotate(sw);c.translate(-84,-200)
|
||||
c.lineWidth=6;c.fillStyle=DRS;c.beginPath();c.moveTo(74,150);c.quadraticCurveTo(64,170,60,200);c.lineTo(122,204);c.quadraticCurveTo(116,188,104,180);c.quadraticCurveTo(102,160,96,150);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=DRD;c.lineWidth=5;c.beginPath();c.moveTo(60,200);c.quadraticCurveTo(90,192,122,204);c.lineTo(124,210);c.quadraticCurveTo(90,200,58,206);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(76,150);c.quadraticCurveTo(86,162,96,150);c.quadraticCurveTo(86,156,76,150);c.fill();c.stroke()
|
||||
c.save();c.translate(84,108);c.scale(.56,.56);c.translate(-128,-120)
|
||||
const back=()=>{c.moveTo(118,40);c.bezierCurveTo(56,40,40,110,46,196);c.quadraticCurveTo(40,236,58,262);c.quadraticCurveTo(80,250,96,268);c.quadraticCurveTo(110,236,104,190);c.lineTo(170,150);c.bezierCurveTo(196,96,180,40,118,40)}
|
||||
c.strokeStyle='#fff';c.lineWidth=22;c.beginPath();back();c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=7;c.fillStyle=HD;c.beginPath();back();c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,190,152,186,96);c.bezierCurveTo(184,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,112,140,-1);eye(c,162,140,1)
|
||||
c.fillStyle=BL;for(const x of[86,174])c.beginPath(),c.ellipse(x,164,12,7,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(130,168);c.quadraticCurveTo(140,176,150,167);c.stroke()
|
||||
const hair=()=>{c.moveTo(70,150);c.bezierCurveTo(52,74,90,38,130,38);c.bezierCurveTo(172,38,208,76,190,140)
|
||||
c.quadraticCurveTo(188,118,182,104);c.quadraticCurveTo(172,104,164,112);c.quadraticCurveTo(162,98,152,92);c.quadraticCurveTo(144,106,134,110)
|
||||
c.quadraticCurveTo(132,98,124,92);c.quadraticCurveTo(116,104,104,110);c.quadraticCurveTo(100,98,94,96);c.quadraticCurveTo(84,110,80,118);c.quadraticCurveTo(72,130,70,150);c.closePath()}
|
||||
c.strokeStyle='#fff';c.lineWidth=20;c.beginPath();hair();c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();hair();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[s,e]of[[1.24,1.36],[1.44,1.52],[1.6,1.7]])c.beginPath(),c.arc(130,106,58,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=6;for(const a of[-.9,.9])c.beginPath(),c.ellipse(58+cos(a+PI)*0,70+18*sin(a),24,13,a*.6,0,T),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(58,70,9,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
const h1=[146,170+4*p1],h2=[162,167+4*p2]
|
||||
ds(c,17,9,DRS,()=>{c.moveTo(90,158);c.lineTo(106,178);c.lineTo(h1[0]-6,h1[1]+2)})
|
||||
ds(c,17,9,DRS,()=>{c.moveTo(96,154);c.lineTo(118,170);c.lineTo(h2[0]-6,h2[1]+2)})
|
||||
c.restore()
|
||||
c.fillStyle=SKIN;c.lineWidth=5;for(const[x,y]of[h1,h2])c.beginPath(),c.ellipse(x,y,9,7,-.2,0,T),c.fill(),c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// cream-haired hime-cut girl nibbling a chocolate stick snack bite by bite until it's gone, then pulling a fresh one out of the box
|
||||
const{sin,cos,PI,min,max,floor}=Math,T=2*PI
|
||||
export const frames=24,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#fff1c9',HD='#e8c98f',HL='#fff',EYE='#a9744f',ED='#6b4226',SW='#ff8787',SD='#e03131',COL='#fff',RIB='#4dabf7',BIS='#f0c27b',CHO='#7b4a2e',BOX='#fa5252',BL='#ffa8a8',MO='#c92a2a'
|
||||
const ease=x=>x<=0?0:x>=1?1:x*x*(3-2*x),M=[140,172],A=.38,D=[cos(A),sin(A)],B=[46,184],FULL=74
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+3,y+4,5.5,7.5,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-1,y-4,4.5,0,T);c.fill();c.beginPath();c.arc(x+7,y+9,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
// stick held at (x,y) pointing along angle a, chocolate except the 16px biscuit handle
|
||||
const stick=(c,x,y,L,a=A+PI)=>{
|
||||
if(L<2)return
|
||||
const u=[cos(a),sin(a)],e=[x+u[0]*L,y+u[1]*L],h=min(16,L),m=[x+u[0]*h,y+u[1]*h]
|
||||
for(const[l,s]of[[11,'#111'],[5,BIS]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(x,y),c.lineTo(...e),c.stroke()
|
||||
if(L>16){c.lineWidth=6;c.strokeStyle=CHO;c.beginPath();c.moveTo(...m);c.lineTo(...e);c.stroke()}
|
||||
c.strokeStyle='#111'
|
||||
}
|
||||
export default(c,t)=>{
|
||||
// five bites over the first 60% of the loop, each shortening the stick a chunk
|
||||
const bt=min(1,t/.6),bite=floor(bt*5),bp=bt*5-bite,L=t<.6?FULL-(bite+ease(bp/.3))*FULL/5:0
|
||||
const chew=t<.68&&bp>.25,happy=t>=.6&&t<.7
|
||||
const tip=[M[0]+D[0]*L,M[1]+D[1]*L],lerp=(a,b,s)=>[a[0]+(b[0]-a[0])*s,a[1]+(b[1]-a[1])*s]
|
||||
const end=[M[0]+D[0]*FULL,M[1]+D[1]*FULL]
|
||||
const H=t<.62?tip:t<.78?lerp([M[0]+D[0]*14,M[1]+D[1]*14],B,ease((t-.62)/.16)):t<.94?lerp(B,end,ease((t-.78)/.16)):end
|
||||
const carry=t>=.78?FULL:0,ang=t>=.78&&t<.94?-PI/2+(A+PI*1.5)*ease((t-.8)/.12):A+PI
|
||||
c.lineWidth=6
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(198,34,210,100,204,200);c.lineTo(52,200);c.bezierCurveTo(46,100,58,34,128,34);c.fill();c.stroke()
|
||||
// sweater with a sailor collar
|
||||
c.fillStyle=SW;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,256);c.lineTo(202,256);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=COL;c.beginPath();c.moveTo(100,190);c.lineTo(128,226);c.lineTo(156,190);c.lineTo(170,198);c.lineTo(128,244);c.lineTo(86,198);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=RIB;for(const d of[-1,1])c.beginPath(),c.moveTo(128,232),c.lineTo(128+d*16,222),c.lineTo(128+d*14,242),c.closePath(),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(128,232,4.5,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,168);c.lineTo(116,194);c.lineTo(128,212);c.lineTo(140,194);c.lineTo(140,168);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,192);c.moveTo(140,168);c.lineTo(140,192);c.stroke()
|
||||
// face
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(chew||happy){c.lineWidth=6;for(const x of[103,153])c.beginPath(),c.arc(x,146,13,PI*1.15,PI*1.85),c.stroke()}
|
||||
else{eye(c,103,140,-1);eye(c,153,140,1)}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,160,13,6,0,0,T),c.fill()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+k-2,163),c.lineTo(128+d*44+k+2,157),c.stroke()
|
||||
// little mouth: open around the stick while biting, munching otherwise
|
||||
c.lineWidth=3.5;c.fillStyle=MO
|
||||
if(L>2&&!chew){c.beginPath();c.ellipse(M[0]-4,M[1],7,6,0,0,T);c.fill();c.stroke()}
|
||||
else{c.beginPath();c.moveTo(M[0]-12,M[1]-2);c.quadraticCurveTo(M[0]-8,M[1]+3+2*sin(T*t*12),M[0]-4,M[1]);c.quadraticCurveTo(M[0],M[1]+3,M[0]+4,M[1]-2);c.stroke()}
|
||||
if(chew){c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.arc(164,162,6+3*sin(T*t*12),-PI*.5,PI*.5);c.fill();c.stroke()}
|
||||
// hime cut: straight blunt bangs and long side locks
|
||||
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,190)
|
||||
c.bezierCurveTo(50,74,88,36,128,36);c.bezierCurveTo(168,36,206,74,190,190);c.lineTo(178,190);c.lineTo(176,112)
|
||||
c.lineTo(80,112);c.lineTo(78,190);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;for(const x of[100,128,156])c.beginPath(),c.moveTo(x,90),c.lineTo(x,110),c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,100,56,PI*a,PI*e),c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=3;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,100,60,PI*a,PI*e),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// the box in her other hand, tilted toward her
|
||||
c.save();c.translate(58,226);c.rotate(-.3)
|
||||
for(const x of[-8,0,8])stick(c,x,-24,22+(x==0?4:0),-PI/2)
|
||||
c.lineWidth=5;c.fillStyle=BOX;c.beginPath();c.roundRect(-20,-30,40,64,4);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(-12,-16,24,26,3);c.fill();c.stroke()
|
||||
c.strokeStyle=CHO;c.lineWidth=4;c.beginPath();c.moveTo(-6,6);c.lineTo(6,-10);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
for(const[l,s]of[[24,'#111'],[15,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(86,262),c.lineTo(76,238),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(76,234,11,12,-.3,0,T);c.fill();c.stroke()
|
||||
// the stick and the hand holding its biscuit end
|
||||
stick(c,...H,L||carry,ang)
|
||||
for(const[l,s]of[[24,'#111'],[15,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(184,262),c.quadraticCurveTo(H[0]+30,H[1]+44,H[0]+4,H[1]+10),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(H[0]+2,H[1]+5,10,9,0,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(H[0]-6,H[1]-2,4,6,A,0,T);c.fill();c.stroke()
|
||||
// crumbs from each bite
|
||||
if(t<.62&&bp<.5){c.fillStyle=CHO;c.lineWidth=2;for(const[dx,dy,o]of[[0,0,0],[6,4,.2],[-4,6,.4]]){const p=bp*2,x=M[0]+4+dx,y=M[1]+12+dy+30*p*p;c.beginPath();c.arc(x,y,2.5,0,T);c.fill();c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// chibi with chestnut pigtails shaking an instant photo while the picture develops, beaming at it, then flipping it over to a fresh blank one
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=90
|
||||
const SKIN='#ffe3d1',HAIR='#8b4f2f',HD='#6a3a21',HL='#d9a382',EYE='#c0803f',ED='#7f5539',BL='#ffa8a8',CAR='#fcc419',CAD='#f59f00',SKT='#4dabf7',SKD='#1c7ed6',SCR='#f06595',CAM='#ffc9de',CMD='#f783ac',SHOE='#e03131'
|
||||
const ds=(c,a,b,s,f)=>{for(const[l,k]of[[a,'#111'],[b,s]])c.lineWidth=l,c.strokeStyle=k,c.beginPath(),f(),c.stroke();c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x+5,y,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x+5,y-10,13,10,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+6,y+1,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+1,y-6,4.5,0,T);c.fill();c.beginPath();c.arc(x+10,y+7,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const star=(c,x,y,r)=>{c.fillStyle='#ffd43b';c.lineWidth=3;c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,q=k%2?r*.35:r;c.lineTo(x+cos(a)*q,y+sin(a)*q)}c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t,i)=>{
|
||||
const sh=t<.6,dev=sh?1-t/.6:t<.85?0:1,fl=t>=.85?cos(PI*(t-.85)/.15):1,happy=!sh&&t<.85,j=sh?(i%2?1:-1):0,ov=t>=.85&&fl<0?1:dev
|
||||
c.translate(140,246);c.scale(1.1,1.1);c.translate(-140,-246)
|
||||
for(const d of[-1,1])ds(c,13,7,SKIN,()=>{c.moveTo(104+d*10,200);c.lineTo(104+d*11,232)})
|
||||
c.fillStyle='#fff';c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.roundRect(98+d*11,222,12,12,3),c.fill(),c.stroke()
|
||||
c.fillStyle=SHOE;c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.ellipse(104+d*13,240,12,6,0,0,T),c.fill(),c.stroke()
|
||||
c.lineWidth=6;c.fillStyle=SKT;c.beginPath();c.moveTo(84,180);c.lineTo(124,180);c.lineTo(134,208);c.lineTo(74,208);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=SKD;c.lineWidth=3;c.beginPath();for(const x of[94,104,114])c.moveTo(x,184),c.lineTo(x+(x-104)*.3,206);c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=6;c.fillStyle=CAR;c.beginPath();c.moveTo(90,128);c.quadraticCurveTo(76,134,78,186);c.lineTo(130,186);c.quadraticCurveTo(132,134,118,128);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(94,128);c.lineTo(104,148);c.lineTo(114,128);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=CAD;for(const y of[156,170])c.beginPath(),c.arc(104,y,3,0,T),c.fill(),c.stroke()
|
||||
ds(c,6,2.5,CMD,()=>{c.moveTo(94,130);c.lineTo(90,158);c.moveTo(114,130);c.lineTo(118,158)})
|
||||
c.fillStyle=CAM;c.lineWidth=5;c.beginPath();c.roundRect(84,152,40,28,6);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(104,167,9,0,T);c.fill();c.stroke();c.fillStyle='#495057';c.beginPath();c.arc(104,167,4.5,0,T);c.fill()
|
||||
c.fillStyle=CMD;c.lineWidth=3;c.beginPath();c.rect(88,156,7,5);c.fill();c.stroke()
|
||||
ds(c,17,9,CAR,()=>{c.moveTo(86,136);c.lineTo(72,170);c.lineTo(80,186)})
|
||||
c.fillStyle=SKIN;c.lineWidth=5;c.beginPath();c.arc(81,188,7,0,T);c.fill();c.stroke()
|
||||
c.save();c.translate(104,86);c.scale(.6,.6);c.translate(-128,-120)
|
||||
for(const d of[-1,1]){c.save();c.translate(128+d*66,150);c.rotate(d*.15)
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(0,0);c.bezierCurveTo(d*30,10,d*30,60,d*10,98);c.quadraticCurveTo(d*2,108,-d*6,96);c.bezierCurveTo(d*10,60,-d*4,26,0,0);c.fill();c.stroke()
|
||||
c.restore();c.fillStyle=SCR;c.beginPath();c.ellipse(128+d*64,150,14,10,d*.5,0,T);c.fill();c.stroke()}
|
||||
c.lineWidth=7;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,208,100,196,156);c.lineTo(60,156);c.bezierCurveTo(48,100,60,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(happy){c.lineWidth=7;for(const x of[103,153])c.beginPath(),c.arc(x,146,15,1.15*PI,1.85*PI),c.stroke()}else eye(c,103,140,-1),eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.fillStyle='#c92a2a';c.beginPath();c.moveTo(114,164);c.quadraticCurveTo(128,186,142,164);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#ff8fab';c.lineWidth=3;c.beginPath();c.ellipse(128,176,6,4,0,0,T);c.fill()
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(68,150)
|
||||
c.bezierCurveTo(52,76,88,38,128,38);c.bezierCurveTo(168,38,204,76,188,150)
|
||||
c.quadraticCurveTo(188,118,180,102);c.lineTo(166,108);c.lineTo(160,92);c.lineTo(146,104);c.lineTo(138,88);c.lineTo(126,104)
|
||||
c.lineTo(116,88);c.lineTo(106,106);c.lineTo(94,94);c.lineTo(88,110);c.lineTo(78,104);c.quadraticCurveTo(70,124,68,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[s,e]of[[1.26,1.38],[1.46,1.54],[1.62,1.72]])c.beginPath(),c.arc(128,106,58,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
const hx=172+5*j,hy=118
|
||||
ds(c,17,9,CAR,()=>{c.moveTo(120,136);c.lineTo(150,140);c.lineTo(hx,hy+6)})
|
||||
c.save();c.translate(hx+4,hy-24);c.rotate(.22*j+.05);c.scale(fl,1)
|
||||
c.fillStyle=fl<0?'#e9ecef':'#fff';c.lineWidth=5;c.beginPath();c.roundRect(-24,-30,48,58,3);c.fill();c.stroke()
|
||||
if(fl>=0){c.save();c.beginPath();c.rect(-18,-24,36,36);c.clip()
|
||||
c.fillStyle='#a5d8ff';c.fillRect(-18,-24,36,36);c.fillStyle='#69db7c';c.beginPath();c.ellipse(-4,16,30,14,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#ffd43b';c.lineWidth=3;c.beginPath();c.arc(8,-12,6,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(-8,-10,7,4,0,0,T);c.fill();c.stroke()
|
||||
if(ov>0)c.fillStyle=`rgba(92,75,64,${min(1,ov)})`,c.fillRect(-18,-24,36,36)
|
||||
c.restore();c.lineWidth=3;c.strokeRect(-18,-24,36,36)}
|
||||
c.restore()
|
||||
c.fillStyle=SKIN;c.lineWidth=5;c.beginPath();c.ellipse(hx,hy,9,10,0,0,T);c.fill();c.stroke()
|
||||
if(sh)for(const d of[-1,1])ds(c,6,2.5,'#fff',()=>{c.moveTo(hx+4+d*34,hy-34);c.quadraticCurveTo(hx+4+d*40,hy-24,hx+4+d*34,hy-14)})
|
||||
if(happy){const s=sin(PI*(t-.6)/.25);star(c,214,60,9*s);star(c,146,58,6*s);star(c,222,124,5*s)}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// potter girl with a mahogany ponytail and a clay smudge on her cheek coning clay up into a tall pot and pressing it back down on a spinning wheel
|
||||
// the dark back hair gets a white rim so it doesn't vanish on black
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=90
|
||||
const SKIN='#ffe3d1',HAIR='#8a3330',HD='#5c1f1f',HL='#d19494',EYE='#0ca678',ED='#087f5b',BL='#ffa8a8',APR='#4dabf7',APD='#1c7ed6',CLAY='#c08552',CLD='#8d5a33',CLL='#dba67a',WH='#ced4da',WHD='#868e96',BASE='#495057'
|
||||
const ds=(c,a,b,s,f)=>{for(const[l,k]of[[a,'#111'],[b,s]])c.lineWidth=l,c.strokeStyle=k,c.beginPath(),f(),c.stroke();c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x,y+7,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-3,13,10,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+8,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y+2,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+12,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const h=(1-cos(T*t))/2,H=24+34*h,top=206-H,bw=34-6*h,mw=30-12*h,tw=18-4*h,sp=t*6%1
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(100,148);c.quadraticCurveTo(60,158,52,240);c.lineTo(204,240);c.quadraticCurveTo(196,158,156,148);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=APR;c.lineWidth=6;c.beginPath();c.moveTo(104,170);c.lineTo(152,170);c.lineTo(160,240);c.lineTo(96,240);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(106,170);c.lineTo(112,152);c.moveTo(150,170);c.lineTo(144,152);c.stroke()
|
||||
c.fillStyle=CLD;for(const[x,y]of[[112,190],[140,184],[148,202]])c.beginPath(),c.ellipse(x,y,4,3,.5,0,T),c.fill()
|
||||
c.fillStyle=SKIN;c.lineWidth=6;c.beginPath();c.moveTo(118,134);c.lineTo(118,152);c.quadraticCurveTo(128,160,138,152);c.lineTo(138,134);c.fill();c.beginPath();c.moveTo(118,134);c.lineTo(118,150);c.moveTo(138,134);c.lineTo(138,150);c.stroke()
|
||||
c.save();c.translate(128,94);c.scale(.68,.68);c.translate(-128,-120)
|
||||
const tail=()=>{c.moveTo(150,40);c.bezierCurveTo(206,10,236,60,222,120);c.quadraticCurveTo(218,150,200,170);c.quadraticCurveTo(206,130,196,96);c.quadraticCurveTo(186,70,164,62)}
|
||||
const back=()=>{c.moveTo(128,36);c.bezierCurveTo(196,36,208,100,198,150);c.lineTo(58,150);c.bezierCurveTo(48,100,60,36,128,36)}
|
||||
c.strokeStyle='#fff';c.lineWidth=20;for(const f of[tail,back])c.beginPath(),f(),c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();tail();c.fill();c.stroke()
|
||||
c.fillStyle=HD;c.beginPath();back();c.fill();c.stroke()
|
||||
c.fillStyle=APR;c.lineWidth=5;c.beginPath();c.ellipse(158,50,12,9,.5,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1);eye(c,153,140,1)
|
||||
c.fillStyle=BL;c.beginPath();c.ellipse(172,164,13,7,0,0,T);c.fill()
|
||||
c.fillStyle=CLAY;c.lineWidth=3;c.beginPath();c.moveTo(70,158);c.quadraticCurveTo(84,150,96,156);c.quadraticCurveTo(92,164,80,166);c.quadraticCurveTo(72,166,70,158);c.fill();c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#c92a2a';c.beginPath();c.moveTo(118,166);c.quadraticCurveTo(128,176,138,166);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#ff8fab';c.lineWidth=3;c.beginPath();c.ellipse(132,171,4,3,0,0,T);c.fill();c.stroke()
|
||||
const fr=()=>{c.moveTo(68,150);c.bezierCurveTo(52,78,90,40,128,40);c.bezierCurveTo(166,40,204,78,188,150)
|
||||
c.quadraticCurveTo(186,120,178,104);c.quadraticCurveTo(168,98,156,104);c.quadraticCurveTo(150,92,138,88);c.quadraticCurveTo(134,102,124,108)
|
||||
c.quadraticCurveTo(120,96,110,94);c.quadraticCurveTo(102,106,90,110);c.quadraticCurveTo(80,104,76,108);c.quadraticCurveTo(70,126,68,150);c.closePath()}
|
||||
c.strokeStyle='#fff';c.lineWidth=20;c.beginPath();fr();c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();fr();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[s,e]of[[1.26,1.38],[1.46,1.54],[1.62,1.72]])c.beginPath(),c.arc(128,106,58,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
c.lineWidth=7;c.fillStyle=BASE;c.strokeStyle='#fff';c.lineWidth=14;c.beginPath();c.moveTo(90,222);c.lineTo(166,222);c.lineTo(178,250);c.lineTo(78,250);c.closePath();c.stroke();c.strokeStyle='#111';c.lineWidth=7;c.fill();c.stroke()
|
||||
c.fillStyle=WHD;c.lineWidth=6;c.beginPath();c.ellipse(128,217,62,12,0,0,PI);c.lineTo(66,212);c.ellipse(128,212,62,12,0,PI,0,true);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=WH;c.beginPath();c.ellipse(128,212,62,12,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=WHD;for(let k=0;k<6;k++){const a=k*T/6+t*T*2;if(sin(a)>0)c.beginPath(),c.ellipse(128+50*cos(a),212+9*sin(a),4,2,0,0,T),c.fill()}
|
||||
const pot=()=>{c.moveTo(128-bw,207);c.bezierCurveTo(128-bw-8,207-H*.4,128-mw,207-H*.7,128-tw,top);c.lineTo(128+tw,top);c.bezierCurveTo(128+mw,207-H*.7,128+bw+8,207-H*.4,128+bw,207);c.quadraticCurveTo(128,214,128-bw,207)}
|
||||
c.fillStyle=CLAY;c.lineWidth=6;c.beginPath();pot();c.fill();c.stroke()
|
||||
c.save();c.beginPath();pot();c.clip();c.strokeStyle=CLD;c.lineWidth=2.5
|
||||
for(let k=0;k<4;k++){const y=207-H*(k+.5)/4;c.beginPath();c.moveTo(80,y);c.lineTo(176,y);c.stroke()}
|
||||
c.strokeStyle=CLL;c.lineWidth=4;for(const o of[0,.5]){const x=128-bw+2*bw*((sp+o)%1);c.beginPath();c.moveTo(x,214);c.lineTo(x+(128-x)*.4,top);c.stroke()}
|
||||
c.restore()
|
||||
c.fillStyle=CLD;c.lineWidth=4;c.beginPath();c.ellipse(128,top,tw,4,0,0,T);c.fill();c.stroke()
|
||||
const hy=207-H*.55,hw=bw*.5+mw*.5+4
|
||||
ds(c,20,11,'#fff',()=>{c.moveTo(100,164);c.lineTo(78,186)});ds(c,18,9,SKIN,()=>{c.moveTo(78,186);c.lineTo(128-hw-6,hy)})
|
||||
ds(c,20,11,'#fff',()=>{c.moveTo(156,164);c.lineTo(178,186)});ds(c,18,9,SKIN,()=>{c.moveTo(178,186);c.lineTo(128+hw+6,hy)})
|
||||
c.fillStyle=SKIN;c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*hw,hy,10,13,d*.2,0,T),c.fill(),c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// chibi in a pumpkin dress and cat-ear headband shaking her jack-o-lantern candy bucket, candies hopping out and dropping back in
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#862e9c',HD='#5f1f6e',HL='#e5c4ee',EYE='#94d82d',ED='#5c940d',PUMP='#ff922b',PD='#e8590c',STEM='#51cf66',SD='#2f9e44',EAR='#495057',PINK='#faa2c1',SOCK='#b197fc',BL='#ffa8a8',MO='#c92a2a',CANDY=['#f783ac','#4dabf7','#ffd43b','#69db7c']
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const candy=(c,x,y,col,a)=>{
|
||||
c.save();c.translate(x,y);c.rotate(a);c.fillStyle=col;c.lineWidth=3.5
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(d*6,0),c.lineTo(d*15,-6),c.lineTo(d*15,6),c.closePath(),c.fill(),c.stroke()
|
||||
c.beginPath();c.ellipse(0,0,8,6.5,0,0,T);c.fill();c.stroke();c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const s=5*sin(2*T*t),b=0,Z=.62,bx=194,by=214+s
|
||||
// twin tails behind everything
|
||||
c.save();c.translate(122,70+b);c.scale(Z,Z);c.translate(-128,-40)
|
||||
c.fillStyle=HAIR;c.lineWidth=8
|
||||
for(const d of[-1,1]){const sw=d*6*sin(2*T*t+1);c.beginPath();c.moveTo(128+d*58,110);c.bezierCurveTo(128+d*96,130,128+d*92+sw,190,128+d*72+sw,226);c.quadraticCurveTo(128+d*60,214,128+d*62,196);c.bezierCurveTo(128+d*64,160,128+d*52,140,128+d*44,126);c.closePath();c.fill();c.stroke()}
|
||||
c.restore()
|
||||
// legs, stockings and shoes
|
||||
for(const d of[-1,1]){for(const[l,col]of[[16,'#111'],[9,SOCK]])c.lineWidth=l,c.strokeStyle=col,c.beginPath(),c.moveTo(122+d*12,208),c.lineTo(122+d*13,234),c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=3;for(const y of[214,222,230])c.beginPath(),c.moveTo(122+d*12-4,y),c.lineTo(122+d*12+4,y),c.stroke()}
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=EAR
|
||||
for(const d of[-1,1]){c.beginPath();c.ellipse(122+d*15,240,11,6.5,0,0,T);c.lineWidth=12;c.strokeStyle='#fff';c.stroke();c.lineWidth=5;c.strokeStyle='#111';c.fill();c.stroke()}
|
||||
// arm holding the bucket
|
||||
for(const[l,col]of[[16,'#111'],[9,SKIN]])c.lineWidth=l,c.strokeStyle=col,c.beginPath(),c.moveTo(146,180+b),c.lineTo(bx-6,by-46),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// pumpkin dress
|
||||
c.save();c.translate(122,196+b)
|
||||
c.lineWidth=6;c.fillStyle=PUMP;c.beginPath();c.ellipse(0,0,44,30,0,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle=PD;c.lineWidth=4;for(const k of[-1,0,1])c.beginPath(),c.ellipse(k*20,0,k?14:12,28,0,k<0?PI*.5:-PI*.5,k<0?PI*1.5:PI*.5),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=STEM
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(0,-28),c.quadraticCurveTo(d*14,-38,d*24,-26),c.quadraticCurveTo(d*12,-24,0,-24),c.closePath(),c.fill(),c.stroke()
|
||||
c.restore()
|
||||
// candies hopping out of the bucket, drawn behind its front
|
||||
for(let k=0;k<3;k++){const p=(2*t+k/3)%1,h=46*sin(PI*p);if(p<.9)candy(c,bx-14+k*14+(k-1)*10*p,by-26-h,CANDY[(k+(2*t+k/3>=1?1:0))%4],(k-1)*1.5*p+k)}
|
||||
// jack-o-lantern bucket
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(bx-24,by-18);c.quadraticCurveTo(bx,by-56,bx+24,by-18);c.lineWidth=11;c.stroke();c.strokeStyle=EAR;c.lineWidth=4;c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=6;c.fillStyle=PUMP;c.beginPath();c.ellipse(bx,by,30,26,0,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle=PD;c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.ellipse(bx+d*12,by,10,24,0,0,T),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle=PD;c.lineWidth=5;c.beginPath();c.ellipse(bx,by-22,22,6,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.moveTo(bx+d*12,by-8),c.lineTo(bx+d*5,by+1),c.lineTo(bx+d*18,by+1),c.closePath(),c.fill()
|
||||
c.beginPath();c.moveTo(bx-15,by+8);c.quadraticCurveTo(bx,by+22,bx+15,by+8);c.lineTo(bx+8,by+12);c.lineTo(bx+4,by+8);c.lineTo(bx-4,by+12);c.lineTo(bx-8,by+8);c.closePath();c.fill()
|
||||
c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.arc(bx-6,by-46,8,0,T);c.fill();c.stroke()
|
||||
if(Math.abs(s)>3.5){c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(bx+d*40,by-14),c.lineTo(bx+d*48,by-20),c.moveTo(bx+d*40,by-2),c.lineTo(bx+d*50,by-2),c.stroke()}
|
||||
// head
|
||||
c.save();c.translate(122,70+b);c.scale(Z,Z);c.translate(-128,-40)
|
||||
c.lineWidth=8;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,206,96,196,150);c.lineTo(60,150);c.bezierCurveTo(50,96,60,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1)
|
||||
if(t>=.5&&t<.75){c.lineWidth=8;c.beginPath();c.moveTo(166,130);c.lineTo(146,141);c.lineTo(166,152);c.stroke()}else eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,162,14,7,0,0,T),c.fill()
|
||||
c.lineWidth=4.5;c.fillStyle=MO;c.beginPath();c.moveTo(114,164);c.quadraticCurveTo(128,186,142,164);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(132,165);c.lineTo(136,173);c.lineTo(139,165);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=8;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,146)
|
||||
c.bezierCurveTo(50,74,88,38,128,38);c.bezierCurveTo(168,38,206,74,190,146)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(170,104,164,112);c.quadraticCurveTo(160,96,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,122,90);c.quadraticCurveTo(116,104,106,108);c.quadraticCurveTo(102,96,92,96);c.quadraticCurveTo(86,106,80,112)
|
||||
c.quadraticCurveTo(70,124,66,146);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,58,PI*a,PI*e),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// cat-ear headband, white rimmed so it shows on black
|
||||
for(const d of[-1,1]){const tw=d>0&&t>=.25&&t<.375?.25:0;c.save();c.translate(128+d*44,52);c.rotate(d*(.35+tw))
|
||||
c.beginPath();c.moveTo(-24,14);c.lineTo(0,-38);c.lineTo(24,14);c.closePath();c.lineWidth=20;c.strokeStyle='#fff';c.stroke();c.fillStyle=EAR;c.fill();c.lineWidth=8;c.strokeStyle='#111';c.stroke()
|
||||
c.fillStyle=PINK;c.beginPath();c.moveTo(-11,8);c.lineTo(0,-18);c.lineTo(11,8);c.closePath();c.fill();c.restore()}
|
||||
c.restore()
|
||||
// free arm making a cat paw by her cheek
|
||||
const pw=4*sin(2*T*t+1)
|
||||
for(const[l,col]of[[16,'#111'],[9,SKIN]])c.lineWidth=l,c.strokeStyle=col,c.beginPath(),c.moveTo(98,180+b),c.quadraticCurveTo(70,172,74+pw*.3,146+pw+b),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.arc(76,140+pw+b,10,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;for(const k of[-4,4])c.beginPath(),c.moveTo(76+k,132+pw+b),c.lineTo(76+k,138+pw+b),c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// girl in a santa hat and red coat tossing a wrapped present up past her head and catching it, eyes following it, snowflakes drifting
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#6b4226',HD='#4a2c17',HL='#c08a5e',EYE='#2f9e44',ED='#1b5e20',RED='#fa5252',RD='#c92a2a',FUR='#fff',BOX='#51cf66',BXD='#2f9e44',RIB='#ffd43b',BL='#ffa8a8',MO='#c92a2a'
|
||||
const cl=x=>max(0,min(1,x))
|
||||
const eye=(c,x,y,d,dx,dy)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip()
|
||||
c.fillStyle=EYE;c.beginPath();c.ellipse(x+dx,y+2+dy,11.5,15,0,0,T);c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x+dx,y-8+dy,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+dx,y+3+dy,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4+dx,y-5+dy,4.5,0,T);c.fill();c.beginPath();c.arc(x+5+dx,y+8+dy,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const flake=(c,x,y,r)=>{for(const[w,s]of[[7,'#111'],[3,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();for(let k=0;k<3;k++){const a=k*PI/3;c.moveTo(x-r*cos(a),y-r*sin(a));c.lineTo(x+r*cos(a),y+r*sin(a))}c.stroke()}c.strokeStyle='#111'}
|
||||
export default(c,t)=>{
|
||||
const a=cl((t-.1)/.62),air=a>0&&a<1,h=sin(PI*a),px=136+76*h,py=230-182*h+(t<.1?6*sin(PI*t/.1):0)+(t>.72&&t<.86?8*sin(PI*(t-.72)/.14):0),hu=air?10*h:0,dx=3*h,dy=-5*h
|
||||
for(const[x,o]of[[30,0],[226,.5],[56,.3]]){const p=(t+o)%1;flake(c,x+8*sin(T*p),20+200*p,7)}
|
||||
c.save();c.translate(128,256);c.scale(.82,.82);c.translate(-128,-256)
|
||||
// hat tip flops behind the head
|
||||
const fl=8*sin(T*t)
|
||||
c.lineWidth=7;c.fillStyle=RED;c.beginPath();c.moveTo(72,74);c.bezierCurveTo(90,18,160,0,200+fl,40);c.quadraticCurveTo(206+fl,58,214+fl,74);c.quadraticCurveTo(190,56,184,74);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=FUR;c.beginPath();c.arc(214+fl,82,15,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,50);c.bezierCurveTo(194,50,204,110,198,172);c.quadraticCurveTo(186,182,172,170);c.lineTo(84,170);c.quadraticCurveTo(70,182,58,172);c.bezierCurveTo(52,110,62,50,128,50);c.fill();c.stroke()
|
||||
// coat
|
||||
c.fillStyle=RED;c.beginPath();c.moveTo(102,188);c.quadraticCurveTo(52,196,40,262);c.lineTo(216,262);c.quadraticCurveTo(204,196,154,188);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=FUR;c.beginPath();c.moveTo(94,190);c.quadraticCurveTo(128,214,162,190);c.quadraticCurveTo(166,204,156,210);c.quadraticCurveTo(128,226,100,210);c.quadraticCurveTo(90,204,94,190);c.fill();c.stroke()
|
||||
c.lineWidth=5;c.fillStyle=RIB;for(const y of[234,252])c.beginPath(),c.arc(128,y,5,0,T),c.fill(),c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=SKIN;c.beginPath();c.rect(116,170,24,20);c.fill();c.beginPath();c.moveTo(116,170);c.lineTo(116,188);c.moveTo(140,170);c.lineTo(140,188);c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,108);c.bezierCurveTo(70,160,100,190,128,192);c.bezierCurveTo(156,190,186,160,182,108);c.bezierCurveTo(182,58,74,58,74,108);c.fill();c.stroke()
|
||||
eye(c,103,146,-1,dx,dy);eye(c,153,146,1,dx,dy)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,166,12,6,0,0,T),c.fill()
|
||||
c.lineWidth=4;c.fillStyle=MO;c.beginPath()
|
||||
if(air)c.moveTo(117,170),c.quadraticCurveTo(128,186,139,170),c.closePath(),c.fill(),c.stroke();else c.arc(128,170,7,.15*PI,.85*PI),c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,150)
|
||||
c.bezierCurveTo(58,104,72,78,92,74);c.lineTo(166,74);c.bezierCurveTo(186,78,198,104,190,150)
|
||||
c.quadraticCurveTo(186,124,176,112);c.quadraticCurveTo(168,110,162,118);c.quadraticCurveTo(156,104,146,100);c.quadraticCurveTo(140,112,132,116)
|
||||
c.quadraticCurveTo(126,102,116,98);c.quadraticCurveTo(110,112,100,116);c.quadraticCurveTo(94,104,86,106);c.quadraticCurveTo(76,120,66,150);c.closePath();c.fill();c.stroke()
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*60,150),c.quadraticCurveTo(128+d*76,170,128+d*68,186),c.quadraticCurveTo(128+d*60,172,128+d*56,160),c.fill(),c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[x,y]of[[100,90],[150,90]])c.beginPath(),c.moveTo(x-8,y+4),c.quadraticCurveTo(x,y-2,x+8,y+4),c.stroke();c.strokeStyle='#111'
|
||||
// fur band of the hat
|
||||
c.lineWidth=7;c.fillStyle=FUR;c.beginPath();c.moveTo(64,82);c.quadraticCurveTo(128,52,192,82);c.quadraticCurveTo(198,92,190,100);c.quadraticCurveTo(128,72,66,100);c.quadraticCurveTo(58,92,64,82);c.fill();c.stroke()
|
||||
c.restore()
|
||||
// arms: sleeves with fur cuffs, hands lift a little as she tosses
|
||||
for(const d of[-1,1]){const hx=128+d*(24+4*h),hy=242-hu
|
||||
for(const[l,s]of[[30,'#111'],[19,RED]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128+d*66,256),c.quadraticCurveTo(128+d*52,236,hx+d*10,hy+6),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=FUR;c.beginPath();c.ellipse(hx+d*12,hy+6,9,12,d*.6,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.ellipse(hx,hy,11,9,0,0,T);c.fill();c.stroke()}
|
||||
// the present
|
||||
c.save();c.translate(px,py);c.rotate(air?T*a:0)
|
||||
c.lineWidth=5;c.fillStyle=BOX;c.beginPath();c.roundRect(-20,-16,40,32,4);c.fill();c.stroke()
|
||||
c.fillStyle=RIB;c.beginPath();c.rect(-5,-16,10,32);c.fill();c.stroke()
|
||||
c.fillStyle=BXD;c.beginPath();c.roundRect(-23,-20,46,10,3);c.fill();c.stroke();c.fillStyle=RIB;c.beginPath();c.rect(-5,-20,10,10);c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(0,-20),c.quadraticCurveTo(d*16,-36,d*4,-22),c.closePath(),c.fill(),c.stroke()
|
||||
c.restore()
|
||||
if(t>.72&&t<.84){c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*40,214),c.lineTo(128+d*50,208),c.moveTo(128+d*40,224),c.lineTo(128+d*52,224),c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// girl with neon pink hair watching her jelly slime pet bounce on top of her head, both grinning, boing lines on each landing
|
||||
const{sin,cos,PI,max,round,pow}=Math,T=2*PI
|
||||
export const frames=16,delay=70
|
||||
const SKIN='#ffe3d1',HAIR='#f06595',HD='#c2255c',HL='#ffdeeb',EYE='#94d82d',ED='#5c940d',TOP='#ffec99',TD='#fab005',SL='#99e9f2',SD='#3bc9db',BL='#ffa8a8'
|
||||
const eye=(c,x,y,d,u)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip();c.beginPath();c.ellipse(x,y-u+2,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.fillStyle=ED;c.beginPath();c.ellipse(x,y-u-8,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y-u+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-u-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y-u+8,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const u=(t*2)%1,h=round(26*sin(PI*u)),sq=pow(1-sin(PI*u),3),st=cos(PI*u)*(1-sq)*.18,dip=round(3*sq)
|
||||
c.save();c.translate(128,262);c.scale(.8,.8);c.translate(-128,-262+dip)
|
||||
c.lineWidth=6;c.fillStyle=HD;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(200,34,216,100,208,170);c.quadraticCurveTo(214,214,196,236);c.lineTo(60,236);c.quadraticCurveTo(42,214,48,170);c.bezierCurveTo(40,100,56,34,128,34);c.fill();c.stroke()
|
||||
c.fillStyle=TOP;c.beginPath();c.moveTo(98,190);c.quadraticCurveTo(58,198,50,256);c.lineTo(206,256);c.quadraticCurveTo(198,198,158,190);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=TD;c.lineWidth=4;for(const y of[222,238])c.beginPath(),c.moveTo(62,y),c.quadraticCurveTo(128,y-10,194,y),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(128,206),c.quadraticCurveTo(128+d*18,206,128+d*30,190),c.lineTo(128+d*8,188),c.closePath(),c.fill(),c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(116,168,24,22);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
const up=round(6-h/8);eye(c,103,140,-1,up);eye(c,153,140,1,up)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,162,12,6,0,0,T),c.fill()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-2,165),c.lineTo(128+d*42+k+2,159),c.stroke()
|
||||
c.lineWidth=4;c.fillStyle='#c92a2a';c.beginPath();c.moveTo(114,164);c.quadraticCurveTo(128,186,142,164);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle='#ff8fab';c.beginPath();c.ellipse(128,180,9,6,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(62,170)
|
||||
c.bezierCurveTo(46,74,88,34,128,34);c.bezierCurveTo(168,34,210,74,194,170)
|
||||
c.quadraticCurveTo(190,128,180,104);c.quadraticCurveTo(170,98,166,112);c.quadraticCurveTo(160,94,148,88);c.quadraticCurveTo(144,102,136,108)
|
||||
c.quadraticCurveTo(130,92,120,88);c.quadraticCurveTo(116,102,106,108);c.quadraticCurveTo(100,94,90,94);c.quadraticCurveTo(86,108,84,114)
|
||||
c.quadraticCurveTo(76,104,74,104);c.quadraticCurveTo(66,134,62,170);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
if(sq>.45){c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.arc(128+d*58,38,10,d<0?PI*1.05:PI*1.55,d<0?PI*1.45:PI*1.95),c.stroke(),c.beginPath(),c.arc(128+d*58,38,18,d<0?PI*1.05:PI*1.55,d<0?PI*1.45:PI*1.95),c.stroke()}
|
||||
const w=56*(1+sq*.3-st),hh=46*(1-sq*.3+st),by=40-h
|
||||
c.save();c.translate(128,by)
|
||||
const sp=()=>{c.beginPath();c.moveTo(-w,0);c.bezierCurveTo(-w,-hh*.9,-w*.5,-hh*1.25,0,-hh*1.25);c.bezierCurveTo(w*.5,-hh*1.25,w,-hh*.9,w,0);c.quadraticCurveTo(0,8,-w,0);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle=SL;sp();c.fill();c.save();c.clip();c.fillStyle=SD;c.beginPath();c.ellipse(0,6,w*1.1,hh*.4,0,0,T);c.fill();c.restore();sp();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(-w*.45,-hh*.8,7,4,-.6,0,T);c.fill();c.beginPath();c.arc(-w*.62,-hh*.5,3,0,T);c.fill()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(d*16,-hh*.52,6,8,0,0,T),c.fill()
|
||||
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*16-2,-hh*.52-3,2.5,0,T),c.fill()
|
||||
c.fillStyle='#ff8fab';for(const d of[-1,1])c.beginPath(),c.ellipse(d*30,-hh*.34,7,4,0,0,T),c.fill()
|
||||
c.lineWidth=4;c.beginPath();c.arc(0,-hh*.4,7,.15*PI,.85*PI);c.stroke()
|
||||
c.restore();c.restore()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// mint-haired girl with a soft-serve cone that tilts and starts to drip, she licks the drip up the side and beams with happy eyes
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#96f2d7',HD='#20c997',HL='#e6fcf5',EYE='#ff922b',ED='#d9480f',TEE='#fff',STR='#f783ac',VAN='#fff9db',VD='#ffe8a3',CONE='#e0a96d',CD='#b07d4f',BL='#ffa8a8',MO='#c92a2a',TG='#ff8fab',HT='#ff6b9d'
|
||||
const ease=x=>x<=0?0:x>=1?1:x*x*(3-2*x)
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+3,y+3,5.5,7.5,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-1,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+7,y+8,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const heart=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.beginPath();c.moveTo(0,8);c.bezierCurveTo(-13,-1,-9,-12,0,-5);c.bezierCurveTo(9,-12,13,-1,0,8);c.restore();c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const lick=ease((t-.38)/.08)-ease((t-.54)/.08),happy=t>=.4&&t<.82,r=-.04+.1*sin(T*t)-.3*lick,cx=214-28*lick,cy=208-8*lick,drip=t<.46?ease(t/.4):0
|
||||
c.lineWidth=6
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(198,34,212,100,206,200);c.quadraticCurveTo(190,214,170,204);c.lineTo(86,204);c.quadraticCurveTo(66,214,50,200);c.bezierCurveTo(44,100,58,34,128,34);c.fill();c.stroke()
|
||||
c.fillStyle=HAIR;c.beginPath();c.arc(128,32,18,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=STR;c.beginPath();c.ellipse(128,46,14,6,0,0,T);c.fill();c.stroke()
|
||||
// striped tee
|
||||
c.fillStyle=TEE;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,256);c.lineTo(202,256);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=STR;for(const y of[206,226,246])c.fillRect(40,y,180,9);c.restore()
|
||||
c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,256);c.lineTo(202,256);c.quadraticCurveTo(194,198,156,190);c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,168);c.lineTo(116,194);c.quadraticCurveTo(128,204,140,194);c.lineTo(140,168);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,192);c.moveTo(140,168);c.lineTo(140,192);c.stroke()
|
||||
// face
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(happy){c.lineWidth=6;for(const x of[103,153])c.beginPath(),c.arc(x,146,13,PI*1.15,PI*1.85),c.stroke()}
|
||||
else{eye(c,103,140,-1);eye(c,153,140,1)}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,160,13,6,0,0,T),c.fill()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+k-2,163),c.lineTo(128+d*44+k+2,157),c.stroke()
|
||||
c.lineWidth=3.5;c.fillStyle=MO
|
||||
if(lick>.05){c.beginPath();c.ellipse(132,170,8,7,0,0,T);c.fill();c.stroke()}
|
||||
else{c.beginPath();c.moveTo(120,166);c.quadraticCurveTo(128,176,136,166);c.stroke()}
|
||||
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(70,150)
|
||||
c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,150)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(170,100,164,110);c.quadraticCurveTo(156,94,140,92);c.quadraticCurveTo(138,104,130,110)
|
||||
c.quadraticCurveTo(122,96,108,94);c.quadraticCurveTo(106,106,96,112);c.quadraticCurveTo(90,100,82,100);c.quadraticCurveTo(76,108,76,112)
|
||||
c.quadraticCurveTo(70,128,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,102,56,PI*a,PI*e),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// cone in her hand, tilting
|
||||
for(const[l,s]of[[26,'#111'],[17,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(230,256),c.lineTo(cx+6,cy+12),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
c.save();c.translate(cx,cy);c.rotate(r)
|
||||
c.lineWidth=5;c.fillStyle=CONE;c.beginPath();c.moveTo(-22,-34);c.lineTo(22,-34);c.lineTo(2,30);c.lineTo(-2,30);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.strokeStyle=CD;c.lineWidth=3;c.beginPath();for(let k=-3;k<4;k++)c.moveTo(-30+k*12,-36),c.lineTo(10+k*12,34),c.moveTo(30+k*12,-36),c.lineTo(-10+k*12,34);c.stroke();c.restore()
|
||||
c.lineWidth=5
|
||||
for(const[y,w]of[[-42,26],[-60,21],[-76,15]]){c.fillStyle=VAN;c.beginPath();c.ellipse(0,y,w,11,0,0,T);c.fill();c.stroke();c.strokeStyle=VD;c.lineWidth=3;c.beginPath();c.arc(-w*.2,y-1,w*.6,PI*1.1,PI*1.6);c.stroke();c.strokeStyle='#111';c.lineWidth=5}
|
||||
c.fillStyle=VAN;c.beginPath();c.moveTo(-8,-84);c.quadraticCurveTo(0,-104,10,-96);c.quadraticCurveTo(4,-90,8,-82);c.fill();c.stroke()
|
||||
// the drip slides down the cone until she licks it off
|
||||
if(drip>0){const len=6+30*drip;c.fillStyle=VAN;c.beginPath();c.moveTo(-24,-40);c.lineTo(-24,-40+len);c.arc(-19,-40+len,5,PI,0,true);c.lineTo(-14,-40);c.fill();c.stroke()}
|
||||
c.restore()
|
||||
c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(cx+2,cy+6,14,12,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(cx-10,cy+2);c.lineTo(cx+4,cy+2);c.moveTo(cx-10,cy+10);c.lineTo(cx+4,cy+10);c.stroke()
|
||||
// tongue sweeping up the side of the swirl
|
||||
if(lick>.05){
|
||||
const a=-.4-.5*ease((t-.4)/.14),L=4+12*lick
|
||||
c.save();c.translate(136,172);c.rotate(a);c.fillStyle=TG;c.lineWidth=4
|
||||
c.beginPath();c.moveTo(0,-7);c.lineTo(L,-7);c.arc(L,0,7,-PI/2,PI/2);c.lineTo(0,7);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=MO;c.lineWidth=2;c.beginPath();c.moveTo(2,0);c.lineTo(L,0);c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
if(t>.5&&t<.86){c.fillStyle=HT;c.lineWidth=3.5;for(const[x,y,o]of[[52,92,0],[208,60,.1]]){const p=(t-.5-o)/.3;if(p>0&&p<1)heart(c,x,y-20*p,1.1*sin(PI*p))}}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// blindfolded chibi playing suikawari on the beach: wobbles around with her stick raised, swings it down onto the watermelon with a bonk, raises it again
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#ffa94d',HD='#f76707',HL='#ffe8cc',TEE='#fff',TS='#ff6b6b',SHORT='#4dabf7',SD='#1c7ed6',BAND='#fff',STICK='#c08a5e',SAND='#ffe8a3',SAND2='#f5d27a',TOWEL='#74c0fc',MEL='#51cf66',MD='#2b8a3e',BL='#ffa8a8',MO='#c92a2a'
|
||||
const cl=x=>max(0,min(1,x)),ease=x=>x*x*(3-2*x)
|
||||
const spark=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r);for(const[px,py,qx,qy]of[[r,0,r*.18,-r*.18],[0,r,r*.18,r*.18],[-r,0,-r*.18,r*.18],[0,-r,-r*.18,-r*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const sw=ease(cl((t-.52)/.1))*(1-ease(cl((t-.74)/.2))),wob=t<.52?.1*sin(T*2*t/.52):0,hit=t>=.62&&t<.74,hk=hit?sin(PI*(t-.62)/.12):0
|
||||
// sand and towel
|
||||
c.lineWidth=6;c.fillStyle=SAND;c.beginPath();c.ellipse(128,236,124,18,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=SAND2;for(const[x,y]of[[40,236],[90,244],[150,230],[226,240]])c.beginPath(),c.arc(x,y,2.5,0,T),c.fill()
|
||||
c.fillStyle=TOWEL;c.beginPath();c.moveTo(160,226);c.lineTo(242,222);c.lineTo(248,244);c.lineTo(164,248);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle='#fff';for(const x of[176,202,228])c.beginPath(),c.rect(x,216,10,36),c.fill();c.restore()
|
||||
c.beginPath();c.moveTo(160,226);c.lineTo(242,222);c.lineTo(248,244);c.lineTo(164,248);c.closePath();c.stroke()
|
||||
// watermelon, squashing on the hit
|
||||
c.save();c.translate(204,226);c.scale(1+.12*hk,1-.14*hk)
|
||||
c.fillStyle=MEL;c.lineWidth=6;c.beginPath();c.ellipse(0,-20,30,24,0,0,T);c.fill();c.stroke()
|
||||
c.save();c.beginPath();c.ellipse(0,-20,30,24,0,0,T);c.clip();c.strokeStyle=MD;c.lineWidth=6
|
||||
for(const x of[-18,-4,10,24]){c.beginPath();c.moveTo(x-4,-46);for(let k=0;k<6;k++)c.lineTo(x+(k%2?4:-4)-(x/30)*k*2,-40+k*8);c.stroke()}c.restore()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(-12,-32,6,3,-.5,0,T);c.fill()
|
||||
c.restore()
|
||||
if(hit){c.fillStyle='#ffd43b';c.lineWidth=4;c.beginPath();for(let k=0;k<16;k++){const a=k*PI/8,r=(k%2?10:24)*(.6+.4*hk);c.lineTo(198+r*cos(a),190+r*sin(a))}c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;for(const[dx,dy]of[[-30,-24],[30,-26]])spark(c,198+dx,190+dy,8*hk)}
|
||||
// the whole girl wobbles around her feet
|
||||
c.save();c.translate(96,238);c.rotate(wob);c.translate(-96,-238)
|
||||
// legs and sandals
|
||||
for(const d of[-1,1]){const f=t<.52?3*sin(T*4*t/.52+(d>0?PI:0)):0
|
||||
for(const[l,s]of[[16,'#111'],[9,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(96+d*11,206),c.lineTo(96+d*(13+f),230),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=TS;c.beginPath();c.ellipse(96+d*(14+f),236,11,5,0,0,T);c.fill();c.stroke()}
|
||||
// shorts and tee
|
||||
c.lineWidth=5;c.fillStyle=SHORT;c.beginPath();c.moveTo(78,190);c.lineTo(114,190);c.lineTo(118,212);c.lineTo(98,212);c.lineTo(96,204);c.lineTo(94,212);c.lineTo(74,212);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=TEE;c.beginPath();c.moveTo(82,162);c.quadraticCurveTo(70,166,72,182);c.lineTo(76,196);c.lineTo(116,196);c.lineTo(120,182);c.quadraticCurveTo(122,166,110,162);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=TS;c.lineWidth=5;c.beginPath();c.moveTo(76,180);c.lineTo(118,180);c.stroke();c.strokeStyle='#111'
|
||||
// stick and arms: raised back over her shoulder, then swung down onto the melon
|
||||
const G=[116+34*sw,36+122*sw],a=-2.65+3.4*sw,L=64,S=[G[0]+L*cos(a),G[1]+L*sin(a)],B=[G[0]-12*cos(a),G[1]-12*sin(a)]
|
||||
const stick=()=>{for(const[l,s]of[[13,'#111'],[6,STICK]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(...B),c.lineTo(...S),c.stroke();c.strokeStyle='#111'}
|
||||
const arms=()=>{for(const sx of[76,116]){const qx=(sx+(sx-96)*1.6)*(1-sw)+((sx+G[0])/2+6)*sw,qy=104*(1-sw)+((170+G[1])/2+14)*sw;for(const[l,s]of[[15,'#111'],[8,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(sx,170),c.quadraticCurveTo(qx,qy,G[0],G[1]),c.stroke()}
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.arc(G[0],G[1],9,0,T);c.fill();c.stroke()}
|
||||
if(sw<.5)stick(),arms()
|
||||
// high ponytail swinging
|
||||
const pt=.25*sin(T*2*t)+(sw>.5?.6:0)
|
||||
c.save();c.translate(78,52);c.rotate(-.5+pt*.4)
|
||||
c.fillStyle=HAIR;c.lineWidth=5;c.beginPath();c.moveTo(0,0);c.bezierCurveTo(-30,4,-42,40,-30,70);c.quadraticCurveTo(-20,58,-14,64);c.bezierCurveTo(-18,40,-6,16,8,8);c.closePath();c.fill();c.stroke()
|
||||
c.restore()
|
||||
// head
|
||||
const Z=.6;c.save();c.translate(96,66);c.scale(Z,Z);c.translate(-128,-40)
|
||||
c.lineWidth=8;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,206,96,196,150);c.lineTo(60,150);c.bezierCurveTo(50,96,60,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,14,7,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.fillStyle=MO;c.beginPath()
|
||||
if(hit)c.moveTo(112,164),c.quadraticCurveTo(128,190,144,164),c.closePath(),c.fill(),c.stroke();else c.moveTo(114,170),c.quadraticCurveTo(122,164,128,170),c.quadraticCurveTo(134,176,142,168),c.stroke()
|
||||
c.lineWidth=8;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,140);c.bezierCurveTo(50,74,88,38,128,38);c.bezierCurveTo(168,38,206,74,190,140)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(170,102,164,110);c.quadraticCurveTo(160,96,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,122,90);c.quadraticCurveTo(116,104,106,108);c.quadraticCurveTo(102,96,92,96);c.quadraticCurveTo(86,106,80,112);c.quadraticCurveTo(70,124,66,140);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[a0,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,58,PI*a0,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
// blindfold with fluttering tails
|
||||
c.fillStyle=BAND;c.lineWidth=8;c.beginPath();c.moveTo(66,116);c.quadraticCurveTo(128,104,192,116);c.lineTo(190,150);c.quadraticCurveTo(128,140,66,150);c.closePath();c.fill();c.stroke()
|
||||
const fl=10*sin(T*4*t);c.beginPath();c.moveTo(66,128);c.quadraticCurveTo(40,120+fl,22,132+fl);c.lineTo(28,148+fl);c.quadraticCurveTo(44,140,66,140);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(64,134);c.quadraticCurveTo(44,150-fl,36,170-fl);c.lineTo(50,174-fl);c.quadraticCurveTo(56,152,68,142);c.fill();c.stroke()
|
||||
c.restore()
|
||||
if(sw>=.5)stick(),arms()
|
||||
// sweat drop while she wobbles
|
||||
if(t<.5){c.fillStyle='#a5d8ff';c.lineWidth=4;const y=58+10*((t*4)%1);c.beginPath();c.moveTo(136,y);c.quadraticCurveTo(144,y+12,136,y+18);c.quadraticCurveTo(128,y+12,136,y);c.fill();c.stroke()}
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// surfer girl with a teal ponytail and a yellow rash guard rides along a curling wave, arms out for balance, spray flying off the tail
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=12,delay=85
|
||||
const SKIN='#ffe3d1',HAIR='#1098ad',HD='#0b7285',HL='#c5f6fa',EYE='#fd7e14',ED='#d9480f',RG='#fcc419',RD='#f08c00',SH='#ff6b6b',SHD='#e03131',BRD='#fff',BS='#20c997',HIB='#f06595',WA='#4dabf7',WD='#1c7ed6',WB='#1971c2',FO='#fff',BL='#ffa8a8'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip()
|
||||
c.beginPath();c.ellipse(x+3,y+2,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x+3,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x+3,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+5,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+9,y+8,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
export default(c,t,i)=>{
|
||||
const f=T*t,bx=150+5*sin(f),by=192+4*sin(2*f+.5),ba=.1+.05*sin(f+.8),lb=sin(f)
|
||||
const B=(x,y)=>[bx+x*cos(ba)-y*sin(ba),by+x*sin(ba)+y*cos(ba)]
|
||||
const face=()=>{c.beginPath();c.moveTo(16,238);c.lineTo(16,150);c.bezierCurveTo(18,90,24,46,58,34);c.bezierCurveTo(86,26,118,40,124,62);c.quadraticCurveTo(122,78,106,80);c.quadraticCurveTo(110,66,96,62);c.bezierCurveTo(80,60,72,96,84,132);c.bezierCurveTo(100,180,170,196,244,200);c.quadraticCurveTo(250,220,244,238);c.quadraticCurveTo(130,248,16,238);c.closePath()}
|
||||
c.fillStyle=WA;c.lineWidth=6;face();c.fill();c.save();c.clip()
|
||||
c.fillStyle=WB;c.beginPath();c.moveTo(96,62);c.bezierCurveTo(80,60,72,96,84,132);c.bezierCurveTo(60,120,40,90,54,58);c.quadraticCurveTo(74,44,96,62);c.fill()
|
||||
c.fillStyle=WD;c.beginPath();c.moveTo(16,150);c.bezierCurveTo(30,180,50,200,66,256);c.lineTo(16,256);c.fill()
|
||||
c.strokeStyle='#a5d8ff';c.lineWidth=4;for(let k=0;k<4;k++){const u=((k/4+t)%1),x=100+u*170,y=150+u*48;c.beginPath();c.moveTo(x-30,y-14);c.quadraticCurveTo(x-10,y-2,x+14,y+2);c.stroke()}
|
||||
c.restore();c.strokeStyle='#111';c.lineWidth=6;face();c.stroke()
|
||||
for(let k=0;k<7;k++){const a=k/6,x=60+60*a,y=34-10*sin(PI*a)+4*sin(f*2+k*1.3),r=9+3*sin(f*2+k);c.fillStyle=FO;c.lineWidth=4;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle=FO;c.lineWidth=4;c.beginPath();c.moveTo(124,62);c.quadraticCurveTo(132,78,118,86);c.quadraticCurveTo(116,74,106,80);c.quadraticCurveTo(122,78,124,62);c.fill();c.stroke()
|
||||
for(let k=0;k<6;k++){const u=(t*2+k/6)%1,x=136-u*60+8*sin(k*2),y=86+u*u*90-30*sin(PI*u),r=4*(1-u)+2;c.fillStyle=FO;c.lineWidth=3.5;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke()}
|
||||
const tail=B(-52,0)
|
||||
for(let k=0;k<7;k++){const u=(t*2+k/7)%1,a=PI*1.1+.5*(k%3)/2,x=tail[0]+cos(a)*u*60,y=tail[1]-6+sin(a)*u*50+u*u*40,r=(1-u)*6+1.5;c.fillStyle=FO;c.lineWidth=3.5;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke()}
|
||||
c.save();c.translate(bx,by);c.rotate(ba)
|
||||
c.fillStyle=BRD;c.lineWidth=5;c.beginPath();c.moveTo(-60,-4);c.lineTo(44,-6);c.quadraticCurveTo(70,-4,74,4);c.quadraticCurveTo(66,10,44,8);c.lineTo(-60,6);c.quadraticCurveTo(-68,1,-60,-4);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=BS;c.lineWidth=4;c.beginPath();c.moveTo(-54,1);c.lineTo(52,1);c.stroke();c.strokeStyle=SH;c.lineWidth=3;c.beginPath();c.moveTo(-50,-1);c.lineTo(-30,-1);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=BS;c.lineWidth=4;c.beginPath();c.moveTo(-50,6);c.lineTo(-58,18);c.lineTo(-42,7);c.closePath();c.fill();c.stroke()
|
||||
c.restore()
|
||||
c.fillStyle=FO;c.lineWidth=4;for(let k=0;k<5;k++){const p=B(-50+k*26,8),r=5+3*sin(f*3+k*2);c.beginPath();c.arc(p[0],p[1]+2,r,PI,T);c.closePath();c.fill();c.stroke()}
|
||||
const hip=[bx-4,by-40+lb*1.5],fb=B(-26,-6),ff=B(24,-6)
|
||||
for(const[ft,kx]of[[fb,-20],[ff,18]]){const k=[(hip[0]+ft[0])/2+kx*.5,(hip[1]+ft[1])/2-2];line(c,[hip,k,ft],9,SKIN)}
|
||||
for(const ft of[fb,ff]){c.save();c.translate(...ft);c.rotate(ba);c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.ellipse(3,-2,10,5,0,0,T);c.fill();c.stroke();c.restore()}
|
||||
c.fillStyle=SH;c.lineWidth=5;c.beginPath();c.moveTo(hip[0]-18,hip[1]-10);c.lineTo(hip[0]+18,hip[1]-10);c.lineTo(hip[0]+26,hip[1]+10);c.lineTo(hip[0]+4,hip[1]+12);c.lineTo(hip[0],hip[1]+4);c.lineTo(hip[0]-4,hip[1]+12);c.lineTo(hip[0]-26,hip[1]+10);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=SHD;c.lineWidth=3;c.beginPath();c.moveTo(hip[0]+12,hip[1]-6);c.lineTo(hip[0]+18,hip[1]+8);c.stroke();c.fillStyle='#fff';c.beginPath();c.arc(hip[0]-12,hip[1]+2,3,0,T);c.fill();c.strokeStyle='#111'
|
||||
const sx=bx-2,sy=hip[1]-36
|
||||
for(const d of[-1,1]){const s=[sx+d*14,sy+4],w=d*lb*.5,e=[sx+d*34,sy+2-10*w],h=[sx+d*54,sy-4-22*w];line(c,[s,e,h],9,RG);c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.arc(h[0]+d*2,h[1],6.5,0,T);c.fill();c.stroke();c.strokeStyle=RD;c.lineWidth=3;c.beginPath();c.moveTo(h[0]-d*5,h[1]-6);c.lineTo(h[0]-d*5,h[1]+6);c.stroke();c.strokeStyle='#111'}
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(sx-6,sy-10,12,10);c.fill()
|
||||
c.fillStyle=RG;c.lineWidth=5;c.beginPath();c.moveTo(sx-14,sy-6);c.lineTo(sx+14,sy-6);c.quadraticCurveTo(sx+22,sy+14,sx+18,hip[1]-6);c.lineTo(sx-18,hip[1]-6);c.quadraticCurveTo(sx-22,sy+14,sx-14,sy-6);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(sx-17,sy+12);c.lineTo(sx+17,sy+8);c.lineTo(sx+18,sy+16);c.lineTo(sx-18,sy+20);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=RD;c.lineWidth=4;c.beginPath();c.moveTo(sx-9,sy-6);c.quadraticCurveTo(sx,sy+2,sx+9,sy-6);c.quadraticCurveTo(sx,sy-11,sx-9,sy-6);c.fill();c.stroke()
|
||||
c.save();c.translate(sx+2,sy-28+lb);c.rotate(.04*sin(f));c.scale(.5,.5);c.translate(-128,-128)
|
||||
c.save();c.translate(70,104);c.rotate(1.2+.18*sin(f*2))
|
||||
c.lineWidth=8;c.fillStyle=HAIR;c.beginPath();c.moveTo(0,-10);c.bezierCurveTo(40,-6,60,40,40,86);c.quadraticCurveTo(52,110,28,124);c.quadraticCurveTo(26,100,10,110);c.bezierCurveTo(22,70,8,30,-10,10);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=5;c.beginPath();c.moveTo(6,6);c.bezierCurveTo(30,26,36,58,28,90);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=RD;c.lineWidth=7;c.beginPath();c.ellipse(0,0,10,14,0,0,T);c.fill();c.stroke();c.restore()
|
||||
c.lineWidth=8;c.fillStyle=HAIR;c.beginPath();c.moveTo(126,36);c.bezierCurveTo(192,36,200,94,190,146);c.lineTo(64,146);c.bezierCurveTo(54,94,62,36,126,36);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(i==9){c.lineWidth=7;for(const x of[107,157])c.beginPath(),c.moveTo(x-15,142),c.quadraticCurveTo(x,150,x+15,142),c.stroke()}else eye(c,107,140,-1),eye(c,157,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(132+d*42,162,12,6,0,0,T),c.fill()
|
||||
c.fillStyle='#c92a2a';c.lineWidth=5;c.beginPath();c.moveTo(118,164);c.quadraticCurveTo(132,186,146,164);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=8;c.fillStyle=HAIR;c.beginPath();c.moveTo(70,140)
|
||||
c.bezierCurveTo(56,80,86,46,130,46);c.bezierCurveTo(176,46,200,84,186,140)
|
||||
c.quadraticCurveTo(184,116,174,100);c.quadraticCurveTo(168,108,160,114);c.quadraticCurveTo(158,96,146,88);c.quadraticCurveTo(136,104,124,110)
|
||||
c.quadraticCurveTo(122,96,114,90);c.quadraticCurveTo(102,104,94,112);c.quadraticCurveTo(92,100,86,96);c.quadraticCurveTo(76,112,70,140);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=8;for(const[a0,a1]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a0,PI*a1),c.stroke();c.strokeStyle='#111'
|
||||
c.save();c.translate(178,80);c.fillStyle=HIB;c.lineWidth=6;for(let k=0;k<5;k++){const b=k*T/5-PI/2;c.beginPath();c.ellipse(13*cos(b),13*sin(b),11,8,b,0,T);c.fill();c.stroke()}c.fillStyle='#ffd43b';c.beginPath();c.arc(0,0,7,0,T);c.fill();c.stroke();c.restore()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// swimmer in a cobalt cap and goggles doing the front crawl, arms windmilling over the water, turning to breathe, splashes at each stroke
|
||||
const{sin,cos,PI,max,min,hypot}=Math,T=2*PI
|
||||
export const frames=16,delay=75
|
||||
const SKIN='#ffe3d1',HAIR='#3b5bdb',CAP='#4263eb',CD='#364fc7',GOG='#ff922b',LENS='#c5f6fa',EYE='#7950f2',SUIT='#f06595',WA='#4dabf7',WD='#1c7ed6',WL='#a5d8ff',BL='#ffa8a8'
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const WY=174
|
||||
const surf=(x,t)=>WY+4*sin(x/256*T*3-T*t)
|
||||
export default(c,t,i)=>{
|
||||
const f=T*t,br=max(0,sin(f+.6)),bob=2*sin(2*f)
|
||||
c.fillStyle=WA;c.lineWidth=6;c.beginPath();c.moveTo(10,surf(10,t)-10);for(let x=10;x<=246;x+=6)c.lineTo(x,surf(x,t)-10);c.lineTo(246,200);c.quadraticCurveTo(128,206,10,200);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=WL;c.lineWidth=3;for(const[x0,y]of[[40,172],[180,178]]){c.beginPath();c.moveTo(x0,y);c.lineTo(x0+26,y);c.stroke()}c.strokeStyle='#111'
|
||||
const G=()=>{c.translate(128,172);c.scale(1.35,1.35);c.translate(-128,-166)}
|
||||
const sh=[[118,168+bob],[98,166+bob]],arm=(k)=>{const a=f+k*PI,s=sh[k],hx=s[0]+44*cos(a),hy=s[1]+40*sin(a)-4,ex=s[0]+24*cos(a),ey=s[1]+18*sin(a)-16*max(0,-sin(a));return[s,[ex,ey],[hx,hy],a]}
|
||||
const draw=k=>{const[s,e,h]=arm(k);line(c,[s,e,h],10,SKIN);c.fillStyle=SKIN;c.lineWidth=5;c.beginPath();c.ellipse(h[0],h[1],8,7,0,0,T);c.fill();c.stroke()}
|
||||
c.save();G()
|
||||
draw(1)
|
||||
c.fillStyle=SUIT;c.lineWidth=6;c.beginPath();c.ellipse(110,172+bob,34,13,-.08,PI,T);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.ellipse(110,166+bob,17,6,-.08,PI,T);c.fill();c.lineWidth=4;c.stroke()
|
||||
c.save();c.translate(158,146+bob);c.rotate(-.15-.3*br);c.scale(.48,.48);c.translate(-128,-128)
|
||||
c.lineWidth=10;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,162,12,6,0,0,T),c.fill()
|
||||
c.fillStyle='#c92a2a';c.lineWidth=7;c.beginPath();if(br>.5)c.ellipse(132,168,11,13*br,0,0,T),c.fill();else c.moveTo(118,168),c.quadraticCurveTo(128,174,138,168);c.stroke()
|
||||
c.fillStyle=CAP;c.lineWidth=10;c.beginPath();c.moveTo(66,120);c.bezierCurveTo(52,30,204,30,190,120);c.quadraticCurveTo(128,100,66,120);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=CD;c.lineWidth=6;c.beginPath();c.arc(128,96,52,PI*1.15,PI*1.5);c.stroke();c.strokeStyle='#fff';c.lineWidth=7;c.beginPath();c.arc(128,96,52,PI*1.58,PI*1.7);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=HAIR;c.lineWidth=8;c.beginPath();c.moveTo(70,118);c.quadraticCurveTo(80,132,94,122);c.quadraticCurveTo(88,114,70,118);c.fill();c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=16;c.beginPath();c.moveTo(66,128);c.lineTo(190,128);c.stroke();c.strokeStyle=GOG;c.lineWidth=8;c.stroke();c.strokeStyle='#111'
|
||||
for(const x of[103,153]){c.fillStyle=GOG;c.lineWidth=9;c.beginPath();c.ellipse(x,132,24,21,0,0,T);c.fill();c.stroke();c.fillStyle=LENS;c.lineWidth=5;c.beginPath();c.ellipse(x,132,16,14,0,0,T);c.fill();c.stroke()
|
||||
if(br>.5&&i%8<3){c.lineWidth=7;c.beginPath();c.arc(x,138,9,PI*1.15,PI*1.85);c.stroke()}else{c.fillStyle=EYE;c.beginPath();c.ellipse(x+3,134,7,9,0,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.ellipse(x+4,135,3.5,5,0,0,T);c.fill()}
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-7,126,5,4,-.5,0,T);c.fill()}
|
||||
c.restore()
|
||||
draw(0)
|
||||
c.restore()
|
||||
c.fillStyle=WA;c.lineWidth=6;c.beginPath();c.moveTo(4,surf(4,t)+6);for(let x=4;x<=252;x+=6)c.lineTo(x,surf(x,t)+6+3*sin(x/40+f));c.lineTo(252,236);c.quadraticCurveTo(128,248,4,236);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=WD;c.globalAlpha=.35;c.beginPath();c.ellipse(118,206,70,14,0,0,T);c.fill();c.globalAlpha=1;c.restore()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;for(const[x,y,w]of[[30,196,22],[200,214,30],[96,226,26]]){c.beginPath();c.moveTo(x,y);c.quadraticCurveTo(x+w/2,y-5,x+w,y);c.stroke()}c.strokeStyle='#111'
|
||||
c.save();G()
|
||||
for(const k of[0,1]){const[,,h,a]=arm(k),m=((a%T)+T)%T,u=(m-PI*.02)/(PI*.6);if(u>0&&u<1){const s=sin(PI*u),x=sh[k][0]+42,y=WY-2;c.fillStyle='#fff';c.lineWidth=3.5;for(const[dx,dy,r]of[[-12,-14,4],[0,-22,5],[12,-12,4],[20,-20,3],[-20,-20,3]]){c.beginPath();c.arc(x+dx*s,y+dy*s,r*max(.4,s),0,T);c.fill();c.stroke()}}}
|
||||
c.restore()
|
||||
for(const[x,o]of[[222,0],[238,.5],[30,.25]]){const u=(t*2+o)%1,y=140-u*40,r=3+4*u;if(u<.85){c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.arc(x+4*sin(u*T),y,r,0,T);c.fill();c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// festival girl in a happi coat and hachimaki pounding a big taiko drum with two bachi, sticks alternating, impact rings on the drumhead
|
||||
const{sin,cos,PI,max,min,hypot}=Math,T=2*PI
|
||||
export const frames=16,delay=70
|
||||
const SKIN='#ffe3d1',HAIR='#c92a2a',HD='#8f1d1d',HL='#ffa8a8',EYE='#fd7e14',ED='#d9480f',HAPPI='#364fc7',HP='#fff',BAND='#fff',KNOT='#e03131',DRUM='#a9744f',DD='#7f5539',HEAD='#fff4e6',RIV='#ffd43b',BACHI='#e9c46a',BL='#ffa8a8',MO='#c92a2a',TOM=['#e03131','#ffd43b','#4dabf7']
|
||||
const cl=x=>max(0,min(1,x)),ease=x=>x*x*(3-2*x)
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,14,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3;c.beginPath();c.ellipse(x,y+2,11.5,14,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-4);c.quadraticCurveTo(x,y-14,x+16,y-6);c.moveTo(x+d*15,y-6);c.lineTo(x+d*21,y-11);c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-d*16,y-28);c.lineTo(x+d*14,y-20);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const ph=d=>(2*t+(d>0?.5:0))%1,up=p=>p<.12?0:p<.55?ease((p-.12)/.43):p<.85?1:1-ease((p-.85)/.15),hit=d=>ph(d)<.14,anyhit=hit(-1)||hit(1)
|
||||
const hand=d=>{const u=up(ph(d)),R=[128+d*82,96],D=[128+d*44,152];return[D[0]+(R[0]-D[0])*u,D[1]+(R[1]-D[1])*u,u]}
|
||||
c.save();c.translate(128,6);c.scale(.78,.78);c.translate(-128,0)
|
||||
// bun and back hair
|
||||
c.lineWidth=8;c.fillStyle=HAIR;c.beginPath();c.arc(128,30,26,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,40);c.bezierCurveTo(196,40,206,100,196,150);c.lineTo(60,150);c.bezierCurveTo(50,100,60,40,128,40);c.fill();c.stroke()
|
||||
// happi coat
|
||||
c.fillStyle=HAPPI;c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(48,196,36,262);c.lineTo(220,262);c.quadraticCurveTo(208,196,156,188);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HP;c.lineWidth=6;for(let y=208;y<262;y+=18)c.beginPath(),c.moveTo(48,y),c.lineTo(60,y+6),c.moveTo(208,y),c.lineTo(196,y+6),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=HP;c.lineWidth=7;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*24,186),c.lineTo(128+d*4,262),c.lineTo(128+d*18,262),c.lineTo(128+d*36,190),c.closePath(),c.fill(),c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.lineTo(128,214);c.lineTo(140,190);c.lineTo(140,168);c.fill();c.lineWidth=6;c.beginPath();c.moveTo(116,168);c.lineTo(116,188);c.moveTo(140,168);c.lineTo(140,188);c.stroke()
|
||||
// face
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1);eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,162,13,6,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.fillStyle=MO;c.beginPath()
|
||||
if(anyhit)c.moveTo(112,162),c.quadraticCurveTo(128,190,144,162),c.closePath(),c.fill(),c.stroke();else c.moveTo(116,168),c.lineTo(140,168),c.stroke()
|
||||
// bangs
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,140);c.bezierCurveTo(50,74,88,40,128,40);c.bezierCurveTo(168,40,206,74,190,140)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(170,102,164,110);c.quadraticCurveTo(160,96,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,122,90);c.quadraticCurveTo(116,104,106,108);c.quadraticCurveTo(102,96,92,96);c.quadraticCurveTo(86,106,80,112);c.quadraticCurveTo(70,124,66,140);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[a,e]of[[1.24,1.36],[1.62,1.74]])c.beginPath(),c.arc(128,104,58,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
// hachimaki with fluttering knot
|
||||
c.fillStyle=BAND;c.lineWidth=7;c.beginPath();c.moveTo(64,76);c.quadraticCurveTo(128,58,192,76);c.lineTo(194,92);c.quadraticCurveTo(128,74,62,92);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=KNOT;c.beginPath();c.arc(128,70,9,0,T);c.fill();c.stroke()
|
||||
const fl=8*sin(4*T*t);c.fillStyle=BAND;for(const[dy,k]of[[0,1],[12,-1]])c.beginPath(),c.moveTo(192,80+dy*.5),c.quadraticCurveTo(212,70+dy+fl*k,232,78+dy+fl*k),c.lineTo(228,90+dy+fl*k),c.quadraticCurveTo(212,84+dy,194,90+dy*.5),c.closePath(),c.fill(),c.stroke()
|
||||
c.restore()
|
||||
// drum
|
||||
c.lineWidth=6;c.fillStyle=DRUM;c.beginPath();c.moveTo(42,176);c.quadraticCurveTo(34,214,46,250);c.lineTo(210,250);c.quadraticCurveTo(222,214,214,176);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=DD;c.lineWidth=4;for(const x of[78,104,152,178])c.beginPath(),c.moveTo(x,186),c.lineTo(x+(x-128)*.06,248),c.stroke();c.strokeStyle='#111'
|
||||
c.save();c.translate(128,220);c.lineWidth=4;for(let k=0;k<3;k++){c.save();c.rotate(k*T/3);c.fillStyle=TOM[k];c.beginPath();c.arc(0,-8,9,0,T);c.moveTo(9,-8);c.quadraticCurveTo(14,8,-6,14);c.quadraticCurveTo(4,4,0,1);c.fill();c.stroke();c.restore()}c.restore()
|
||||
c.fillStyle=RIV;c.lineWidth=3;for(let k=0;k<9;k++){const x=50+k*19.5;c.beginPath();c.arc(x,190+((x-128)/80)**2*-6+6,3.5,0,T);c.fill();c.stroke()}
|
||||
const flex=anyhit?3:0
|
||||
c.fillStyle=HEAD;c.lineWidth=6;c.beginPath();c.ellipse(128,174,88,22-flex,0,0,T);c.fill();c.stroke()
|
||||
// impact rings and burst lines
|
||||
for(const d of[-1,1])if(hit(d)){const p=ph(d)/.14,x=128+d*22;c.strokeStyle=d<0?'#ff8787':'#74c0fc';c.lineWidth=4;for(const k of[0,.5]){const r=cl(p*1.2-k);if(r>0)c.beginPath(),c.ellipse(x,180,14+26*r,4+6*r,0,0,T),c.stroke()}
|
||||
c.strokeStyle='#111';c.lineWidth=5;for(const a of[-2.4,-1.9,-1.2])c.beginPath(),c.moveTo(x+d*20+30*cos(a),196+30*sin(a)*.6),c.lineTo(x+d*20+44*cos(a),196+44*sin(a)*.6),c.stroke()}
|
||||
// arms and bachi
|
||||
for(const d of[-1,1]){const[hx,hy,u]=hand(d),sx=128+d*50,sy=158,th=PI/2+d*.6+(-PI+d*-.1)*u,bx=hx+44*cos(th),by=hy+44*sin(th),ux=cos(th),uy=sin(th)
|
||||
for(const[l,s]of[[22,'#111'],[13,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(sx,sy),c.quadraticCurveTo(sx+d*18,(sy+hy)/2,hx,hy),c.stroke()
|
||||
for(const[l,s]of[[12,'#111'],[5,BACHI]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(hx-ux*8,hy-uy*8),c.lineTo(bx,by),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.arc(hx,hy,10,0,T);c.fill();c.stroke()
|
||||
if(u>.6){c.lineWidth=4;c.beginPath();c.arc(hx,hy,22,d<0?PI*.9:-PI*.1,d<0?PI*1.3:PI*.1+.2);c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// tanuki girl with silver-blue hair, round ears and a ringed tail wagging, then the leaf on her head spins and she vanishes in a puff of smoke and pops back
|
||||
const{sin,cos,PI,max,min,round}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#a5b4cb',HD='#748ba8',HL='#e7ecf5',DK='#5c677d',EYE='#a9744f',ED='#6b4226',TOP='#ffa94d',TD='#e8590c',OBI='#38d9a9',LF='#51cf66',LD='#2f9e44',BL='#ffa8a8',SM='#f1f3f5'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const leaf=(c,x,y,a,s)=>{c.save();c.translate(x,y);c.rotate(a);c.scale(s,1)
|
||||
c.fillStyle=LF;c.lineWidth=5;c.beginPath();c.moveTo(0,16);c.bezierCurveTo(-22,4,-18,-18,0,-26);c.bezierCurveTo(18,-18,22,4,0,16);c.fill();c.stroke()
|
||||
c.strokeStyle=LD;c.lineWidth=3;c.beginPath();c.moveTo(0,20);c.lineTo(0,-20);c.moveTo(0,0);c.lineTo(-9,-8);c.moveTo(0,0);c.lineTo(9,-8);c.stroke();c.strokeStyle='#111';c.restore()}
|
||||
const cloud=(c,cx,cy,r,a)=>{
|
||||
const P=[[0,0,1]];for(let k=0;k<8;k++){const b=k*T/8+a;P.push([cos(b)*.7,sin(b)*.62,.48+.08*(k%2)])}
|
||||
for(const[lw,col]of[[12,'#111'],[0,SM]])for(const[x,y,s]of P){c.beginPath();c.arc(cx+x*r,cy+y*r,s*r*.7,0,T);if(lw)c.lineWidth=lw,c.strokeStyle=col,c.stroke();else c.fillStyle=col,c.fill()}
|
||||
c.strokeStyle='#ced4da';c.lineWidth=4;for(let k=0;k<3;k++){const b=k*T/3+a*2;c.beginPath();c.arc(cx+cos(b)*r*.3,cy+sin(b)*r*.25,r*.16,b,b+PI*1.2);c.stroke()}c.strokeStyle='#111'
|
||||
}
|
||||
export default(c,t,i)=>{
|
||||
const r=[0,0,0,0,0,0,0,.55,1,1,1,1,.7,.35,0,0][i],hide=i>=8&&i<12
|
||||
const tada=i>=12||i==0,wag=.25*sin(2*T*t),la=i==6||i==7?(i-5)*1.6:0
|
||||
if(!hide){
|
||||
c.save();c.translate(160,192);c.rotate(.4+wag)
|
||||
const tp=()=>{c.beginPath();c.moveTo(-12,0);c.bezierCurveTo(-44,-30,-34,-96,10,-98);c.bezierCurveTo(52,-96,56,-34,20,2);c.closePath()}
|
||||
c.fillStyle=HAIR;c.lineWidth=6;tp();c.fill();c.save();tp();c.clip();c.fillStyle=DK;for(const y of[-30,-62])c.beginPath(),c.ellipse(8,y,40,8,-.06,0,T),c.fill();c.beginPath();c.ellipse(10,-98,34,16,0,0,T);c.fill();c.restore();tp();c.stroke();c.restore()
|
||||
for(const x of[118,138]){line(c,[[x,198],[x,220]],8,SKIN);c.lineWidth=5;c.fillStyle=DK;c.beginPath();c.ellipse(x+(x<128?-3:3),224,11,7,0,0,T);c.fill();c.lineWidth=10;c.strokeStyle='#fff';c.stroke();c.lineWidth=5;c.strokeStyle='#111';c.stroke()}
|
||||
c.fillStyle=TOP;c.lineWidth=5;c.beginPath();c.moveTo(110,136);c.lineTo(146,136);c.quadraticCurveTo(160,170,162,204);c.lineTo(94,204);c.quadraticCurveTo(96,170,110,136);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(114,136);c.lineTo(128,156);c.lineTo(142,136);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=OBI;c.beginPath();c.rect(102,164,52,14);c.fill();c.stroke()
|
||||
c.strokeStyle=TD;c.lineWidth=3;c.beginPath();c.moveTo(108,186);c.lineTo(104,200);c.moveTo(148,186);c.lineTo(152,200);c.stroke();c.strokeStyle='#111'
|
||||
for(const d of[-1,1]){line(c,[[128+d*16,140],[128+d*30,160],[128+d*22,176]],8,TOP);c.lineWidth=4;c.fillStyle=SKIN;c.beginPath();c.arc(128+d*20,176,6,0,T);c.fill();c.stroke()}
|
||||
c.save();c.translate(128,92);c.scale(.6,.6);c.translate(-128,-128)
|
||||
c.lineWidth=9;c.fillStyle=HAIR;for(const d of[-1,1]){c.beginPath();c.arc(128+d*58,50,24,0,T);c.fill();c.stroke();c.fillStyle=DK;c.beginPath();c.arc(128+d*58,52,12,0,T);c.fill();c.fillStyle=HAIR}
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,40);c.bezierCurveTo(200,40,212,100,202,160);c.quadraticCurveTo(190,174,176,166);c.lineTo(80,166);c.quadraticCurveTo(66,174,54,160);c.bezierCurveTo(44,100,56,40,128,40);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(tada||i==3){c.lineWidth=8;for(const x of[103,153])c.beginPath(),c.moveTo(x-15,144),c.quadraticCurveTo(x,128,x+15,144),c.stroke()}else eye(c,103,140,-1),eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,163,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=6;if(tada){c.fillStyle='#c92a2a';c.beginPath();c.moveTo(114,164);c.quadraticCurveTo(128,184,142,164);c.closePath();c.fill();c.stroke()}else{c.beginPath();c.moveTo(116,166);c.quadraticCurveTo(122,172,128,166);c.quadraticCurveTo(134,172,140,166);c.stroke()}
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,146)
|
||||
c.bezierCurveTo(54,72,90,40,128,40);c.bezierCurveTo(166,40,202,72,190,146)
|
||||
c.quadraticCurveTo(186,114,176,102);c.quadraticCurveTo(168,106,162,114);c.quadraticCurveTo(156,98,146,92);c.quadraticCurveTo(140,106,130,112)
|
||||
c.quadraticCurveTo(124,98,114,92);c.quadraticCurveTo(108,106,98,112);c.quadraticCurveTo(94,100,86,100);c.quadraticCurveTo(80,110,78,116)
|
||||
c.quadraticCurveTo(70,116,66,146);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=7;for(const[a,e]of[[1.26,1.38],[1.46,1.54],[1.62,1.72]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
}
|
||||
if(r>0)cloud(c,128,138,94*r,t*T)
|
||||
const ly=hide?84-6*sin(T*t*4):42,ls=hide||la?cos(T*t*4+la):1
|
||||
leaf(c,128,ly,hide?.3*sin(T*t*2):-.2,max(.15,Math.abs(ls)))
|
||||
if(tada&&!r){const k=i?i-14:2;c.lineWidth=5;for(const a of[-2.4,-1.6,-.8])c.beginPath(),c.moveTo(128+cos(a)*(60+k*10),110+sin(a)*(60+k*10)),c.lineTo(128+cos(a)*(74+k*10),110+sin(a)*(74+k*10)),c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// girl with a sunflower ponytail and visor tosses the ball up, swings the racket over her head and serves it streaking away
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=20,delay=70
|
||||
const SKIN='#ffe3d1',HAIR='#fcc419',HD='#f08c00',HL='#fff3bf',EYE='#20c997',ED='#087f5b',VIS='#51cf66',VD='#2f9e44',RQ='#ff6b6b',BAND='#ffd43b',BALL='#d8f5a2',BL='#ffa8a8'
|
||||
const eye=(c,x,y,d,ly)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip();const Y=y+ly
|
||||
c.beginPath();c.ellipse(x,Y+2,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.save();c.clip();c.fillStyle=ED;c.beginPath();c.ellipse(x,Y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,Y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,Y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,Y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,Y+8,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const line=(c,pts,w,col)=>{for(const[lw,s]of[[w+8,'#111'],[w,col]]){c.lineWidth=lw;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y],k)=>k?c.lineTo(x,y):c.moveTo(x,y));c.stroke()}c.strokeStyle='#111'}
|
||||
const ss=u=>u*u*(3-2*u)
|
||||
const key=(K,t)=>{let k=0;while(k<K.length-2&&t>=K[k+1][0])k++;const[t0,...a]=K[k],[t1,...b]=K[k+1],u=ss(min(1,max(0,(t-t0)/(t1-t0))));return a.map((v,j)=>v+(b[j]-v)*u)}
|
||||
const RK=[[0,168,172,1.3],[.12,168,172,1.3],[.32,152,104,1.95],[.44,150,84,3.6],[.5,150,72,4.55],[.6,176,128,6.6],[.8,168,172,7.58],[1,168,172,7.58]]
|
||||
const TK=[[0,92,170],[.1,92,170],[.24,100,70],[.5,102,76],[.62,116,150],[.8,92,170],[1,92,170]]
|
||||
const ball=(c,x,y,r)=>{c.fillStyle=BALL;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-r*.9,y,r*.8,-.9,.9);c.stroke();c.strokeStyle='#111'}
|
||||
export default(c,t,i)=>{
|
||||
const oy=t>.28&&t<.5?6*sin(PI*(t-.28)/.22*.5):t>=.5&&t<.66?-5*sin(PI*(t-.5)/.16):0
|
||||
const[rx,ry,ra]=key(RK,t),[tx,ty]=key(TK,t),behind=t>.22&&t<.47,look=t>.12&&t<.52?-5:0
|
||||
const racket=()=>{
|
||||
c.save();c.translate(rx,ry);c.rotate(ra)
|
||||
line(c,[[0,0],[24,0]],6,'#fff');c.strokeStyle=RQ;c.lineWidth=6;c.beginPath();c.moveTo(-4,0);c.lineTo(12,0);c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(22,0);c.lineTo(30,-7);c.moveTo(22,0);c.lineTo(30,7);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(48,0,20,15,0,0,T);c.fill();c.save();c.clip();c.strokeStyle='#adb5bd';c.lineWidth=1.5;for(let k=-20;k<=20;k+=6)c.beginPath(),c.moveTo(28,k),c.lineTo(68,k),c.moveTo(48+k,-16),c.lineTo(48+k,16),c.stroke();c.restore()
|
||||
c.strokeStyle='#111';c.lineWidth=10;c.beginPath();c.ellipse(48,0,20,15,0,0,T);c.stroke();c.strokeStyle=RQ;c.lineWidth=5;c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
}
|
||||
c.fillStyle='#dee2e6';c.beginPath();c.ellipse(120,234,48,6,0,0,T);c.fill()
|
||||
const head=g=>{c.save();c.translate(116,90+oy);c.rotate(t>.1&&t<.52?-.07:0);c.scale(.56,.56);c.translate(-128,-120);g();c.restore()}
|
||||
head(()=>{
|
||||
c.save();c.translate(178,36);c.rotate(-.15+.22*sin(T*t-1.2)+(t>.48&&t<.7?.25:0))
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(-8,0);c.bezierCurveTo(56,-14,92,50,80,124);c.quadraticCurveTo(72,140,58,146);c.quadraticCurveTo(66,96,4,30);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=5;c.beginPath();c.moveTo(14,8);c.bezierCurveTo(50,22,68,66,66,116);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=VIS;c.lineWidth=7;c.beginPath();c.arc(0,4,11,0,T);c.fill();c.stroke();c.restore()
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(208,36,216,100,206,158);c.lineTo(50,158);c.bezierCurveTo(40,100,48,36,128,36);c.fill();c.stroke();c.strokeStyle=HD;c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*70,100),c.quadraticCurveTo(128+d*74,130,128+d*70,150),c.stroke();c.strokeStyle='#111'
|
||||
})
|
||||
if(behind)racket()
|
||||
const lg=[[110,-1],[128,1]]
|
||||
for(const[x,d]of lg){const fx=x+d*8,kx=x+d*(2+oy*.6);line(c,[[x,186+oy],[kx,206+oy*.5],[fx,222]],8,SKIN);line(c,[[fx,214],[fx,222]],8,'#fff')}
|
||||
for(const[x,d]of lg){const fx=x+d*8;c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(fx+d*4,228,12,7,0,0,T);c.fill();c.stroke();c.strokeStyle=VIS;c.lineWidth=3;c.beginPath();c.moveTo(fx+d*4-6,228);c.quadraticCurveTo(fx+d*4,224,fx+d*4+7,227);c.stroke();c.strokeStyle='#111'}
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(102,164+oy);c.lineTo(134,164+oy);c.lineTo(148,194+oy);c.lineTo(88,194+oy);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;for(const x of[104,118,132])c.beginPath(),c.moveTo(118+(x-118)*.6,168+oy),c.lineTo(x,192+oy),c.stroke()
|
||||
c.strokeStyle=VIS;c.lineWidth=4;c.beginPath();c.moveTo(91,188+oy);c.lineTo(145,188+oy);c.stroke();c.strokeStyle='#111'
|
||||
const sl=[104,138+oy],sr=[132,138+oy]
|
||||
line(c,[sl,[(sl[0]+tx)/2-8,(sl[1]+ty)/2],[tx,ty]],8,SKIN)
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(110,124+oy,12,10);c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(104,130+oy);c.lineTo(128,130+oy);c.lineTo(136,140+oy);c.lineTo(134,168+oy);c.lineTo(100,168+oy);c.lineTo(98,140+oy);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=VIS;c.lineWidth=4;c.beginPath();c.moveTo(108,131+oy);c.lineTo(116,142+oy);c.lineTo(124,131+oy);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=SKIN;c.lineWidth=4;c.beginPath();c.arc(tx,ty,6.5,0,T);c.fill();c.stroke()
|
||||
head(()=>{
|
||||
c.lineWidth=9;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(t>.48&&t<.54){c.lineWidth=8;for(const[x,d]of[[103,1],[153,-1]])c.beginPath(),c.moveTo(x-12*d,128),c.lineTo(x+10*d,140),c.lineTo(x-12*d,152),c.stroke()}else eye(c,103,140,-1,look),eye(c,153,140,1,look)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,162,12,6,0,0,T),c.fill()
|
||||
c.fillStyle='#c92a2a';c.lineWidth=6;c.beginPath()
|
||||
if(t>.46&&t<.6)c.ellipse(128,168,8,10,0,0,T),c.fill();else if(t>.6||t<.1)c.moveTo(116,164),c.quadraticCurveTo(128,182,140,164),c.closePath(),c.fill();else c.moveTo(120,168),c.lineTo(136,168)
|
||||
c.stroke()
|
||||
c.fillStyle=HAIR;c.lineWidth=9;c.beginPath();c.moveTo(68,150);c.bezierCurveTo(52,74,88,40,128,40);c.bezierCurveTo(168,40,204,74,188,150)
|
||||
c.quadraticCurveTo(184,128,176,114);c.quadraticCurveTo(168,112,162,122);c.quadraticCurveTo(156,108,146,106);c.quadraticCurveTo(140,116,132,122)
|
||||
c.quadraticCurveTo(126,108,118,106);c.quadraticCurveTo(112,116,104,122);c.quadraticCurveTo(98,108,90,110);c.quadraticCurveTo(84,116,80,122);c.quadraticCurveTo(70,130,68,150);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=8;c.beginPath();c.moveTo(66,84);c.quadraticCurveTo(128,58,190,84);c.lineTo(190,98);c.quadraticCurveTo(128,72,66,98);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=VIS;c.beginPath();c.moveTo(62,96);c.quadraticCurveTo(128,66,194,96);c.quadraticCurveTo(204,110,186,110);c.quadraticCurveTo(128,90,70,110);c.quadraticCurveTo(52,110,62,96);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=VD;c.lineWidth=5;c.beginPath();c.moveTo(80,102);c.quadraticCurveTo(128,86,176,102);c.stroke();c.strokeStyle='#111'
|
||||
})
|
||||
line(c,[sr,[(sr[0]+rx)/2+10,(sr[1]+ry)/2+(ry<120?10:0)],[rx,ry]],8,SKIN)
|
||||
if(!behind)racket()
|
||||
c.fillStyle=BAND;c.lineWidth=4;c.beginPath();c.arc(rx,ry,7,0,T);c.fill();c.stroke()
|
||||
if(t<.2||t>=.84){const s=t>=.84?min(1,(t-.84)/.06):1;ball(c,tx+2,ty-9,8*s)}
|
||||
else if(t<.5){const u=(t-.2)/.3,x=101+(138-101)*u,y=62-(62-26)*(1-(1-u)**2)*1.1+(u>.85?(u-.85)*30:0);ball(c,x,y,8)}
|
||||
else if(t<.62){const u=(t-.5)/.12,x=140+u*150,y=28+u*30;c.strokeStyle='#fff';for(const k of[-6,0,6]){line(c,[[x-60,y+k*.9-10*.2],[x-14,y+k]],2,'#fff')}ball(c,x,y,8)
|
||||
if(t<.56){c.fillStyle='#fff3bf';c.lineWidth=4;c.beginPath();for(let k=0;k<16;k++){const r=k%2?8:20,b=k*PI/8;c.lineTo(140+r*cos(b),30+r*sin(b))}c.closePath();c.fill();c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// girl praying under the teru teru bozu she hung in the window: the rain stops, clouds roll away, the sun comes out and she cheers, then the clouds return
|
||||
const{sin,cos,PI,max,min,round}=Math,T=2*PI
|
||||
export const frames=16,delay=90
|
||||
const SKIN='#ffe3d1',HAIR='#69db7c',HD='#2f9e44',HL='#d3f9d8',EYE='#5c7cfa',ED='#364fc7',SHIRT='#fff',STR='#74c0fc',WOOD='#c08a5e',WD='#8d6346',RAIN=[159,179,200],SUN=[116,192,252],CLOUD='#dee2e6',CD='#adb5bd',SUNC='#ffd43b',DOLL='#fff',TIE='#fa5252',CLIP='#ffd43b',BL='#ffa8a8',MO='#c92a2a'
|
||||
const cl=x=>max(0,min(1,x)),ease=x=>x*x*(3-2*x)
|
||||
const mix=(a,b,k)=>`rgb(${a.map((v,i)=>round(v+(b[i]-v)*k)).join()})`
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
// clouds use the union-outline trick: stroke every puff wide, then fill them all over the inner lines
|
||||
const cloud=(c,x,y,s,f)=>{c.fillStyle=f;c.lineWidth=10;c.beginPath();for(const[dx,dy,r]of[[-22,4,16],[0,-6,22],[24,4,15]])c.moveTo(x+(dx+r)*s,y+dy*s),c.arc(x+dx*s,y+dy*s,r*s,0,T);c.stroke();c.fill()}
|
||||
export default(c,t)=>{
|
||||
const w=t<.85?ease(cl((t-.38)/.16)):1-ease(cl((t-.85)/.15)),sunny=w>.6,X0=40,Y0=12,X1=232,Y1=198
|
||||
// window: sky panes behind the frame
|
||||
c.lineWidth=7;c.fillStyle=mix(RAIN,SUN,w);c.beginPath();c.rect(X0+10,Y0+10,X1-X0-20,Y1-Y0-20);c.fill()
|
||||
c.save();c.beginPath();c.rect(X0+10,Y0+10,X1-X0-20,Y1-Y0-20);c.clip()
|
||||
if(w>.05){const s=w,rot=T*t;c.save();c.translate(92,62+30*(1-w));c.scale(s,s);c.strokeStyle='#111';c.lineWidth=7
|
||||
for(let k=0;k<8;k++){const a=rot/4+k*PI/4;c.beginPath();c.moveTo(30*cos(a),30*sin(a));c.lineTo(44*cos(a),44*sin(a));c.stroke()}
|
||||
c.strokeStyle=SUNC;c.lineWidth=3;for(let k=0;k<8;k++){const a=rot/4+k*PI/4;c.beginPath();c.moveTo(30*cos(a),30*sin(a));c.lineTo(44*cos(a),44*sin(a));c.stroke()}
|
||||
c.strokeStyle='#111';c.fillStyle=SUNC;c.lineWidth=6;c.beginPath();c.arc(0,0,22,0,T);c.fill();c.stroke();c.restore()}
|
||||
if(w<.5){c.globalAlpha=1-2*w;for(const[x,o]of[[70,0],[110,.4],[150,.7],[190,.2],[210,.55],[130,.9]]){const y=((t*3+o)%1)*200;for(const[l,s]of[[6,'#5c7cfa'],[2.5,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(x-y*.15,Y0+y),c.lineTo(x-y*.15-4,Y0+y+18),c.stroke()}c.globalAlpha=1;c.strokeStyle='#111'}
|
||||
const cx=t<.85?w*170:-170*w;cloud(c,92+cx,36,1.1,w<.5?CD:CLOUD);cloud(c,170+cx,48,1,w<.5?CD:CLOUD);cloud(c,128+cx*1.2,24,.8,w<.5?CD:CLOUD)
|
||||
c.restore()
|
||||
// frame and cross bars
|
||||
c.lineWidth=6;c.fillStyle=WOOD
|
||||
c.beginPath();c.rect(X0,Y0,X1-X0,Y1-Y0);c.rect(X0+10,Y1-10,X1-X0-20,-(Y1-Y0-20));c.fill('evenodd');c.stroke()
|
||||
c.beginPath();c.rect(X0+10,Y0+10,X1-X0-20,Y1-Y0-20);c.stroke()
|
||||
c.beginPath();c.rect(131,Y0+10,10,Y1-Y0-20);c.fill();c.stroke();c.beginPath();c.rect(X0+10,100,X1-X0-20,10);c.fill();c.stroke()
|
||||
c.strokeStyle=WD;c.lineWidth=3;for(const[x,y]of[[60,18],[200,18],[60,192],[200,192]])c.beginPath(),c.moveTo(x-10,y),c.lineTo(x+10,y),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=WD;c.lineWidth=6;c.beginPath();c.roundRect(X0-10,Y1-4,X1-X0+20,14,4);c.fill();c.stroke()
|
||||
// teru teru bozu swinging on its string from the top bar
|
||||
const a=.22*sin(T*t);c.save();c.translate(182,Y0+10);c.rotate(a)
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(0,0);c.lineTo(0,34);c.stroke()
|
||||
c.fillStyle=DOLL;c.lineWidth=5;c.beginPath();c.moveTo(-10,52);c.quadraticCurveTo(-26,74,-24,88);c.quadraticCurveTo(-16,82,-10,90);c.quadraticCurveTo(-4,82,2,90);c.quadraticCurveTo(8,82,14,90);c.quadraticCurveTo(20,82,26,88);c.quadraticCurveTo(26,72,10,52);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.arc(0,40,18,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle=TIE;c.lineWidth=5;c.beginPath();c.moveTo(-11,56);c.lineTo(11,56);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*7,38,2.6,0,T),c.fill()
|
||||
c.lineWidth=2.5;c.beginPath();c.arc(0,42,5,.15*PI,.85*PI);c.stroke()
|
||||
if(sunny){c.fillStyle='#ffa8a8';for(const d of[-1,1])c.beginPath(),c.ellipse(d*11,45,4,2.5,0,0,T),c.fill()}
|
||||
c.restore()
|
||||
// the girl in front, bottom left
|
||||
c.save();c.translate(84,256);c.scale(.66,.66);c.translate(-128,-256)
|
||||
c.lineWidth=9;c.fillStyle=HD;c.beginPath();c.moveTo(128,40);c.bezierCurveTo(196,40,206,100,198,168);c.lineTo(58,168);c.bezierCurveTo(50,100,60,40,128,40);c.fill();c.stroke()
|
||||
c.fillStyle=SHIRT;c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(56,198,44,262);c.lineTo(212,262);c.quadraticCurveTo(200,198,156,188);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.strokeStyle=STR;c.lineWidth=9;for(let y=206;y<262;y+=20)c.beginPath(),c.moveTo(40,y),c.lineTo(216,y),c.stroke();c.restore()
|
||||
c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(56,198,44,262);c.lineTo(212,262);c.quadraticCurveTo(200,198,156,188);c.closePath();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(116,168,24,24);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(sunny){c.lineWidth=8;for(const x of[103,153])c.beginPath(),c.arc(x,146,13,PI*1.15,PI*1.85),c.stroke()}
|
||||
else if(w<.2){c.lineWidth=8;for(const x of[103,153])c.beginPath(),c.arc(x,140,12,PI*.15,PI*.85),c.stroke()}
|
||||
else eye(c,103,140,-1),eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,162,14,7,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.fillStyle=MO;c.beginPath()
|
||||
if(sunny)c.moveTo(112,164),c.quadraticCurveTo(128,190,144,164),c.closePath(),c.fill(),c.stroke();else c.arc(128,168,7,1.15*PI,1.85*PI),c.stroke()
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(62,166);c.bezierCurveTo(46,74,88,38,128,38);c.bezierCurveTo(168,38,210,74,194,166)
|
||||
c.quadraticCurveTo(186,124,178,106);c.quadraticCurveTo(170,102,164,110);c.quadraticCurveTo(160,96,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,122,90);c.quadraticCurveTo(116,104,106,108);c.quadraticCurveTo(102,96,92,96);c.quadraticCurveTo(86,106,80,112);c.quadraticCurveTo(70,126,62,166);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=7;for(const[a0,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,58,PI*a0,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=CLIP;c.lineWidth=7;for(const k of[0,1])c.beginPath(),c.roundRect(150+k*6,70+k*14,28,10,4),c.fill(),c.stroke()
|
||||
// hands: clasped under her chin while praying, thrown up when the sun comes out
|
||||
const u=ease(cl((w-.5)/.3))
|
||||
for(const d of[-1,1]){const hx=128+d*(10+70*u),hy=200-140*u
|
||||
for(const[l,s]of[[30,'#111'],[17,SHIRT]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128+d*66,250),c.quadraticCurveTo(128+d*(60+30*u),220-60*u,hx,hy+14),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=7;c.fillStyle=SKIN;c.beginPath();c.ellipse(hx,hy,15,17,0,0,T);c.fill();c.stroke()}
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// baby-blue haired girl lying on her tummy on a bed, giggling at her phone and kicking her feet while hearts pop out of the screen
|
||||
const{sin,cos,PI,abs,min}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#ffe3d1',HAIR='#a5d8ff',HD='#4dabf7',HL='#e7f5ff',PJ='#ffd43b',PD='#fab005',BED='#eebefa',BD='#cc5de8',PH='#f783ac',SCR='#e3fafc',SOCK='#b197fc',BL='#ff8fab',MO='#c92a2a',TG='#ff8fab',HT='#ff6b9d'
|
||||
const heart=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.beginPath();c.moveTo(0,8);c.bezierCurveTo(-13,-1,-9,-12,0,-5);c.bezierCurveTo(9,-12,13,-1,0,8);c.restore();c.fill();c.stroke()}
|
||||
// one leg: thigh along the bed from the hip, calf swinging up from the knee
|
||||
const leg=(c,a,dx)=>{
|
||||
const kx=212+dx,ky=206,fx=kx+cos(a)*50,fy=ky-sin(a)*50
|
||||
for(const[l,s]of[[22,'#111'],[13,PJ]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(176,200),c.lineTo(kx,ky),c.stroke()
|
||||
for(const[l,s]of[[19,'#111'],[10,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(kx,ky),c.lineTo(fx,fy),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SOCK;c.beginPath();c.ellipse(fx,fy,8,12,-a+PI/2,0,T);c.fill();c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const g=abs(sin(2*T*t)),Z=.68
|
||||
c.lineWidth=6
|
||||
// bed
|
||||
c.fillStyle=BED;c.beginPath();c.roundRect(10,204,236,32,14);c.fill();c.stroke()
|
||||
c.strokeStyle=BD;c.lineWidth=4;c.beginPath();c.moveTo(24,222);c.lineTo(232,222);c.stroke();c.strokeStyle='#111'
|
||||
leg(c,1.25+.5*sin(2*T*t+PI),10)
|
||||
// body lying along the bed
|
||||
c.lineWidth=6;c.fillStyle=PJ;c.beginPath();c.moveTo(96,170);c.quadraticCurveTo(150,166,184,182);c.quadraticCurveTo(196,198,180,208);c.lineTo(100,208);c.quadraticCurveTo(84,192,96,170);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=PD;for(const[x,y]of[[124,184],[150,190],[170,196],[134,200]])c.beginPath(),c.arc(x,y,4.5,0,T),c.fill()
|
||||
leg(c,1.25+.5*sin(2*T*t),0)
|
||||
// arm propped on its elbow, hand holding the phone up
|
||||
for(const[l,s]of[[22,'#111'],[13,PJ]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(108,184),c.lineTo(80,202),c.lineTo(58,166),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// head, bobbing with each giggle
|
||||
c.save();c.translate(112,110+3*g);c.rotate(.08);c.scale(Z,Z);c.translate(-128,-128)
|
||||
c.lineWidth=9;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,210,110,198,176);c.quadraticCurveTo(188,198,170,190);c.lineTo(86,190);c.quadraticCurveTo(68,198,58,176);c.bezierCurveTo(46,110,60,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
c.lineWidth=9;for(const x of[103,153])c.beginPath(),c.arc(x,148,13,PI*1.15,PI*1.85),c.stroke()
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,16,8,0,0,T),c.fill()
|
||||
c.lineWidth=4;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*44+k-2,168),c.lineTo(128+d*44+k+2,160),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle=MO;c.beginPath();c.moveTo(114,166);c.quadraticCurveTo(128,166,142,166);c.quadraticCurveTo(140,180+6*g,128,182+6*g);c.quadraticCurveTo(116,180+6*g,114,166);c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=TG;c.beginPath();c.ellipse(128,184+6*g,8,6,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=9;c.fillStyle=HAIR;c.beginPath();c.moveTo(64,156)
|
||||
c.bezierCurveTo(48,74,88,36,128,36);c.bezierCurveTo(168,36,208,74,192,156)
|
||||
c.quadraticCurveTo(188,120,182,106);c.quadraticCurveTo(172,100,166,112);c.quadraticCurveTo(160,96,146,92);c.quadraticCurveTo(142,108,132,112)
|
||||
c.quadraticCurveTo(126,96,112,94);c.quadraticCurveTo(108,108,98,112);c.quadraticCurveTo(92,98,82,102);c.quadraticCurveTo(78,112,78,112)
|
||||
c.quadraticCurveTo(68,130,64,156);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=7;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,102,56,PI*a,PI*e),c.stroke()
|
||||
c.restore()
|
||||
c.strokeStyle='#111'
|
||||
// phone in her hand, screen turned toward her
|
||||
c.save();c.translate(46,146);c.rotate(-.3)
|
||||
c.lineWidth=5;c.fillStyle=PH;c.beginPath();c.roundRect(-15,-30,30,52,7);c.fill();c.stroke()
|
||||
c.fillStyle=SCR;c.beginPath();c.roundRect(-10,-24,20,38,3);c.fill();c.lineWidth=3;c.stroke()
|
||||
c.fillStyle=HT;c.lineWidth=2;heart(c,0,-6,.75)
|
||||
c.restore()
|
||||
c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(58,166,10,11,0,0,T);c.fill();c.stroke()
|
||||
// hearts popping out of the screen and floating up
|
||||
c.fillStyle=HT;c.lineWidth=3.5
|
||||
for(const o of[0,.33,.66]){const p=(t+o)%1,x=34+14*p+8*sin(T*p*2),y=108-84*p;if(p>.04)heart(c,x,y,.4+.8*min(1,p*3)*(1-p*.4))}
|
||||
// giggle ticks beside her head
|
||||
for(const[l,s]of[[7,'#111'],[2.5,'#fff']]){c.lineWidth=l;c.strokeStyle=s;for(const k of[-1,0,1])c.beginPath(),c.moveTo(176+4*g,72+k*14),c.lineTo(190+4*g,68+k*18),c.stroke()}
|
||||
c.strokeStyle='#111'
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// sleepy aqua-haired girl with a headband and towel brushing her teeth, the brush scrubbing back and forth, foam bubbles puffing and floating off, rinse cup in her other hand
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=16,delay=70
|
||||
const SKIN='#ffe3d1',HAIR='#66d9e8',HD='#1098ad',HL='#e3fafc',EYE='#9775fa',ED='#5f3dc4',BAND='#f783ac',TEE='#ffa94d',TOW='#fff',STR='#74c0fc',CUP='#faa2c1',WAT='#a5d8ff',BRUSH='#69db7c',BL='#ffa8a8',MO='#c92a2a'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-4,y+6,3,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
// drowsy lid
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(x-19,y+2);c.quadraticCurveTo(x,y-8,x+19,y+2);c.lineTo(x+19,y-24);c.lineTo(x-19,y-24);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y+2);c.quadraticCurveTo(x,y-7,x+16,y+2);c.moveTo(x+d*15,y+1);c.lineTo(x+d*20,y-3);c.stroke()
|
||||
}
|
||||
const bub=(c,x,y,r)=>{c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke();c.save();c.fillStyle=WAT;c.beginPath();c.arc(x-r*.35,y-r*.35,r*.28,0,T);c.fill();c.restore()}
|
||||
export default(c,t)=>{
|
||||
const ox=6*sin(4*T*t)
|
||||
c.lineWidth=6
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(198,34,212,100,204,178);c.quadraticCurveTo(190,192,178,182);c.lineTo(78,182);c.quadraticCurveTo(66,192,52,178);c.bezierCurveTo(44,100,58,34,128,34);c.fill();c.stroke()
|
||||
c.fillStyle=TEE;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,256);c.lineTo(202,256);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,168);c.lineTo(116,194);c.lineTo(140,194);c.lineTo(140,168);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,192);c.moveTo(140,168);c.lineTo(140,192);c.stroke()
|
||||
// striped towel around the neck
|
||||
c.fillStyle=TOW;c.beginPath();c.moveTo(90,186);c.quadraticCurveTo(128,212,166,186);c.lineTo(178,198);c.lineTo(172,256);c.lineTo(146,256);c.lineTo(150,214);c.quadraticCurveTo(128,222,106,214);c.lineTo(110,256);c.lineTo(84,256);c.lineTo(78,198);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=STR;c.lineWidth=4;c.beginPath();for(const y of[232,244])c.moveTo(85,y),c.lineTo(108,y),c.moveTo(148,y),c.lineTo(171,y);c.stroke();c.strokeStyle='#111'
|
||||
// left hand holding the rinse cup
|
||||
for(const[l,s]of[[24,'#111'],[15,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(70,256),c.lineTo(62,232),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5
|
||||
c.fillStyle=CUP;c.beginPath();c.moveTo(34,190);c.lineTo(76,190);c.lineTo(72,236);c.lineTo(38,236);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=WAT;c.beginPath();c.ellipse(55,194,19,5,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(42,204);c.lineTo(44,226);c.stroke()
|
||||
c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(64,220,13,11,0,0,T);c.fill();c.stroke()
|
||||
// face
|
||||
c.lineWidth=6;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,140,-1);eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,160,13,6,0,0,T),c.fill()
|
||||
c.fillStyle=MO;c.lineWidth=3.5;c.beginPath();c.ellipse(130,170,9,7,0,0,T);c.fill();c.stroke()
|
||||
// hair pulled back off the forehead under a headband, side locks framing the face
|
||||
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(68,150);c.bezierCurveTo(52,74,88,34,128,34);c.bezierCurveTo(168,34,204,74,188,150)
|
||||
c.quadraticCurveTo(184,128,182,112);c.bezierCurveTo(176,74,80,74,74,112);c.quadraticCurveTo(72,128,68,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.26,1.36],[1.44,1.54],[1.64,1.74]])c.beginPath(),c.arc(128,96,56,PI*a,PI*e),c.stroke()
|
||||
// soft bangs falling from under the headband, tips ending above the eyes, then the band over their roots
|
||||
c.strokeStyle='#111';c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(78,110);c.quadraticCurveTo(128,68,178,110)
|
||||
c.quadraticCurveTo(176,114,170,118);c.quadraticCurveTo(164,106,152,104);c.quadraticCurveTo(150,112,142,118)
|
||||
c.quadraticCurveTo(136,104,124,102);c.quadraticCurveTo(122,112,114,118);c.quadraticCurveTo(108,106,96,104)
|
||||
c.quadraticCurveTo(92,112,86,116);c.quadraticCurveTo(80,114,78,110);c.closePath();c.fill();c.stroke()
|
||||
for(const[l,st]of[[17,'#111'],[10,BAND]])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(72,116),c.bezierCurveTo(78,72,178,72,184,116),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=4;c.fillStyle=BAND
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(166,84),c.quadraticCurveTo(166+d*14,70,166+d*16,88),c.quadraticCurveTo(166+d*8,94,166,84),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(166,84,4,0,T);c.fill();c.stroke()
|
||||
// toothbrush scrubbing, its head tucked in her cheek
|
||||
const bx=136+ox
|
||||
for(const[l,s]of[[13,'#111'],[6,BRUSH]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(bx,170),c.lineTo(bx+70,182),c.stroke()
|
||||
for(const[l,s]of[[24,'#111'],[15,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(186,256),c.lineTo(bx+66,198),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(bx+64,184,12,13,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(bx+56,180);c.lineTo(bx+66,180);c.moveTo(bx+56,187);c.lineTo(bx+66,187);c.stroke()
|
||||
// foam puffing around her lips
|
||||
c.fillStyle='#fff';c.lineWidth=3
|
||||
for(const[k,[x,y,r]]of[[118,174,5],[124,180,5],[134,179,5.5],[141,173,4.5],[114,166,3.5]].entries())bub(c,x,y,r+1.2*sin(4*T*t+k*1.7))
|
||||
// bubbles drifting up from her mouth and popping
|
||||
for(const o of[0,.5]){
|
||||
const p=(2*t+o)%1,x=108-56*p,y=170-50*p+4*sin(T*p*2)
|
||||
if(p<.85)bub(c,x,y,3+4*p)
|
||||
else{c.lineWidth=3;const r=10;c.beginPath();for(let k=0;k<6;k++){const a=k*T/6;c.moveTo(x+cos(a)*r*.6,y+sin(a)*r*.6);c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.stroke()}
|
||||
}
|
||||
// scrub lines by the brush hand
|
||||
for(const[l,s]of[[7,'#111'],[2.5,'#fff']]){c.lineWidth=l;c.strokeStyle=s;for(const k of[-1,1])c.beginPath(),c.arc(bx+64,184,24,PI*(k>0?-.25:.75),PI*(k>0?.05:1.05)),c.stroke()}
|
||||
c.strokeStyle='#111'
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// moon viewing: silver-haired girl in a kimono sits on the veranda with a tri-color dango skewer under a huge full moon, susuki swaying, the moon rabbit pounding mochi
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=12,delay=110
|
||||
const SKIN='#ffe3d1',HAIR='#e9ecef',HD='#adb5bd',HL='#fff',EYE='#fab005',ED='#e67700',KIM='#7048e8',KD='#5f3dc4',OBI='#ffd43b',MOON='#fff3bf',MD='#ffe066',RAB='#fcc419',WOOD='#c08a5e',WD='#8d6346',SUS='#e8c99b',SUD='#c9a46a',STEM='#94d82d',D1='#faa2c1',D2='#fff',D3='#8ce99a',BL='#ffa8a8',MO='#c92a2a'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip()
|
||||
c.fillStyle=EYE;c.beginPath();c.ellipse(x+3,y,11.5,15,0,0,T);c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x+3,y-10,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+3,y+1,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-1,y-7,4.5,0,T);c.fill();c.beginPath();c.arc(x+8,y+6,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
const plume=(c,x,y,a)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle=SUS;c.lineWidth=4;c.beginPath();c.moveTo(0,0);c.bezierCurveTo(-12,-14,-8,-40,4,-54);c.bezierCurveTo(14,-36,12,-12,0,0);c.fill();c.stroke()
|
||||
c.strokeStyle=SUD;c.lineWidth=2.5;for(const k of[-14,-26,-38])c.beginPath(),c.moveTo(2,k),c.lineTo(-5,k+6),c.moveTo(3,k),c.lineTo(9,k+6),c.stroke();c.strokeStyle='#111';c.restore()}
|
||||
export default(c,t)=>{
|
||||
const sw=k=>.12*sin(T*t+k),blink=t>=.58&&t<.67,pound=sin(2*T*t),db=3*sin(T*t)
|
||||
// full moon
|
||||
c.lineWidth=7;c.fillStyle=MOON;c.beginPath();c.arc(150,100,90,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=MD;for(const[x,y,r]of[[112,58,12],[196,72,9],[184,150,14],[120,150,8]])c.beginPath(),c.arc(x,y,r,0,T),c.fill()
|
||||
// moon rabbit pounding mochi
|
||||
c.fillStyle=RAB;c.save();c.translate(166,108)
|
||||
c.beginPath();c.ellipse(0,0,16,13,0,0,T);c.fill();c.beginPath();c.arc(10,-12,9,0,T);c.fill()
|
||||
for(const k of[0,1])c.beginPath(),c.ellipse(8+k*7,-28,4,11,.2+k*.25,0,T),c.fill()
|
||||
c.beginPath();c.ellipse(-16,12,13,6,0,0,T);c.fill();c.beginPath();c.rect(-20,12,8,12);c.fill()
|
||||
c.save();c.translate(2,-4);c.rotate(-.9+.5*pound);c.fillRect(-2,-26,4,26);c.fillRect(-8,-32,16,8);c.restore()
|
||||
c.restore()
|
||||
// susuki grass in a vase
|
||||
for(const[k,a0]of[[0,-.3],[1,-.02],[2,.2]]){const a=a0+sw(k)
|
||||
c.lineWidth=8;c.strokeStyle='#111';c.beginPath();c.moveTo(214,190);c.quadraticCurveTo(214+40*sin(a)*.4,150,214+90*sin(a),190-90*cos(a));c.stroke()
|
||||
c.lineWidth=3.5;c.strokeStyle=STEM;c.stroke();c.strokeStyle='#111';plume(c,214+90*sin(a),190-90*cos(a),a)}
|
||||
c.fillStyle='#4dabf7';c.lineWidth=6;c.beginPath();c.moveTo(200,186);c.lineTo(228,186);c.quadraticCurveTo(232,200,226,210);c.lineTo(202,210);c.quadraticCurveTo(196,200,200,186);c.fill();c.stroke()
|
||||
// veranda plank
|
||||
c.lineWidth=6;c.fillStyle=WOOD;c.beginPath();c.roundRect(14,208,228,16,5);c.fill();c.stroke()
|
||||
c.strokeStyle=WD;c.lineWidth=3;for(const x of[60,130,196])c.beginPath(),c.moveTo(x,212),c.lineTo(x+18,212),c.stroke();c.strokeStyle='#111'
|
||||
// dangling legs
|
||||
for(const[d,ph]of[[-1,0],[1,PI]]){const k=7*sin(T*t+ph)
|
||||
for(const[l,s]of[[17,'#111'],[10,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(84+d*11,214),c.lineTo(84+d*11+k*.4,234),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(84+d*11+k*.5,240,9,6,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=WD;c.beginPath();c.roundRect(76+d*11+k*.5,242,16,5,2);c.fill();c.stroke()}
|
||||
// ponytail flowing behind
|
||||
c.fillStyle=HAIR;c.lineWidth=6;c.beginPath();c.moveTo(56,118);c.bezierCurveTo(30,140,30,180,40,206);c.quadraticCurveTo(52,196,54,206);c.bezierCurveTo(56,176,60,150,72,130);c.closePath();c.fill();c.stroke()
|
||||
// kimono body sitting
|
||||
c.fillStyle=KIM;c.beginPath();c.moveTo(72,146);c.quadraticCurveTo(58,154,56,178);c.lineTo(50,212);c.quadraticCurveTo(84,220,118,212);c.lineTo(112,178);c.quadraticCurveTo(110,154,96,146);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=OBI;c.beginPath();c.rect(56,178,56,14);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(74,146);c.lineTo(84,166);c.lineTo(94,146);c.fill();c.stroke()
|
||||
c.fillStyle='#ff922b';c.lineWidth=3;for(const[x,y]of[[64,202],[100,204],[66,164]]){c.beginPath();for(let k=0;k<5;k++){const a=k*T/5-PI/2;c.lineTo(x+6*cos(a),y+6*sin(a));c.lineTo(x+2.5*cos(a+PI/5),y+2.5*sin(a+PI/5))}c.closePath();c.fill();c.stroke()}
|
||||
// head
|
||||
const Z=.58;c.save();c.translate(84,64);c.scale(Z,Z);c.translate(-128,-40)
|
||||
c.lineWidth=8;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,206,96,196,150);c.lineTo(60,150);c.bezierCurveTo(50,96,60,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(blink){c.lineWidth=8;for(const x of[103,153])c.beginPath(),c.arc(x,140,12,PI*.15,PI*.85),c.stroke()}else eye(c,103,140,-1),eye(c,153,140,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,162,14,7,0,0,T),c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.arc(132,166,8,.15*PI,.85*PI);c.stroke()
|
||||
c.lineWidth=8;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,146);c.bezierCurveTo(50,74,88,38,128,38);c.bezierCurveTo(168,38,206,74,190,146)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(170,102,164,110);c.quadraticCurveTo(160,96,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,122,90);c.quadraticCurveTo(116,104,106,108);c.quadraticCurveTo(102,96,92,96);c.quadraticCurveTo(86,106,80,112);c.quadraticCurveTo(70,124,66,146);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HD;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,58,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#e64980';c.lineWidth=6;for(let k=0;k<5;k++){const a=k*T/5;c.beginPath();c.arc(66+9*cos(a),84+9*sin(a),7,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle=OBI;c.beginPath();c.arc(66,84,6,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
// dango in her hand
|
||||
c.save();c.translate(110,168+db);c.rotate(.55)
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(0,10);c.lineTo(0,-66);c.stroke();c.strokeStyle=WOOD;c.lineWidth=3;c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=5;for(const[k,f]of[[0,D3],[1,D2],[2,D1]])c.fillStyle=f,c.beginPath(),c.arc(0,-16-k*18,10,0,T),c.fill(),c.stroke()
|
||||
c.restore()
|
||||
for(const[l,s]of[[16,'#111'],[9,KIM]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(96,154),c.quadraticCurveTo(104,176,110,170+db),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.arc(111,169+db,7,0,T);c.fill();c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// violinist with an auburn side braid playing with her eyes closed, the bow sawing across the strings, swaying gently, notes drifting up
|
||||
const{sin,cos,PI}=Math,T=2*PI
|
||||
export const frames=12,delay=100
|
||||
const SKIN='#ffe3d1',HAIR='#a0442a',HD='#7a2f1c',HL='#e3a08a',BL='#ffa8a8',DRS='#20c997',DRD='#0ca678',VIO='#e8590c',VIL='#ffa94d',FB='#495057',BOW='#a9744f',HAIRB='#fff',RIB='#ffd43b'
|
||||
const ds=(c,a,b,s,f)=>{for(const[l,k]of[[a,'#111'],[b,s]])c.lineWidth=l,c.strokeStyle=k,c.beginPath(),f(),c.stroke();c.strokeStyle='#111'}
|
||||
const shut=(c,x,y,d)=>{c.lineWidth=7;c.beginPath();c.arc(x,y-6,15,.12*PI,.88*PI);c.stroke();c.lineWidth=4;c.beginPath();for(const a of[.3,.5,.7])c.moveTo(x+15*cos(a*PI),y-6+15*sin(a*PI)),c.lineTo(x+20*cos(a*PI),y-6+20*sin(a*PI)+1);c.moveTo(x+d*14,y-1);c.lineTo(x+d*22,y+2);c.stroke()}
|
||||
const note=(c,x,y,s,col)=>{ds(c,8,3,col,()=>{c.moveTo(x+6*s,y);c.lineTo(x+6*s,y-22*s);c.quadraticCurveTo(x+18*s,y-16*s,x+14*s,y-6*s)});c.fillStyle=col;c.lineWidth=3.5;c.beginPath();c.ellipse(x,y,7*s,5*s,-.4,0,T);c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const sw=.035*sin(T*t),st=50+34*sin(2*T*t),va=-.22,vx=150,vy=172,ba=-1.02,bd=[cos(ba),sin(ba)]
|
||||
const L=(x,y)=>[vx+x*cos(va)-y*sin(va),vy+x*sin(va)+y*cos(va)],cp=L(-12,0),hx=cp[0]-bd[0]*st,hy=cp[1]-bd[1]*st
|
||||
for(const[x,o,col]of[[206,0,'#ffd43b'],[230,.5,'#f783ac']]){const p=(t+o)%1,s=sin(PI*p);if(s>.2)note(c,x+5*sin(p*T),110-70*p,.45+.4*s,col)}
|
||||
c.save();c.translate(128,256);c.rotate(sw);c.translate(-128,-256)
|
||||
c.lineWidth=7;c.fillStyle=DRS;c.beginPath();c.moveTo(102,150);c.quadraticCurveTo(62,160,54,250);c.lineTo(202,250);c.quadraticCurveTo(194,160,154,150);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(108,152);c.quadraticCurveTo(128,176,148,152);c.quadraticCurveTo(128,162,108,152);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.lineWidth=6;c.beginPath();c.moveTo(118,134);c.lineTo(118,152);c.quadraticCurveTo(128,160,138,152);c.lineTo(138,134);c.fill();c.beginPath();c.moveTo(118,134);c.lineTo(118,150);c.moveTo(138,134);c.lineTo(138,150);c.stroke()
|
||||
c.save();c.translate(124,98);c.rotate(.14);c.scale(.68,.68);c.translate(-128,-120)
|
||||
c.lineWidth=7;c.fillStyle=HD;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(198,36,210,100,198,170);c.lineTo(58,170);c.bezierCurveTo(46,100,58,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
shut(c,103,142,-1);shut(c,153,142,1)
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,164,13,7,0,0,T),c.fill()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const x of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+x-2,167),c.lineTo(128+d*44+x+2,161),c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(119,168);c.quadraticCurveTo(128,175,137,168);c.stroke()
|
||||
c.lineWidth=7;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,160)
|
||||
c.bezierCurveTo(48,78,86,38,128,38);c.bezierCurveTo(170,38,208,78,190,160)
|
||||
c.quadraticCurveTo(190,124,180,104);c.quadraticCurveTo(160,100,142,86);c.quadraticCurveTo(140,98,132,104)
|
||||
c.quadraticCurveTo(122,92,118,84);c.quadraticCurveTo(106,104,84,112);c.quadraticCurveTo(70,130,66,160);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=6;for(const[s,e]of[[1.26,1.38],[1.46,1.54],[1.62,1.72]])c.beginPath(),c.arc(128,106,58,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
for(let k=0;k<5;k++){const x=78-k*1.5,y=150+k*17;c.fillStyle=k%2?HD:HAIR;c.lineWidth=6;c.beginPath();c.ellipse(x,y,13-k,11,.15,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle=RIB;c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.ellipse(71+d*10,236,10,6,d*.4,0,T),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(71,236,5,0,T);c.fill();c.stroke()
|
||||
const nk=L(80,0)
|
||||
ds(c,20,11,DRS,()=>{c.moveTo(150,170);c.lineTo(196,196);c.lineTo(nk[0]-4,nk[1]+10)})
|
||||
c.save();c.translate(vx,vy);c.rotate(va)
|
||||
const body=()=>{c.moveTo(-40,0);c.bezierCurveTo(-40,-26,-16,-26,-10,-16);c.quadraticCurveTo(-2,-12,6,-16);c.bezierCurveTo(14,-24,36,-22,36,0);c.bezierCurveTo(36,22,14,24,6,16);c.quadraticCurveTo(-2,12,-10,16);c.bezierCurveTo(-16,26,-40,26,-40,0)}
|
||||
c.fillStyle=VIO;c.lineWidth=6;c.beginPath();body();c.fill();c.stroke()
|
||||
c.strokeStyle=VIL;c.lineWidth=4;c.beginPath();c.moveTo(-30,-12);c.quadraticCurveTo(-20,-18,-12,-12);c.moveTo(14,-14);c.quadraticCurveTo(24,-16,30,-8);c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=2.5;for(const d of[-1,1]){c.beginPath();c.moveTo(-6,d*8);c.bezierCurveTo(-2,d*14,2,d*4,6,d*10);c.stroke()}
|
||||
c.fillStyle=FB;c.lineWidth=5;c.beginPath();c.moveTo(-4,-5);c.lineTo(80,-4);c.lineTo(80,4);c.lineTo(-4,5);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=VIO;c.beginPath();c.roundRect(78,-6,16,12,3);c.fill();c.stroke();c.beginPath();c.arc(98,0,7,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(-34,-6);c.lineTo(-20,-4);c.lineTo(-20,4);c.lineTo(-34,6);c.closePath();c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=1.2;c.beginPath();for(const y of[-3,-1,1,3])c.moveTo(-22,y*.9),c.lineTo(80,y*.9);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.rect(-14,-8,4,16);c.fill();c.stroke()
|
||||
c.restore()
|
||||
c.fillStyle=SKIN;c.lineWidth=5;c.beginPath();c.ellipse(nk[0],nk[1],10,11,0,0,T);c.fill();c.stroke()
|
||||
ds(c,20,11,DRS,()=>{c.moveTo(104,170);c.lineTo(84,206);c.lineTo(hx-4,hy+4)})
|
||||
c.restore()
|
||||
const tx=hx+bd[0]*150,ty=hy+bd[1]*150,nx=-bd[1],ny=bd[0]
|
||||
ds(c,9,4,BOW,()=>{c.moveTo(hx,hy);c.lineTo(tx,ty)})
|
||||
ds(c,6,2.5,HAIRB,()=>{c.moveTo(hx+nx*6,hy+ny*6);c.lineTo(tx+nx*3,ty+ny*3)})
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(hx+nx*7,hy+ny*7);c.lineTo(hx+bd[0]*14+nx*7,hy+bd[1]*14+ny*7);c.lineTo(hx+bd[0]*14,hy+bd[1]*14);c.lineTo(hx,hy);c.fill()
|
||||
c.fillStyle=SKIN;c.lineWidth=5;c.beginPath();c.ellipse(hx,hy,11,11,0,0,T);c.fill();c.stroke()
|
||||
if(Math.abs(cos(2*T*t))>.5)for(const d of[-1,1])ds(c,6,2.5,'#fff',()=>{c.moveTo(cp[0]+d*14+12,cp[1]-12);c.lineTo(cp[0]+d*18+16,cp[1]-18)})
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// strawberry bedhead girl in star pajamas stretching both arms overhead in a big morning yawn, eyes squeezed shut and tears beading
|
||||
const{sin,PI}=Math,T=2*PI
|
||||
export const frames=16,delay=90
|
||||
const SKIN='#ffe3d1',HAIR='#ff9fb8',HD='#e8708f',HL='#ffe0ea',EYE='#20c997',ED='#087f5b',PJ='#bac8ff',PD='#748ffc',BL='#ffa8a8',MO='#c92a2a',TG='#ff8fab',TEAR='#a5d8ff'
|
||||
const ease=x=>x<=0?0:x>=1?1:x*x*(3-2*x)
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x+4,y+7,2.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
// heavy sleepy lid: a dome that hides the top half of the eye
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(x-19,y+5);c.quadraticCurveTo(x,y-12,x+19,y+5);c.lineTo(x+19,y-24);c.lineTo(x-19,y-24);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y+5);c.quadraticCurveTo(x,y-11,x+16,y+5);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-9,y+23);c.quadraticCurveTo(x,y+26,x+9,y+23);c.stroke()
|
||||
}
|
||||
const drop=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r*1.6);c.quadraticCurveTo(x+r,y-r*.2,x+r,y+r*.3);c.arc(x,y+r*.3,r,0,PI);c.quadraticCurveTo(x-r,y-r*.2,x,y-r*1.6);c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const s=ease((t-.06)/.28)-ease((t-.62)/.22),w=sin(2*T*t),hy=-6*s
|
||||
c.translate(128,256);c.scale(.86,.86);c.translate(-128,-256)
|
||||
// bedhead strands sticking up, drawn first so the hair covers their roots
|
||||
// bedhead flyaways curling off the top and sides
|
||||
for(const[x0,y0,x1,y1,x2,y2,o]of[[104,48,84,40,80,22,0],[136,40,150,24,140,12,1.4],[186,66,206,66,214,48,2.8],[70,80,52,74,48,58,4.2]])
|
||||
for(const[l,st]of[[10,'#111'],[4,HAIR]])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(x0,y0+hy),c.quadraticCurveTo(x1,y1+hy,x2+4*sin(2*T*t+o),y2+hy+2*sin(2*T*t+o)),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=6
|
||||
c.fillStyle=HD;c.beginPath();c.moveTo(128,34+hy);c.bezierCurveTo(200,34+hy,214,100,206,168)
|
||||
for(const[x,y]of[[198,186],[188,170],[176,192],[164,178]])c.lineTo(x,y)
|
||||
c.lineTo(92,178);for(const[x,y]of[[80,192],[68,170],[58,186],[50,168]])c.lineTo(x,y)
|
||||
c.bezierCurveTo(42,100,56,34+hy,128,34+hy);c.fill();c.stroke()
|
||||
// pajama top with piping and star print
|
||||
c.fillStyle=PJ;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,262);c.lineTo(202,262);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=2.5;for(const[x,y]of[[78,236],[178,230],[112,250],[150,252]]){c.beginPath();for(let k=0;k<10;k++){const r=k%2?2.5:6,a=k*PI/5-PI/2;c.lineTo(x+Math.cos(a)*r,y+Math.sin(a)*r)}c.closePath();c.fill();c.stroke()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.moveTo(128,212),c.lineTo(128+d*30,190),c.lineTo(128+d*34,204),c.closePath(),c.fill(),c.stroke()
|
||||
c.fillStyle=PD;for(const y of[226,246])c.beginPath(),c.arc(128,y,3.5,0,T),c.fill(),c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,170+hy);c.lineTo(116,192);c.lineTo(128,210);c.lineTo(140,192);c.lineTo(140,170+hy);c.fill();c.beginPath();c.moveTo(116,170+hy);c.lineTo(116,192);c.moveTo(140,170+hy);c.lineTo(140,192);c.stroke()
|
||||
// arms rise from the sides to clasp overhead
|
||||
const hands=[]
|
||||
for(const d of[-1,1]){
|
||||
// elbow and hand swing out to the side on the way up instead of crossing the chest
|
||||
const q=(a,b,e)=>(1-s)*(1-s)*a+2*s*(1-s)*b+s*s*e
|
||||
const ex=128+d*q(66,118,102),ey=q(270,220,112),hx=128+d*q(58,124,20),hy2=q(312,160,16)
|
||||
for(const[l,st]of[[30,'#111'],[19,PJ]])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(128+d*46,214),c.lineTo(ex,ey),c.lineTo(hx,hy2+10),c.stroke()
|
||||
hands.push([hx,hy2])
|
||||
}
|
||||
c.strokeStyle='#111'
|
||||
c.save();c.translate(0,hy)
|
||||
c.lineWidth=6;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(s<.35){eye(c,103,142,-1);eye(c,153,142,1)}
|
||||
else for(const d of[-1,1]){
|
||||
const x=128+d*25
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x+d*13,132);c.lineTo(x-d*9,142);c.lineTo(x+d*13,150);c.stroke()
|
||||
c.fillStyle=TEAR;c.lineWidth=3;drop(c,x+d*20,146,2+4*s)
|
||||
}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,162,13,6,0,0,T),c.fill()
|
||||
c.lineWidth=3.5
|
||||
if(s>.12){
|
||||
const rx=6+8*s,ry=4+13*s,my=166+3*s
|
||||
c.fillStyle=MO;c.beginPath();c.ellipse(128,my,rx,ry,0,0,T);c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=TG;c.beginPath();c.ellipse(128,my+ry,rx*.8,ry*.55,0,0,T);c.fill();c.restore()
|
||||
c.beginPath();c.ellipse(128,my,rx,ry,0,0,T);c.stroke()
|
||||
}else c.beginPath(),c.moveTo(120,168),c.quadraticCurveTo(124,165,128,168),c.quadraticCurveTo(132,171,136,168),c.stroke()
|
||||
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(68,150)
|
||||
c.bezierCurveTo(52,74,88,36,128,36);c.bezierCurveTo(168,36,204,74,188,150)
|
||||
c.quadraticCurveTo(186,118,180,104);c.quadraticCurveTo(172,96,168,112);c.quadraticCurveTo(160,88,146,90);c.quadraticCurveTo(144,104,134,112)
|
||||
c.quadraticCurveTo(128,92,114,90);c.quadraticCurveTo(112,104,100,110);c.quadraticCurveTo(94,92,84,100);c.quadraticCurveTo(84,110,78,114)
|
||||
c.quadraticCurveTo(70,128,68,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke()
|
||||
c.restore()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN
|
||||
for(const[k,[x,y]]of hands.entries())c.beginPath(),c.ellipse(x,y+4,10,12,(k?-1:1)*.5,0,T),c.fill(),c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// snow maiden with long ice-blue hair blowing frosty breath over her palm, the wisps curling away and turning into spinning snowflakes, sleeve drifting
|
||||
const{sin,cos,PI,max,min,round}=Math,T=2*PI
|
||||
export const frames=16,delay=80
|
||||
const SKIN='#f8f0fc',HAIR='#a5d8ff',HD='#4dabf7',HL='#fff',EYE='#91a7ff',ED='#364fc7',KI='#fff',KP='#d0ebff',OBI='#9775fa',OD='#7048e8',ICE='#e7f5ff',BL='#d0bfff'
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+3,y+3,5.5,7.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x,y+1,4,0,T);c.fill();c.beginPath();c.arc(x+7,y+9,2,0,T);c.fill()
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(x-17,y-21,34,15);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-5);c.quadraticCurveTo(x,y-11,x+16,y-5);c.moveTo(x+d*15,y-6);c.lineTo(x+d*21,y-11);c.stroke()
|
||||
c.lineWidth=2.5;for(const k of[-6,0,6])c.beginPath(),c.moveTo(x+k,y-6),c.lineTo(x+k*1.4,y-11),c.stroke()
|
||||
}
|
||||
const flake=(c,x,y,r,a)=>{for(const[lw,col]of[[8,'#111'],[3.5,'#fff']]){c.lineWidth=lw;c.strokeStyle=col;c.beginPath()
|
||||
for(let k=0;k<6;k++){const b=a+k*PI/3,ex=x+cos(b)*r,ey=y+sin(b)*r,mx=x+cos(b)*r*.55,my=y+sin(b)*r*.55
|
||||
c.moveTo(x,y);c.lineTo(ex,ey);for(const s of[-1,1])c.moveTo(mx,my),c.lineTo(mx+cos(b+s*.8)*r*.35,my+sin(b+s*.8)*r*.35)}
|
||||
c.stroke()}c.strokeStyle='#111'}
|
||||
const path=p=>[146+92*p+6*sin(p*T*2),178-24*p-96*p*p]
|
||||
export default(c,t)=>{
|
||||
const sw=.06*sin(T*t),puff=1+.12*sin(4*T*t)
|
||||
c.save();c.translate(116,256);c.scale(.9,.9);c.translate(-128,-256)
|
||||
c.lineWidth=6;c.fillStyle=HD;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(200,34,214,100,206,160);c.bezierCurveTo(214,200,206,236,214,256);c.lineTo(42,256);c.bezierCurveTo(50,236,42,200,50,160);c.bezierCurveTo(42,100,56,34,128,34);c.fill();c.stroke()
|
||||
c.fillStyle=KI;c.beginPath();c.moveTo(98,186);c.quadraticCurveTo(58,194,48,256);c.lineTo(208,256);c.quadraticCurveTo(198,194,158,186);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=KP;for(const[x,y]of[[70,236],[96,220],[180,230]])c.beginPath(),c.arc(x,y,7,0,T),c.fill()
|
||||
c.fillStyle=KI;c.beginPath();c.moveTo(104,186);c.lineTo(128,222);c.lineTo(152,186);c.fill();c.stroke()
|
||||
c.fillStyle=OBI;c.beginPath();c.moveTo(68,240);c.quadraticCurveTo(128,230,190,240);c.lineTo(194,256);c.lineTo(64,256);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=SKIN;c.beginPath();c.rect(116,168,24,24);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
c.lineWidth=6;for(const[x,d]of[[103,-1],[153,1]]){c.beginPath();c.moveTo(x-16,138);c.quadraticCurveTo(x,150,x+16,138);c.moveTo(x+d*15,140);c.lineTo(x+d*21,136);c.stroke();c.lineWidth=3;for(const k of[-8,0,8])c.beginPath(),c.moveTo(x+k,145),c.lineTo(x+k*1.2,151),c.stroke();c.lineWidth=6}
|
||||
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,162,12,6,0,0,T),c.fill()
|
||||
c.lineWidth=4;c.fillStyle='#e64980';c.beginPath();c.ellipse(140,168,5*puff,6*puff,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(62,170)
|
||||
c.bezierCurveTo(46,74,88,34,128,34);c.bezierCurveTo(168,34,210,74,194,170)
|
||||
c.quadraticCurveTo(188,128,182,106);c.quadraticCurveTo(176,108,170,116);c.quadraticCurveTo(164,100,156,94);c.quadraticCurveTo(150,104,142,110)
|
||||
c.quadraticCurveTo(136,98,128,92);c.quadraticCurveTo(120,98,114,110);c.quadraticCurveTo(106,104,100,94);c.quadraticCurveTo(92,100,86,116)
|
||||
c.quadraticCurveTo(80,108,74,106);c.quadraticCurveTo(68,128,62,170);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
for(const d of[-1,1]){c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(128+d*58,106);c.quadraticCurveTo(128+d*70,170,128+d*62,226);c.quadraticCurveTo(128+d*52,212,128+d*50,196);c.quadraticCurveTo(128+d*54,150,128+d*48,112);c.closePath();c.fill();c.stroke()}
|
||||
flake(c,82,62,13,PI/6)
|
||||
c.save();c.translate(182,192);c.rotate(sw)
|
||||
c.fillStyle=KI;c.lineWidth=6;c.beginPath();c.moveTo(-14,0);c.lineTo(8,-6);c.lineTo(42,52);c.quadraticCurveTo(36,76,30,80);c.quadraticCurveTo(10,70,-2,74);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=KP;c.beginPath();c.arc(18,46,7,0,T);c.fill();c.strokeStyle=KP;c.lineWidth=4;c.beginPath();c.moveTo(6,68);c.quadraticCurveTo(20,72,30,72);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
c.fillStyle=SKIN;c.lineWidth=5;c.beginPath();c.moveTo(168,196);c.quadraticCurveTo(170,180,188,178);c.quadraticCurveTo(204,176,212,180);c.quadraticCurveTo(212,188,202,190);c.quadraticCurveTo(186,196,178,200);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(196,180);c.quadraticCurveTo(204,182,206,186);c.stroke()
|
||||
c.restore()
|
||||
for(let k=0;k<5;k++){const p=(t*2+k/5)%1,[x,y]=path(p)
|
||||
if(p<.35){const r=4+9*p/.35;c.fillStyle=ICE;c.lineWidth=4;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke();c.strokeStyle=KP;c.lineWidth=2.5;c.beginPath();c.arc(x,y,r*.55,PI,PI*1.7);c.stroke();c.strokeStyle='#111'}
|
||||
else{const r=round(14*min(1,(1-p)/.2)*min(1,(p-.35)/.12+.5));if(r>2)flake(c,x,y,r,p*5)}}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// manga alchemist with fluffy white curls and goggles pushed up, pouring one flask into another until it puffs a smoke cloud and sparkles, then she grins
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=20,delay=80,rim=4
|
||||
const TL=createCanvas(6,6),tx=TL.getContext('2d');tx.fillStyle='#111';for(const[a,b]of[[1.5,1.5],[4.5,4.5]])tx.beginPath(),tx.arc(a,b,1.05,0,T),tx.fill()
|
||||
const ease=x=>x<0?0:x>1?1:x*x*(3-2*x)
|
||||
// keen eye: toned iris, ink pupil, star sparkle when excited
|
||||
const eye=(c,x,y,d,tn,star)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11,13,0,0,T);c.clip();c.fillStyle=tn;c.fillRect(x-12,y-12,24,28);c.fillStyle='#111';c.fillRect(x-12,y-12,24,8);c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11,13,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.arc(x,y+3,5,0,T);c.fill()
|
||||
c.fillStyle='#fff';if(star){c.save();c.translate(x-3,y-1);c.beginPath();for(let k=0;k<8;k++){const r=k%2?2:6,a=k*PI/4;c.lineTo(r*cos(a),r*sin(a))}c.fill();c.restore()}else{c.beginPath();c.arc(x-4,y-3,4,0,T);c.fill()}
|
||||
c.beginPath();c.arc(x+4,y+8,1.8,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-19,x+16,y-6);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(x+d*14,y-8);c.lineTo(x+d*20,y-12);c.stroke()
|
||||
}
|
||||
// fluffy curl outline: a ring of bumps
|
||||
const curls=(c,cx,cy,rx,ry,a0,a1,n,b)=>{for(let k=0;k<=n;k++){const a=a0+(a1-a0)*k/n,x=cx+rx*cos(a),y=cy+ry*sin(a);k?c.arc(x,y,b,a-1.3,a+1.3):c.moveTo(x+b*cos(a-1.3),y+b*sin(a-1.3))}}
|
||||
const puff=(c,x,y,s)=>{c.beginPath();for(let k=0;k<7;k++){const a=k*T/7,q=s*(k%2?.8:1);c.arc(x+s*.8*cos(a),y+s*.65*sin(a),q*.55,a-1.5,a+1.5)}c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat')
|
||||
const pour=ease(t/.15)-ease((t-.42)/.12),flow=t>.12&&t<.42,boom=t>=.44&&t<.86,bp=(t-.44)/.42,star=t>.5&&t<.9
|
||||
// fluffy white curls behind
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();curls(c,128,104,72,66,PI*.82,PI*2.18,13,15);c.lineTo(170,178);c.lineTo(86,178);c.closePath();c.fill();c.stroke()
|
||||
// lab coat over a toned shirt and an ink tie
|
||||
c.beginPath();c.moveTo(100,184);c.quadraticCurveTo(58,194,48,256);c.lineTo(208,256);c.quadraticCurveTo(198,194,156,184);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=tn;c.beginPath();c.moveTo(110,186);c.lineTo(128,232);c.lineTo(146,186);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.lineWidth=3;c.beginPath();c.moveTo(124,192);c.lineTo(132,192);c.lineTo(134,214);c.lineTo(128,222);c.lineTo(122,214);c.closePath();c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(100,186);c.lineTo(118,236);c.lineTo(110,256);c.moveTo(156,186);c.lineTo(138,236);c.lineTo(146,256);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,22);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,166);c.lineTo(116,186);c.moveTo(140,166);c.lineTo(140,186);c.stroke()
|
||||
// face
|
||||
c.save();c.translate(128,118);c.scale(.82,.82);c.translate(-128,-128)
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
const lx=flow?-4:0
|
||||
eye(c,103+lx,140,-1,tn,star);eye(c,153+lx,140,1,tn,star)
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-3,168),c.lineTo(128+d*42+k+3,160),c.stroke()
|
||||
c.lineWidth=4;c.fillStyle='#111';c.beginPath()
|
||||
if(star)c.moveTo(114,166),c.quadraticCurveTo(128,184,142,166),c.closePath(),c.fill(),c.stroke()
|
||||
else c.moveTo(122,170),c.lineTo(134,168),c.stroke()
|
||||
// curly fringe
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();curls(c,128,96,62,40,PI*1.02,PI*1.98,8,14);c.lineTo(186,140);c.quadraticCurveTo(176,108,160,104);c.quadraticCurveTo(128,114,96,104);c.quadraticCurveTo(80,108,70,140);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const[x,y]of[[98,78],[128,70],[158,78]])c.arc(x,y,7,PI*.2,PI*1.2);c.stroke()
|
||||
// goggles pushed up on her head: strap, two lenses with a glint
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(66,82);c.quadraticCurveTo(128,40,190,82);c.stroke()
|
||||
for(const x of[106,150]){c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.arc(x,62,17,0,T);c.fill();c.stroke();c.fillStyle=tn;c.beginPath();c.arc(x,62,11,0,T);c.fill();c.lineWidth=3;c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(x-6,58);c.lineTo(x-1,53);c.stroke();c.strokeStyle='#111'}
|
||||
c.restore()
|
||||
// pouring flask tips in from the right, a toned stream dripping into the round flask
|
||||
const fx=166,fy=176
|
||||
c.save();c.translate(fx,fy);c.rotate(-1.1*pour)
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(-6,-26);c.lineTo(-6,-10);c.lineTo(-22,20);c.lineTo(22,20);c.lineTo(6,-10);c.lineTo(6,-26);c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(-26,6-8*pour*(t<.42?1:0),52,20);c.restore();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(-9,-26);c.lineTo(9,-26);c.stroke()
|
||||
c.restore()
|
||||
if(flow){c.lineWidth=7;c.strokeStyle='#111';c.beginPath();c.moveTo(fx-30,fy-16);c.quadraticCurveTo(fx-44,fy-6,fx-62,fy+8);c.stroke();c.strokeStyle='#fff';c.lineWidth=3;c.stroke();c.strokeStyle='#111'}
|
||||
// round-bottom flask held up on the left
|
||||
const rx=100,ry=214
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(rx-7,ry-44);c.lineTo(rx-7,ry-24);c.arc(rx,ry,26,-PI/2-.27,PI*1.5+.27,true);c.lineTo(rx+7,ry-24);c.lineTo(rx+7,ry-44);c.closePath();c.fill()
|
||||
c.save();c.clip();c.fillStyle=tn;c.fillRect(rx-30,ry-4-6*ease((t-.12)/.3)*(t<.86?1:0),60,40);c.restore();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(rx-10,ry-44);c.lineTo(rx+10,ry-44);c.stroke()
|
||||
if(t>.2&&t<.86){c.fillStyle='#fff';c.lineWidth=2;for(let k=0;k<3;k++){const q=(t*6+k/3)%1;c.beginPath();c.arc(rx-10+k*10,ry+12-24*q,2+2*q,0,T);c.fill();c.stroke()}}
|
||||
// hands
|
||||
c.fillStyle='#fff';c.lineWidth=4;for(const[x,y]of[[rx+20,ry+14],[fx+14,fy+10]])c.beginPath(),c.ellipse(x,y,11,10,0,0,T),c.fill(),c.stroke()
|
||||
// the reaction: a smoke cloud billows up from the round flask, sparkles pop around it
|
||||
if(boom&&bp<.97){
|
||||
const s=(10+16*ease(bp/.35))*(1-ease((bp-.72)/.28)),x=rx-26-40*bp,y=ry-60-62*bp
|
||||
c.fillStyle='#fff';c.lineWidth=4;if(bp>.15)puff(c,x+20,y+30,s*.6);puff(c,x,y,s)
|
||||
c.lineWidth=2.5;for(const[xx,yy,r]of[[x+32,y-22,8],[x-22,y+30,6],[x+6,y-34,5]]){const q=r*sin(PI*min(1,bp*1.6));if(q>1){c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,w=k%2?q*.35:q;c.lineTo(xx+w*cos(a),yy+w*sin(a))}c.closePath();c.fill();c.stroke()}}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// manga chibi bard with a light screentone braid and a feathered cap, strumming a lute while notes float up and one boot taps the beat
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=90,rim=4
|
||||
// a finer, lighter tone tile for the hair
|
||||
const TL=createCanvas(6,6),tx=TL.getContext('2d');tx.fillStyle='#111';for(const[a,b]of[[1.5,1.5],[4.5,4.5]])tx.beginPath(),tx.arc(a,b,1.05,0,T),tx.fill()
|
||||
const TF=createCanvas(5,5),fx=TF.getContext('2d');fx.fillStyle='#111';fx.beginPath();fx.arc(2.5,2.5,.75,0,T);fx.fill()
|
||||
// happy closed-eye arc with lashes, for the singing
|
||||
const shut=(c,x,y,d)=>{c.lineWidth=6;c.beginPath();c.arc(x,y+6,13,PI*1.15,PI*1.85);c.stroke();c.lineWidth=3;c.beginPath();c.moveTo(x+d*12,y);c.lineTo(x+d*18,y-3);c.stroke()}
|
||||
const note=(c,x,y,s,two)=>{
|
||||
c.save();c.translate(x,y);c.scale(s,s);c.fillStyle='#111';c.lineWidth=3
|
||||
for(const d of two?[0,14]:[0]){c.beginPath();c.ellipse(d,0,6,4.5,-.4,0,T);c.fill();c.beginPath();c.moveTo(d+5,-2);c.lineTo(d+5,-24);c.stroke()}
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(5,-24);two?c.lineTo(19,-24):c.quadraticCurveTo(14,-18,12,-10);c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat'),fn=c.createPattern(TF,'repeat')
|
||||
const st=sin(t*T*4),tap=max(0,sin(t*T*2))
|
||||
// floating notes drift up and out to the left
|
||||
for(let k=0;k<2;k++){const p=(t+k/2)%1,s=.9*sin(PI*min(1,p*1.3));if(s>.1)note(c,70-34*p+8*sin(p*T),150-110*p,s,k)}
|
||||
// boots: the right one taps
|
||||
c.fillStyle='#fff';c.lineWidth=5
|
||||
c.beginPath();c.moveTo(94,214);c.lineTo(94,236);c.quadraticCurveTo(80,238,82,246);c.lineTo(108,246);c.lineTo(108,214);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.translate(140,246);c.rotate(-.22*tap);c.beginPath();c.moveTo(-10,-32);c.lineTo(-10,-10);c.lineTo(-12,0);c.lineTo(16,0);c.quadraticCurveTo(18,-8,4,-10);c.lineTo(4,-32);c.closePath();c.fill();c.stroke();c.restore()
|
||||
if(tap>.8){c.lineWidth=3;c.beginPath();c.moveTo(162,236);c.lineTo(172,232);c.moveTo(162,244);c.lineTo(174,246);c.stroke()}
|
||||
// short toned cape and a white tunic with a scalloped hem and belt
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(96,132);c.quadraticCurveTo(70,160,66,206);c.lineTo(170,206);c.quadraticCurveTo(166,160,140,132);c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(60,128,120,82);c.restore();c.stroke()
|
||||
c.beginPath();c.moveTo(100,134);c.quadraticCurveTo(86,170,84,206);for(let k=0;k<5;k++)c.quadraticCurveTo(90+k*14,220,98+k*14,206);c.quadraticCurveTo(150,170,136,134);c.closePath();c.fillStyle='#fff';c.fill();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(88,180);c.lineTo(150,180);c.stroke()
|
||||
// head with a fine-tone braid falling over one shoulder
|
||||
c.save();c.translate(118,78);c.scale(.58,.58);c.translate(-128,-120)
|
||||
const braid=()=>{c.beginPath();for(let k=0;k<6;k++)c.ellipse(70-k*2,170+k*22,14,14,0,0,T)}
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(66,150);c.bezierCurveTo(48,70,86,36,128,36);c.bezierCurveTo(170,36,208,70,190,150);c.closePath();c.fill();c.save();c.clip();c.fillStyle=fn;c.fillRect(40,30,180,130);c.restore();c.stroke()
|
||||
for(let k=0;k<6;k++){c.beginPath();c.ellipse(70-k*2,170+k*22,15-k,14,0,0,T);c.fillStyle='#fff';c.fill();c.save();c.clip();c.fillStyle=fn;c.fillRect(40,150,60,150);c.restore();c.stroke()}
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(62,296);c.lineTo(52,318);c.lineTo(64,312);c.lineTo(70,324);c.lineTo(74,300);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
shut(c,103,136,-1);shut(c,153,136,1)
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-3,166),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
c.fillStyle='#111';c.lineWidth=4;c.beginPath();c.ellipse(128,168,7,5+3*abs(st),0,0,T);c.fill();c.stroke()
|
||||
const fr=()=>{c.beginPath();c.moveTo(70,136);c.bezierCurveTo(56,74,90,44,128,44);c.bezierCurveTo(166,44,200,74,186,136);c.quadraticCurveTo(178,104,160,100);c.quadraticCurveTo(144,112,128,98);c.quadraticCurveTo(110,112,94,100);c.quadraticCurveTo(78,104,70,136);c.closePath()}
|
||||
c.lineWidth=7;c.fillStyle='#fff';fr();c.fill();c.save();fr();c.clip();c.fillStyle=fn;c.fillRect(50,40,160,100);c.restore();fr();c.stroke()
|
||||
// floppy cap tilted on top with a long feather
|
||||
c.save();c.translate(128,52);c.rotate(-.18)
|
||||
c.fillStyle='#fff';c.lineWidth=7;c.beginPath();c.ellipse(0,0,74,22,0,0,T);c.fill();c.stroke();c.beginPath();c.moveTo(-60,-4);c.quadraticCurveTo(-40,-48,10,-46);c.quadraticCurveTo(64,-44,62,-4);c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(-64,-50,130,46);c.restore();c.stroke()
|
||||
c.save();c.translate(40,-30);c.rotate(-.7+.12*sin(t*T*2));c.scale(1.2,1.2)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(-18,-50,0,-110);c.quadraticCurveTo(22,-50,0,0);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(0,-4);c.lineTo(0,-100);for(let k=1;k<7;k++)c.moveTo(0,-k*14),c.lineTo(-8,-k*14-6),c.moveTo(0,-k*14),c.lineTo(8,-k*14-6);c.stroke()
|
||||
c.restore();c.restore()
|
||||
c.restore()
|
||||
// the lute: toned pear body with an ink rose hole, neck up to the right, bent pegbox
|
||||
c.save();c.translate(108,176);c.rotate(-.5)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.roundRect(20,-7,82,14,4);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(100,-8);c.lineTo(124,-14);c.lineTo(126,6);c.lineTo(100,8);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const x of[108,116])for(const d of[-1,1])c.beginPath(),c.moveTo(x,d*9),c.lineTo(x,d*15),c.stroke()
|
||||
const bd=()=>{c.beginPath();c.moveTo(28,0);c.bezierCurveTo(26,-30,-34,-38,-40,0);c.bezierCurveTo(-34,38,26,30,28,0);c.closePath()}
|
||||
c.lineWidth=6;bd();c.fill();c.save();bd();c.clip();c.fillStyle=tn;c.fillRect(-44,-40,40,80);c.restore();bd();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.arc(0,0,9,0,T);c.fill()
|
||||
c.lineWidth=1.6;c.beginPath();for(const d of[-3,0,3])c.moveTo(-30,d),c.lineTo(100,d);c.stroke()
|
||||
c.restore()
|
||||
// fretting hand on the neck, strumming hand flicking over the strings
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(170,136,10,9,0,0,T);c.fill();c.stroke()
|
||||
const sx=110+4*st,sy=166+8*st
|
||||
for(const[l,s]of[[18,'#111'],[10,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(96,146),c.quadraticCurveTo(86,166,sx-6,sy),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=4;c.beginPath();c.ellipse(sx,sy,10,9,0,0,T);c.fill();c.stroke()
|
||||
if(abs(st)>.8){c.lineWidth=2.5;c.beginPath();c.moveTo(sx+16,sy-8);c.lineTo(sx+24,sy-12);c.moveTo(sx+16,sy+2);c.lineTo(sx+26,sy+2);c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// chibi batter in a helmet with a dotted side ponytail waggles the bat, the pitch comes in, crack! the ball blasts off in a burst
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs,min,max}=Math,T=2*PI
|
||||
export const frames=18,delay=70,rim=4
|
||||
const tile=(s,col,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[s/2,s/2],[0,0],[s,0],[0,s],[s,s]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const DOT=tile(6,'#111',1.4),LITE=tile(5,'#fff',1)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u),lerp=(a,b,k)=>a+(b-a)*k
|
||||
const key=(ks,t)=>{for(let n=1;n<ks.length;n++)if(t<=ks[n][0]){const[a,va]=ks[n-1],[b,vb]=ks[n];return lerp(va,vb,ease((t-a)/(b-a)))}return ks.at(-1)[1]}
|
||||
const limb=(c,p,w)=>{for(const[l,s]of[[w+8,'#111'],[w,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...p[0]);for(const q of p.slice(1))c.lineTo(...q);c.stroke()}c.strokeStyle='#111'}
|
||||
const ball=(c,x,y,r,a)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(0,0,r,0,T);c.fill();c.stroke();c.lineWidth=2.5
|
||||
for(const d of[-1,1]){c.beginPath();c.arc(d*r*1.5,0,r*1.05,PI-.7,PI+.7);if(d<0)c.arc(d*r*1.5,0,r*1.05,-.7,.7);c.stroke()}c.restore()}
|
||||
const eye=(c,x,y,d,lt,sq)=>{
|
||||
if(sq){c.lineWidth=8;c.beginPath();c.moveTo(x-14,y-8+d*0);c.lineTo(x+(d>0?-2:2)*-6,y+2);c.lineTo(x-14+28*(d>0?0:1)*0+14,y+12);c.stroke();return}
|
||||
c.save();c.beginPath();c.moveTo(x-17,y-4+d*4);c.lineTo(x+17,y-4-d*4);c.lineTo(x+17,y+22);c.lineTo(x-17,y+22);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,15,0,0,T);c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+5,y+3,9,12,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-4,y+7,22,10)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+2,y-1,3.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-4+d*4);c.lineTo(x+16,y-4-d*4);c.lineTo(x+d*21,y-10);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const dt=c.createPattern(DOT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
// bat angle around the hands: cocked up behind the head, sweeps through level at contact, wraps around after
|
||||
const wag=t<.3?.12*sin(t*T*7):0,ba=key([[0,-2.1],[.3,-2.1],[.4,-2.5],[.46,-.15],[.52,1.3],[.7,1.5],[.85,-2.1],[1,-2.1]],t)+wag
|
||||
const tw=key([[0,0],[.36,-.2],[.46,.3],[.6,.4],[.8,0],[1,0]],t),hd=[[86,118],[94,128],[150,140],[122,108]]
|
||||
const hp=t<.36?hd[0]:t<.46?[lerp(86,150,ease((t-.36)/.1)),lerp(118,140,ease((t-.36)/.1))]:t<.6?[lerp(150,122,ease((t-.46)/.14)),lerp(140,104,ease((t-.46)/.14))]:[lerp(122,86,ease((t-.6)/.25)),lerp(104,118,ease((t-.6)/.25))]
|
||||
const hit=t>=.44&&t<.52
|
||||
c.save();c.translate(118,246);c.scale(1.02,1.02);c.translate(-118,-246)
|
||||
// pitch flies in from the right, gets crushed up and away
|
||||
if(t>=.24&&t<.45){const k=(t-.24)/.21;ball(c,lerp(260,166,k),lerp(110,140,k)-30*sin(PI*k),lerp(6,11,k),k*8)
|
||||
c.lineWidth=3.5;for(const d of[-6,6])c.beginPath(),c.moveTo(lerp(260,166,k)+18,lerp(110,140,k)-30*sin(PI*k)+d),c.lineTo(lerp(260,166,k)+44,lerp(110,140,k)-30*sin(PI*k)+d-6),c.stroke()}
|
||||
if(t>=.46&&t<.66){const k=(t-.46)/.2,x=lerp(172,248,k),y=lerp(132,8,k);c.lineWidth=4;for(const d of[-9,0,9])c.beginPath(),c.moveTo(x-46+d,y+64+d*.6),c.lineTo(x-12+d,y+16+d*.6),c.stroke();ball(c,x,y,13*(1-.35*k),t*30)}
|
||||
c.fillStyle=dt;c.beginPath();c.ellipse(118,240,54,8,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(150,236);c.lineTo(176,236);c.lineTo(180,242);c.lineTo(163,248);c.lineTo(146,242);c.closePath();c.fill();c.stroke()
|
||||
// legs
|
||||
const H=[110,178],st=tw
|
||||
for(const[d,kx,fx]of[[-1,-26,-36],[1,22+20*max(0,st),34]]){const p=[H[0]+d*9,H[1]],kn=[p[0]+kx*.6,p[1]+28],ft=[H[0]+fx,236]
|
||||
limb(c,[p,kn,ft],16);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(ft[0]+4,ft[1]+2,15,8,0,0,T);c.fill();c.stroke();c.fillStyle='#111';c.fillRect(ft[0]-8,ft[1]+3,24,5)}
|
||||
// body
|
||||
c.save();c.translate(...H);c.rotate(tw*.4)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-24,-6);c.lineTo(24,-6);c.lineTo(26,14);c.lineTo(-26,14);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const x of[-14,0,14])c.moveTo(x,-6),c.lineTo(x,14);c.stroke()
|
||||
c.beginPath();c.moveTo(-22,-4);c.lineTo(24,-4);c.lineTo(26,-48);c.quadraticCurveTo(2,-56,-22,-48);c.closePath();c.fillStyle='#fff';c.lineWidth=5;c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(-30,-56,22,56);c.fillRect(10,-56,22,56);c.restore();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(2,-52);c.lineTo(2,-4);c.stroke();c.fillStyle='#111';for(const y of[-40,-28,-16])c.beginPath(),c.arc(6,y,2,0,T),c.fill()
|
||||
c.fillStyle='#111';c.fillRect(-24,-10,50,6)
|
||||
c.restore()
|
||||
if(hit){const x=hp[0]+70*cos(ba)+14,y=hp[1]+70*sin(ba)-18;c.fillStyle='#fff';c.lineWidth=5;c.beginPath();for(let j=0;j<18;j++){const r=j%2?12:26,b=j*PI/9;c.lineTo(x+cos(b)*r,y+sin(b)*r)}c.closePath();c.fill();c.stroke()}
|
||||
// bat (drawn after the burst so the barrel stays readable at contact)
|
||||
const L=90,tip=[hp[0]+L*cos(ba),hp[1]+L*sin(ba)]
|
||||
if(t>=.4&&t<.52){c.lineWidth=4;for(let j=0;j<3;j++){c.beginPath();c.arc(hp[0],hp[1],46+j*12,-2.5,ba,false);c.stroke()}}
|
||||
c.save();c.translate(...hp);c.rotate(ba);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-8,-4);c.lineTo(30,-5);c.quadraticCurveTo(L+4,-11,L,0);c.quadraticCurveTo(L+4,11,30,5);c.lineTo(-8,4);c.closePath();c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(34,-14,50,28);c.restore();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(-10,0,4,7,0,0,T);c.fill();c.fillRect(-6,-5,14,10);c.restore()
|
||||
// arms grip the bat together
|
||||
const S=[H[0]+4+16*tw,H[1]-44]
|
||||
for(const[o,g]of[[[S[0]-10,S[1]],[hp[0]+4*cos(ba),hp[1]+4*sin(ba)]],[[S[0]+12,S[1]+2],[hp[0]-6*cos(ba),hp[1]-6*sin(ba)]]]){const e=[(o[0]+g[0])/2-6,(o[1]+g[1])/2+12];limb(c,[o,e,g],12)}
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(...hp,10,0,T);c.fill();c.stroke()
|
||||
// head in a helmet with a ponytail out the back
|
||||
c.save();c.translate(H[0]+4+10*tw,H[1]-92);c.rotate(-.05+tw*.2);c.scale(.66,.66);c.translate(-128,-121)
|
||||
c.lineWidth=9;c.fillStyle='#fff'
|
||||
const pt=()=>{c.beginPath();c.moveTo(78,120);c.quadraticCurveTo(28,130+8*sin(t*T*2),26,196+10*sin(t*T*2+1));c.quadraticCurveTo(52,170,80,152);c.closePath()}
|
||||
pt();c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(10,100,90,110);c.restore();c.lineWidth=9;pt();c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
const sq=t>=.42&&t<.56
|
||||
eye(c,112,138,-1,lt,0);eye(c,160,138,1,lt,0)
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const j of[-6,0,6])c.beginPath(),c.moveTo(136+d*42+j-3,166),c.lineTo(136+d*42+j+3,158),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath()
|
||||
if(sq)c.moveTo(122,162),c.lineTo(154,162),c.quadraticCurveTo(150,186,138,186),c.quadraticCurveTo(126,186,122,162),c.fill(),c.stroke()
|
||||
else c.moveTo(128,168),c.lineTo(146,166),c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(76,120);c.quadraticCurveTo(80,104,96,106);c.lineTo(104,94);c.lineTo(112,108);c.lineTo(120,98);c.lineTo(124,110);c.lineTo(80,124);c.closePath();c.fill()
|
||||
c.lineWidth=9;c.beginPath();c.moveTo(64,112);c.bezierCurveTo(58,40,198,40,192,104);c.lineTo(214,108);c.quadraticCurveTo(220,116,212,120);c.lineTo(178,118);c.quadraticCurveTo(130,94,78,118);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(78,108,12,14,0,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=7;c.beginPath();c.arc(124,94,44,PI*1.18,PI*1.42);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// chibi with a sprout ponytail staggers under a tall stack of moving boxes, the stack swaying and the top box nearly sliding off
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max,exp}=Math,T=2*PI
|
||||
export const frames=20,delay=80,rim=4
|
||||
const tone=(c,s,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle='#111';for(const[a,b]of[[0,0],[s,0],[0,s],[s,s],[s/2,s/2]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return c.createPattern(k,'repeat')}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
const bump=exp(-(((t-.55)/.09)**2)),sw=sin(t*T*2)*(1+1.4*bump),strain=bump>.4,bx=150-sw*4
|
||||
// shadow and legs, stepping to keep balance
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(130,244,62,8,0,0,T);c.fillStyle=FINE;c.fill()
|
||||
for(const[x,o]of[[128,0],[160,PI]]){const lift=max(0,sin(t*T*2+o))*6
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.rect(x-7-sw*2,214,14,24-lift);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.roundRect(x-12-sw*2,234-lift,26,10,5);c.fill();c.fillStyle='#fff';c.fillRect(x-8-sw*2,240-lift,18,2)}
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(116-sw*2,200);c.lineTo(172-sw*2,200);c.lineTo(176-sw*2,222);c.lineTo(112-sw*2,222);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
// girl's body behind the stack, head peeking out on the left
|
||||
const hx=96-sw*5,hy=100
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(hx-4,hy+20);c.lineTo(hx+20,hy+24);c.quadraticCurveTo(150,128,166,206);c.lineTo(108-sw*2,210);c.quadraticCurveTo(80,150,hx-4,hy+20);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.save();c.translate(hx,hy);c.rotate(-.12-sw*.05);c.scale(.5,.5);c.translate(-128,-130)
|
||||
c.lineWidth=11;c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(strain){c.lineWidth=9;for(const d of[-1,1]){const x=128+d*25;c.beginPath();c.moveTo(x-d*13,128);c.lineTo(x+d*10,140);c.lineTo(x-d*13,152);c.stroke()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.roundRect(110,160,36,20,6);c.fill();c.stroke();c.beginPath();c.moveTo(110,170);c.lineTo(146,170);for(const x of[119,128,137])c.moveTo(x,160),c.lineTo(x,180);c.stroke()}
|
||||
else{for(const[x,d]of[[103,-1],[153,1]]){
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,142,14,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,142,14,16,0,0,T);c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+5,134,11,13,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+1,129,4.5,5.5,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=9;c.beginPath();c.moveTo(x-16,136);c.quadraticCurveTo(x,124,x+16,136);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-12,114+d*4);c.lineTo(x+12,114-d*4);c.stroke()}
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(116,172);for(let k=1;k<=4;k++)c.lineTo(116+k*6,k%2?167:172);c.stroke()}
|
||||
c.lineWidth=5;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*42+k-3,168),c.lineTo(128+d*42+k+3,160),c.stroke()
|
||||
// short ink hair with a sprout ponytail on top
|
||||
c.lineWidth=11;c.fillStyle='#111';c.beginPath();c.moveTo(70,150);c.bezierCurveTo(50,72,88,40,128,40);c.bezierCurveTo(168,40,206,72,186,150)
|
||||
c.quadraticCurveTo(182,118,174,104);c.quadraticCurveTo(162,112,152,108);c.quadraticCurveTo(144,96,134,106);c.quadraticCurveTo(124,96,112,106);c.quadraticCurveTo(102,98,92,108);c.quadraticCurveTo(80,112,76,124);c.quadraticCurveTo(72,138,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=9;c.beginPath();c.arc(128,104,50,1.3*PI,1.7*PI);c.stroke();c.strokeStyle='#111'
|
||||
c.save();c.translate(128,42);c.rotate(sw*.25);c.fillStyle='#111';c.lineWidth=9;for(const a of[-.7,0,.7])c.beginPath(),c.ellipse(sin(a)*20,-24-cos(a)*6,9,24,a,0,T),c.fill(),c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(-14,-10,28,14,6);c.fill();c.stroke();c.restore()
|
||||
c.restore()
|
||||
// her arm wrapping round the stack to the bottom box
|
||||
for(const[l,s]of[[26,'#111'],[16,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(hx-2,hy+40),c.quadraticCurveTo(hx-22,hy+86,bx-42,204),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// sweat drops flying off
|
||||
c.fillStyle='#fff';c.lineWidth=3.5;for(const[o,dx]of[[0,-1],[.5,1]]){const p=(t*2+o)%1;if(p<.7){const x=hx+dx*(26+30*p),y=hy-30-24*sin(PI*p/.7);c.beginPath();c.moveTo(x,y-8);c.quadraticCurveTo(x+6,y+2,x,y+5);c.quadraticCurveTo(x-6,y+2,x,y-8);c.fill();c.stroke()}}
|
||||
// the stack: each box sways a little more than the one below; the top one skids at the scary moment
|
||||
const box=(x,y,a,w,h,k)=>{c.save();c.translate(x,y);c.rotate(a);c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.rect(-w/2,-h/2,w,h);c.fill();c.stroke()
|
||||
c.fillStyle=FINE;c.fillRect(-7,-h/2,14,h);c.lineWidth=3;c.strokeRect(-7,-h/2,14,h)
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(-w/2,-h/2+8);c.lineTo(-10,-h/2+8);c.moveTo(10,-h/2+8);c.lineTo(w/2,-h/2+8);c.stroke()
|
||||
if(k==1){c.lineWidth=4;for(const x of[-24,-14])c.beginPath(),c.moveTo(x,6),c.lineTo(x,-6),c.moveTo(x-4,-2),c.lineTo(x,-7),c.lineTo(x+4,-2),c.stroke()}
|
||||
if(k==2){c.lineWidth=3.5;c.beginPath();c.moveTo(18,-8);c.lineTo(30,-8);c.lineTo(27,4);c.lineTo(21,4);c.closePath();c.stroke();c.beginPath();c.moveTo(24,4);c.lineTo(24,12);c.moveTo(20,12);c.lineTo(28,12);c.stroke()}
|
||||
c.restore()}
|
||||
let x=bx,y=196,a=0
|
||||
for(let k=0;k<4;k++){const w=[76,70,66,58][k],h=[40,40,38,34][k]
|
||||
if(k)x+=sw*(2+k*2.5)+(k==3?bump*14:0),y-=[0,40,39,36][k],a=sw*(.02+k*.025)+(k==3?bump*.3:0)
|
||||
box(x,y,a,w,h,k)}
|
||||
// hands gripping the bottom box
|
||||
c.lineWidth=6;c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.ellipse(bx+d*40,204,10,13,0,0,T),c.fill(),c.stroke()
|
||||
// wobble marks beside the top box when it nearly goes
|
||||
if(bump>.3){c.lineWidth=4;for(const k of[0,1])c.beginPath(),c.moveTo(x+40+k*8,y-14),c.quadraticCurveTo(x+46+k*8,y,x+40+k*8,y+14),c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// chibi boxer with a white wolf cut and flapping sweatband bounces in her stance, then fires jab, jab, cross into impact bursts
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs}=Math,T=2*PI
|
||||
export const frames=20,delay=60,rim=4
|
||||
const J=[0,1,.45,0,1,.45,0,0,0,0,0,0,0,0,0,0,0,0,0,0],X=[0,0,0,0,0,0,0,-.3,1,1,.5,0,0,0,0,0,0,0,0,0]
|
||||
const tile=(s,col,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[s/2,s/2],[0,0],[s,0],[0,s],[s,s]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const DOT=tile(6,'#111',1.3),LITE=tile(5,'#fff',1)
|
||||
const limb=(c,p,w)=>{for(const[l,s]of[[w+8,'#111'],[w,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...p[0]);for(const q of p.slice(1))c.lineTo(...q);c.stroke()}c.strokeStyle='#111'}
|
||||
const burst=(c,x,y,R)=>{c.fillStyle='#fff';c.lineWidth=4;c.beginPath();for(let k=0;k<20;k++){const r=k%2?R*.55:R,a=k*PI/10;c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;for(let k=0;k<8;k++){const a=k*PI/4+.4;c.beginPath();c.moveTo(x+cos(a)*(R+6),y+sin(a)*(R+5));c.lineTo(x+cos(a)*(R+13),y+sin(a)*(R+13));c.stroke()}}
|
||||
const glove=(c,x,y,a)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle='#111';c.lineWidth=5
|
||||
c.beginPath();c.roundRect(-26,-10,12,20,4);c.fill();c.stroke();c.fillStyle='#fff';c.fillRect(-24,-6,8,12)
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(2,0,19,17,0,0,T);c.fill();c.stroke();c.beginPath();c.ellipse(-4,-13,9,7,-.3,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3.5;c.beginPath();c.arc(4,-1,11,-1.7,-.5);c.stroke();c.beginPath();c.moveTo(-8,-6);c.quadraticCurveTo(-2,2,-8,9);c.stroke();c.strokeStyle='#111';c.restore()}
|
||||
const eye=(c,x,y,d,lt,o)=>{
|
||||
c.save();c.beginPath();c.moveTo(x-17,y-8+d*3);c.lineTo(x+17,y-8-d*3);c.lineTo(x+17,y+24);c.lineTo(x-17,y+24);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+3,13,15,0,0,T);c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+5,y+5,9,12,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-6,y+8,22,12)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+2,y+1,4,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-8+d*3);c.lineTo(x+16,y-8-d*3);c.lineTo(x+d*22,y-14+d*0);c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-14,y-26+d*6);c.lineTo(x+14,y-26-d*6);c.stroke()
|
||||
}
|
||||
export default(c,t,i)=>{
|
||||
const dt=c.createPattern(DOT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const j=J[i],x=X[i],hit=j==1||x==1,b=x>0||j>0?0:3*abs(sin(i/20*T*2)),lean=.14*Math.max(0,x)
|
||||
const hx=90,hy=174+b
|
||||
c.save();c.translate(hx,hy);c.rotate(lean)
|
||||
// legs and boots
|
||||
for(const[p,f]of[[[[-8,0],[-24,30],[-30,58]],-34],[[[10,0],[24,28],[30,58]],32]]){limb(c,p,15)
|
||||
c.fillStyle='#111';c.lineWidth=4;c.beginPath();c.roundRect(f-14+(f>0?2:-2),48,26,18,6);c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2;c.beginPath();for(const y of[53,58])c.moveTo(f-6,y),c.lineTo(f+6,y);c.stroke();c.strokeStyle='#111'}
|
||||
// trunks
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-26,-8);c.lineTo(26,-8);c.lineTo(32,18);c.lineTo(6,22);c.lineTo(2,10);c.lineTo(-2,22);c.lineTo(-30,18);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle='#111';c.fillRect(-30,-8,60,8);c.fillStyle=dt;c.fillRect(18,0,16,24);c.fillRect(-34,0,12,24);c.restore();c.stroke()
|
||||
// tank top
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(-22,-6);c.lineTo(24,-6);c.lineTo(26,-50);c.quadraticCurveTo(4,-60,-20,-52);c.closePath();c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(-30,-62,60,58);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(-4,-56);c.quadraticCurveTo(6,-40,16,-56);c.fill();c.stroke()
|
||||
c.restore()
|
||||
// head
|
||||
const tl=sin(i/20*T*2)
|
||||
c.save();c.translate(hx+8+lean*60,hy-100+b*.3);c.rotate(lean*.6);c.scale(.7,.7);c.translate(-128,-121)
|
||||
c.lineWidth=9
|
||||
for(const d of[0,1]){const w=8*sin(i/20*T*3+d)+tl*4;c.fillStyle='#111';c.beginPath();c.moveTo(70,92);c.quadraticCurveTo(40,84+d*16+w,8,90+d*28+w);c.lineTo(14,104+d*28+w);c.quadraticCurveTo(44,98+d*14,70,104);c.closePath();c.fill();c.stroke()}
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(62,176);c.lineTo(70,140);c.bezierCurveTo(44,70,88,30,130,30);c.bezierCurveTo(178,30,214,74,190,140);c.lineTo(198,174);c.lineTo(182,160);c.lineTo(178,182);c.lineTo(164,162);c.lineTo(92,162);c.lineTo(80,184);c.lineTo(76,160);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,110,138,-1,lt);eye(c,160,138,1,lt)
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(135+d*42+k-3,166),c.lineTo(135+d*42+k+3,158),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath()
|
||||
if(hit||j||x>0)c.moveTo(124,164),c.lineTo(152,162),c.quadraticCurveTo(148,184,138,184),c.quadraticCurveTo(126,184,124,164),c.fill(),c.stroke(),c.fillStyle='#fff',c.beginPath(),c.moveTo(128,165),c.lineTo(134,165),c.lineTo(131,172),c.fill()
|
||||
else c.moveTo(126,166),c.quadraticCurveTo(138,172,150,164),c.stroke(),c.fillStyle='#fff',c.beginPath(),c.moveTo(140,169),c.lineTo(146,167),c.lineTo(144,175),c.closePath(),c.fill(),c.lineWidth=3,c.stroke()
|
||||
c.lineWidth=9;c.fillStyle='#fff';c.beginPath();c.moveTo(72,120);c.lineTo(70,92);c.bezierCurveTo(84,52,172,52,186,92);c.lineTo(186,124);c.lineTo(174,104);c.lineTo(166,126);c.lineTo(154,100);c.lineTo(142,122);c.lineTo(130,98);c.lineTo(118,124);c.lineTo(106,100);c.lineTo(94,120);c.lineTo(86,102);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const[a,b2,e,f]of[[100,60,96,82],[150,58,156,80],[126,46,124,64]])c.beginPath(),c.moveTo(a,b2),c.quadraticCurveTo((a+e)/2+6,(b2+f)/2,e,f),c.stroke()
|
||||
c.fillStyle='#111';c.lineWidth=9;c.beginPath();c.moveTo(68,98);c.quadraticCurveTo(128,66,188,98);c.lineTo(188,84);c.quadraticCurveTo(128,52,68,84);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(84,86);c.quadraticCurveTo(128,64,172,86);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
// arms: lead (far) then rear (near); guard positions blend to full reach
|
||||
const sh=(px,py)=>[hx+px*cos(lean)-py*sin(lean),hy+b*0+px*sin(lean)+py*cos(lean)]
|
||||
for(const[e,s,g,r,el]of[[j,sh(16,-48),[150,124],[194,104],[34,-40]],[Math.max(0,x),sh(-6,-46),[134,140],[198,112],[18,-22]]]){
|
||||
const gx=g[0]+(r[0]-g[0])*e,gy=g[1]+(r[1]-g[1])*e+b,ex=s[0]+(gx-s[0])*.5+el[0]*(1-e)*.6,ey=s[1]+(gy-s[1])*.5+el[1]*(1-e)*-.6+18*(1-e)
|
||||
if(e>.9){c.lineWidth=4;for(const d of[-12,0,12])c.beginPath(),c.moveTo(gx-70+abs(d)*2,gy+d*1.4),c.lineTo(gx-30,gy+d*1.4),c.stroke()}
|
||||
if(e==1)burst(c,gx+18,gy,s[0]<hx?24:19)
|
||||
limb(c,[s,[ex,ey],[gx-14,gy]],13);glove(c,gx,gy,(1-e)*-.9)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// chibi in a bucket hat and long coat waits at a bus stop: taps her foot, checks her wristwatch, sighs as a leaf blows past
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=24,delay=80,rim=4
|
||||
const tone=(c,s,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle='#111';for(const[a,b]of[[0,0],[s,0],[0,s],[s,s],[s/2,s/2]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return c.createPattern(k,'repeat')}
|
||||
const ease=u=>(u=max(0,min(1,u)),u*u*(3-2*u))
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
const tap=t<.34,watch=t>=.34&&t<.68,sigh=t>=.7&&t<.92,ar=watch?ease((t-.34)/.08)*(1-ease((t-.6)/.08)):0
|
||||
// bus stop: pole, round sign with a bus, timetable
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(62,238,26,7,0,0,T);c.fill();c.stroke()
|
||||
for(const[l,s]of[[13,'#111'],[5,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(62,236),c.lineTo(62,60),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.arc(62,46,32,0,T);c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.arc(62,46,26,0,T);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(46,30,32,28,6);c.fill();c.fillStyle='#111';c.fillRect(50,35,10,9);c.fillRect(64,35,10,9);c.fillRect(48,50,28,2)
|
||||
c.fillStyle='#fff';for(const x of[52,72])c.beginPath(),c.arc(x,59,4,0,T),c.fill()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.rect(36,96,52,62);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.fillRect(42,102,40,8);c.lineWidth=2.5;for(let y=118;y<154;y+=8)c.beginPath(),c.moveTo(44,y),c.lineTo(58,y),c.moveTo(64,y),c.lineTo(80,y),c.stroke()
|
||||
// girl, small full body
|
||||
c.save();c.translate(158,112);c.scale(.62,.62);c.translate(-128,-120)
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.ellipse(128,322,62,10,0,0,T);c.fillStyle=FINE;c.fill()
|
||||
// legs and boots, the right one tapping
|
||||
const tp=tap?max(0,sin(t/.34*T*3))*.45:0
|
||||
c.fillStyle='#fff';c.lineWidth=8;for(const[x,a]of[[108,0],[148,tp]]){c.beginPath();c.rect(x-9,276,18,34);c.fill();c.stroke();c.save();c.translate(x-12,318);c.rotate(-a);c.fillStyle='#111';c.beginPath();c.moveTo(0,0);c.lineTo(0,-14);c.lineTo(26,-14);c.quadraticCurveTo(34,-12,34,0);c.closePath();c.fill();c.stroke();c.restore();c.fillStyle='#fff'}
|
||||
if(tp>.3){c.lineWidth=5;for(const k of[-1,0,1])c.beginPath(),c.moveTo(186+k*2,300+k*10),c.lineTo(198+k*4,296+k*12),c.stroke()}
|
||||
// long toned coat with buttons and belt
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(100,182);c.quadraticCurveTo(76,200,66,288);c.lineTo(190,288);c.quadraticCurveTo(180,200,156,182);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(128,190);c.lineTo(128,288);c.stroke();c.fillStyle='#111';c.fillRect(80,240,96,10);c.fillStyle='#fff';for(const y of[210,264])for(const x of[116,140])c.beginPath(),c.arc(x,y,5,0,T),c.fill(),c.stroke()
|
||||
// arms: left hangs, right comes up to read the watch
|
||||
for(const[l,s]of[[30,'#111'],[19,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(98,196),c.quadraticCurveTo(80,226,84,256),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=7;c.beginPath();c.arc(84,262,11,0,T);c.fill();c.stroke()
|
||||
// striped scarf with a tail
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.roundRect(96,172,64,22,10);c.fill();c.stroke();c.beginPath();c.moveTo(140,186);c.lineTo(150,236);c.lineTo(132,238);c.lineTo(126,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';for(const x of[110,128,146])c.fillRect(x-3,174,6,18);c.fillRect(132,206,16,5);c.fillRect(134,220,16,5)
|
||||
// hatched hair framing the face
|
||||
c.save();c.beginPath();c.moveTo(64,196);c.bezierCurveTo(44,76,86,40,128,40);c.bezierCurveTo(170,40,212,76,192,196);c.quadraticCurveTo(176,186,170,196);c.lineTo(86,196);c.quadraticCurveTo(80,186,64,196);c.closePath();c.fillStyle='#fff';c.fill();c.lineWidth=8;c.stroke();c.clip()
|
||||
c.lineWidth=3;for(let k=-140;k<160;k+=7)c.beginPath(),c.moveTo(k+40,40),c.lineTo(k+180,200),c.stroke();c.restore()
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
// face
|
||||
if(sigh){c.lineWidth=7;for(const x of[103,153])c.beginPath(),c.moveTo(x-14,146),c.quadraticCurveTo(x,154,x+14,146),c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.ellipse(128,170,5,4,0,0,T);c.stroke()}
|
||||
else{const[lx,ly]=watch?[4,6]:[-5,0];for(const[x,d]of[[103,-1],[153,1]]){
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,142,13,15,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,142,13,15,0,0,T);c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+lx,142+ly,10.5,13,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+lx-4,137+ly,4,5,0,0,T);c.fill();c.beginPath();c.arc(x+lx+4,148+ly,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,137);c.quadraticCurveTo(x,126,x+16,137);c.moveTo(x+d*14,134);c.lineTo(x+d*21,129);c.stroke()}
|
||||
c.lineWidth=5;c.beginPath();if(watch)c.moveTo(120,172),c.lineTo(136,170);else c.moveTo(120,170),c.quadraticCurveTo(128,166,136,170);c.stroke()}
|
||||
c.lineWidth=4;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*42+k-3,168),c.lineTo(128+d*42+k+3,160),c.stroke()
|
||||
if(watch){c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(186,100);c.quadraticCurveTo(198,118,192,128);c.quadraticCurveTo(180,128,182,116);c.closePath();c.fill();c.stroke()}
|
||||
// hatched bangs under the hat
|
||||
c.save();c.lineWidth=8;c.beginPath();c.moveTo(76,104);c.lineTo(180,104);c.quadraticCurveTo(178,124,168,128);c.quadraticCurveTo(160,116,150,122);c.quadraticCurveTo(140,112,128,124);c.quadraticCurveTo(116,112,104,122);c.quadraticCurveTo(94,116,86,128);c.quadraticCurveTo(76,122,76,104);c.closePath();c.fillStyle='#fff';c.fill();c.stroke();c.clip()
|
||||
c.lineWidth=3;for(let k=-60;k<120;k+=7)c.beginPath(),c.moveTo(k+70,100),c.lineTo(k+100,132),c.stroke();c.restore()
|
||||
// white bucket hat with stitch rings and an ink band
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.ellipse(128,102,82,18,0,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(80,100);c.bezierCurveTo(82,34,174,34,176,100);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(80,86);c.quadraticCurveTo(128,96,176,86);c.lineTo(176,98);c.quadraticCurveTo(128,108,80,98);c.closePath();c.fill()
|
||||
c.lineWidth=3;c.setLineDash([5,5]);c.beginPath();c.ellipse(128,104,70,12,0,.1*PI,.9*PI);c.stroke();c.beginPath();c.moveTo(92,74);c.quadraticCurveTo(128,60,164,74);c.stroke();c.setLineDash([])
|
||||
// the watch arm goes on top so the raised wrist isn't hidden by her hair
|
||||
const wx=184-30*ar,wy=256-110*ar
|
||||
for(const[l,s]of[[30,'#111'],[19,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(158,196),c.quadraticCurveTo(190-10*ar,226-50*ar,wx,wy),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=7;c.beginPath();c.arc(wx,wy+8-4*ar,11,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.roundRect(wx-13,wy-6,26,10,3);c.fill();c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(wx,wy-1,8,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(wx,wy-1);c.lineTo(wx,wy-6);c.moveTo(wx,wy-1);c.lineTo(wx+4,wy+1);c.stroke()
|
||||
c.restore()
|
||||
// sigh puff and the leaf blowing past
|
||||
if(sigh){const p=ease((t-.7)/.12);c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(136-18*p,146-6*p,5+7*p,4+4*p,0,0,T);c.fill();c.stroke()}
|
||||
if(t>=.5&&t<.98){const p=(t-.5)/.48,x=262-290*p,y=196-56*p+14*sin(p*T*2),a=p*T*2.5
|
||||
c.save();c.translate(x,y);c.rotate(a);c.scale(1.35,1.35);c.lineWidth=3.5;c.fillStyle='#fff';c.beginPath();c.moveTo(-14,0);c.quadraticCurveTo(0,-11,14,0);c.quadraticCurveTo(0,11,-14,0);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(-18,0);c.lineTo(12,0);for(const k of[-6,2])c.moveTo(k,0),c.lineTo(k+5,-5),c.moveTo(k,0),c.lineTo(k+5,5);c.stroke();c.restore()
|
||||
c.lineWidth=3;for(const k of[0,1])c.beginPath(),c.moveTo(x+22+k*8,y-6+k*10),c.lineTo(x+40+k*8,y-8+k*10),c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// girl with sleek ink hair kneeling at a low desk, writing the kanji 心 stroke by stroke with a big brush, a happy beat, then the ink fades for the next try
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max,hypot}=Math,T=2*PI
|
||||
export const frames=24,delay=90,rim=4
|
||||
const tile=createCanvas(6,6),q=tile.getContext('2d');q.fillStyle='#111';for(const[x,y]of[[1.5,1.5],[4.5,4.5]])q.beginPath(),q.arc(x,y,1.1,0,T),q.fill()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const C=[128,224],S=.95
|
||||
// strokes of 心 as points with brush radius, in stroke order
|
||||
const ST=[[[-30,-2,2],[-27,5,3.5],[-24,12,4.5]],[[-16,-15,4],[-15.5,4,4.5],[-9,15,4.5],[6,18,4],[21,13,3.5],[28,7,3],[22,1,1.5]],[[-3,-21,2],[0,-15,3.5],[4,-10,4.5]],[[20,-19,2],[24,-12,3.5],[28,-6,4.5]]]
|
||||
const P=ST.map(s=>s.map(([x,y,r])=>[C[0]+x*S,C[1]+y*S,r]))
|
||||
const len=s=>{let l=0;for(let k=1;k<s.length;k++)l+=hypot(s[k][0]-s[k-1][0],s[k][1]-s[k-1][1]);return l}
|
||||
// point at fraction p along a stroke
|
||||
const along=(s,p)=>{let d=p*len(s);for(let k=1;k<s.length;k++){const a=s[k-1],b=s[k],l=hypot(b[0]-a[0],b[1]-a[1]);if(d<=l||k==s.length-1){const f=min(1,d/l);return[a[0]+(b[0]-a[0])*f,a[1]+(b[1]-a[1])*f,a[2]+(b[2]-a[2])*f]}d-=l}}
|
||||
const ink=(c,s,p)=>{const l=len(s),n=Math.ceil(l*p);for(let k=0;k<=n;k++){const[x,y,r]=along(s,min(p,k/l));c.beginPath();c.arc(x,y,r,0,T);c.fill()}}
|
||||
// timeline: [start,end] of each stroke, the brush glides between them
|
||||
const W=[[.06,.14],[.18,.4],[.44,.5],[.54,.62]],REST=[218,212]
|
||||
const lerp=(a,b,f)=>[a[0]+(b[0]-a[0])*f,a[1]+(b[1]-a[1])*f]
|
||||
const brushAt=t=>{
|
||||
if(t<W[0][0])return lerp(REST,P[0][0],t/W[0][0])
|
||||
for(let k=0;k<4;k++){const[a,b]=W[k];if(t<a)return lerp(along(P[k-1],1),P[k][0],(t-W[k-1][1])/(a-W[k-1][1]));if(t<b)return along(P[k],(t-a)/(b-a))}
|
||||
return t<.72?lerp(along(P[3],1),REST,(t-.62)/.1):REST
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),done=t>=.66&&t<.9,fade=t<.88?1:1-(t-.88)/.12,[bx,by]=brushAt(t)
|
||||
c.save();c.translate(128,-6);c.scale(.86,.86);c.translate(-128,0)
|
||||
// ink hair: sleek, center parted, sides down past the ears
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,206,100,196,170);c.lineTo(60,170);c.bezierCurveTo(50,100,60,36,128,36);c.fill();c.stroke()
|
||||
// white kimono with a dotted collar crossing
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,186);c.quadraticCurveTo(58,196,48,236);c.lineTo(208,236);c.quadraticCurveTo(198,196,156,186);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(112,186);c.lineTo(132,236);c.lineTo(146,236);c.lineTo(124,186);c.closePath();toned(c,dot)
|
||||
c.beginPath();c.moveTo(144,186);c.lineTo(118,222);c.lineTo(108,214);c.lineTo(132,186);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,22);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,166);c.lineTo(116,184);c.moveTo(140,166);c.lineTo(140,184);c.stroke()
|
||||
// white ribbon on the low ponytail, peeking by her neck
|
||||
c.lineWidth=4;for(const r of[-.5,.6])c.beginPath(),c.ellipse(186,176,13,7,r,0,T),c.fill(),c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
// eyes cast down at the paper, or shut happy when the kanji is done
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
if(done){c.lineWidth=5;c.beginPath();c.arc(x,144,12,1.15*PI,1.85*PI);c.stroke();continue}
|
||||
c.save();c.beginPath();c.rect(x-16,138,32,24);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,146,12,13,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,152,9,11,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-3,148,3,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-14,139);c.quadraticCurveTo(x,135,x+14,139);c.lineTo(x+d*19,143);c.stroke()
|
||||
c.lineWidth=2.5;for(const k of[-6,0,6])c.beginPath(),c.moveTo(x+k,139),c.lineTo(x+k*1.3,134),c.stroke()
|
||||
}
|
||||
c.lineWidth=3.5;for(const[x,d]of[[103,1],[153,-1]])c.beginPath(),c.moveTo(x-12,120+d*3),c.lineTo(x+12,120-d*3),c.stroke()
|
||||
c.lineWidth=3;c.beginPath();if(done)c.arc(128,166,6,.15*PI,.85*PI);else c.moveTo(123,170),c.lineTo(133,170);c.stroke()
|
||||
if(done){c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<3;k++)c.beginPath(),c.moveTo(128+d*42+(k-1)*5-3,166),c.lineTo(128+d*42+(k-1)*5+3,158),c.stroke()}
|
||||
// sleek ink bangs parted in the middle, white shine
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(70,150);c.bezierCurveTo(54,76,88,38,128,38);c.bezierCurveTo(168,38,202,76,186,150)
|
||||
c.quadraticCurveTo(184,112,170,100);c.quadraticCurveTo(144,88,128,64);c.quadraticCurveTo(112,88,86,100);c.quadraticCurveTo(72,112,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<=22;k++){const a=PI*(1.24+k*.024);c.lineTo(128+(k%2?54:50)*cos(a),106+(k%2?54:50)*sin(a))}for(let k=22;k>=0;k--){const a=PI*(1.24+k*.024);c.lineTo(128+(k%2?47:43)*cos(a),106+(k%2?47:43)*sin(a))}c.fill()
|
||||
c.restore()
|
||||
// low desk with a felt mat, paper, paperweight and inkstone
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(16,184);c.lineTo(240,184);c.lineTo(250,250);c.lineTo(6,250);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.rect(56,188,144,62);toned(c,dot)
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(66,194,124,56);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.rect(66,190,124,9);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.roundRect(204,200,32,42,5);c.fill();c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.roundRect(209,205,22,32,4);c.stroke();c.strokeStyle='#111'
|
||||
// the kanji so far, fading out at the end of the loop
|
||||
c.globalAlpha=fade;c.fillStyle='#111'
|
||||
for(let k=0;k<4;k++){const[a,b]=W[k];if(t>=a)ink(c,P[k],min(1,(t-a)/(b-a)))}
|
||||
c.globalAlpha=1
|
||||
if(done){c.fillStyle='#fff';c.lineWidth=2.5;const s=6+3*sin(T*(t-.66)*4);for(const[x,y]of[[176,204],[78,242]]){c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,r=k%2?s*.35:s;c.lineTo(x+r*cos(a),y+r*sin(a))}c.closePath();c.fill();c.stroke()}}
|
||||
// writing arm and brush, the hand stays above the paper
|
||||
const hx=bx+12,hy=by-54
|
||||
for(const[l,s]of[[28,'#111'],[22,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(166,160),c.quadraticCurveTo((166+hx)/2+6,(160+hy)/2+18,hx+8,hy+4),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(bx-1,by-8);c.lineTo(bx+16,by-86);c.lineTo(bx+24,by-84);c.lineTo(bx+7,by-7);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const f of[.3,.62])c.beginPath(),c.moveTo(bx-1+17*f,by-8-78*f),c.lineTo(bx+7+17*f,by-7-77*f),c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(bx-2,by-10);c.quadraticCurveTo(bx-3,by-2,bx,by+1);c.quadraticCurveTo(bx+6,by-2,bx+8,by-9);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(hx,hy,12,13,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(hx-8,hy-4);c.quadraticCurveTo(hx,hy-1,hx+8,hy-5);c.moveTo(hx-8,hy+4);c.quadraticCurveTo(hx,hy+7,hx+8,hy+3);c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// puzzled girl with fluffy curly light-tone hair tilting her head, finger on her cheek, question marks orbiting her head, one blink
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=14,delay=110,rim=4
|
||||
const LT=(()=>{const t=createCanvas(6,6),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(3,3,1.1,0,T);x.fill();return t})()
|
||||
const toned=(c,path)=>{path();c.fillStyle='#fff';c.fill();c.fillStyle=c.createPattern(LT,'repeat');c.fill();path();c.stroke()}
|
||||
// curls: scallops bulging outward around an ellipse from angle a0 to a1
|
||||
const curls=(c,cx,cy,rx,ry,a0,a1,n,b)=>{for(let k=0;k<=n;k++){const a=a0+(a1-a0)*k/n,x=cx+rx*cos(a),y=cy+ry*sin(a)
|
||||
if(!k){c.lineTo(x,y);continue}const m=a-(a1-a0)/n/2;c.quadraticCurveTo(cx+(rx+b)*cos(m),cy+(ry+b)*sin(m),x,y)}}
|
||||
const qm=(c,x,y,s,a)=>{c.save();c.translate(x,y);c.rotate(a);c.scale(s,s);c.beginPath();c.moveTo(-9,-8);c.bezierCurveTo(-9,-22,12,-22,11,-9);c.bezierCurveTo(10,-1,1,0,1,8);for(const[l,st]of[[11,'#111'],[4.5,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3;c.strokeStyle='#111';c.beginPath();c.arc(1,19,4.5,0,T);c.fill();c.stroke();c.restore()}
|
||||
export default(c,t,i)=>{
|
||||
const r=.17*sin(t*T),shut=i==8,P=(x,y)=>[128+(x-128)*cos(r)-(y-186)*sin(r),186+(x-128)*sin(r)+(y-186)*cos(r)]
|
||||
const Q=[0,1,2].map(k=>{const a=t*T+k*T/3;return[128+86*cos(a),34+14*sin(a),.72+.22*sin(a),sin(a),.3*sin(a*2)]})
|
||||
for(const q of Q)if(q[3]<0)qm(c,q[0],q[1],q[2],q[4])
|
||||
c.lineWidth=6;c.fillStyle='#fff'
|
||||
c.beginPath();c.moveTo(100,192);c.quadraticCurveTo(66,198,60,250);c.lineTo(196,250);c.quadraticCurveTo(190,198,156,192);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.moveTo(128,196),c.lineTo(128+d*26,190),c.quadraticCurveTo(128+d*24,206,128+d*8,210),c.closePath(),c.fill(),c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,26);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.save();c.translate(128,186);c.rotate(r);c.translate(-128,-186)
|
||||
c.lineWidth=6;toned(c,()=>{c.beginPath();curls(c,128,110,70,62,PI*.7,PI*2.3,11,14);c.closePath()})
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){const y=140
|
||||
if(shut){c.lineWidth=5;c.beginPath();c.arc(x,y-4,11,.15*PI,.85*PI);c.stroke();continue}
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,12,15,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+3,y+1,9,12,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x,y-4,4.5,0,T);c.fill();c.beginPath();c.arc(x+6,y+6,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-14,y-7);c.quadraticCurveTo(x,y-18,x+14,y-7);c.moveTo(x+d*13,y-8);c.lineTo(x+d*19,y-12);c.stroke()}
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(94,116);c.quadraticCurveTo(104,110,114,116);c.moveTo(144,114);c.quadraticCurveTo(154,108,164,112);c.stroke()
|
||||
c.lineWidth=2.2;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-3,165),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128,156);c.lineTo(127,159);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(121,170);c.quadraticCurveTo(126,166,130,169);c.quadraticCurveTo(134,172,137,168);c.stroke()
|
||||
c.lineWidth=6;toned(c,()=>{c.beginPath();c.moveTo(70,150);curls(c,128,104,60,58,PI*.9,PI*2.1,8,14);c.lineTo(186,150);c.quadraticCurveTo(184,124,176,112);curls(c,128,138,48,30,PI*1.9,PI*1.1,5,-12);c.quadraticCurveTo(72,124,70,150);c.closePath()})
|
||||
c.strokeStyle='#fff';c.lineWidth=7;c.beginPath();c.arc(128,108,52,PI*1.28,PI*1.45);c.stroke();c.beginPath();c.arc(128,108,52,PI*1.55,PI*1.72);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(176,82,8,0,T);c.fill();c.stroke();c.beginPath();c.arc(176,82,3,0,T);c.stroke()
|
||||
c.restore()
|
||||
const[hx,hy]=P(96,162)
|
||||
for(const[l,s]of[[24,'#111'],[14,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(76,252),c.quadraticCurveTo(66,220,hx-12,hy+38),c.stroke()
|
||||
c.save();c.translate(hx,hy);c.rotate(r+.35);c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4.5
|
||||
c.beginPath();c.roundRect(-5,-4,10,30,5);c.fill();c.stroke();c.beginPath();c.ellipse(-6,32,15,13,.3,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(-3,8,6,14,3);c.fill();c.lineWidth=2.5;c.beginPath();c.moveTo(-16,28);c.quadraticCurveTo(-8,32,-2,28);c.moveTo(-18,36);c.quadraticCurveTo(-10,40,-2,36);c.stroke();c.restore()
|
||||
for(const q of Q)if(q[3]>=0)qm(c,q[0],q[1],q[2],q[4])
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// beanie girl at a claw machine: the claw drops, grabs a bear plush, lifts it, it slips back onto the pile and she groans
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=24,delay=80,rim=4
|
||||
const tone=(c,s,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle='#111';for(const[a,b]of[[0,0],[s,0],[0,s],[s,s],[s/2,s/2]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return c.createPattern(k,'repeat')}
|
||||
const ease=u=>(u=max(0,min(1,u)),u*u*(3-2*u))
|
||||
const bear=(c,x,y,a=0)=>{c.save();c.translate(x,y);c.rotate(a);c.lineWidth=4;c.fillStyle='#fff'
|
||||
for(const d of[-1,1])c.beginPath(),c.arc(d*11,-11,6,0,T),c.fill(),c.stroke()
|
||||
c.beginPath();c.ellipse(0,14,12,10,0,0,T);c.fill();c.stroke();c.beginPath();c.arc(0,0,14,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*5,-1,2.2,0,T),c.fill();c.beginPath();c.ellipse(0,5,3,2,0,0,T);c.fill();c.restore()}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
// claw timeline: drop, close, lift, wobble, the bear slips back, the claw opens again
|
||||
const ry=t<.18?64+64*ease(t/.18):t<.26?128:t<.46?128-64*ease((t-.26)/.2):64
|
||||
const open=t<.18?1:t<.26?1-.65*ease((t-.18)/.08):t<.52?.35:t<.72?.35+.65*ease((t-.52)/.2):1
|
||||
const wob=t>=.44&&t<.52?3*sin((t-.44)*T*25):0,cx=190+wob
|
||||
const held=t>=.18&&t<.5,fall=t>=.5&&t<.6?ease((t-.5)/.1):1,by=held?ry+34:t>=.5&&t<.6?ry+34+(150-ry-34)*fall*fall:150
|
||||
const sad=t>=.6&&t<.92,shock=t>=.5&&t<.6,hope=t<.5
|
||||
// cabinet
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.roundRect(108,8,140,234,12);c.fill();c.stroke()
|
||||
c.beginPath();c.roundRect(114,14,128,26,8);c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3;for(let k=0;k<6;k++){const on=(k+Math.floor(t*12))%2;c.beginPath();c.arc(124+k*21.5,27,on?5:3.5,0,T);c.fill();c.stroke()}
|
||||
c.lineWidth=6;c.beginPath();c.rect(120,46,116,124);c.fillStyle='#fff';c.fill();c.stroke()
|
||||
c.save();c.beginPath();c.rect(120,46,116,124);c.clip()
|
||||
// rail, carriage and rope
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(120,54);c.lineTo(236,54);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.roundRect(cx-12,50,24,10,3);c.fill()
|
||||
for(const[l,s]of[[6,'#111'],[2,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(cx,60),c.lineTo(cx,ry),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// prize pile: a bunny, a star ball, a cat, plus the bear when it's down
|
||||
c.lineWidth=4;c.fillStyle='#fff'
|
||||
c.beginPath();c.arc(146,158,16,0,T);c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.ellipse(146+d*7,134,5,12,d*.2,0,T),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(146,148,12,0,T);c.fill();c.stroke();c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(146+d*4,147,1.8,0,T),c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(222,156,17,0,T);c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<10;k++){const r=k%2?4:9,b=k*PI/5-PI/2;c.lineTo(222+cos(b)*r,156+sin(b)*r)}c.closePath();c.fill();c.lineWidth=2.5;c.stroke()
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.arc(168,166,13,0,T);c.fill();c.stroke()
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(168+d*12,160),c.lineTo(168+d*10,148),c.lineTo(168+d*3,155),c.fill(),c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(168+d*4,166,1.8,0,T),c.fill()
|
||||
bear(c,held?cx:190,by,held?wob*.04:t>=.5&&t<.6?fall*1.2:0)
|
||||
if(t>=.58&&t<.66){c.lineWidth=3;for(const d of[-1,1])c.beginPath(),c.moveTo(190+d*20,160),c.lineTo(190+d*30,152),c.stroke()}
|
||||
// claw head and prongs
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.ellipse(cx,ry+4,12,8,0,PI,T);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(cx+d*7,ry+4),c.lineTo(cx+d*(8+12*open),ry+22),c.lineTo(cx+d*(2+10*open),ry+32),c.stroke()
|
||||
// glass shine
|
||||
c.strokeStyle='#ccc';c.lineWidth=5;for(const[x,l]of[[130,40],[146,20]])c.beginPath(),c.moveTo(x,62+l),c.lineTo(x+l,62),c.stroke()
|
||||
c.restore();c.strokeStyle='#111'
|
||||
// control panel: joystick and button
|
||||
c.lineWidth=5;c.beginPath();c.roundRect(120,180,116,46,6);c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(204,214,14,6,0,0,T);c.fill();c.stroke()
|
||||
for(const[l,s]of[[9,'#111'],[3,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(204,214),c.lineTo(204+(hope?4*sin(t*T*6):0),194),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=4;c.fillStyle='#111';c.beginPath();c.arc(204+(hope?4*sin(t*T*6):0),192,7,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(220,196,6,0,T);c.fill();c.stroke()
|
||||
// girl in front, bust, a little smaller than full size
|
||||
const dy=sad?6:0
|
||||
c.save();c.translate(76,170+dy);c.scale(.8,.8);c.translate(-128,-130)
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(98,186);c.quadraticCurveTo(52,196,44,270);c.lineTo(212,270);c.quadraticCurveTo(204,196,158,186);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(92,190);c.quadraticCurveTo(128,222,164,190);c.quadraticCurveTo(128,206,92,190);c.fill();c.stroke()
|
||||
for(const d of[-1,1])c.lineWidth=4,c.beginPath(),c.moveTo(128+d*14,204),c.lineTo(128+d*16,236),c.stroke()
|
||||
// ink side tufts under the beanie
|
||||
c.lineWidth=7;c.fillStyle='#111';for(const d of[-1,1]){c.beginPath();c.moveTo(128+d*52,92);c.quadraticCurveTo(128+d*76,130,128+d*70,170);c.quadraticCurveTo(128+d*82,166,128+d*86,176);c.quadraticCurveTo(128+d*76,140,128+d*64,96);c.closePath();c.fill();c.stroke()}
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
// face
|
||||
if(sad){c.lineWidth=6;for(const d of[-1,1]){const x=128+d*25;c.beginPath();c.moveTo(x-d*12,130);c.lineTo(x+d*10,140);c.lineTo(x-d*12,150);c.stroke()}
|
||||
c.lineWidth=4.5;c.beginPath();c.moveTo(112,170);for(let k=1;k<=6;k++)c.lineTo(112+k*5.3,k%2?165:170);c.stroke()
|
||||
c.lineWidth=3;for(const x of[96,106,116,140,150,160])c.beginPath(),c.moveTo(x,112),c.lineTo(x,126),c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(186,112);c.quadraticCurveTo(196,128,190,136);c.quadraticCurveTo(180,136,182,126);c.closePath();c.fill();c.stroke()}
|
||||
else if(shock){c.lineWidth=5;c.fillStyle='#fff';for(const x of[103,153])c.beginPath(),c.ellipse(x,140,12,14,0,0,T),c.fill(),c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(128,170,7,9,0,0,T);c.fill()}
|
||||
else{const lx=hope?4:2;for(const[x,d]of[[103,-1],[153,1]]){
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+lx,138,10.5,14,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+lx-4,133,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+lx+4,146,2,0,T);c.fill()
|
||||
if(hope){c.save();c.translate(x+lx+3,141);c.beginPath();for(let k=0;k<8;k++){const r=k%2?1.4:4.5,b=k*PI/4;c.lineTo(cos(b)*r,sin(b)*r)}c.fill();c.restore()}
|
||||
c.restore();c.lineWidth=6;c.beginPath();c.moveTo(x-16,135);c.quadraticCurveTo(x,120,x+16,135);c.moveTo(x+d*14,132);c.lineTo(x+d*21,127);c.stroke()}
|
||||
c.lineWidth=4.5;c.beginPath();if(hope)c.arc(128,168,6,0,T);else c.moveTo(120,170),c.lineTo(136,170);c.stroke()}
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*42+k-3,166),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
// knit beanie: ribbed dome, toned band, pom-pom
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(62,112);c.bezierCurveTo(56,20,200,20,194,112);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.lineWidth=3;for(let x=74;x<190;x+=12)c.beginPath(),c.moveTo(x,96),c.quadraticCurveTo(128+(x-128)*.6,40,128+(x-128)*.2,30),c.stroke();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.roundRect(58,92,140,26,10);c.fillStyle='#fff';c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<16;k++){const r=k%2?13:17,b=k*PI/8;c.lineTo(128+cos(b)*r,26+sin(b)*r)}c.closePath();c.fill();c.lineWidth=5;c.stroke()
|
||||
// little ink bangs peeking under the band
|
||||
c.fillStyle='#111';c.lineWidth=5;for(const[x,w]of[[92,12],[112,14],[140,14],[162,12]])c.beginPath(),c.moveTo(x-w/2,116),c.quadraticCurveTo(x,132,x+w/2+2,116),c.fill(),c.stroke()
|
||||
// fists up while hoping
|
||||
if(hope){c.lineWidth=6;c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*30,206-4*Math.abs(sin(t*T*6)),15,13,0,0,T),c.fill(),c.stroke()}
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// flustered girl with dark-tone wavy hair and a white hairband frantically waving both palms "no no no", motion arcs, sweat drops flying, panicked eyes
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=70,rim=4
|
||||
const DK=(()=>{const t=createCanvas(5,5),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(2.5,2.5,1.7,0,T);x.fill();return t})()
|
||||
const toned=(c,path)=>{path();c.fillStyle='#fff';c.fill();c.fillStyle=c.createPattern(DK,'repeat');c.fill();path();c.stroke()}
|
||||
const hand=(c,x,y,a)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle='#fff';c.lineWidth=4.5
|
||||
for(const[fx,h]of[[-12,20],[-4,25],[4,24],[12,19]])c.beginPath(),c.roundRect(fx-4,-30-h,8,h+6,4),c.fill(),c.stroke()
|
||||
c.beginPath();c.roundRect(-16,-34,32,32,11);c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(-19,-14,6,11,-.6,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(-13,-31,26,26,9);c.fill()
|
||||
c.lineWidth=2;c.beginPath();c.moveTo(-6,-14);c.quadraticCurveTo(0,-10,6,-16);c.stroke();c.restore()}
|
||||
export default(c,t)=>{
|
||||
const w=sin(4*T*t),a=.45*w,dx=8*w
|
||||
c.lineWidth=6
|
||||
toned(c,()=>{c.beginPath();c.moveTo(72,90);c.bezierCurveTo(44,120,62,150,48,182);c.quadraticCurveTo(56,196,70,190);c.quadraticCurveTo(64,200,80,206);c.lineTo(176,206);c.quadraticCurveTo(192,200,186,190);c.quadraticCurveTo(200,196,208,182);c.bezierCurveTo(194,150,212,120,184,90);c.closePath()})
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,192);c.quadraticCurveTo(66,198,60,250);c.lineTo(196,250);c.quadraticCurveTo(190,198,156,192);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(110,192);c.quadraticCurveTo(128,208,146,192);c.stroke();c.beginPath();c.arc(128,214,4,0,T);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,26);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){const y=140
|
||||
c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.ellipse(x,y,12,15,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.arc(x+(t*16%2<1?1:-1),y+1,3.5,0,T);c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(x-13,y-9);c.quadraticCurveTo(x,y-19,x+13,y-9);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(x+d*12,y-24);c.lineTo(x-d*8,y-31);c.stroke()}
|
||||
c.lineWidth=2.2;for(const d of[-1,1])for(let k=0;k<4;k++)c.beginPath(),c.moveTo(128+d*44+k*5-10,166),c.lineTo(128+d*44+k*5-4,157),c.stroke()
|
||||
c.fillStyle='#111';c.lineWidth=3;c.beginPath();c.moveTo(115,168);for(let k=1;k<=6;k++)c.lineTo(115+k*4.3,168+(k%2?-3:0));c.quadraticCurveTo(128,186,115,168);c.closePath();c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.ellipse(128,177,4,2.5,0,0,T);c.fill()
|
||||
c.lineWidth=6
|
||||
toned(c,()=>{c.beginPath();c.moveTo(70,150);c.bezierCurveTo(54,74,88,40,128,40);c.bezierCurveTo(168,40,202,74,186,150);c.quadraticCurveTo(184,120,176,106);c.quadraticCurveTo(168,116,160,104);c.quadraticCurveTo(150,118,142,104);c.quadraticCurveTo(134,116,124,104);c.quadraticCurveTo(114,118,106,104);c.quadraticCurveTo(96,116,88,104);c.quadraticCurveTo(80,114,78,106);c.quadraticCurveTo(72,124,70,150);c.closePath()})
|
||||
c.strokeStyle='#fff';c.lineWidth=7;c.beginPath();c.arc(128,110,58,PI*1.25,PI*1.45);c.stroke();c.beginPath();c.arc(128,110,58,PI*1.55,PI*1.75);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(74,92);c.bezierCurveTo(84,42,172,42,182,92);c.lineTo(174,94);c.bezierCurveTo(166,54,90,54,82,94);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3.5
|
||||
for(const d of[-1,1])for(const o of[0,.5]){const p=(t*2+o)%1,s=min(1,p*5,(1-p)*3)
|
||||
if(s>.15){c.save();c.translate(128+d*(70+p*30),100-p*26);c.rotate(-d*(PI/2+.5));c.scale(s,s);c.beginPath();c.moveTo(0,-11);c.quadraticCurveTo(8,0,7,4);c.arc(0,4,7,0,PI);c.quadraticCurveTo(-8,0,0,-11);c.fill();c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(-3,-18);c.lineTo(-3,-26);c.moveTo(3,-18);c.lineTo(3,-24);c.stroke();c.restore()}}
|
||||
for(const d of[-1,1]){const hx=128+d*64+dx,hy=212
|
||||
for(const[l,s]of[[24,'#111'],[14,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128+d*38,240),c.quadraticCurveTo(128+d*60,250,hx,hy+4),c.stroke()
|
||||
c.strokeStyle='#111';hand(c,hx,hy,a)
|
||||
if(abs(w)>.6){c.lineWidth=3;const sd=w>0?1:-1;for(const r of[44,54])c.beginPath(),c.arc(hx,hy,r,-PI/2-sd*.9,-PI/2-sd*.35,sd<0),c.stroke()}}
|
||||
}
|
||||
const abs=Math.abs
|
||||
@@ -0,0 +1,44 @@
|
||||
// fired-up girl with an ink high ponytail: fighting-spirit flames roar up behind her, fist pumped, flames burning in her eyes, shouting
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=12,delay=80,rim=4
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const DOT=(()=>{const t=createCanvas(6,6),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(3,3,1.6,0,T);x.fill();return t})()
|
||||
// fire tongues: base point, tip point, width, phase; each is a hooked teardrop and they all merge into one blaze
|
||||
const P=[[62,230,14,118,26,0],[72,206,30,56,28,.25],[98,180,72,18,30,.5],[128,170,128,4,32,.75],[158,180,184,18,30,.1],[184,206,226,56,28,.35],[194,230,242,118,26,.6]]
|
||||
const blaze=(c,t,k)=>{c.beginPath();for(const[bx,by,tx,ty,w,o]of P){const sx=tx+8*sin(T*(2*t+o)),sy=ty+(1-k)*60+10*sin(T*(2*t+o)+1),W=w*k,h=by-sy,e=tx<128?-1:tx>128?1:0
|
||||
c.moveTo(bx-W,by);c.bezierCurveTo(bx-W,by-h*.5,sx-W*.7+e*10,sy+h*.35,sx,sy);c.bezierCurveTo(sx+W*.4,sy+h*.4,bx+W*1.1,by-h*.45,bx+W,by);c.closePath()}}
|
||||
const flame=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x+r*.9,y-r*.1,x+r*.5,y+r*.6);c.quadraticCurveTo(x,y+r*.9,x-r*.5,y+r*.6);c.quadraticCurveTo(x-r*.9,y-r*.1,x,y-r);c.closePath()}
|
||||
export default(c,t)=>{
|
||||
c.lineWidth=14;blaze(c,t,1);c.stroke();c.fillStyle='#fff';c.fill()
|
||||
c.fillStyle=c.createPattern(DOT,'repeat');blaze(c,t+.15,.55);c.fill();c.lineWidth=3;c.stroke()
|
||||
c.lineWidth=6;c.strokeStyle='#111'
|
||||
const sw=.12*sin(T*t)
|
||||
c.save();c.translate(166,52);c.rotate(-.15+sw);c.fillStyle='#111';c.beginPath();c.moveTo(-4,-8);c.bezierCurveTo(40,-30,70,-10,84,-40);c.lineTo(80,-20);c.lineTo(92,-26);c.quadraticCurveTo(80,6,56,14);c.lineTo(62,22);c.quadraticCurveTo(30,30,0,16);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(14,-2);c.bezierCurveTo(34,-14,52,-8,68,-22);c.moveTo(16,8);c.bezierCurveTo(32,4,46,6,60,0);c.stroke();c.restore()
|
||||
c.strokeStyle='#111';c.lineWidth=6
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(72,152);c.bezierCurveTo(52,70,88,36,128,36);c.bezierCurveTo(168,36,204,70,184,152);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(66,198,60,250);c.lineTo(196,250);c.quadraticCurveTo(190,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(96,190);c.lineTo(128,226);c.lineTo(160,190);c.lineTo(178,206);c.lineTo(128,240);c.lineTo(78,206);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(128,226);c.lineTo(114,236);c.lineTo(118,250);c.lineTo(128,240);c.lineTo(138,250);c.lineTo(142,236);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.rect(116,166,24,26);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){const y=142
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(x-15,y-6);c.lineTo(x+15,y-6+d*0);c.quadraticCurveTo(x+14,y+16,x,y+16);c.quadraticCurveTo(x-14,y+16,x-15,y-6);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+4,9,12,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';flame(c,x,y+5,7+1.5*sin(T*(t*3)+d));c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-d*16,y-4);c.lineTo(x+d*16,y-9);c.stroke();c.lineWidth=4;c.beginPath();c.moveTo(x+d*15,y-9);c.lineTo(x+d*21,y-14);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(x-d*12,y-16);c.lineTo(x+d*14,y-26);c.lineTo(x+d*16,y-20);c.lineTo(x-d*10,y-11);c.closePath();c.fill()}
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128,157);c.lineTo(127,160);c.stroke()
|
||||
const m=4*sin(2*T*t)
|
||||
c.fillStyle='#111';c.lineWidth=3.5;c.beginPath();c.moveTo(114,166);c.lineTo(142,166);c.quadraticCurveTo(140,182+m,128,182+m);c.quadraticCurveTo(116,182+m,114,166);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(117,167,22,4);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(66,154);c.bezierCurveTo(52,74,88,38,128,38);c.bezierCurveTo(168,38,204,74,190,154);c.quadraticCurveTo(186,118,178,104);c.lineTo(170,116);c.lineTo(164,100);c.lineTo(152,116);c.lineTo(144,98);c.lineTo(132,114);c.lineTo(124,96);c.lineTo(112,114);c.lineTo(104,98);c.lineTo(92,116);c.lineTo(86,102);c.lineTo(78,114);c.quadraticCurveTo(70,128,66,154);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=6;c.beginPath();c.arc(128,110,58,PI*1.25,PI*1.42);c.stroke();c.beginPath();c.arc(128,110,58,PI*1.55,PI*1.72);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(158,52,14,12,4);c.fill();c.stroke()
|
||||
const fy=-8*max(0,sin(2*T*t))
|
||||
for(const[l,s]of[[30,'#111'],[19,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(178,252),c.quadraticCurveTo(204,232,206,186+fy),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.roundRect(186,150+fy,40,40,12);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const y of[160,170,180])c.moveTo(190,y+fy),c.lineTo(212,y+fy);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(188,158+fy);c.quadraticCurveTo(206,164+fy,214,182+fy);c.stroke()
|
||||
if(fy<-3)for(const[x0,y0,x1,y1]of[[232,150,244,142],[236,166,248,166],[230,182,242,190]])for(const[l,s]of[[8,'#fff'],[4,'#111']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(x0,y0+fy),c.lineTo(x1,y1+fy),c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// flustered girl with dotted low pigtails: her ink heart pops out in front of her chest and pounds doki-doki, beat lines flaring on each thump
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,exp}=Math,T=2*PI
|
||||
export const frames=16,delay=70,rim=4
|
||||
const DOT=(()=>{const t=createCanvas(6,6),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(3,3,1.5,0,T);x.fill();return t})()
|
||||
const toned=(c,path,shine)=>{path();c.fillStyle='#fff';c.fill();c.fillStyle=c.createPattern(DOT,'repeat');c.fill();if(shine){c.save();c.clip();shine();c.restore()}path();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const p=t*2%1,b=exp(-(((p-.06)/.07)**2))+.75*exp(-(((p-.3)/.07)**2)),s=.8*(1+.24*b),j=b>.4?1:0
|
||||
c.lineWidth=6
|
||||
for(const d of[-1,1]){c.save();c.translate(128,0);c.scale(d,1)
|
||||
toned(c,()=>{c.beginPath();c.moveTo(54,144);c.bezierCurveTo(94,164,100,214,84,244);c.quadraticCurveTo(72,232,60,238);c.quadraticCurveTo(70,204,44,172);c.closePath()})
|
||||
c.restore()}
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(66,198,62,250);c.lineTo(194,250);c.quadraticCurveTo(190,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(100,192);c.lineTo(128,232);c.lineTo(156,192);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,26);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){const y=140
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+1,10,14,0,0,T);c.clip();c.fillStyle=c.createPattern(DOT,'repeat');c.fillRect(x-12,y-14,24,30);c.fillStyle='#111';c.fillRect(x-12,y-14,24,8);c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+1,10,14,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,4.5,6,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-3+j,y-4,4,0,T);c.fill();c.beginPath();c.arc(x+4-j,y+7,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-15,y-6);c.quadraticCurveTo(x,y-18,x+15,y-6);c.moveTo(x+d*14,y-7);c.lineTo(x+d*20,y-12);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(x+d*11,y-22);c.lineTo(x-d*9,y-28);c.stroke()}
|
||||
c.lineWidth=2.2;for(const d of[-1,1])for(let k=0;k<5;k++)c.beginPath(),c.moveTo(128+d*42+k*5-13,167),c.lineTo(128+d*42+k*5-7,156),c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128,156);c.lineTo(127,159);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(117,170);for(let k=1;k<=6;k++)c.lineTo(117+k*3.7,170+(k%2?-2.4:1.2));c.stroke()
|
||||
c.lineWidth=6
|
||||
toned(c,()=>{c.beginPath();c.moveTo(68,152);c.bezierCurveTo(52,74,88,36,128,36);c.bezierCurveTo(168,36,204,74,188,152);c.quadraticCurveTo(186,118,178,104);c.lineTo(168,114);c.lineTo(160,98);c.lineTo(148,112);c.lineTo(138,96);c.lineTo(126,112);c.lineTo(114,98);c.lineTo(104,112);c.lineTo(94,100);c.lineTo(82,112);c.quadraticCurveTo(72,126,68,152);c.closePath()},
|
||||
()=>{c.strokeStyle='#fff';c.lineWidth=8;c.beginPath();c.arc(128,110,58,PI*1.22,PI*1.4);c.stroke();c.beginPath();c.arc(128,110,58,PI*1.5,PI*1.8);c.stroke()})
|
||||
c.fillStyle='#fff';c.lineWidth=4
|
||||
for(const d of[-1,1]){const x=128+d*62,y=158
|
||||
for(const e of[-1,1])c.beginPath(),c.moveTo(x,y),c.lineTo(x+e*13,y-9),c.lineTo(x+e*13,y+9),c.closePath(),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(x,y,5,0,T);c.fill();c.stroke()}
|
||||
c.save();c.translate(128,222);c.scale(s,s);c.lineWidth=6/s;c.beginPath();c.moveTo(0,22);c.bezierCurveTo(-40,-4,-26,-36,0,-18);c.bezierCurveTo(26,-36,40,-4,0,22);c.closePath()
|
||||
c.fillStyle='#111';c.fill();c.stroke();c.strokeStyle='#fff';c.lineWidth=5/s;c.beginPath();c.arc(-15,-12,9,PI*1.05,PI*1.65);c.stroke();c.restore()
|
||||
if(b>.12)for(const d of[-1,1])for(const a of[-.6,0,.6]){const r1=40,r2=44+18*b
|
||||
for(const[l,st]of[[9,'#fff'],[4,'#111']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(128+d*cos(a)*r1,220+sin(a)*r1*.8),c.lineTo(128+d*cos(a)*r2,220+sin(a)*r2*.8),c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// chibi fencer with a screentone bun lunges from en garde, her foil bending into the hanging target with a touche spark
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs,min,max}=Math,T=2*PI
|
||||
export const frames=16,delay=70,rim=4
|
||||
const tile=(s,col,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[s/2,s/2],[0,0],[s,0],[0,s],[s,s]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const DOT=tile(6,'#111',1.3),SOFT=tile(5,'#111',.9),LITE=tile(5,'#fff',1)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u),mix=(a,b,k)=>a.map((v,n)=>v+(b[n]-v)*k)
|
||||
const limb=(c,p,w)=>{for(const[l,s]of[[w+8,'#111'],[w,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...p[0]);for(const q of p.slice(1))c.lineTo(...q);c.stroke()}c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d,lt)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,12,15,0,0,T);c.fill()
|
||||
c.save();c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+5,y+3,9,12,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-6,y+6,22,12)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+2,y-2,4,0,T);c.fill();c.beginPath();c.arc(x+9,y+9,1.8,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-14,y-10);c.quadraticCurveTo(x,y-18,x+14,y-10);c.lineTo(x+d*20,y-16);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(x-12,y-30);c.quadraticCurveTo(x,y-34,x+12,y-30-d*2);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const dt=c.createPattern(DOT,'repeat'),sf=c.createPattern(SOFT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const p=t<.25?0:t<.38?ease((t-.25)/.13):t<.62?1:1-ease((t-.62)/.3),hit=t>=.36&&t<.62,fl=hit?sin(min(1,(t-.36)/.1)*PI/2)*(1-.6*max(0,(t-.46)/.16)):0
|
||||
const st=t<.25?3*abs(sin(t*T*4)):0
|
||||
const H=mix([96,170-st],[116,184],p),lean=.16*p
|
||||
// hanging target
|
||||
const sw=.03*sin(t*T)+(hit?.05*sin((t-.36)*T*3)*(1-(t-.36)/.26):0),tx=226+80*sin(sw),ty=-10+80*cos(sw)+30
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(226,6);c.lineTo(tx,ty-18);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(tx,ty,15,0,T);c.fill();c.stroke();c.fillStyle=dt;c.beginPath();c.arc(tx,ty,10,0,T);c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.arc(tx,ty,5,0,T);c.fill()
|
||||
// rear leg, front leg
|
||||
const rk=mix([72,206],[78,212],p),rf=mix([58,234],[40,236],p),fk=mix([128,204],[168,206],p),ff=mix([142,236],[178,236],p)
|
||||
for(const[k,f,d]of[[rk,rf,-1],[fk,ff,1]]){limb(c,[[H[0]+d*8,H[1]],k,f],14)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(f[0]+d*6,f[1]+4,15,8,0,0,T);c.fill();c.stroke()}
|
||||
// knickers
|
||||
c.save();c.translate(...H);c.rotate(lean)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-22,-8);c.lineTo(22,-8);c.lineTo(26,14);c.lineTo(-26,14);c.closePath();c.fill();c.save();c.clip();c.fillStyle=sf;c.fillRect(-30,-8,60,24);c.restore();c.stroke()
|
||||
// jacket
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(-22,-4);c.lineTo(24,-4);c.lineTo(26,-50);c.quadraticCurveTo(2,-60,-22,-50);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(8,-56);c.lineTo(20,-6);c.stroke();c.save();c.beginPath();c.moveTo(-22,-4);c.lineTo(-4,-4);c.lineTo(-10,-50);c.lineTo(-22,-48);c.closePath();c.clip();c.fillStyle=dt;c.fillRect(-30,-60,40,60);c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(-10,-62,22,10,4);c.fill();c.stroke()
|
||||
c.restore()
|
||||
const sh=(x,y)=>[H[0]+x*cos(lean)-y*sin(lean),H[1]+x*sin(lean)+y*cos(lean)]
|
||||
// rear arm: curled up behind the head in guard, thrown straight back on the lunge
|
||||
const rs=sh(-14,-46),re=mix([rs[0]-24,rs[1]-26],[rs[0]-28,rs[1]+6],p),rh=mix([rs[0]-6,rs[1]-56],[rs[0]-56,rs[1]+16],p)
|
||||
limb(c,[rs,re,rh],12);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(...rh,8,0,T);c.fill();c.stroke()
|
||||
// head
|
||||
c.save();c.translate(...sh(8,-98));c.rotate(lean*.5);c.scale(.66,.66);c.translate(-128,-121)
|
||||
c.lineWidth=9;c.fillStyle='#fff'
|
||||
c.beginPath();c.arc(62,96,30,0,T);c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(20,60,90,80);c.restore();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.arc(62,96,18,.4,2.6);c.stroke()
|
||||
c.lineWidth=9;c.beginPath();c.moveTo(70,150);c.bezierCurveTo(52,70,90,36,130,36);c.bezierCurveTo(176,36,206,74,190,140);c.lineTo(160,150);c.closePath();c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(40,20,180,140);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,112,140,-1,lt);eye(c,160,140,1,lt)
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(136+d*42+k-3,166),c.lineTo(136+d*42+k+3,158),c.stroke()
|
||||
c.lineWidth=5;c.beginPath();if(hit)c.moveTo(128,166),c.quadraticCurveTo(140,178,152,164);else c.moveTo(132,170),c.lineTo(146,169);c.stroke()
|
||||
c.lineWidth=9;c.fillStyle='#fff';c.beginPath();c.moveTo(74,124);c.bezierCurveTo(70,70,110,46,150,50);c.bezierCurveTo(176,54,190,76,188,118);c.lineTo(176,98);c.lineTo(166,112);c.quadraticCurveTo(156,90,140,100);c.lineTo(134,114);c.quadraticCurveTo(122,92,106,104);c.lineTo(100,118);c.quadraticCurveTo(92,104,86,120);c.lineTo(80,108);c.lineTo(74,124);c.closePath();c.fill()
|
||||
c.save();c.clip();c.fillStyle=dt;c.fillRect(60,30,140,100);c.restore();c.stroke();c.strokeStyle='#fff';c.lineWidth=7;for(const[a,e]of[[1.2,1.32],[1.4,1.55],[1.63,1.74]])c.beginPath(),c.arc(130,118,58,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
// sword arm, guard, foil
|
||||
const ss=sh(14,-46),hd=mix([150,144-st],[168,132],p),el=mix([ss[0]+18,ss[1]+22],[(ss[0]+hd[0])/2,(ss[1]+hd[1])/2-2],p)
|
||||
const tip=hit?[tx-13,ty+3]:mix([hd[0]+62,hd[1]-26],[hd[0]+64,hd[1]-6],p),mx=(hd[0]+tip[0])/2,my=(hd[1]+tip[1])/2-34*fl
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(...hd);c.quadraticCurveTo(mx,my,...tip);c.stroke();c.strokeStyle='#fff';c.lineWidth=2.5;c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(tip[0],tip[1],4,0,T);c.fill();c.stroke()
|
||||
limb(c,[ss,el,hd],12)
|
||||
const a=Math.atan2(my-hd[1],mx-hd[0])
|
||||
c.save();c.translate(...hd);c.rotate(a);c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(8,0,6,15,0,0,T);c.fill();c.stroke();c.fillStyle=dt;c.beginPath();c.ellipse(9,0,3,10,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(...hd,9,0,T);c.fill();c.stroke()
|
||||
if(hit&&t<.52){const k=(t-.36)/.16,R=14+10*k;c.lineWidth=3.5;for(let j=0;j<8;j++){const b=j*PI/4+.2;c.beginPath();c.moveTo(tip[0]+cos(b)*R*.6,tip[1]+sin(b)*R*.6);c.lineTo(tip[0]+cos(b)*R,tip[1]+sin(b)*R);c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// band girl with long wavy white hair and a big bow playing a flute held off to the side, fingers lifting on the keys, notes drifting up
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,atan2,hypot}=Math,T=2*PI
|
||||
export const frames=16,delay=80,rim=4
|
||||
const tile=createCanvas(6,6),q=tile.getContext('2d');q.fillStyle='#111';for(const[x,y]of[[1.5,1.5],[4.5,4.5]])q.beginPath(),q.arc(x,y,1.1,0,T),q.fill()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const F0=[10,200],F1=[158,163],A=atan2(F1[1]-F0[1],F1[0]-F0[0]),L=hypot(F1[0]-F0[0],F1[1]-F0[1])
|
||||
const at=u=>[F0[0]+u*cos(A),F0[1]+u*sin(A)]
|
||||
const note=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.rotate(-.35);c.beginPath();c.ellipse(0,0,7,5,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(x+6*s,y-2*s);c.lineTo(x+6*s,y-24*s);c.quadraticCurveTo(x+14*s,y-18*s,x+14*s,y-10*s);c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const dot=c.createPattern(tile,'repeat')
|
||||
// long wavy hair behind
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128,34);c.bezierCurveTo(200,34,214,100,204,150)
|
||||
c.quadraticCurveTo(222,176,206,200);c.quadraticCurveTo(222,222,204,244);c.lineTo(52,244);c.quadraticCurveTo(34,222,50,200);c.quadraticCurveTo(34,176,52,150);c.bezierCurveTo(42,100,56,34,128,34);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const o of[0,14])c.beginPath(),c.moveTo(128+d*(62-o),150),c.quadraticCurveTo(128+d*(78-o),180,128+d*(64-o),206),c.quadraticCurveTo(128+d*(80-o),226,128+d*(68-o),240),c.stroke()
|
||||
// big ink bow on the back of her head
|
||||
c.lineWidth=5;c.fillStyle='#111';for(const[x,y,r]of[[170,42,-.5],[200,70,.9]])c.beginPath(),c.ellipse(x,y,20,11,r,0,T),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(186,58,8,0,T);c.fill();c.stroke();c.strokeStyle='#fff';c.lineWidth=2;for(const[x,y,r]of[[170,42,-.5],[200,70,.9]])c.beginPath(),c.ellipse(x,y,13,5,r,0,T),c.stroke();c.strokeStyle='#111'
|
||||
// marching band jacket with white cords
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(62,196,54,250);c.lineTo(202,250);c.quadraticCurveTo(194,196,156,188);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;for(const y of[214,228,242])c.beginPath(),c.moveTo(108,y),c.lineTo(148,y),c.stroke()
|
||||
c.fillStyle='#fff';for(const y of[214,228,242])for(const x of[106,150])c.beginPath(),c.arc(x,y,3.5,0,T),c.fill()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(112,188);c.lineTo(128,200);c.lineTo(144,188);c.lineTo(140,184);c.lineTo(116,184);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.rect(116,166,24,22);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,166);c.lineTo(116,184);c.moveTo(140,166);c.lineTo(140,184);c.stroke()
|
||||
// face, eyes closed while she plays
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){c.lineWidth=5;c.beginPath();c.arc(x,130,14,.22*PI,.78*PI);c.stroke();c.lineWidth=4;c.beginPath();c.moveTo(x+d*11,141);c.lineTo(x+d*17,146);c.moveTo(x+d*5,144);c.lineTo(x+d*8,150);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(x-12,120);c.quadraticCurveTo(x,114,x+12,118);c.stroke()}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<3;k++)c.beginPath(),c.moveTo(128+d*42+(k-1)*5-3,164),c.lineTo(128+d*42+(k-1)*5+3,156),c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(122,166);c.quadraticCurveTo(128,163,134,166);c.stroke()
|
||||
// wavy bangs
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(68,150);c.bezierCurveTo(52,76,88,38,128,38);c.bezierCurveTo(170,38,206,76,188,150)
|
||||
c.quadraticCurveTo(190,118,178,104);c.quadraticCurveTo(176,114,168,116);c.quadraticCurveTo(166,98,150,90);c.quadraticCurveTo(148,106,136,112)
|
||||
c.quadraticCurveTo(130,96,114,90);c.quadraticCurveTo(112,106,98,112);c.quadraticCurveTo(94,100,84,100);c.quadraticCurveTo(70,120,68,150);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const[x,y]of[[100,60],[128,54],[156,60]])c.beginPath(),c.moveTo(x,y),c.quadraticCurveTo(x-6,y+20,x+2,y+36),c.stroke()
|
||||
// her right arm to the far hand, her left arm across to the near hand
|
||||
const hR=at(40),hL=at(96)
|
||||
for(const[l,s]of[[30,'#fff'],[24,'#111']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(92,214),c.quadraticCurveTo(44,240,hR[0]+2,hR[1]+12),c.stroke()
|
||||
for(const[l,s]of[[30,'#fff'],[24,'#111']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(166,214),c.quadraticCurveTo(140,226,hL[0]+4,hL[1]+12),c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;for(const[x,y]of[[hR[0]+2,hR[1]+16],[hL[0]+4,hL[1]+16]])c.beginPath(),c.arc(x,y,9,0,T),c.stroke();c.strokeStyle='#111'
|
||||
// the flute, keys, and fingers lifting in turn
|
||||
c.save();c.translate(...F0);c.rotate(A)
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.roundRect(-2,-6,L+2,12,5);c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const x of[20,128])c.beginPath(),c.moveTo(x,-6),c.lineTo(x,6),c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.ellipse(112,-1,7,4,0,0,T);c.stroke()
|
||||
for(const x of[30,44,58,82,96,108])c.beginPath(),c.arc(x,0,3,0,T),c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4
|
||||
for(const[x,k]of[[30,0],[44,1],[58,2],[82,3],[96,4],[106,5]]){const up=sin(T*(2*t+k/6))>.35?7:0;c.beginPath();c.roundRect(x-5,-17-up,10,14,5);c.fill();c.stroke()}
|
||||
for(const x of[40,94]){c.beginPath();c.roundRect(x-16,4,32,18,8);c.fill();c.stroke()}
|
||||
c.restore()
|
||||
// notes floating up off the end of the flute
|
||||
c.fillStyle='#111';c.strokeStyle='#111'
|
||||
for(const o of[0,.5]){const p=(t+o)%1,s=.6+.7*sin(PI*p);note(c,22+10*sin(T*(p+o)),170-p*130,s)}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// manga genie girl with a high ink ponytail swirls up out of an oil lamp on a puffy smoke tail, arms crossed and smirking, bobs, then gets sucked back in
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=90,rim=4
|
||||
const TL=createCanvas(6,6),tx=TL.getContext('2d');tx.fillStyle='#111';for(const[a,b]of[[1.5,1.5],[4.5,4.5]])tx.beginPath(),tx.arc(a,b,1.05,0,T),tx.fill()
|
||||
const ease=x=>x<0?0:x>1?1:x*x*(3-2*x)
|
||||
// half-lidded sly eye
|
||||
const eye=(c,x,y,d,tn)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+3,14,12,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+5,10,11,0,0,T);c.clip();c.fillStyle='#111';c.fillRect(x-11,y-6,22,22);c.fillStyle=tn;c.beginPath();c.ellipse(x,y+11,8,5,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-3,y+3,3,0,T);c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y);c.lineTo(x+16,y-1);c.lineTo(x+d*20,y-6);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(x-d*12,y-14);c.quadraticCurveTo(x,y-20,x+d*14,y-16);c.stroke()
|
||||
}
|
||||
const genie=(c,tn,t)=>{
|
||||
// high ink ponytail flowing back, white band
|
||||
c.save();c.translate(150,34);c.rotate(.3+.12*sin(t*T*2))
|
||||
c.fillStyle='#111';c.lineWidth=5;c.beginPath();c.moveTo(-10,0);c.bezierCurveTo(40,-30,70,20,76,70);c.quadraticCurveTo(54,50,46,64);c.quadraticCurveTo(36,30,8,14);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(14,0);c.bezierCurveTo(40,-8,56,24,62,52);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(146,40,10,14,.5,0,T);c.fill();c.stroke()
|
||||
// ink hair shell, face, side-parted ink bangs with shine
|
||||
c.fillStyle='#111';c.lineWidth=6;c.beginPath();c.moveTo(70,140);c.bezierCurveTo(52,70,88,42,128,42);c.bezierCurveTo(168,42,204,70,186,140);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,138,-1,tn);eye(c,153,138,1,tn)
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+k-3,168),c.lineTo(128+d*44+k+3,160),c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(116,168);c.quadraticCurveTo(130,176,142,164);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(70,140);c.bezierCurveTo(54,76,88,46,128,46);c.bezierCurveTo(168,46,202,76,186,140);c.quadraticCurveTo(180,108,164,98);c.quadraticCurveTo(130,108,96,92);c.quadraticCurveTo(80,104,70,140);c.closePath();c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(128,110,58,PI*1.27,PI*1.42);c.stroke();c.beginPath();c.arc(128,110,58,PI*1.55,PI*1.72);c.stroke();c.strokeStyle='#111'
|
||||
// hoop earrings
|
||||
c.lineWidth=3;for(const d of[-1,1])c.beginPath(),c.arc(128+d*56,168,8,0,T),c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,180,24,16);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,180);c.lineTo(116,196);c.moveTo(140,180);c.lineTo(140,196);c.stroke()
|
||||
// white top with a toned vest, waist fading into the smoke
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,194);c.quadraticCurveTo(70,200,68,236);c.quadraticCurveTo(96,262,128,264);c.quadraticCurveTo(160,262,188,236);c.quadraticCurveTo(186,200,156,194);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=tn;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*28,196),c.quadraticCurveTo(128+d*58,206,128+d*56,250),c.lineTo(128+d*20,258),c.quadraticCurveTo(128+d*14,220,128+d*14,200),c.closePath(),c.fill(),c.stroke()
|
||||
// arms crossed: upper arms down the sides, forearms folded one over the other, bangles at the wrists
|
||||
const tube=(x0,y0,x1,y1)=>{for(const[l,s]of[[22,'#111'],[13,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(x0,y0),c.lineTo(x1,y1),c.stroke();c.strokeStyle='#111'}
|
||||
tube(84,204,80,234);tube(172,204,176,234);tube(80,236,150,226);tube(176,236,106,226)
|
||||
c.lineWidth=3;for(const x of[142,114])c.beginPath(),c.ellipse(x,226,3,8,0,0,T),c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat')
|
||||
const e=ease(t/.3)-ease((t-.72)/.22),bob=e>.98?3*sin((t-.3)/.42*T*2):0
|
||||
const sx=184,sy=200,gx=sx+(112-sx)*e,gy=sy+(72-sy)*e+bob,gs=.66*max(e,.04)
|
||||
// smoke trail: puffs along a curve from the spout up to her waist, unioned under one outline
|
||||
if(e>.02){const wx=gx+(128-128)*gs,wy=gy+(250-128)*gs,pts=[];for(let k=0;k<=10;k++){const s=k/10,x=sx+(wx-sx)*s+26*e*sin(s*PI*1.6+t*T*2),y=sy+(wy-sy)*s;pts.push([x,y,3+16*e*s+3*sin(k*1.7+t*T*4)])}
|
||||
c.fillStyle='#111';for(const[x,y,r]of pts)c.beginPath(),c.arc(x,y,r+4,0,T),c.fill()
|
||||
c.fillStyle='#fff';for(const[x,y,r]of pts)c.beginPath(),c.arc(x,y,r,0,T),c.fill()
|
||||
c.lineWidth=2.5;for(const[x,y,r]of pts.filter((_,k)=>k%3==2))c.beginPath(),c.arc(x,y,r*.5,0,PI*1.4),c.stroke()}
|
||||
// the lamp: toned body, curved spout, handle loop, lidded top on a foot
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.ellipse(108,240,30,8,0,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(150,222);c.quadraticCurveTo(170,214,sx,sy);c.lineTo(sx+6,sy+6);c.quadraticCurveTo(176,224,152,234);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.arc(54,218,14,PI*.4,PI*1.7);c.stroke()
|
||||
const body=()=>{c.beginPath();c.moveTo(58,226);c.quadraticCurveTo(64,200,108,200);c.quadraticCurveTo(152,200,158,226);c.quadraticCurveTo(108,246,58,226);c.closePath()}
|
||||
body();c.fill();c.save();body();c.clip();c.fillStyle=tn;c.fillRect(50,220,120,30);c.restore();body();c.stroke()
|
||||
c.beginPath();c.ellipse(108,200,22,6,0,0,T);c.fill();c.stroke();c.beginPath();c.arc(108,190,7,0,T);c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.arc(108,214,34,PI*1.15,PI*1.35);c.stroke();c.strokeStyle='#111'
|
||||
if(e<=.02){c.lineWidth=3;c.beginPath();c.moveTo(sx+2,sy-4);c.quadraticCurveTo(sx+10,sy-14,sx+2,sy-22);c.stroke()}
|
||||
// the genie herself
|
||||
if(e>.02){c.save();c.translate(gx,gy);c.scale(gs,gs);c.translate(-128,-128);genie(c,tn,t);c.restore()}
|
||||
// sparkles while she's out
|
||||
if(e>.9){c.fillStyle='#fff';c.lineWidth=2.5;for(const[x,y,r,o]of[[30,70,8,0],[222,120,7,2]]){const q=r*(.6+.4*sin(t*T*4+o));c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,w=k%2?q*.35:q;c.lineTo(x+w*cos(a),y+w*sin(a))}c.closePath();c.fill();c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// chibi gymnast with a crosshatched top bun swings her arms, springs into a tucked backflip and lands arms up with sparkles
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs,min,max}=Math,T=2*PI
|
||||
export const frames=18,delay=70,rim=4
|
||||
const tile=(s,col,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[s/2,s/2],[0,0],[s,0],[0,s],[s,s]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const DOT=tile(6,'#111',1.4),LITE=tile(5,'#fff',1)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u),lerp=(a,b,k)=>a+(b-a)*k,mix=(a,b,k)=>a.map((v,n)=>lerp(v,b[n],k))
|
||||
const key=(ks,t)=>{for(let n=1;n<ks.length;n++)if(t<=ks[n][0]){const[a,va]=ks[n-1],[b,vb]=ks[n];return lerp(va,vb,ease((t-a)/(b-a)))}return ks.at(-1)[1]}
|
||||
const limb=(c,p,w)=>{for(const[l,s]of[[w+8,'#111'],[w,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...p[0]);for(const q of p.slice(1))c.lineTo(...q);c.stroke()}c.strokeStyle='#111'}
|
||||
const xhatch=(c,x0,y0,x1,y1)=>{c.lineWidth=3;c.beginPath();for(let x=x0-(y1-y0);x<x1;x+=11)c.moveTo(x,y1),c.lineTo(x+(y1-y0),y0),c.moveTo(x,y0),c.lineTo(x+(y1-y0),y1);c.stroke()}
|
||||
const star=(c,x,y,r)=>{c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();for(let k=0;k<8;k++){const q=k%2?r*.35:r,a=k*PI/4;c.lineTo(x+cos(a)*q,y+sin(a)*q)}c.closePath();c.fill();c.stroke()}
|
||||
const eye=(c,x,y,d,lt,happy)=>{
|
||||
if(happy){c.lineWidth=8;c.beginPath();c.arc(x,y+6,12,PI*1.1,PI*1.9);c.stroke();return}
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,16,0,0,T);c.fill()
|
||||
c.save();c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+4,y+4,10,13,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-6,y+7,22,12)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+1,y-1,5,0,T);c.fill();c.beginPath();c.arc(x+8,y+10,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-15,y-10);c.quadraticCurveTo(x,y-18,x+15,y-10);c.lineTo(x+d*21,y-14);c.stroke()
|
||||
c.lineWidth=4;for(const q of[-6,0,6])c.beginPath(),c.moveTo(x+q+d*2,y-15),c.lineTo(x+q+d*5,y-21),c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const dt=c.createPattern(DOT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const r=t<.28?0:t<.66?-T*ease((t-.28)/.38):0,h=t>.26&&t<.68?sin(PI*(t-.26)/.42):0
|
||||
const k=key([[0,0],[.28,0],[.36,1],[.56,1],[.66,0],[1,0]],t),cr=key([[0,0],[.14,0],[.24,1],[.28,0],[.64,0],[.7,1],[.82,0],[1,0]],t),ab=key([[0,0],[.12,0],[.24,1],[.3,0],[.64,0],[.7,.7],[.82,0],[1,0]],t)
|
||||
const H=[124,188+14*cr-46*h],P=[0,-28]
|
||||
c.save();c.translate(124,238);c.scale(.9,.9);c.translate(-124,-244)
|
||||
// shadow, motion arcs, landing sparkles
|
||||
c.fillStyle=dt;c.beginPath();c.ellipse(124,240,40*(1-.5*h),7*(1-.5*h),0,0,T);c.fill()
|
||||
if(t>.32&&t<.62){c.lineWidth=4;for(const q of[0,1]){const e=r+PI*1.5,R=72+q*9;c.beginPath();c.arc(H[0],H[1]-28,R,e+.15,e+1.3+q*.2);c.stroke()}}
|
||||
if(t>.8){const s=ease((t-.8)/.12);for(const[x,y,q]of[[52,70,1],[200,56,.8],[214,130,.6],[40,140,.7]])star(c,x,y,14*q*s*(1+.2*sin(t*T*6+x)))}
|
||||
c.save();c.translate(H[0]+P[0],H[1]+P[1]);c.rotate(r);c.translate(-P[0],-P[1])
|
||||
const lg=d=>mix(mix([[2+d,24],[d*2,48]].flat(),[[16,16],[0,34]].flat(),cr),[[22,-14],[6,10]].flat(),k)
|
||||
const S=mix([0,-46],[10,-40],k),HD=mix([4,-94],[30,-72],k),tl=lerp(0,.5,k)
|
||||
const arm=(dx,w)=>{const am=mix(mix(dx>0?[24,-12,48,-34]:[-24,-12,-46,-34],[-14,20,-30,34],ab),[18,8,14,30],k),o=[S[0]+dx,S[1]],e=[o[0]+am[0],o[1]+am[1]],h2=[o[0]+am[2],o[1]+am[3]];limb(c,[o,e,h2],w);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(...h2,8,0,T);c.fill();c.stroke()}
|
||||
const leg=dx=>{const l=lg(dx),hp=[dx,0],kn=[dx+l[0],l[1]],ft=[dx+l[2],l[3]];limb(c,[hp,kn,ft],15);c.save();c.lineWidth=15;c.strokeStyle=dt;c.beginPath();c.moveTo(...hp);c.lineTo(...kn);c.lineTo(...ft);c.stroke();c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(ft[0]+5,ft[1]+3,13,8,0,0,T);c.fill();c.stroke()}
|
||||
arm(-8,12);leg(-6)
|
||||
// jacket
|
||||
c.save();c.translate(0,0);c.rotate(k*.25)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-22,4);c.lineTo(22,4);c.lineTo(24,-44);c.quadraticCurveTo(0,-54,-22,-44);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(4,-48);c.lineTo(4,4);c.stroke();c.fillStyle='#111';c.fillRect(-22,-2,44,7);c.beginPath();c.moveTo(-10,-50);c.lineTo(4,-36);c.lineTo(18,-50);c.lineTo(14,-54);c.lineTo(4,-44);c.lineTo(-6,-54);c.fill()
|
||||
c.restore()
|
||||
leg(6)
|
||||
// head with a crosshatched top bun
|
||||
const happy=t<.14||t>.76
|
||||
c.save();c.translate(...HD);c.rotate(tl);c.scale(.64,.64);c.translate(-128,-121)
|
||||
c.lineWidth=9;c.fillStyle='#fff'
|
||||
const bun=()=>{c.beginPath();c.arc(128,22,30,0,T)},back=()=>{c.beginPath();c.moveTo(70,150);c.bezierCurveTo(52,74,88,40,128,40);c.bezierCurveTo(168,40,204,74,186,150);c.closePath()}
|
||||
for(const f of[bun,back]){f();c.fill();c.save();c.clip();xhatch(c,40,-20,220,170);c.restore();c.lineWidth=9;f();c.stroke()}
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(100,40,56,16,8);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,110,140,-1,lt,happy);eye(c,158,140,1,lt,happy)
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const j of[-6,0,6])c.beginPath(),c.moveTo(134+d*42+j-3,168),c.lineTo(134+d*42+j+3,160),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath()
|
||||
if(happy)c.moveTo(120,164),c.lineTo(150,164),c.quadraticCurveTo(146,184,135,184),c.quadraticCurveTo(124,184,120,164),c.fill(),c.stroke()
|
||||
else c.moveTo(126,168),c.quadraticCurveTo(136,174,146,168),c.stroke()
|
||||
const fr=()=>{c.beginPath();c.moveTo(72,120);c.bezierCurveTo(66,70,100,50,128,52);c.bezierCurveTo(160,50,192,70,186,120);c.quadraticCurveTo(170,96,150,90);c.quadraticCurveTo(146,104,134,110);c.quadraticCurveTo(126,94,112,90);c.quadraticCurveTo(96,100,84,102);c.quadraticCurveTo(78,108,72,120);c.closePath()}
|
||||
fr();c.fill();c.save();c.clip();xhatch(c,60,40,200,130);c.restore();c.lineWidth=9;fr();c.stroke()
|
||||
c.restore()
|
||||
arm(8,12)
|
||||
c.restore()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// student council girl with long white hair and an ink alice band stamping a document twice with her hanko, thunk lines, a smug beat, then the sheet goes on the pile
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=20,delay=80,rim=4
|
||||
const tile=createCanvas(6,6),q=tile.getContext('2d');q.fillStyle='#111';for(const[x,y]of[[1.5,1.5],[4.5,4.5]])q.beginPath(),q.arc(x,y,1.1,0,T),q.fill()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const ease=f=>f<=0?0:f>=1?1:.5-.5*cos(PI*f)
|
||||
const P1=138,P2=178,UP=138,DN=180
|
||||
// the seal: a ring with 可 inside
|
||||
const seal=(c,x,y)=>{c.lineWidth=3;c.beginPath();c.arc(x,y,12,0,T);c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(x-8,y-6);c.lineTo(x+8,y-6);c.moveTo(x+5,y-6);c.lineTo(x+5,y+8);c.lineTo(x+2,y+6);c.stroke();c.beginPath();c.rect(x-6,y-2,7,6);c.stroke()}
|
||||
const doc=(c,x,m1,m2)=>{c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.rect(x,198,96,50);c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const[y,l]of[[208,70],[215,56],[222,40]])c.beginPath(),c.moveTo(x+8,y),c.lineTo(x+8+l,y),c.stroke()
|
||||
c.setLineDash([3,3]);for(const o of[24,64])c.beginPath(),c.arc(x+o+14,230,13,0,T),c.stroke();c.setLineDash([])
|
||||
if(m1)seal(c,x+38,230);if(m2)seal(c,x+78,230)}
|
||||
export default(c,t)=>{
|
||||
const dot=c.createPattern(tile,'repeat')
|
||||
// where the stamp is: [x, fist y, down]
|
||||
let sx=P1,fy=UP,down=0
|
||||
if(t<.08);else if(t<.14)fy=UP+(DN-UP)*ease((t-.08)/.06);else if(t<.2)fy=DN,down=1;else if(t<.28)fy=DN+(UP-DN)*ease((t-.2)/.08)
|
||||
else if(t<.36)sx=P1+(P2-P1)*ease((t-.28)/.08);else if(t<.42)sx=P2,fy=UP+(DN-UP)*ease((t-.36)/.06);else if(t<.48)sx=P2,fy=DN,down=1
|
||||
else if(t<.56)sx=P2,fy=DN+(UP-DN)*ease((t-.48)/.08);else if(t<.76)sx=P2;else if(t<.9)sx=P2+(P1-P2)*ease((t-.76)/.14)
|
||||
const smug=t>=.56&&t<.76,m1=t>=.2&&t<.9,m2=t>=.48&&t<.9,slide=t>=.76&&t<.9?ease((t-.76)/.14):0
|
||||
c.save();c.translate(128,-8);c.scale(.9,.9);c.translate(-128,0)
|
||||
// long straight white hair behind
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128,34);c.bezierCurveTo(202,34,214,100,206,230);c.lineTo(50,230);c.bezierCurveTo(42,100,54,34,128,34);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const o of[10,26])c.beginPath(),c.moveTo(128+d*(78-o*.3),130),c.lineTo(128+d*(80-o*.3),226),c.stroke()
|
||||
// dotted blazer, white shirt, ink tie
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(62,196,54,250);c.lineTo(202,250);c.quadraticCurveTo(194,196,156,188);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(108,189);c.lineTo(128,232);c.lineTo(148,189);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(123,194);c.lineTo(133,194);c.lineTo(137,226);c.lineTo(128,234);c.lineTo(119,226);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,168,24,22);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,188);c.moveTo(140,168);c.lineTo(140,188);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
// sharp almond eyes watching the stamp, or shut smug
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
if(smug){c.lineWidth=5;c.beginPath();c.moveTo(x-13,142);c.quadraticCurveTo(x,148,x+13,141);c.lineTo(x+d*18,137);c.stroke();continue}
|
||||
c.save();c.beginPath();c.moveTo(x-15,142);c.quadraticCurveTo(x,122,x+16,137);c.quadraticCurveTo(x+4,162,x-15,142);c.clip()
|
||||
c.fillStyle='#fff';c.fillRect(x-16,126,32,32);c.fillStyle='#111';c.beginPath();c.ellipse(x+3,144,9,12,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x,139,3.4,0,T);c.fill();c.beginPath();c.arc(x+6,150,1.6,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-15,142);c.quadraticCurveTo(x,122,x+16,137);c.lineTo(x+d*20,132);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-10,148);c.quadraticCurveTo(x,152,x+10,146);c.stroke()
|
||||
}
|
||||
c.lineWidth=3.5;for(const[x,d]of[[103,1],[153,-1]])c.beginPath(),c.moveTo(x-13,118+d*2),c.quadraticCurveTo(x,113,x+13,118-d*2),c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(120,168);c.quadraticCurveTo(130,172,138,166);c.stroke()
|
||||
// side-swept white bangs under an ink alice band
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(68,156);c.bezierCurveTo(52,76,88,40,128,40);c.bezierCurveTo(168,40,206,76,188,156)
|
||||
c.quadraticCurveTo(188,120,180,106);c.quadraticCurveTo(160,104,140,116);c.quadraticCurveTo(150,104,152,92);c.quadraticCurveTo(124,106,100,118)
|
||||
c.quadraticCurveTo(108,104,108,94);c.quadraticCurveTo(92,104,80,120);c.quadraticCurveTo(70,132,68,156);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const[x,y]of[[150,64],[124,62],[98,70]])c.beginPath(),c.moveTo(x,y),c.quadraticCurveTo(x-14,y+18,x-22,y+34),c.stroke()
|
||||
c.lineWidth=12;c.beginPath();c.arc(128,104,62,1.12*PI,1.88*PI);c.stroke()
|
||||
c.restore()
|
||||
if(smug){c.fillStyle='#fff';c.lineWidth=3;const s=7+3*sin(T*(t-.56)*5);for(const[x,y]of[[36,44],[214,30]]){c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,r=k%2?s*.35:s;c.lineTo(x+r*cos(a),y+r*sin(a))}c.closePath();c.fill();c.stroke()}}
|
||||
// desk, the finished pile, the sheet being stamped
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(16,188);c.lineTo(240,188);c.lineTo(250,252);c.lineTo(6,252);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;for(let k=0;k<4;k++){c.beginPath();c.rect(12+k%2*3,232-k*8,72,14);c.fill();c.stroke()}seal(c,58,214)
|
||||
doc(c,100,0,0)
|
||||
if(m1&&t<.9){c.save();c.translate(-90*slide,-8*slide);doc(c,100,m1,m2);c.restore()}
|
||||
// impact lines when the stamp lands
|
||||
if(down){c.lineWidth=3.5;for(let k=0;k<7;k++){const a=PI*(1.05+k*.15);c.beginPath();c.moveTo(sx+20*cos(a),226+14*sin(a));c.lineTo(sx+34*cos(a),226+26*sin(a));c.stroke()}}
|
||||
// stamping arm with the council armband, fist and hanko
|
||||
const S=[166,156],F=[sx+4,fy],at=f=>[S[0]+(F[0]-S[0])*f,S[1]+(F[1]-S[1])*f]
|
||||
for(const[l,st]of[[30,'#111'],[23,'#fff'],[23,dot]])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(...S),c.lineTo(...F),c.stroke()
|
||||
const[a0,a1]=[at(.25),at(.45)];c.lineCap='butt';for(const[l,st]of[[30,'#111'],[24,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(...a0),c.lineTo(...a1),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=3;const m=at(.35);c.beginPath();c.arc(m[0],m[1],5,0,T);c.stroke();c.lineCap='round'
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.roundRect(sx-9,fy+8,18,38,4);c.fill();c.stroke()
|
||||
c.lineWidth=2;c.beginPath();c.moveTo(sx-9,fy+40);c.lineTo(sx+9,fy+40);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.roundRect(sx-14,fy-6,30,20,9);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const y of[fy,fy+7])c.beginPath(),c.moveTo(sx-10,y),c.lineTo(sx+4,y),c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// girl with short white bunches irons a wrinkled t-shirt flat, steam puffing, it sparkles, then she turns it over to do the other side
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max,abs}=Math,T=2*PI
|
||||
export const frames=20,delay=90,rim=4
|
||||
const tone=(c,s,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle='#111';for(const[a,b]of[[0,0],[s,0],[0,s],[s,s],[s/2,s/2]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return c.createPattern(k,'repeat')}
|
||||
const ease=u=>(u=max(0,min(1,u)),u*u*(3-2*u))
|
||||
const W=[[84,166],[102,192],[118,170],[124,214],[142,188],[150,224],[160,166],[172,200],[186,172]]
|
||||
const spark=(c,x,y,r)=>{c.fillStyle='#fff';c.lineWidth=2.5;c.beginPath();for(let k=0;k<8;k++){const q=k%2?r*.3:r,b=k*PI/4;c.lineTo(x+cos(b)*q,y+sin(b)*q)}c.closePath();c.fill();c.stroke()}
|
||||
const shirt=c=>{c.beginPath();c.moveTo(116,150);c.quadraticCurveTo(136,164,156,150);c.lineTo(184,156);c.lineTo(204,182);c.lineTo(182,192);c.lineTo(176,182);c.lineTo(176,238);c.lineTo(96,238);c.lineTo(96,182);c.lineTo(90,192);c.lineTo(68,182);c.lineTo(88,156);c.closePath()}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
const sweep=t<.55,glow=t>=.55&&t<.68,u=ease((t-.68)/.26),ix=sweep?84+96*ease(t/.55):t<.68?180:180-96*u,lift=t>=.68?40*sin(PI*u):0
|
||||
const fl=t>=.68&&t<.86?(t-.68)/.18:t>=.86?1:0,sy=fl?abs(cos(PI*fl)):1,wr=sweep||fl>.5
|
||||
// girl behind the board
|
||||
c.save();c.translate(128,76);c.scale(.72,.72);c.translate(-128,-130)
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(98,184);c.quadraticCurveTo(40,196,30,290);c.lineTo(226,290);c.quadraticCurveTo(216,196,158,184);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=7;c.beginPath();c.moveTo(104,186);c.lineTo(128,214);c.lineTo(152,186);c.fill();c.stroke()
|
||||
c.beginPath();c.rect(116,166,24,22);c.fill();c.beginPath();c.moveTo(116,166);c.lineTo(116,186);c.moveTo(140,166);c.lineTo(140,186);c.stroke()
|
||||
// white bunches sticking out at ear level, ink ties
|
||||
for(const d of[-1,1]){c.save();c.translate(128+d*62,112);c.rotate(d*(.5+.06*sin(t*T*2)));c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(0,-14);c.quadraticCurveTo(d*44,-6,d*40,30);c.quadraticCurveTo(d*30,44,d*20,34);c.quadraticCurveTo(d*24,14,0,12);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(d*10,-4);c.quadraticCurveTo(d*30,4,d*30,24);c.stroke();c.fillStyle='#111';c.beginPath();c.roundRect(-9,-14,18,26,6);c.fill();c.restore()}
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(glow){c.lineWidth=7;for(const x of[103,153])c.beginPath(),c.moveTo(x-15,146),c.quadraticCurveTo(x,128,x+15,146),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath();c.moveTo(114,166);c.quadraticCurveTo(128,186,142,166);c.closePath();c.fill();c.stroke()}
|
||||
else{const lx=(ix-132)*.07;for(const[x,d]of[[103,-1],[153,1]]){
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,142,13,15,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,142,13,15,0,0,T);c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+lx,148,10.5,13,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+lx-4,143,4,5,0,0,T);c.fill();c.beginPath();c.arc(x+lx+4,154,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,138);c.quadraticCurveTo(x,128,x+16,138);c.moveTo(x+d*14,136);c.lineTo(x+d*21,131);c.stroke()}
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(120,170);c.quadraticCurveTo(128,175,136,170);c.stroke()}
|
||||
c.lineWidth=4;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*42+k-3,168),c.lineTo(128+d*42+k+3,160),c.stroke()
|
||||
// white hair, rounded fringe with a few strands, ink headband
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(72,150);c.bezierCurveTo(52,70,90,34,128,34);c.bezierCurveTo(166,34,204,70,184,150)
|
||||
c.quadraticCurveTo(184,118,176,106);c.quadraticCurveTo(168,116,158,118);c.quadraticCurveTo(152,104,140,104);c.quadraticCurveTo(132,116,120,116);c.quadraticCurveTo(114,104,102,104);c.quadraticCurveTo(92,116,82,112);c.quadraticCurveTo(74,128,72,150);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const[a,b,q,r]of[[100,58,90,90],[156,58,168,88]])c.beginPath(),c.moveTo(a,b),c.quadraticCurveTo((a+q)/2-6,(b+r)/2,q,r),c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(76,82);c.quadraticCurveTo(128,48,180,82);c.lineTo(178,96);c.quadraticCurveTo(128,64,78,96);c.closePath();c.fill()
|
||||
c.restore()
|
||||
// ironing board with a toned cover and X legs
|
||||
for(const[l,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(70,214),c.lineTo(186,252),c.moveTo(186,214),c.lineTo(70,252),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=6
|
||||
const board=y=>{c.beginPath();c.moveTo(8,178+y);c.quadraticCurveTo(16,148+y,60,148+y);c.lineTo(232,148+y);c.quadraticCurveTo(248,148+y,248,162+y);c.lineTo(248,192+y);c.quadraticCurveTo(248,206+y,232,206+y);c.lineTo(60,206+y);c.quadraticCurveTo(16,206+y,8,178+y);c.closePath()}
|
||||
board(8);c.fillStyle='#fff';c.fill();c.stroke();board(0);c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
// her other hand resting on the board
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.ellipse(222,164,14,10,.3,0,T);c.fill();c.stroke()
|
||||
// the t-shirt, squashed top to bottom while it turns over, hanging past the board's edge
|
||||
c.save();c.translate(136,194);c.scale(1,sy);c.translate(-136,-194)
|
||||
c.lineWidth=6;shirt(c);c.fillStyle='#fff';c.fill();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();if(fl>.5)c.moveTo(118,152),c.quadraticCurveTo(136,158,154,152);else c.moveTo(120,152),c.quadraticCurveTo(136,174,152,152);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(96,214);c.lineTo(176,214);c.stroke()
|
||||
if(wr){c.lineWidth=4;for(const[x,y]of W)if(!sweep||x>ix-6){c.beginPath();for(let k=0;k<4;k++)c.lineTo(x-7+k*5,y+(k%2?5:-1));c.stroke()}}
|
||||
c.restore()
|
||||
if(glow)for(const[x,y,o]of[[104,176,0],[166,206,.5],[178,166,.25]])spark(c,x,y,11*(.6+.4*sin((t*6+o)*T)))
|
||||
// arm and iron, nose to the right, black sole, open handle
|
||||
const iy=188-lift,ia=t>=.68?-.3*sin(PI*u):0
|
||||
c.save();c.translate(ix,iy);c.rotate(ia)
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(-34,8);c.lineTo(26,8);c.quadraticCurveTo(38,6,38,0);c.quadraticCurveTo(24,-16,-30,-14);c.quadraticCurveTo(-38,-4,-34,8);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(-34,8);c.lineTo(26,8);c.quadraticCurveTo(38,6,38,0);c.lineTo(-36,2);c.closePath();c.fill()
|
||||
c.save();c.beginPath();c.moveTo(-30,0);c.lineTo(30,0);c.quadraticCurveTo(24,-16,-30,-14);c.closePath();c.clip();c.fillStyle=FINE;c.fillRect(-40,-20,80,20);c.restore()
|
||||
for(const[l,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(-20,-13),c.quadraticCurveTo(-18,-34,-2,-34),c.quadraticCurveTo(14,-34,16,-13),c.stroke()
|
||||
c.restore();c.strokeStyle='#111'
|
||||
const hx=ix-2+34*sin(ia),hy=iy-36+lift*0
|
||||
for(const[l,s]of[[24,'#111'],[14,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(76,128),c.quadraticCurveTo(min(hx,110)-20,hy-10,hx,hy),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.ellipse(hx,hy,13,10,0,0,T);c.fill();c.stroke()
|
||||
// steam puffs off the iron while it presses
|
||||
if(sweep)for(let k=0;k<3;k++){const p=(t*5+k/3)%1,x=ix-34+k*10-p*8,y=iy-10-p*34,r=4+7*sin(PI*p);c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// manga kappa girl with messy screentone hair, the water dish on her head sloshing and a turtle-shell backpack, munching a cucumber with webbed hands: bite, chew, chew
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=90,rim=4
|
||||
const TL=createCanvas(6,6),tx=TL.getContext('2d');tx.fillStyle='#111';for(const[a,b]of[[1.5,1.5],[4.5,4.5]])tx.beginPath(),tx.arc(a,b,1.05,0,T),tx.fill()
|
||||
// big round eyes, iris toned with an ink top band and a ring of tiny highlights
|
||||
const eye=(c,x,y,d,tn)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,15,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,12,13.5,0,0,T);c.clip();c.fillStyle=tn;c.fillRect(x-13,y-12,26,28);c.fillStyle='#111';c.beginPath();c.ellipse(x,y-9,14,9,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,12,13.5,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.arc(x,y+3,5.5,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-3,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2.2,0,T);c.fill();c.beginPath();c.arc(x+6,y-1,1.4,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-17,y-6);c.quadraticCurveTo(x,y-20,x+17,y-6);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(x+d*15,y-8);c.lineTo(x+d*21,y-13);c.moveTo(x+d*16,y-3);c.lineTo(x+d*22,y-5);c.stroke()
|
||||
}
|
||||
const cuke=(c,x,y,tn)=>{
|
||||
// bumpy cucumber, bitten off at the right end
|
||||
c.save();c.translate(x,y);c.rotate(-.12)
|
||||
c.beginPath();c.moveTo(-50,-10);c.quadraticCurveTo(-62,0,-50,10);c.lineTo(34,12);for(let k=0;k<5;k++)c.lineTo(38+(k%2)*5,12-k*6);c.lineTo(34,-12);c.closePath()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(-64,-14,94,10);c.restore();c.stroke()
|
||||
c.fillStyle='#111';for(const[a,b]of[[-36,2],[-20,-6],[-6,4],[10,-4],[22,6]])c.beginPath(),c.arc(a,b,1.8,0,T),c.fill()
|
||||
c.lineWidth=2;c.beginPath();c.ellipse(36,0,3,8,0,0,T);c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
export default(c,t,i)=>{
|
||||
const tn=c.createPattern(TL,'repeat')
|
||||
// bite on the first quarter, then two chews with puffed cheeks
|
||||
const u=t*4%1,ph=t*4|0,bite=ph==0,lift=bite?sin(PI*u):0,puff=bite?0:abs(sin(PI*u)),sl=sin(2*T*t)
|
||||
c.save();c.translate(128,136);c.scale(.88,.88);c.translate(-128,-128)
|
||||
// turtle shell backpack peeking out round her shoulders: toned and white plates around an inner ring
|
||||
const shell=()=>{c.beginPath();c.ellipse(128,200,108,92,0,0,T)}
|
||||
c.lineWidth=6;c.fillStyle='#fff';shell();c.fill()
|
||||
c.save();shell();c.clip();c.fillStyle=tn;for(let k=0;k<12;k+=2){c.beginPath();c.moveTo(128,200);c.arc(128,200,120,k*T/12-PI/12,(k+1)*T/12-PI/12);c.fill()}
|
||||
c.lineWidth=4;c.beginPath();for(let k=0;k<12;k++){const a=k*T/12-PI/12;c.moveTo(128+70*cos(a),200+60*sin(a));c.lineTo(128+120*cos(a),200+110*sin(a))}c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(128,200,70,60,0,0,T);c.fill();c.stroke();c.restore()
|
||||
c.lineWidth=6;shell();c.stroke()
|
||||
// messy toned hair behind
|
||||
c.beginPath();c.moveTo(128,40);c.bezierCurveTo(62,40,54,102,64,150);for(const[x,y]of[[58,168],[74,164],[72,182],[90,174]])c.quadraticCurveTo(x-10,y-12,x,y)
|
||||
c.lineTo(166,174);for(const[x,y]of[[184,182],[182,164],[198,168],[192,150]])c.quadraticCurveTo(x+10,y-12,x,y)
|
||||
c.bezierCurveTo(202,102,194,40,128,40);c.fillStyle='#fff';c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(50,40,160,150);c.restore();c.stroke()
|
||||
// shell straps over a white hoodie
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(98,186);c.quadraticCurveTo(62,196,56,258);c.lineTo(200,258);c.quadraticCurveTo(194,196,158,186);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=10;c.beginPath();for(const d of[-1,1])c.moveTo(128+d*42,190),c.quadraticCurveTo(128+d*38,220,128+d*44,258);c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,22);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,166);c.lineTo(116,186);c.moveTo(140,166);c.lineTo(140,186);c.stroke()
|
||||
// face, cheeks puff out while chewing
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(puff>.05)for(const d of[-1,1])c.beginPath(),c.arc(128+d*42,160,12+5*puff,d<0?PI*.55:-PI*.55,d<0?PI*1.45:PI*.45),c.fill(),c.stroke()
|
||||
if(bite){eye(c,103,138,-1,tn);eye(c,153,138,1,tn)}
|
||||
else{c.lineWidth=5;for(const x of[103,153])c.beginPath(),c.arc(x,144,12,PI*1.15,PI*1.85),c.stroke()}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-3,166),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
c.lineWidth=3.5;c.fillStyle='#fff';c.beginPath()
|
||||
if(bite)c.ellipse(128,170,8,4+6*lift,0,0,T),c.fillStyle='#111',c.fill()
|
||||
else c.moveTo(120,168),c.quadraticCurveTo(124,172+2*puff,128,168),c.quadraticCurveTo(132,172+2*puff,136,168)
|
||||
c.stroke()
|
||||
// messy toned bangs with stray tufts
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(70,148);c.bezierCurveTo(54,74,88,44,128,44);c.bezierCurveTo(168,44,202,74,186,148)
|
||||
for(const[x,y]of[[180,110],[170,120],[164,100],[150,116],[142,96],[130,114],[118,96],[108,116],[98,100],[88,120],[78,108]])c.quadraticCurveTo(x+4,y-8,x,y)
|
||||
c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(60,40,140,90);c.restore();c.stroke()
|
||||
// the dish, water sloshing side to side, a drop hopping out now and then
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(128,50,44,13,0,0,T);c.fill();c.stroke()
|
||||
c.save();c.beginPath();c.ellipse(128,50,34,8,0,0,T);c.clip();c.fillStyle=tn;c.beginPath();c.moveTo(90,62);c.lineTo(90,50+6*sl);c.quadraticCurveTo(128,44-6*sl,166,50-6*sl);c.lineTo(166,62);c.fill()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(94,50+5*sl);c.quadraticCurveTo(128,44-6*sl,162,50-5*sl);c.stroke();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(128,50,34,8,0,0,T);c.stroke()
|
||||
const dp=t*2%1;if(dp<.5){const q=dp/.5,dx=128+(sl>0?-1:1)*(30+20*q),dy=44-30*sin(PI*q);c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.moveTo(dx,dy-7);c.quadraticCurveTo(dx+5,dy,dx,dy+4);c.quadraticCurveTo(dx-5,dy,dx,dy-7);c.fill();c.stroke()}
|
||||
// cucumber held up in both webbed hands
|
||||
const cy=200-16*lift
|
||||
cuke(c,128+(bite?8:24),cy,tn)
|
||||
for(const d of[-1,1]){const hx=128+d*34+(bite?8:24),hy=cy+6;c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(hx,hy,12,11,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(const k of[-6,0,6])c.moveTo(hx+k,hy-11),c.quadraticCurveTo(hx+k+3,hy-4,hx+k,hy+2);c.stroke()}
|
||||
if(!bite&&puff>.6){c.fillStyle='#111';for(const[a,b]of[[164,176],[170,186]])c.beginPath(),c.arc(a,b,2,0,T),c.fill()}
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// girl in an apron dips a ladle into a steaming pot, blows on it, sips, and melts into a happy ^^ with flowers
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=20,delay=80,rim=4
|
||||
const tone=(c,s,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle='#111';for(const[a,b]of[[0,0],[s,0],[0,s],[s,s],[s/2,s/2]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return c.createPattern(k,'repeat')}
|
||||
const ease=u=>(u=max(0,min(1,u)),u*u*(3-2*u))
|
||||
const mix=(a,b,u)=>a.map((v,k)=>v+(b[k]-v)*u)
|
||||
// ladle keyframes: [bowl x, bowl y, tilt, hand x, hand y]
|
||||
const K=[[0,[150,252,0,176,226]],[.18,[150,252,0,176,226]],[.3,[126,178,0,184,206]],[.4,[126,178,0,184,206]],[.46,[124,160,-.5,182,196]],[.56,[124,160,-.5,182,196]],[.64,[150,206,0,188,222]],[.86,[150,206,0,188,222]],[1,[150,252,0,176,226]]]
|
||||
const pose=t=>{for(let k=1;k<K.length;k++)if(t<=K[k][0])return mix(K[k-1][1],K[k][1],ease((t-K[k-1][0])/(K[k][0]-K[k-1][0])));return K[0][1]}
|
||||
const flower=(c,x,y,r,a)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle='#fff';c.lineWidth=3;for(let k=0;k<5;k++){const b=k*T/5;c.beginPath();c.ellipse(cos(b)*r*.6,sin(b)*r*.6,r*.5,r*.35,b,0,T);c.fill();c.stroke()}c.fillStyle='#111';c.beginPath();c.arc(0,0,r*.25,0,T);c.fill();c.restore()}
|
||||
const spark=(c,x,y,r)=>{c.fillStyle='#fff';c.lineWidth=2.5;c.beginPath();for(let k=0;k<8;k++){const q=k%2?r*.3:r,b=k*PI/4;c.lineTo(x+cos(b)*q,y+sin(b)*q)}c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
const[bx,by,ba,hx,hy]=pose(t),dip=t<.24||t>.92,blow=t>=.3&&t<.42,sip=t>=.44&&t<.58,yum=t>=.6&&t<.9
|
||||
// girl
|
||||
c.save();c.translate(124,112);c.scale(.9,.9);c.translate(-128,-130)
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(62,170);c.bezierCurveTo(44,70,84,30,128,30);c.bezierCurveTo(172,30,212,70,194,170);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(98,184);c.quadraticCurveTo(48,194,40,290);c.lineTo(216,290);c.quadraticCurveTo(208,194,158,184);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
// apron bib with a frill and neck strap
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.moveTo(96,214);c.lineTo(160,214);c.lineTo(166,290);c.lineTo(90,290);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();for(let k=0;k<=8;k++)c.lineTo(96+k*8,214+(k%2?-5:0));c.stroke()
|
||||
c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*30,214),c.lineTo(128+d*16,188),c.stroke()
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,24);c.fill();c.beginPath();c.moveTo(116,166);c.lineTo(116,188);c.moveTo(140,166);c.lineTo(140,188);c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
// eyes
|
||||
if(sip){c.lineWidth=6;for(const x of[103,153])c.beginPath(),c.moveTo(x-14,140),c.quadraticCurveTo(x,148,x+14,140),c.stroke()}
|
||||
else if(yum){c.lineWidth=6;for(const x of[103,153])c.beginPath(),c.moveTo(x-14,146),c.quadraticCurveTo(x,128,x+14,146),c.stroke()}
|
||||
else for(const[x,d]of[[103,-1],[153,1]]){
|
||||
const ly=dip?6:blow?5:0,lx=dip?3:blow?1:0,lid=blow?8:0
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+lx,140+ly,10.5,14,0,0,T);c.fill()
|
||||
c.fillStyle='#777';c.beginPath();c.ellipse(x+lx,152+ly,9,5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+lx-4,135+ly,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+lx+4,147+ly,2,0,T);c.fill()
|
||||
if(lid){c.fillStyle='#fff';c.fillRect(x-14,122,28,10+lid)}c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,135+lid);c.quadraticCurveTo(x,121+lid,x+16,135+lid);c.moveTo(x+d*14,132+lid);c.lineTo(x+d*21,127+lid);c.stroke()}
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128,154);c.lineTo(126,157);c.stroke()
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*42+k-3,166),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
if(yum){c.lineWidth=4.5;c.fillStyle='#111';c.beginPath();c.moveTo(114,166);c.quadraticCurveTo(128,186,142,166);c.closePath();c.fill();c.stroke()}
|
||||
else if(blow){c.lineWidth=4;c.beginPath();c.arc(128,170,4,0,T);c.stroke()}
|
||||
else if(!sip){c.lineWidth=4;c.beginPath();c.moveTo(121,169);c.quadraticCurveTo(128,173,135,169);c.stroke()}
|
||||
// swept ink bangs, forehead showing on her right, two white clips
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(66,160);c.bezierCurveTo(48,72,86,36,128,36);c.bezierCurveTo(170,36,208,72,190,160)
|
||||
c.quadraticCurveTo(188,128,182,112);c.quadraticCurveTo(172,122,162,128);c.quadraticCurveTo(160,114,148,108);c.quadraticCurveTo(140,116,130,118);c.quadraticCurveTo(126,106,114,100)
|
||||
c.quadraticCurveTo(100,94,90,98);c.quadraticCurveTo(80,108,76,124);c.quadraticCurveTo(70,140,66,160);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=6;for(const[s,e]of[[1.28,1.38],[1.46,1.56],[1.64,1.74]])c.beginPath(),c.arc(128,104,52,PI*s,PI*e),c.stroke()
|
||||
c.lineWidth=5;for(const k of[0,1])c.beginPath(),c.moveTo(94+k*8,90+k*3),c.lineTo(108+k*8,98+k*3),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// low side ponytail over her shoulder with a white tie
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(70,150);c.bezierCurveTo(52,190,58,230,44,268);c.quadraticCurveTo(62,258,70,270);c.bezierCurveTo(80,230,90,196,84,160);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(72,190);c.quadraticCurveTo(66,220,58,250);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.roundRect(62,166,22,12,5);c.fill();c.stroke()
|
||||
// hand on cheek while it tastes good
|
||||
if(yum){c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.ellipse(78,172,14,17,-.3,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const k of[0,1,2])c.beginPath(),c.moveTo(70+k*6,160+k*2),c.lineTo(76+k*6,170+k*2),c.stroke()}
|
||||
c.restore()
|
||||
// pot body, soup, back rim
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(28,228);c.lineTo(36,262);c.lineTo(212,262);c.lineTo(220,228);c.closePath();c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
for(const d of[-1,1]){c.beginPath();c.roundRect(124+d*104-8,226,16,12,4);c.fillStyle='#fff';c.fill();c.stroke()}
|
||||
c.beginPath();c.ellipse(124,228,96,16,0,0,T);c.fillStyle='#fff';c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(124,230,84,10,0,0,T);c.fillStyle=DOT;c.fill()
|
||||
// arm and ladle
|
||||
for(const[l,s]of[[30,'#111'],[19,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(196,262),c.quadraticCurveTo(206,236,hx,hy),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
c.save();c.beginPath();c.rect(0,0,256,dip?229:256);c.clip()
|
||||
for(const[l,s]of[[9,'#111'],[3,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(bx+12*cos(ba),by-4),c.lineTo(hx+6,hy-6),c.stroke()
|
||||
c.strokeStyle='#111';c.save();c.translate(bx,by);c.rotate(ba);c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(-14,-4);c.quadraticCurveTo(-14,12,0,12);c.quadraticCurveTo(14,12,14,-4);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=DOT;c.beginPath();c.ellipse(0,-4,13,3,0,0,T);c.fill();c.restore();c.restore()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy,13,11,-.4,0,T);c.fill();c.stroke()
|
||||
// steam: from the pot always, from the ladle while she blows
|
||||
const steam=(x,y,h,p)=>{for(const[l,s]of[[8,'#111'],[3,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();for(let k=0;k<=12;k++){const v=k/12;c.lineTo(x+5*sin(v*T*1.5+p*T),y-v*h)}c.stroke()}c.strokeStyle='#111'}
|
||||
for(const[x,o]of[[38,0],[58,.5]])steam(x,214,44+8*sin((t+o)*T),t*2+o)
|
||||
if(blow){steam(118,170,22,t*4);c.lineWidth=3;for(const k of[-1,0,1])c.beginPath(),c.moveTo(112,150+k*6),c.lineTo(104,154+k*8),c.stroke()}
|
||||
if(yum){const u=ease((t-.6)/.08);for(const[x,y,r,a]of[[30,70,11,0],[214,56,9,1],[220,120,8,2]])flower(c,x,y,r*u,a+t*3)
|
||||
for(const[x,y,r,o]of[[40,120,9,0],[200,92,8,.5],[60,30,7,.25]])spark(c,x,y,r*(.6+.4*sin((t*4+o)*T))*u)}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// librarian with a pencil through her top bun and round glasses, finger to her lips, shh arcs rolling out and a glint across the lenses
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=16,delay=80,rim=4
|
||||
const tile=createCanvas(6,6),q=tile.getContext('2d');q.fillStyle='#111';for(const[x,y]of[[1.5,1.5],[4.5,4.5]])q.beginPath(),q.arc(x,y,1.1,0,T),q.fill()
|
||||
const ht=createCanvas(6,6),h=ht.getContext('2d');h.strokeStyle='#111';h.lineWidth=1.4;for(const o of[-6,0,6])h.beginPath(),h.moveTo(o,6),h.lineTo(o+6,0),h.stroke()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const face=c=>{c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96)}
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),hat=c.createPattern(ht,'repeat'),shut=i==3||i==4,g=(t-.55)/.25
|
||||
// pencil stuck through the bun, then the bun
|
||||
c.save();c.translate(128,30);c.rotate(-.42);c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.rect(-44,-6,78,12);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(34,-6);c.lineTo(50,0);c.lineTo(34,6);c.closePath();c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.moveTo(45,-2);c.lineTo(50,0);c.lineTo(45,2);c.fill()
|
||||
c.beginPath();c.rect(-52,-6,8,12);toned(c,dot);c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.arc(128,32,23,0,T);toned(c,dot)
|
||||
c.lineWidth=2.5;for(const a of[.6,1.2,2])c.beginPath(),c.arc(128,32,14,a,a+.9),c.stroke()
|
||||
// back of the hair, pulled up
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(128,38);c.bezierCurveTo(196,38,204,96,196,140);c.lineTo(60,140);c.bezierCurveTo(52,96,60,38,128,38);toned(c,dot)
|
||||
// cardigan over a white blouse
|
||||
c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(62,196,54,250);c.lineTo(202,250);c.quadraticCurveTo(194,196,156,188);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(108,189);c.lineTo(118,250);c.lineTo(138,250);c.lineTo(148,189);c.closePath();c.fill();c.stroke()
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(128,206),c.lineTo(128+d*22,190),c.lineTo(128+d*16,212),c.closePath(),c.fill(),c.stroke()
|
||||
c.fillStyle='#111';for(const y of[226,240])c.beginPath(),c.arc(128,y,3,0,T),c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,168,24,24);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.lineWidth=6;face(c);c.fill();c.stroke()
|
||||
// calm half-lidded eyes behind the glasses
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
if(shut){c.lineWidth=5;c.beginPath();c.arc(x,136,12,.15*PI,.85*PI);c.stroke();continue}
|
||||
c.save();c.beginPath();c.rect(x-16,135,32,24);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,142,12,14,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,145,9,12,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-3,142,3.4,0,T);c.fill();c.beginPath();c.arc(x+3,151,1.6,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-14,136);c.quadraticCurveTo(x,132,x+14,136);c.lineTo(x+d*19,131);c.stroke()
|
||||
}
|
||||
c.lineWidth=3.5;for(const[x,d]of[[103,1],[153,-1]])c.beginPath(),c.moveTo(x-14,112+d*2),c.quadraticCurveTo(x,107,x+14,112-d*2),c.stroke()
|
||||
// round glasses with a glint sweeping across
|
||||
c.lineWidth=4.5;for(const x of[103,153])c.beginPath(),c.arc(x,140,20,0,T),c.stroke()
|
||||
c.beginPath();c.moveTo(123,138);c.quadraticCurveTo(128,133,133,138);c.moveTo(83,134);c.lineTo(76,130);c.moveTo(173,134);c.lineTo(180,130);c.stroke()
|
||||
if(g>0&&g<1)for(const x of[103,153]){c.save();c.beginPath();c.arc(x,140,18,0,T);c.clip();c.fillStyle='#fff';const o=-44+g*88;c.beginPath();c.moveTo(x+o-6,110);c.lineTo(x+o+6,110);c.lineTo(x+o-14,170);c.lineTo(x+o-26,170);c.closePath();c.fill();c.restore()}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<3;k++)c.beginPath(),c.moveTo(128+d*44+(k-1)*5-3,170),c.lineTo(128+d*44+(k-1)*5+3,162),c.stroke()
|
||||
c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.ellipse(128,168,6,4,0,0,T);c.fill();c.stroke()
|
||||
// center-parted bangs and side locks
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(70,140);c.bezierCurveTo(54,70,90,40,128,40);c.bezierCurveTo(166,40,202,70,186,140)
|
||||
c.quadraticCurveTo(184,120,174,118);c.quadraticCurveTo(140,110,128,66);c.quadraticCurveTo(116,110,82,118);c.quadraticCurveTo(72,120,70,140);c.closePath();toned(c,dot)
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*48,104),c.quadraticCurveTo(128+d*60,142,128+d*52,180),c.quadraticCurveTo(128+d*46,146,128+d*40,108),c.closePath(),toned(c,dot)
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<=24;k++){const a=PI*(1.24+k*.022);c.lineTo(128+(k%2?54:50)*cos(a),106+(k%2?54:50)*sin(a))}for(let k=24;k>=0;k--){const a=PI*(1.24+k*.022);c.lineTo(128+(k%2?46:42)*cos(a),106+(k%2?46:42)*sin(a))}c.fill()
|
||||
// hand with one finger up to her lips
|
||||
for(const[l,s]of[[30,'#111'],[23,'#fff'],[23,dot]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(70,252),c.quadraticCurveTo(84,214,118,200),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(122,148,13,40,6.5);c.fill();c.stroke()
|
||||
c.lineWidth=2;c.beginPath();c.arc(128.5,155,4,PI,0);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.roundRect(110,178,34,26,11);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const y of[186,194])c.beginPath(),c.moveTo(114,y),c.quadraticCurveTo(122,y+3,130,y),c.stroke()
|
||||
// book stack on the left
|
||||
for(const[x,y,w,p]of[[6,228,72,hat],[12,210,64,'#fff'],[2,192,70,dot]]){c.lineWidth=4;c.beginPath();c.roundRect(x,y,w,18,3);toned(c,p);c.fillStyle='#fff';c.beginPath();c.rect(x+w-12,y+3,8,12);c.fill();c.stroke()}
|
||||
c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.moveTo(30,192);c.lineTo(30,178);c.lineTo(36,184);c.lineTo(42,178);c.lineTo(42,192);c.fill();c.stroke()
|
||||
// shh arcs rolling out to the right
|
||||
c.lineCap='round';for(let k=0;k<3;k++){const u=(k+t*3)%3/3,r=40+u*36,w=1-u;if(w<.08)continue;c.lineWidth=2+4.5*w;c.beginPath();c.arc(150,168,r,-.42,.42);c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// calm girl in a kimono whisks matcha in a tea bowl with a bamboo chasen, foam bubbling up, eyes serene
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=80,rim=4
|
||||
const tone=(c,s,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle='#111';for(const[a,b]of[[0,0],[s,0],[0,s],[s,s],[s/2,s/2]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return c.createPattern(k,'repeat')}
|
||||
const B=[[-30,0,0],[-14,-4,.25],[4,3,.5],[22,-2,.75],[-4,-6,.1],[12,5,.6],[-22,4,.4],[30,2,.85],[0,0,.3]]
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8),HAIR=tone(c,5,1.1)
|
||||
const look=t>=.5&&t<.75,wx=128+16*sin(t*T*4),wy=4*abs(cos(t*T*4))
|
||||
// girl
|
||||
c.save();c.translate(128,90);c.scale(.78,.78);c.translate(-128,-130)
|
||||
// low bun at the back with a kanzashi stick
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.arc(186,156,22,0,T);c.fill();c.fillStyle=HAIR;c.fill();c.stroke()
|
||||
for(const[l,s]of[[9,'#111'],[3,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(168,134),c.lineTo(214,170),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#111';c.beginPath();c.arc(166,132,6,0,T);c.fill()
|
||||
// kimono: crossed collar, toned with little white flowers
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(96,184);c.quadraticCurveTo(40,196,30,300);c.lineTo(226,300);c.quadraticCurveTo(216,196,160,184);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle='#fff';c.lineWidth=2.5;for(const[x,y]of[[62,236],[198,226],[80,286],[186,284]]){for(let k=0;k<5;k++){const b=k*T/5;c.beginPath();c.arc(x+cos(b)*5,y+sin(b)*5,4,0,T);c.fill();c.stroke()}}c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=7;c.beginPath();c.moveTo(102,184);c.lineTo(156,260);c.lineTo(140,268);c.lineTo(94,196);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(154,184);c.lineTo(110,250);c.lineTo(124,258);c.lineTo(162,196);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.rect(116,166,24,22);c.fill();c.beginPath();c.moveTo(116,166);c.lineTo(116,186);c.moveTo(140,166);c.lineTo(140,186);c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
// serene closed eyes, opening for a look at the tea
|
||||
if(look)for(const[x,d]of[[103,-1],[153,1]]){
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,146,13,10,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,146,13,10,0,0,T);c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+1,151,10,12,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-3,146,3.5,4,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,141);c.quadraticCurveTo(x,134,x+16,141);c.moveTo(x+d*14,139);c.lineTo(x+d*20,135);c.stroke()}
|
||||
else{c.lineWidth=6;for(const[x,d]of[[103,-1],[153,1]]){c.beginPath();c.moveTo(x-15,142);c.quadraticCurveTo(x,152,x+15,142);c.stroke();c.lineWidth=4;for(const k of[-8,0,8])c.beginPath(),c.moveTo(x+k,149-abs(k)*.2),c.lineTo(x+k*1.2,155-abs(k)*.2),c.stroke();c.lineWidth=6}}
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(120,170);c.quadraticCurveTo(128,175,136,170);c.stroke()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,163,10,5,0,0,T),c.stroke()
|
||||
// screentone hair, side-parted, neat
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(70,156);c.bezierCurveTo(50,72,88,34,128,34);c.bezierCurveTo(168,34,206,72,186,156)
|
||||
c.quadraticCurveTo(184,120,178,108);c.quadraticCurveTo(160,92,140,90);c.quadraticCurveTo(128,96,122,108);c.quadraticCurveTo(110,96,94,104);c.quadraticCurveTo(80,116,76,130);c.quadraticCurveTo(72,142,70,156);c.closePath();c.fill();c.fillStyle=HAIR;c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(130,40);c.quadraticCurveTo(124,70,122,104);c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=7;for(const[s,e]of[[1.24,1.36],[1.6,1.72]])c.beginPath(),c.arc(128,108,54,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
// the tea bowl: glazed lower half, matcha inside, bubbling foam
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(60,214);c.quadraticCurveTo(62,254,100,254);c.lineTo(156,254);c.quadraticCurveTo(194,254,196,214);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=FINE;c.beginPath();c.moveTo(56,238);for(let x=56;x<=200;x+=12)c.quadraticCurveTo(x+6,230+(x%24?6:-2),x+12,238);c.lineTo(200,256);c.lineTo(56,256);c.fill();c.lineWidth=3;c.beginPath();c.moveTo(56,238);for(let x=56;x<=200;x+=12)c.quadraticCurveTo(x+6,230+(x%24?6:-2),x+12,238);c.stroke();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.ellipse(128,214,68,14,0,0,T);c.fillStyle='#fff';c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(128,216,58,9,0,0,T);c.fillStyle=DOT;c.fill()
|
||||
c.lineWidth=2.5;c.fillStyle='#fff';for(const[x,y,o]of B){const p=(t*2+o)%1,r=1.5+3.5*sin(PI*p);c.beginPath();c.arc(128+x,216+y,r,0,T);c.fill();c.stroke()}
|
||||
// her hands: one steadying the bowl, one gripping the whisk
|
||||
for(const[l,s]of[[24,'#111'],[14,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(84,160),c.quadraticCurveTo(48,184,54,218),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.ellipse(58,224,14,18,.3,0,T);c.fill();c.stroke()
|
||||
// chasen: bamboo handle and a bulb of tines, swishing side to side
|
||||
for(const[l,s]of[[13,'#111'],[6,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(wx,160+wy),c.lineTo(wx,184+wy),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(wx-6,182+wy);c.quadraticCurveTo(wx-20,198+wy,wx-12,212+wy);c.lineTo(wx+12,212+wy);c.quadraticCurveTo(wx+20,198+wy,wx+6,182+wy);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const k of[-8,-3,2,7])c.beginPath(),c.moveTo(wx+k*.6,186+wy),c.quadraticCurveTo(wx+k*1.6,200+wy,wx+k,210+wy),c.stroke()
|
||||
c.lineWidth=3;const dir=cos(t*T*4)>0?-1:1;for(const k of[0,1,2])c.beginPath(),c.moveTo(wx+dir*(20+k*6),192+wy+k*6),c.lineTo(wx+dir*(28+k*6),192+wy+k*6),c.stroke()
|
||||
for(const[l,s]of[[24,'#111'],[14,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(176,160),c.quadraticCurveTo(204,176,wx+14,166+wy),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.ellipse(wx+6,166+wy,13,11,0,0,T);c.fill();c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// manga medusa girl whose hair is little snakes wiggling and flicking their tongues, her ink sunglasses slipping down her nose until she pushes them back up
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min,atan2}=Math,T=2*PI
|
||||
export const frames=12,delay=110,rim=4
|
||||
const TL=createCanvas(6,6),tx=TL.getContext('2d');tx.fillStyle='#111';for(const[a,b]of[[1.5,1.5],[4.5,4.5]])tx.beginPath(),tx.arc(a,b,1.05,0,T),tx.fill()
|
||||
const ease=x=>x<0?0:x>1?1:x*x*(3-2*x)
|
||||
// gem-like eye: toned iris with a vertical slit
|
||||
const eye=(c,x,y,d,tn)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,15,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11,13,0,0,T);c.clip();c.fillStyle=tn;c.fillRect(x-12,y-12,24,28);c.fillStyle='#111';c.fillRect(x-12,y-12,24,7);c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11,13,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,2.5,9,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-5,y-2,3.5,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-5);c.quadraticCurveTo(x,y-18,x+16,y-5);c.lineTo(x+d*19,y-10);c.stroke()
|
||||
}
|
||||
// a little snake: toned banded body along a wiggling curve, round head with dot eyes and a flicking forked tongue
|
||||
const snake=(c,pts,tn,tongue)=>{
|
||||
for(const[l,s]of[[17,'#111'],[10,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();for(const[x,y]of pts)c.lineTo(x,y);c.stroke()}
|
||||
c.strokeStyle=tn;c.lineWidth=6;c.setLineDash([5,6]);c.beginPath();for(const[x,y]of pts.slice(0,-2))c.lineTo(x,y);c.stroke();c.setLineDash([]);c.strokeStyle='#111'
|
||||
const[x,y]=pts[pts.length-1],[px,py]=pts[pts.length-3],a=atan2(y-py,x-px)
|
||||
c.save();c.translate(x,y);c.rotate(a)
|
||||
if(tongue>0){c.lineWidth=2.5;c.beginPath();c.moveTo(10,0);c.lineTo(10+10*tongue,0);c.lineTo(14+10*tongue,-4);c.moveTo(10+10*tongue,0);c.lineTo(14+10*tongue,4);c.stroke()}
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(2,0,11,8.5,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(4,d*4,2,0,T),c.fill()
|
||||
c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat')
|
||||
// sunglasses slide down the nose, then a finger pushes them back up
|
||||
const sl=ease(t/.7)*(1-ease((t-.78)/.08)),push=t>.72&&t<.96?sin((t-.72)/.24*PI):0,gy=130+22*sl
|
||||
// snakes: base on the hairline, each curls outward and wiggles with its own phase
|
||||
// [angle on the hairline, length, wiggle phase, curl]
|
||||
const sn=[[-2.55,54,0,-1.3],[-2,50,1.6,1.2],[-1.57,46,3.1,-1.1],[-1.14,50,4.4,1.2],[-.6,54,5.6,1.3],[2.75,46,.8,1.4],[.4,46,3.4,-1.4]]
|
||||
const path=([a0,len,ph,cu])=>{const bx=128+54*cos(a0),by=114+52*sin(a0),out=[];let x=bx,y=by;for(let k=0;k<=12;k++){const s=k/12,a=a0+cu*s+.35*sin(t*T*2+ph+s*3);out.push([x,y]);x+=cos(a)*len/12;y+=sin(a)*len/12}return out}
|
||||
const tg=k=>max(0,sin(t*T*4+k*1.7))
|
||||
// hanging side snakes go behind the face
|
||||
for(const k of[5,6])snake(c,path(sn[k]),tn,tg(k))
|
||||
// chiton: white with a toned sash and a round brooch
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,256);c.lineTo(202,256);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=tn;c.beginPath();c.moveTo(150,192);c.lineTo(170,196);c.lineTo(100,256);c.lineTo(74,256);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(156,202,9,0,T);c.fill();c.stroke();c.beginPath();c.arc(156,202,4,0,T);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,168,24,24);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
// toned hair cap the snakes grow out of
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(68,150);c.bezierCurveTo(50,74,88,48,128,48);c.bezierCurveTo(168,48,206,74,188,150);c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(40,40,180,120);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(74,100);c.bezierCurveTo(70,154,100,186,128,188);c.bezierCurveTo(156,186,186,154,182,100);c.bezierCurveTo(182,50,74,50,74,100);c.fill();c.stroke()
|
||||
eye(c,103,132,-1,tn);eye(c,153,132,1,tn)
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+k-3,170),c.lineTo(128+d*44+k+3,162),c.stroke()
|
||||
// smug little cat smile, a sweat drop when they've slipped far
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(118,172);c.quadraticCurveTo(123,177,128,172);c.quadraticCurveTo(133,177,138,172);c.stroke()
|
||||
if(sl>.8){c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(186,108);c.quadraticCurveTo(196,124,188,130);c.quadraticCurveTo(178,124,186,108);c.fill();c.stroke()}
|
||||
// short wavy toned fringe
|
||||
const fr=()=>{c.beginPath();c.moveTo(72,120);c.bezierCurveTo(60,70,92,52,128,52);c.bezierCurveTo(164,52,196,70,184,120);for(const[x,y]of[[172,96],[156,106],[142,90],[128,104],[114,90],[100,106],[84,96]])c.quadraticCurveTo(x+6,y+10,x,y);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';fr();c.fill();c.save();fr();c.clip();c.fillStyle=tn;c.fillRect(56,48,144,76);c.restore();fr();c.stroke()
|
||||
// sunglasses: ink lenses with glints, covering the lower part of her eyes as they slip
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(78,gy);c.lineTo(70,gy-8);c.moveTo(178,gy);c.lineTo(186,gy-8);c.stroke()
|
||||
for(const x of[103,153]){c.fillStyle='#111';c.beginPath();c.roundRect(x-20,gy-6,40,22,8);c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(x-12,gy+10);c.lineTo(x-4,gy);c.moveTo(x-4,gy+12);c.lineTo(x+2,gy+5);c.stroke();c.strokeStyle='#111';c.lineWidth=5}
|
||||
c.beginPath();c.moveTo(123,gy);c.quadraticCurveTo(128,gy-4,133,gy);c.stroke()
|
||||
// top snakes over the hair cap, hissing
|
||||
for(let k=0;k<5;k++)snake(c,path(sn[k]),tn,tg(k))
|
||||
if(sl<.3){c.lineWidth=2.5;for(const[x,y]of[[46,74],[214,74]])for(let k=0;k<3;k++)c.beginPath(),c.arc(x,y,6+k*5,-.4,.4),c.stroke()}
|
||||
// finger pushing the bridge back up
|
||||
if(push>.02){const fy=gy+26+44*(1-push);c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(122,fy-18,12,30,6);c.fill();c.stroke();c.beginPath();c.ellipse(128,fy+22,18,14,0,0,T);c.fill();c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// glossy ink-haired girl with a side ponytail sneaking a folded note across the desk under her long sleeve, eyes darting, sweat drop, a hand snatches it, phew
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max,floor}=Math,T=2*PI
|
||||
export const frames=20,delay=90,rim=4
|
||||
const tile=createCanvas(6,6),q=tile.getContext('2d');q.fillStyle='#111';for(const[x,y]of[[1.5,1.5],[4.5,4.5]])q.beginPath(),q.arc(x,y,1.1,0,T),q.fill()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const ease=f=>f<=0?0:f>=1?1:.5-.5*cos(PI*f)
|
||||
const X0=134,X1=196
|
||||
const memo=(c,x,y)=>{c.save();c.translate(x,y);c.scale(1.45,1.45);c.lineWidth=2.6;c.fillStyle='#fff';c.beginPath();c.moveTo(-12,-6);c.lineTo(12,-8);c.lineTo(13,6);c.lineTo(-11,8);c.closePath();c.fill();c.stroke();c.lineWidth=1.6;c.beginPath();c.moveTo(-12,-6);c.lineTo(1,1);c.lineTo(12,-8);c.stroke();c.restore()}
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat')
|
||||
const nx=t<.06||t>=.9?X0:t<.32?X0+(X1-X0)*ease((t-.06)/.26):X1,sx=t<.6?nx:t<.74?X1+(X0-X1)*ease((t-.6)/.14):X0
|
||||
const hx=t<.52||t>=.74?280:t<.6?262-48*ease((t-.52)/.08):t<.64?214:214+52*ease((t-.64)/.1)
|
||||
const hasNote=t<.64||t>=.9,taken=t>=.64&&t<.74,phew=t>=.74&&t<.96,dart=t>=.32&&t<.52
|
||||
const px=dart?[6,-6,6,-6][min(3,floor((t-.32)/.05))]:6,sweat=t>=.2&&t<.64
|
||||
c.save();c.translate(-16,2)
|
||||
// side ponytail behind, then the back hair
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(186,70);c.quadraticCurveTo(236,92,226,150);c.quadraticCurveTo(222,176,206,186);c.quadraticCurveTo(214,140,190,100);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2;for(const o of[0,8])c.beginPath(),c.moveTo(196+o,86),c.quadraticCurveTo(222-o*.5,120,212-o,170),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(190,80,11,8,.6,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,206,100,196,166);c.lineTo(60,166);c.bezierCurveTo(50,100,60,36,128,36);c.fill();c.stroke()
|
||||
// long-sleeved dotted cardigan
|
||||
c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(62,196,54,236);c.lineTo(202,236);c.quadraticCurveTo(194,196,156,188);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(110,189);c.lineTo(128,214);c.lineTo(146,189);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.rect(116,168,24,22);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,188);c.moveTo(140,168);c.lineTo(140,188);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
// eyes: sly and sideways, darting, or shut in relief
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
if(phew){c.lineWidth=5;c.beginPath();c.moveTo(x-13,142);c.quadraticCurveTo(x,148,x+13,142);c.stroke();continue}
|
||||
c.save();c.beginPath();c.rect(x-16,135,32,26);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,143,13,14,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+px,145,8,11,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+px-2,141,3,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-14,136);c.quadraticCurveTo(x,132,x+14,136);c.lineTo(x+d*19,131);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(x-12,120+d*-2);c.quadraticCurveTo(x,115,x+12,120+d*2);c.stroke()
|
||||
}
|
||||
if(dart){c.lineWidth=3;for(const x of[103,153])for(const d of[-1,1])c.beginPath(),c.moveTo(x+d*18,150),c.lineTo(x+d*24,150),c.stroke()}
|
||||
c.lineWidth=3;c.beginPath();if(phew)c.ellipse(128,170,4,5,0,0,T);else c.moveTo(118,170),c.lineTo(124,167),c.lineTo(130,170),c.lineTo(136,167);c.stroke()
|
||||
// glossy ink bangs, side parted, with white strand lines and a cowlick
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(68,140);c.bezierCurveTo(52,74,88,38,128,38);c.bezierCurveTo(168,38,204,74,188,140)
|
||||
c.quadraticCurveTo(186,112,174,102);c.quadraticCurveTo(170,110,162,114);c.quadraticCurveTo(156,94,140,86);c.quadraticCurveTo(122,104,96,112);c.quadraticCurveTo(104,100,104,90);c.quadraticCurveTo(86,104,80,116);c.quadraticCurveTo(70,124,68,140);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2;for(const[x,y,e,f]of[[96,58,82,100],[114,48,96,96],[150,48,160,92],[168,60,178,98]])c.beginPath(),c.moveTo(x,y),c.quadraticCurveTo((x+e)/2+4,(y+f)/2,e,f),c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(138,40);c.quadraticCurveTo(148,18,162,24);c.stroke()
|
||||
if(sweat){c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(70,96);c.quadraticCurveTo(82,114,70,120);c.quadraticCurveTo(58,114,70,96);c.fill();c.stroke()}
|
||||
if(phew){const p=(t-.74)/.22;c.fillStyle='#fff';c.lineWidth=3;for(const[o,r]of[[0,8],[12,6]])c.beginPath(),c.arc(108-p*30-o,176+o*.4,r*(1-p*.4),0,T),c.fill(),c.stroke()}
|
||||
c.restore()
|
||||
// desk: top surface and dotted front
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(20,204);c.lineTo(236,204);c.lineTo(248,222);c.lineTo(8,222);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.rect(12,222,232,22);toned(c,dot)
|
||||
// the note, her long sleeve pushing it, the other hand that takes it
|
||||
if(hasNote&&!taken)memo(c,nx,212)
|
||||
for(const[l,st]of[[28,'#111'],[21,'#fff'],[21,dot]])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(150,200),c.quadraticCurveTo(160,214,sx-20,212),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=3.5;for(const k of[0,1])c.beginPath(),c.ellipse(sx-8,208+k*7,4,3,0,0,T),c.fill(),c.stroke()
|
||||
if(hx<270){if(taken)memo(c,hx-6,212)
|
||||
for(const[l,st]of[[26,'#111'],[20,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(hx+20,206),c.lineTo(290,198),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=3;c.beginPath();c.moveTo(hx+20,195);c.lineTo(hx+22,217);c.stroke()
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.roundRect(hx-8,198,28,18,8);c.fill();c.stroke();c.lineWidth=2.5;for(const y of[203,209])c.beginPath(),c.moveTo(hx-4,y),c.lineTo(hx+6,y),c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// chibi scooter rider in a starred helmet with goggles zooms right, striped scarf streaming back, wheels spinning past speed lines
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=60,rim=4
|
||||
const tile=(s,col,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[s/2,s/2],[0,0],[s,0],[0,s],[s,s]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const DOT=tile(6,'#111',1.4),LITE=tile(5,'#fff',1)
|
||||
const limb=(c,p,w)=>{for(const[l,s]of[[w+8,'#111'],[w,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...p[0]);for(const q of p.slice(1))c.lineTo(...q);c.stroke()}c.strokeStyle='#111'}
|
||||
const star=(c,x,y,r)=>{c.beginPath();for(let k=0;k<10;k++){const q=k%2?r*.45:r,a=k*PI/5-PI/2;c.lineTo(x+cos(a)*q,y+sin(a)*q)}c.closePath()}
|
||||
const wheel=(c,x,y,a,dt)=>{c.lineWidth=11;c.beginPath();c.arc(x,y,22,0,T);c.stroke();c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(x,y,16,0,T);c.fill();c.stroke()
|
||||
c.fillStyle=dt;c.beginPath();c.arc(x,y,16,.3,1.5);c.lineTo(x,y);c.fill()
|
||||
c.lineWidth=4;for(let k=0;k<5;k++){const b=a+k*T/5;c.beginPath();c.moveTo(x+cos(b)*5,y+sin(b)*5);c.lineTo(x+cos(b)*15,y+sin(b)*15);c.stroke()}
|
||||
c.fillStyle='#111';c.beginPath();c.arc(x,y,5,0,T);c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.arc(x,y,22,3.6,4.6);c.stroke();c.beginPath();c.arc(x,y,22,.4,1.2);c.stroke();c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d,lt)=>{
|
||||
c.save();c.beginPath();c.moveTo(x-17,y-4+d*3);c.lineTo(x+17,y-4-d*3);c.lineTo(x+17,y+22);c.lineTo(x-17,y+22);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,15,0,0,T);c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+6,y+3,9,12,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-4,y+7,22,10)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+3,y-1,4,0,T);c.fill();c.beginPath();c.arc(x+10,y+9,1.8,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-4+d*3);c.lineTo(x+16,y-4-d*3);c.lineTo(x+d*21,y-10);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const dt=c.createPattern(DOT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
// wheels turn 27deg a frame (6 spoke-periods a loop) so they read as rolling forward
|
||||
const sp=t*T*1.2,b=2*sin(t*T*4),g=2*sin(t*T*4-1),fl=sin(t*T*3)
|
||||
c.lineWidth=4;for(const[y,o,l]of[[34,0,52],[92,.5,40],[150,.25,44],[200,.75,30]]){const x=((o-t*2)%1+1)%1*300-60;c.beginPath();c.moveTo(x,y);c.lineTo(x+l,y);c.stroke()}
|
||||
c.lineWidth=5;c.beginPath();for(let k=0;k<3;k++){const x=((k/3-t*2)%1+1)%1*300-40;c.moveTo(x,246);c.lineTo(x+30,246)}c.stroke()
|
||||
// exhaust puffs drift back and grow
|
||||
c.fillStyle='#fff';c.lineWidth=4;for(let k=0;k<3;k++){const u=(t*2+k/3)%1,x=40-34*u,y=212-10*u-b;c.beginPath();c.arc(x,y,5+9*u,0,T);c.fill();c.stroke()}
|
||||
c.save();c.translate(0,b)
|
||||
// scarf streams back behind everything
|
||||
const nk=[110,120+g-b]
|
||||
c.fillStyle='#fff';c.lineWidth=5
|
||||
for(const[d,len]of[[0,92],[1,70]]){const top=[],bot=[];for(let k=0;k<=6;k++){const x=nk[0]-k*len/6,y=nk[1]-2+d*10-k*3+8*sin(t*T*3-k*.9+d*1.3)*(k/6),h=10-k*.7;top.push([x,y-h]);bot.push([x,y+h])}
|
||||
const sc=()=>{c.beginPath();c.moveTo(...top[0]);for(const p of top.slice(1))c.lineTo(...p);for(const p of [...bot].reverse())c.lineTo(...p);c.closePath()}
|
||||
sc();c.fill();c.save();c.clip();c.lineWidth=5;c.beginPath();for(let k=1;k<6;k+=2){const x=nk[0]-k*len/6;c.moveTo(x,60);c.lineTo(x-4,180)}c.stroke();c.restore();c.lineWidth=5;sc();c.stroke()
|
||||
c.lineWidth=3;for(const q of[-4,0,4]){const p=[nk[0]-len,nk[1]-2+d*10-18+8*sin(t*T*3-5.4+d*1.3)];c.beginPath();c.moveTo(p[0]+2,p[1]+q);c.lineTo(p[0]-8,p[1]+q*1.6);c.stroke()}}
|
||||
// rear cowling, floorboard, leg shield and fork
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.moveTo(44,196);c.quadraticCurveTo(38,160,76,156);c.lineTo(122,158);c.quadraticCurveTo(136,168,132,200);c.lineTo(96,206);c.quadraticCurveTo(70,186,44,196);c.closePath();c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(30,176,110,40);c.fillStyle='#111';c.fillRect(30,170,110,6);c.restore();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.roundRect(64,146,64,14,7);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(96,204);c.lineTo(166,204);c.lineTo(170,214);c.lineTo(100,216);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(184,128);c.lineTo(198,212);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.moveTo(160,206);c.quadraticCurveTo(158,160,176,124);c.lineTo(190,126);c.quadraticCurveTo(196,170,186,206);c.closePath();c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(176,110,30,100);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(186,124);c.quadraticCurveTo(212,124,212,140);c.lineTo(186,144);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(184,184);c.quadraticCurveTo(206,180,214,196);c.lineTo(196,198);c.closePath();c.fill();c.stroke()
|
||||
wheel(c,70,214-b,sp,dt);wheel(c,196,214-b,sp,dt)
|
||||
// rider: legs on the floorboard, torso, then arms to the bars
|
||||
const H=[96,152+g-b]
|
||||
limb(c,[[H[0]+4,H[1]+2],[136,158+g-b],[146,198]],17);c.fillStyle='#111';c.lineWidth=5;c.beginPath();c.roundRect(136,192,26,13,5);c.fill();c.stroke()
|
||||
c.save();c.translate(...H);c.rotate(.22)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-18,6);c.lineTo(20,6);c.lineTo(22,-38);c.quadraticCurveTo(2,-48,-18,-40);c.closePath();c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(-24,-50,18,60);c.restore();c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(4,-42);c.lineTo(6,4);c.stroke();c.fillStyle='#111';for(const y of[-30,-16])c.beginPath(),c.arc(10,y,2.5,0,T),c.fill()
|
||||
c.restore()
|
||||
// head: helmet with goggles and a star decal
|
||||
c.save();c.translate(122,84+g-b);c.rotate(.08);c.scale(.62,.62);c.translate(-128,-121)
|
||||
c.lineWidth=9;c.fillStyle='#fff'
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,116,140,-1,lt);eye(c,164,140,1,lt)
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const j of[-6,0,6])c.beginPath(),c.moveTo(140+d*42+j-3,168),c.lineTo(140+d*42+j+3,160),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath();c.moveTo(128,162);c.lineTo(160,160);c.quadraticCurveTo(156,184,144,184);c.quadraticCurveTo(132,184,128,162);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(134,163);c.lineTo(154,162);c.lineTo(152,168);c.lineTo(136,169);c.closePath();c.fill()
|
||||
c.fillStyle=dt;c.lineWidth=8;c.beginPath();c.moveTo(80,128);c.quadraticCurveTo(84,96,104,96);c.lineTo(112,118);c.lineTo(122,98);c.lineTo(134,116);c.lineTo(146,98);c.lineTo(156,116);c.lineTo(166,98);c.lineTo(176,112);c.quadraticCurveTo(180,100,180,96);c.lineTo(80,100);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=9;c.beginPath();c.moveTo(62,150);c.bezierCurveTo(46,40,98,18,134,20);c.bezierCurveTo(186,22,210,60,200,104);c.lineTo(176,104);c.quadraticCurveTo(128,88,80,104);c.lineTo(76,150);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(118,22);c.quadraticCurveTo(122,60,124,96);c.lineTo(140,94);c.quadraticCurveTo(140,58,134,21);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=5;star(c,86,70,16);c.fill();c.stroke()
|
||||
c.lineWidth=9;c.fillStyle='#111';c.beginPath();c.moveTo(80,92);c.quadraticCurveTo(134,72,196,90);c.stroke()
|
||||
for(const x of[146,182]){c.fillStyle='#fff';c.beginPath();c.arc(x,78,15,0,T);c.fill();c.stroke();c.fillStyle=dt;c.beginPath();c.arc(x,78,9,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-3,74,3.5,0,T);c.fill()}
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(70,146);c.quadraticCurveTo(80,180,104,184);c.stroke()
|
||||
c.restore()
|
||||
// scarf wrap around the neck
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.roundRect(100,122+g-b,34,14,7);c.fill();c.save();c.clip();c.beginPath();for(const x of[106,118,130])c.moveTo(x,110),c.lineTo(x-4,150);c.stroke();c.restore();c.beginPath();c.roundRect(100,122+g-b,34,14,7);c.stroke()
|
||||
// arms reach to the handlebar grip
|
||||
const sh=[104,118+g-b];limb(c,[sh,[146,136],[184,126]],13);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(184,126,9,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// manga chibi mummy girl with screentone twin buns: she twirls, a loose bandage unravels into a spiral round her while she flusters, then it winds back on
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=90,rim=4
|
||||
const TL=createCanvas(6,6),tx=TL.getContext('2d');tx.fillStyle='#111';for(const[a,b]of[[1.5,1.5],[4.5,4.5]])tx.beginPath(),tx.arc(a,b,1.05,0,T),tx.fill()
|
||||
const ease=x=>x<0?0:x>1?1:x*x*(3-2*x)
|
||||
// sleepy round eye with a heavy lid
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,14,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+5,9,10,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-3,y+1,3.5,0,T);c.fill();c.beginPath();c.arc(x+4,y+9,1.6,0,T);c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-15,y-1);c.quadraticCurveTo(x,y-8,x+15,y-1);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(x+d*14,y-2);c.lineTo(x+d*20,y-4);c.stroke()
|
||||
}
|
||||
// wavy wrap lines across whatever shape is clipped
|
||||
// slanted bandage strips with little overlap ticks, across whatever shape is clipped
|
||||
const wraps=(c,x0,x1,y0,y1,g)=>{c.lineWidth=2.5;c.beginPath();for(let y=y0-30;y<y1;y+=g){c.moveTo(x0,y);c.lineTo(x1,y+(x1-x0)*.22);for(let x=x0+14+(y/g|0)%3*9;x<x1;x+=34){const yy=y+(x-x0)*.22;c.moveTo(x,yy);c.lineTo(x+3,yy+g*.5)}}c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat')
|
||||
c.translate(128,242);c.scale(1.16,1.16);c.translate(-128,-242)
|
||||
const u=ease(t/.4)-ease((t-.6)/.36),fl=u>.15,sp=t*T*2,tw=.08*sin(t*T*2)
|
||||
// the loose bandage: a helix around her, from the waist outward; split into behind and in-front passes
|
||||
const L=u*1,pts=[];for(let k=0;k<=60;k++){const s=k/60*L,a=sp+s*T*2.2,R=34+44*s;pts.push([128+R*cos(a),200-120*s+R*.28*sin(a),sin(a)])}
|
||||
// stroke each unbroken run of front (or back) segments as one ribbon, so there are no seams along it
|
||||
const band=front=>{let run=[];const flush=()=>{if(run.length>1)for(const[l,s]of[[14,'#111'],[8,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();for(const[x,y]of run)c.lineTo(x,y);c.stroke()}run=[]}
|
||||
for(let k=1;k<pts.length;k++){if((pts[k-1][2]>0)==front){if(!run.length)run.push(pts[k-1]);run.push(pts[k])}else flush()}flush();c.strokeStyle='#111'}
|
||||
band(false)
|
||||
// twirl lines round her feet
|
||||
if(fl){c.lineWidth=3;c.beginPath();c.ellipse(128,236,52,8,0,PI*.1,PI*.9);c.stroke();c.beginPath();c.ellipse(128,236,62,10,0,PI*1.15,PI*1.5);c.stroke()}
|
||||
// little wrapped feet and body
|
||||
c.fillStyle='#fff';c.lineWidth=5;for(const x of[112,144])c.beginPath(),c.ellipse(x,234,14,8,0,0,T),c.fill(),c.stroke()
|
||||
const body=()=>{c.beginPath();c.moveTo(104,142);c.quadraticCurveTo(92,190,98,232);c.lineTo(158,232);c.quadraticCurveTo(164,190,152,142);c.closePath()}
|
||||
c.lineWidth=6;body();c.fill();c.save();body();c.clip();wraps(c,88,170,150,232,11);c.restore();body();c.stroke()
|
||||
// arms: hands up to her cheeks when flustered, down at her sides otherwise
|
||||
for(const d of[-1,1]){const hx=128+d*(fl?34:42),hy=fl?112:196
|
||||
for(const[l,s]of[[20,'#111'],[12,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128+d*22,152),c.quadraticCurveTo(128+d*48,fl?150:170,hx,hy),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(hx,hy,9,0,T);c.fill();c.stroke();c.lineWidth=2;c.beginPath();c.moveTo(hx-6,hy-2);c.lineTo(hx+6,hy+2);c.stroke()}
|
||||
// head
|
||||
c.save();c.translate(128,92);c.scale(.6,.6);c.translate(-128,-120)
|
||||
// screentone twin buns poking out of the head wrap
|
||||
// screentone twin tails hanging from under the head wrap, tied off with bandage
|
||||
c.lineWidth=7;for(const d of[-1,1]){const tl=()=>{c.beginPath();c.moveTo(128+d*56,90);c.bezierCurveTo(128+d*104,100,128+d*108,170,128+d*96,204);c.quadraticCurveTo(128+d*76,180,128+d*48,120);c.closePath()}
|
||||
c.fillStyle='#fff';tl();c.fill();c.save();tl();c.clip();c.fillStyle=tn;c.fillRect(20,80,216,160);c.restore();tl();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(128+d*52,96);c.lineTo(128+d*84,86);c.lineTo(128+d*86,106);c.lineTo(128+d*56,114);c.closePath();c.fillStyle='#fff';c.fill();c.stroke();c.lineWidth=7}
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(66,150);c.bezierCurveTo(48,70,86,36,128,36);c.bezierCurveTo(170,36,208,70,190,150);c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(40,30,180,130);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(fl){c.lineWidth=6;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*14,128),c.lineTo(128+d*36,136),c.lineTo(128+d*14,144),c.stroke()}
|
||||
else{eye(c,103,134,-1);eye(c,153,134,1)}
|
||||
const bl=fl?6:3;c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<bl;k++)c.beginPath(),c.moveTo(128+d*(48-k*5)-3,166),c.lineTo(128+d*(48-k*5)+3,156),c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath()
|
||||
if(fl){c.moveTo(116,170);for(let k=0;k<6;k++)c.lineTo(120+k*4,k%2?166:172)}else c.arc(128,164,5,.2*PI,.8*PI)
|
||||
c.stroke()
|
||||
if(fl){c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(188,96);c.quadraticCurveTo(198,112,190,118);c.quadraticCurveTo(180,112,188,96);c.fill();c.stroke()}
|
||||
// bandage band across the forehead with a loose flap, and a patch on one cheek
|
||||
const fh=()=>{c.beginPath();c.moveTo(70,78);c.quadraticCurveTo(128,64,186,78);c.lineTo(188,104);c.quadraticCurveTo(128,90,68,104);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';fh();c.fill();c.save();fh();c.clip();wraps(c,60,196,74,104,10);c.restore();fh();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(186,82);c.lineTo(212,70+6*sin(t*T*4));c.lineTo(210,90+6*sin(t*T*4));c.lineTo(188,98);c.fill();c.stroke()
|
||||
c.lineWidth=4;c.save();c.translate(156,160);c.rotate(-.3);c.beginPath();c.rect(-12,-5,24,10);c.fill();c.stroke();c.beginPath();c.moveTo(-4,-5);c.lineTo(-4,5);c.moveTo(4,-5);c.lineTo(4,5);c.lineWidth=2;c.stroke();c.restore()
|
||||
c.restore()
|
||||
band(true)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// chibi girl in a dark hakama with white low braids twirls a naginata overhead like a propeller, blade trailing motion arcs
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,atan2,hypot}=Math,T=2*PI
|
||||
export const frames=16,delay=60,rim=4
|
||||
const tile=(s,col,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[s/2,s/2],[0,0],[s,0],[0,s],[s,s]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const DOT=tile(6,'#111',1.4),LITE=tile(5,'#fff',1)
|
||||
const limb=(c,p,w)=>{for(const[l,s]of[[w+8,'#111'],[w,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...p[0]);for(const q of p.slice(1))c.lineTo(...q);c.stroke()}c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d,lt)=>{
|
||||
c.save();c.beginPath();c.moveTo(x-17,y-6+d*4);c.lineTo(x+17,y-6-d*4);c.lineTo(x+17,y+22);c.lineTo(x-17,y+22);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,15,0,0,T);c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y-1,10,12,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-10,y+3,22,10)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-3,y-5,4,0,T);c.fill();c.beginPath();c.arc(x+4,y+6,1.8,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6+d*4);c.lineTo(x+16,y-6-d*4);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-13,y-25+d*5);c.lineTo(x+13,y-25-d*5);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const dt=c.createPattern(DOT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const th=-T*2*t,b=2*sin(t*T*4),cx=128,cy=42,R=64,r=16
|
||||
const P=a=>[cx+R*cos(a),cy+r*sin(a)],A=P(th),B=P(th+PI)
|
||||
c.save();c.translate(128,136);c.scale(.93,.93);c.translate(-128,-136)
|
||||
c.fillStyle=dt;c.beginPath();c.ellipse(128,242,52,7,0,0,T);c.fill()
|
||||
// motion arcs trail behind the blade
|
||||
c.lineWidth=5;for(const q of[0,1])c.beginPath(),c.ellipse(cx,cy,R+26-q*12,r+8-q*4,0,th+.25,th+1.5-q*.3),c.stroke()
|
||||
// hakama legs, body
|
||||
c.save();c.translate(0,b)
|
||||
const hk=()=>{c.beginPath();c.moveTo(92,184);c.lineTo(164,184);c.lineTo(184,240+2*sin(t*T*4));c.lineTo(134,240);c.lineTo(128,214);c.lineTo(122,240);c.lineTo(72,240-2*sin(t*T*4));c.closePath()}
|
||||
c.fillStyle='#111';c.lineWidth=14;c.strokeStyle='#fff';hk();c.stroke();c.lineWidth=6;c.strokeStyle='#111';hk();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();for(const[x0,x1]of[[108,96],[118,112],[140,146],[150,162]])c.moveTo(x0,190),c.lineTo(x1,236);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=5;for(const x of[96,160])c.beginPath(),c.ellipse(x,244,14,6,0,0,T),c.fill(),c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.moveTo(96,186);c.lineTo(94,150);c.quadraticCurveTo(128,140,162,150);c.lineTo(160,186);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(116,146);c.lineTo(134,172);c.moveTo(140,146);c.lineTo(128,162);c.stroke()
|
||||
c.fillStyle='#111';c.fillRect(94,176,68,10);c.lineWidth=5;c.strokeStyle='#111';c.beginPath();c.moveTo(96,150);c.lineTo(160,180);c.stroke()
|
||||
// low fist at the hip
|
||||
limb(c,[[156,154],[170,176],[154,188]],13);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(152,188,9,0,T);c.fill();c.stroke()
|
||||
// raised arm goes up behind the head to grip the middle of the shaft
|
||||
limb(c,[[154,152],[194,108],[cx+8,cy+8]],14)
|
||||
// head with white low braids
|
||||
c.save();c.translate(124,106);c.scale(.62,.62);c.translate(-128,-121)
|
||||
c.lineWidth=9;c.fillStyle='#fff'
|
||||
for(const d of[-1,1]){const w=4*sin(t*T*4+d);c.beginPath();c.moveTo(128+d*54,140);c.quadraticCurveTo(128+d*70,180,128+d*62+w,226);c.lineTo(128+d*46+w,226);c.quadraticCurveTo(128+d*52,180,128+d*40,146);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const y of[166,186,206])c.beginPath(),c.moveTo(128+d*44+w*(y-140)/86,y),c.quadraticCurveTo(128+d*56,y+8,128+d*66+w*(y-140)/86,y),c.stroke()
|
||||
c.lineWidth=9;c.fillStyle='#111';c.beginPath();c.roundRect(128+d*54-10+w,222,20,14,4);c.fill();c.stroke();c.fillStyle='#fff'}
|
||||
c.beginPath();c.moveTo(68,150);c.bezierCurveTo(50,72,88,36,128,36);c.bezierCurveTo(168,36,206,72,188,150);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,104,140,-1,lt);eye(c,152,140,1,lt)
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const j of[-6,0,6])c.beginPath(),c.moveTo(128+d*44+j-3,166),c.lineTo(128+d*44+j+3,158),c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.quadraticCurveTo(128,176,140,166);c.stroke()
|
||||
c.beginPath();c.moveTo(72,124);c.bezierCurveTo(66,66,100,48,128,50);c.bezierCurveTo(156,48,190,66,184,124);c.lineTo(176,108);c.lineTo(168,116);c.lineTo(160,100);c.lineTo(148,112);c.lineTo(136,96);c.lineTo(128,110);c.lineTo(120,96);c.lineTo(108,112);c.lineTo(96,100);c.lineTo(88,116);c.lineTo(80,108);c.closePath();c.lineWidth=9;c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const[a,y0,e,y1]of[[100,62,94,90],[156,62,162,90],[128,54,128,80]])c.beginPath(),c.moveTo(a,y0),c.quadraticCurveTo((a+e)/2+6,(y0+y1)/2,e,y1),c.stroke()
|
||||
c.restore()
|
||||
c.restore()
|
||||
// naginata: foreshortened shaft, curved blade on the A end, black cap on the B end
|
||||
const near=sin(th)>0,dl=hypot(A[0]-B[0],A[1]-B[1]),u=[(A[0]-B[0])/dl,(A[1]-B[1])/dl],f=.45+.55*dl/(2*R)
|
||||
const blade=()=>{c.save();c.translate(...A);c.rotate(atan2(u[1],u[0]));c.scale(f*(near?.78:.66),near?.78:.66);c.fillStyle='#fff';c.lineWidth=5
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(0,0,5,10,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(4,-6);c.lineTo(34,-8);c.quadraticCurveTo(58,-10,66,-22);c.quadraticCurveTo(60,4,34,6);c.lineTo(4,6);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(10,0);c.lineTo(34,-1);c.quadraticCurveTo(54,-4,60,-14);c.stroke();c.restore()}
|
||||
const shaft=()=>{for(const[l,s]of[[13,'#111'],[6,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(...B),c.lineTo(...A),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#111';c.beginPath();c.arc(...B,7,0,T);c.fill()}
|
||||
if(!near)blade();shaft();if(near)blade()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(cx,cy+4,11,10,0,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// chibi in a striped jumpsuit with an inky braid holds a cool wide stance while her nunchaku whirls in blurred circles at her side
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=50,rim=4
|
||||
const tile=(s,col,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[s/2,s/2],[0,0],[s,0],[0,s],[s,s]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const DOT=tile(6,'#111',1.4),LITE=tile(5,'#fff',1)
|
||||
const limb=(c,p,w)=>{for(const[l,s]of[[w+8,'#111'],[w,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...p[0]);for(const q of p.slice(1))c.lineTo(...q);c.stroke()}c.strokeStyle='#111'}
|
||||
const stick=(c,x,y,a,dt)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.roundRect(-6,0,12,44,5);c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(-6,14,12,30);c.restore();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(-6,10);c.lineTo(6,10);c.stroke();c.restore()}
|
||||
const eye=(c,x,y,d,lt)=>{
|
||||
c.save();c.beginPath();c.moveTo(x-17,y-2+d*5);c.lineTo(x+17,y-2-d*5);c.lineTo(x+17,y+16);c.lineTo(x-17,y+16);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,15,0,0,T);c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+d*-4,y+4,9,12,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-14,y+6,22,10)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+d*-6,y+1,3.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-2+d*5);c.lineTo(x+16,y-2-d*5);c.lineTo(x+d*21,y-6-d*2);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-10,y+14);c.lineTo(x+10,y+14);c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-14,y-18+d*8);c.lineTo(x+14,y-18-d*8);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const dt=c.createPattern(DOT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const a=T*2*t,b=1.5*abs(sin(t*T*4)),hx=180,hy=140+b
|
||||
c.save();c.translate(-6,0)
|
||||
c.fillStyle=dt;c.beginPath();c.ellipse(116,240,62,7,0,0,T);c.fill()
|
||||
// whirl blur rings behind the free stick
|
||||
c.lineWidth=5;for(const q of[0,1,2])c.beginPath(),c.arc(hx+4,hy-34,58-q*9,a-PI/2-1.8+q*.3,a-PI/2-.2),c.stroke()
|
||||
c.save();c.translate(0,b)
|
||||
// horse stance legs with a stripe down the jumpsuit
|
||||
for(const d of[-1,1]){const p=[[116+d*12,182],[116+d*40,206],[116+d*46,234]];limb(c,p,18);c.lineWidth=4;c.beginPath();c.moveTo(p[0][0]+d*7,p[0][1]+2);c.lineTo(p[1][0]+d*8,p[1][1]);c.lineTo(p[2][0]+d*8,p[2][1]-4);c.stroke()
|
||||
c.fillStyle='#111';c.lineWidth=5;c.beginPath();c.ellipse(116+d*50,240-b,16,7,0,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.moveTo(92,186);c.lineTo(92,144);c.quadraticCurveTo(116,134,140,144);c.lineTo(140,186);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const x of[96,136])c.beginPath(),c.moveTo(x,148),c.lineTo(x,184),c.stroke()
|
||||
c.fillStyle='#111';c.fillRect(92,174,48,9);c.lineWidth=4;c.beginPath();c.moveTo(104,142);c.lineTo(116,156);c.lineTo(128,142);c.stroke()
|
||||
// forward guard hand, palm out
|
||||
limb(c,[[96,150],[72,160],[58,140]],13);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.roundRect(48,118,18,26,6);c.fill();c.stroke();c.beginPath();c.ellipse(70,136,5,8,.5,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
// free stick swings behind her head on the left half of its circle
|
||||
const P=[hx+4,hy-34],ca=a-PI/2,ch=[P[0]+12*cos(ca),P[1]+12*sin(ca)],back=cos(ca)<-.2
|
||||
const free=()=>{c.lineWidth=3;c.setLineDash([3,3]);c.beginPath();c.moveTo(...P);c.lineTo(...ch);c.stroke();c.setLineDash([]);stick(c,...ch,ca-PI/2,dt)}
|
||||
if(back)free()
|
||||
// head, with the braid swinging over her shoulder
|
||||
c.save();c.translate(114,98+b);c.rotate(-.06);c.scale(.66,.66);c.translate(-128,-121)
|
||||
c.lineWidth=9;c.fillStyle='#111'
|
||||
const sw=6*sin(t*T*2);c.beginPath();c.moveTo(64,140);c.quadraticCurveTo(44,200,56+sw,248);c.lineTo(76+sw,248);c.quadraticCurveTo(70,198,88,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;for(const y of[176,200,224])c.beginPath(),c.moveTo(56+sw*(y-140)/108,y),c.quadraticCurveTo(66,y+8,78+sw*(y-140)/108,y-4),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(52+sw,244,26,12,4);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(66,154);c.bezierCurveTo(48,74,88,36,128,36);c.bezierCurveTo(168,36,208,74,190,154);c.lineTo(170,154);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,104,140,-1,lt);eye(c,152,140,1,lt)
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.quadraticCurveTo(132,172,144,162);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(70,128);c.lineTo(70,90);c.bezierCurveTo(86,48,170,48,186,90);c.lineTo(188,128);c.lineTo(180,110);c.lineTo(172,122);c.quadraticCurveTo(164,100,150,98);c.lineTo(140,118);c.quadraticCurveTo(132,98,118,96);c.lineTo(100,116);c.quadraticCurveTo(92,104,86,104);c.lineTo(80,122);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=7;for(const[s,e]of[[1.2,1.32],[1.4,1.56],[1.64,1.76]])c.beginPath(),c.arc(128,122,60,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
// spinning nunchaku: the held stick points up from her fist, the free stick whirls on its chain around the held stick's top
|
||||
limb(c,[[136,148+b],[164,140+b],[hx,hy]],13)
|
||||
stick(c,hx-2,hy+8,PI+.1,dt)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(hx,hy,10,0,T);c.fill();c.stroke()
|
||||
if(!back)free()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// tiny chibi with a dotted top bun running in a panicked circle: arms flailing overhead, wailing, sweat flying off, a dust trail puffing behind her
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=20,delay=70,rim=4
|
||||
const DOT=(()=>{const t=createCanvas(6,6),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(3,3,1.5,0,T);x.fill();return t})()
|
||||
const toned=(c,path)=>{path();c.fillStyle='#fff';c.fill();c.fillStyle=c.createPattern(DOT,'repeat');c.fill();path();c.stroke()}
|
||||
const puff=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.fillStyle='#fff';c.lineWidth=4/s;c.beginPath();c.arc(-8,0,8,PI*.5,PI*1.6);c.arc(2,-5,9,PI*1.1,PI*1.95);c.arc(10,1,7,PI*1.4,PI*.5);c.closePath();c.fill();c.stroke();c.restore()}
|
||||
// chibi drawn facing +x with feet at the origin, r is the run cycle angle
|
||||
const chibi=(c,r)=>{
|
||||
for(const d of[-1,1]){const a=d*.7*sin(r);c.lineWidth=7;c.beginPath();c.moveTo(d*5,-22);c.lineTo(d*5+sin(a)*16,-22+cos(a)*20);c.stroke();c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(d*5+sin(a)*16+3,-1+cos(a)*2-4,7,4.5,0,0,T);c.fill();c.stroke()}
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-14,-22);c.lineTo(14,-22);c.lineTo(9,-46);c.lineTo(-9,-46);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=5;toned(c,()=>{c.beginPath();c.arc(-2,-108,13,0,T)})
|
||||
toned(c,()=>{c.beginPath();c.ellipse(0,-74,30,28,0,0,T)})
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(-26,-70);c.quadraticCurveTo(-24,-46,6,-46);c.quadraticCurveTo(30,-48,30,-74);c.lineTo(22,-72);c.lineTo(16,-80);c.lineTo(8,-72);c.lineTo(-2,-82);c.lineTo(-10,-72);c.lineTo(-18,-80);c.closePath();c.fill();c.stroke()
|
||||
for(const x of[-2,16]){c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.ellipse(x,-64,6,7,0,0,T);c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.arc(x+2,-64,1.8,0,T);c.fill()}
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(8,-52,5,4+1.5*sin(4*r),0,0,T);c.fill()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(-8,-76);c.lineTo(0,-73);c.moveTo(12,-73);c.lineTo(20,-76);c.stroke()
|
||||
for(const d of[-1,1]){const a=-PI/2+d*(1.05+.45*sin(2*r+d*1.3)),hx=d*12+cos(a)*36,hy=-44+sin(a)*36
|
||||
for(const[l,st]of[[9,'#111'],[3,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(d*12,-44),c.lineTo(hx,hy),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(hx,hy,6,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.arc(hx-cos(a)*2,hy-sin(a)*2,13,a-d*1.9,a-d*1.2,d>0);c.stroke()}}
|
||||
export default(c,t)=>{
|
||||
const A=t*T+PI/2,pos=a=>[128+76*cos(a),178+30*sin(a)],dep=a=>.8+.2*sin(a)
|
||||
c.fillStyle=c.createPattern(DOT,'repeat');c.beginPath();c.ellipse(128,178,94,40,0,0,T);c.ellipse(128,178,58,22,0,0,T);c.fill('evenodd');c.lineWidth=3;c.beginPath();c.ellipse(128,178,94,40,0,0,T);c.stroke();c.beginPath();c.ellipse(128,178,58,22,0,0,T);c.stroke()
|
||||
const draw=()=>{const[x,y]=pos(A),s=dep(A)*1.35,dir=-sin(A)<0?-1:1
|
||||
for(let k=4;k>=1;k--){const[px,py]=pos(A-k*.38*(dir>0?1:1)),q=dep(A-k*.38)*(1-k/5)*1.2;puff(c,px,py,q)}
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y,18*s,5*s,0,0,T);c.fill()
|
||||
c.save();c.translate(x,y);c.scale(s*dir,s);chibi(c,t*T*4);c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=3.5
|
||||
for(let k=0;k<3;k++){const p=(t*4+k/3)%1,dx=-dir*(14+p*30)*s,dy=(-120+k*14-p*20+p*p*30)*s
|
||||
if(p<.85){c.save();c.translate(x+dx,y+dy);c.rotate(dir*(PI/2+.4));c.scale(s*.8,s*.8);c.beginPath();c.moveTo(0,-10);c.quadraticCurveTo(7,0,6,4);c.arc(0,4,6,0,PI);c.quadraticCurveTo(-7,0,0,-10);c.fill();c.stroke();c.restore()}}}
|
||||
draw()
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// chibi traceur in a dotted hoodie, hood up over white fluffy bangs, sprints past speed lines and speed-vaults a brick wall
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs,min,max,hypot}=Math,T=2*PI
|
||||
export const frames=16,delay=65,rim=4
|
||||
const tile=(s,col,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[s/2,s/2],[0,0],[s,0],[0,s],[s,s]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const DOT=tile(6,'#111',1.4),LITE=tile(5,'#fff',1)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u),lerp=(a,b,k)=>a+(b-a)*k,mix=(a,b,k)=>a.map((v,n)=>lerp(v,b[n],k))
|
||||
const limb=(c,p,w,tone)=>{for(const[l,s]of[[w+8,'#111'],[w,tone||'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...p[0]);for(const q of p.slice(1))c.lineTo(...q);c.stroke()}c.strokeStyle='#111'}
|
||||
// elbow for a two-bone arm, bending backward (-x) so it reads as a facing-right pose
|
||||
const joint=(a,b,L)=>{const dx=b[0]-a[0],dy=b[1]-a[1],d=hypot(dx,dy)/2,o=d<L?Math.sqrt(L*L-d*d)/(2*d):0;return[(a[0]+b[0])/2-dy*o,(a[1]+b[1])/2+dx*o]}
|
||||
const eye=(c,x,y,d,lt)=>{
|
||||
c.save();c.beginPath();c.moveTo(x-17,y-6+d*4);c.lineTo(x+17,y-6-d*4);c.lineTo(x+17,y+22);c.lineTo(x-17,y+22);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,15,0,0,T);c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+6,y+2,9,12,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-4,y+6,22,10)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+4,y-1,3.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6+d*4);c.lineTo(x+16,y-6-d*4);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const dt=c.createPattern(DOT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const f=t*T*3,wx=300-380*t,WT=196
|
||||
const w=ease((t-.38)/.06)*(1-ease((t-.58)/.08)),wp=ease((t-.41)/.03)*(1-ease((t-.575)/.03)),fl=sin(t*T*8)
|
||||
const H=[118,182-3*abs(sin(f))*(1-w)-30*w],lean=.16*(1-w)-.3*w
|
||||
// speed lines and ground dashes scroll left
|
||||
c.lineWidth=4;for(const[y,o,l]of[[44,0,46],[96,.6,40],[150,.3,36],[206,.8,28]]){const x=((o-t*3)%1+1)%1*320-50;c.beginPath();c.moveTo(x,y);c.lineTo(x+l,y);c.stroke()}
|
||||
c.lineWidth=5;c.beginPath();for(let k=0;k<4;k++){const x=((k/4-t*3)%1+1)%1*300-30;c.moveTo(x,246);c.lineTo(x+22,246)}c.stroke()
|
||||
// brick wall
|
||||
if(wx>-80&&wx<260){c.save();c.beginPath();c.rect(wx,WT,64,50);c.clip();c.fillStyle='#fff';c.fillRect(wx,WT,64,50);c.lineWidth=3.5
|
||||
for(let r=0;r<4;r++)for(let k=-1;k<3;k++){const x=wx+k*28+(r%2)*14,y=WT+r*13;c.beginPath();c.rect(x,y,28,13);if((r*3+k)%5==1){c.fillStyle=dt;c.fill()}c.stroke()}
|
||||
c.restore();c.lineWidth=5;c.strokeRect(wx,WT,64,50);c.fillStyle='#111';c.fillRect(wx-3,WT-6,70,8)}
|
||||
c.save();c.translate(120,246);c.scale(.92,.92);c.translate(-120,-246)
|
||||
const S=[H[0]+44*sin(lean),H[1]-44*cos(lean)]
|
||||
// legs: running cycle blended into both legs swept forward over the wall
|
||||
const lg=s=>{const th=.9*sin(f+s),sh=th-.3-1.2*(sin(f+s+1.3)+1)/2,k=[30*sin(th),30*cos(th)];return[...k,k[0]+30*sin(sh),k[1]+30*cos(sh)]}
|
||||
const leg=(s,v,dx)=>{const o=mix(lg(s),v,w),hp=[H[0]+dx,H[1]],kn=[hp[0]+o[0],hp[1]+o[1]],ft=[hp[0]+o[2],hp[1]+o[3]]
|
||||
limb(c,[hp,kn,ft],16,'#111');c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(hp[0]+2,hp[1]+4);c.lineTo(kn[0]+2,kn[1]);c.lineTo(ft[0],ft[1]-4);c.stroke();c.strokeStyle='#111'
|
||||
const a=Math.atan2(ft[1]-kn[1],ft[0]-kn[0])-PI/2;c.save();c.translate(...ft);c.rotate(a);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(-5,4,9,15,0,0,T);c.fill();c.stroke();c.fillStyle='#111';c.fillRect(-15,6,5,0);c.beginPath();c.moveTo(-14,8);c.lineTo(4,8);c.lineTo(4,12);c.lineTo(-14,12);c.fill();c.restore()}
|
||||
const arm=(o,p,tone)=>{limb(c,[o,[o[0]+p[0],o[1]+p[1]],[o[0]+p[2],o[1]+p[3]]],11,tone);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(o[0]+p[2],o[1]+p[3],8,0,T);c.fill();c.stroke()}
|
||||
const ra=s=>{const a=.8*sin(f+s);return[24*sin(a),24*cos(a),24*sin(a)+20*sin(a+1.5),24*cos(a)+20*cos(a+1.5)]}
|
||||
arm([S[0]-6,S[1]+2],mix(ra(0),[-22,-14,-30,-38],w))
|
||||
leg(PI,[30,8,56,22],-5)
|
||||
// hoodie torso with a flapping hem and kangaroo pocket
|
||||
c.save();c.translate(...H);c.rotate(lean)
|
||||
c.fillStyle=dt;c.lineWidth=5;c.beginPath();c.moveTo(-22,6+2*fl);c.lineTo(-24,-36);c.quadraticCurveTo(0,-50,22,-40);c.lineTo(24,4);c.lineTo(0,8);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(-26,-4+2*fl);c.lineTo(24,-6);c.lineTo(26,6);c.lineTo(-30,10+4*fl);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(0,-24);c.lineTo(18,-24);c.lineTo(22,-10);c.lineTo(-2,-10);c.closePath();c.fill();c.stroke()
|
||||
c.restore()
|
||||
leg(0,[33,4,60,16],5)
|
||||
// head inside the hood; hood tail and drawstrings flap behind
|
||||
c.save();c.translate(H[0]+96*sin(lean)+6,H[1]-96*cos(lean)+2);c.rotate(lean*.6);c.scale(.64,.64);c.translate(-128,-121)
|
||||
c.lineWidth=9;c.fillStyle=dt
|
||||
const hood=()=>{c.beginPath();c.moveTo(64,170);c.bezierCurveTo(30,120,46,26,128,26);c.bezierCurveTo(200,26,214,96,196,170);c.closePath()}
|
||||
c.beginPath();c.moveTo(62,96);c.quadraticCurveTo(24,104+6*fl,-4,134+12*fl);c.lineTo(10,146+10*fl);c.lineTo(-2,164+8*fl);c.quadraticCurveTo(36,166,64,156);c.closePath();c.fill();c.stroke()
|
||||
hood();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(76,100);c.bezierCurveTo(72,154,100,184,128,186);c.bezierCurveTo(156,184,184,154,180,100);c.bezierCurveTo(176,60,80,60,76,100);c.fill();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(70,166);c.bezierCurveTo(60,90,90,52,128,52);c.bezierCurveTo(168,52,196,90,186,166);c.stroke()
|
||||
eye(c,112,140,-1,lt);eye(c,160,140,1,lt)
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.save();c.translate(98,170);c.rotate(-.3);c.beginPath();c.roundRect(-14,-7,28,14,5);c.fill();c.stroke();c.beginPath();c.moveTo(-3,-7);c.lineTo(-3,7);c.moveTo(3,-7);c.lineTo(3,7);c.stroke();c.restore()
|
||||
c.lineWidth=3.5;for(const j of[-6,0,6])c.beginPath(),c.moveTo(182+j-3,166),c.lineTo(182+j+3,158),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath()
|
||||
if(w>.3)c.moveTo(128,164),c.lineTo(156,162),c.quadraticCurveTo(152,182,142,182),c.quadraticCurveTo(130,182,128,164),c.fill(),c.stroke(),c.fillStyle='#fff',c.beginPath(),c.moveTo(132,165),c.lineTo(138,165),c.lineTo(135,171),c.fill()
|
||||
else c.moveTo(130,168),c.quadraticCurveTo(142,174,154,164),c.stroke()
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(80,118);c.quadraticCurveTo(78,82,100,72);c.quadraticCurveTo(108,62,124,66);c.quadraticCurveTo(138,58,152,68);c.quadraticCurveTo(174,72,178,92);c.quadraticCurveTo(182,104,176,120);c.quadraticCurveTo(168,104,160,112);c.quadraticCurveTo(152,96,140,106);c.quadraticCurveTo(130,92,118,106);c.quadraticCurveTo(106,94,96,110);c.quadraticCurveTo(88,104,80,118);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3.5;for(const[a,b,e,g]of[[104,80,98,96],[150,78,156,94],[128,72,126,86]])c.beginPath(),c.moveTo(a,b),c.quadraticCurveTo((a+e)/2+5,(b+g)/2,e,g),c.stroke()
|
||||
c.restore()
|
||||
// drawstrings
|
||||
const nk=[H[0]+46*sin(lean)+4,H[1]-46*cos(lean)+2];c.lineWidth=3;for(const d of[-6,6]){const e=[nk[0]+d-10-6*fl*(1+w),nk[1]+22+2*fl];c.beginPath();c.moveTo(nk[0]+d,nk[1]);c.quadraticCurveTo(nk[0]+d-4,nk[1]+12,...e);c.stroke();c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.roundRect(e[0]-3,e[1]-2,6,8,2);c.fill();c.stroke()}
|
||||
// near arm: pumping, then planted on the wall top
|
||||
const ro=[S[0]+4,S[1]+2],run=ra(PI),pl=[min(max(wx+18,64),170),WT-6],ph=mix([ro[0]+run[2],ro[1]+run[3]],pl,wp),el=wp>.01?joint(ro,ph,27):[ro[0]+run[0],ro[1]+run[1]]
|
||||
limb(c,[ro,mix([ro[0]+run[0],ro[1]+run[1]],el,wp),ph],11);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(...ph,8,0,T);c.fill();c.stroke()
|
||||
if(t>=.42&&t<.5){c.lineWidth=4;for(const a of[-2.6,-2,-1.2,-.5])c.beginPath(),c.moveTo(pl[0]+cos(a)*14,pl[1]+sin(a)*14),c.lineTo(pl[0]+cos(a)*24,pl[1]+sin(a)*24),c.stroke()}
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// girl with a white curly bob drops a holed coin into her piggy bank, lifts it to her ear and shakes it to hear the jingle, then sparkles
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=24,delay=80,rim=4
|
||||
const tone=(c,s,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle='#111';for(const[a,b]of[[0,0],[s,0],[0,s],[s,s],[s/2,s/2]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return c.createPattern(k,'repeat')}
|
||||
const ease=u=>(u=max(0,min(1,u)),u*u*(3-2*u))
|
||||
const spark=(c,x,y,r)=>{c.fillStyle='#fff';c.lineWidth=2.5;c.beginPath();for(let k=0;k<8;k++){const q=k%2?r*.3:r,b=k*PI/4;c.lineTo(x+cos(b)*q,y+sin(b)*q)}c.closePath();c.fill();c.stroke()}
|
||||
const curls=(c,cx,cy,rx,ry,n,b,a0,a1)=>{for(let k=0;k<=n;k++){const a=a0+(a1-a0)*k/n,m=a0+(a1-a0)*(k-.5)/n;k?c.quadraticCurveTo(cx+(rx+b)*cos(m),cy+(ry+b)*sin(m),cx+rx*cos(a),cy+ry*sin(a)):c.lineTo(cx+rx*cos(a),cy+ry*sin(a))}}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
const up=ease((t-.4)/.08)*(1-ease((t-.72)/.08)),listen=t>=.46&&t<.74,clink=t>=.32&&t<.42,shine=t>=.78&&t<.94
|
||||
const shake=listen?7*sin(t*T*12):0,px=128+60*up+shake,py=206-66*up,ps=1-.18*up,pa=-.35*up+shake*.02
|
||||
// right hand: holding the coin over the slot, then on the pig, then fetching the next coin
|
||||
const g=t<.26?1:t<.32?1-ease((t-.26)/.06):t<.86?0:ease((t-.86)/.1)
|
||||
const hp=(lo,hi)=>[px+(lo[0]+(hi[0]-lo[0])*up)*ps,py+(lo[1]+(hi[1]-lo[1])*up)*ps]
|
||||
const L=hp([-46,14],[-30,32]),r=hp([46,14],[44,20]),R=[r[0]+(154-r[0])*g,r[1]+(150-r[1])*g]
|
||||
const head=()=>{c.translate(128,186);c.rotate(.08*up);c.translate(-128,-186)}
|
||||
c.save();c.translate(122,106);c.scale(.8,.8);c.translate(-128,-130)
|
||||
// curly white bob behind
|
||||
c.save();head();c.lineWidth=7;c.fillStyle='#fff';c.beginPath();curls(c,128,120,76,82,14,10,.05*PI,.95*PI);curls(c,128,120,76,82,14,10,.95*PI,2.05*PI);c.closePath();c.fill();c.stroke();c.restore()
|
||||
// the same white top every frame: ink peter-pan collar, little bow, button placket, a few folds
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(98,184);c.quadraticCurveTo(50,196,36,340);c.lineTo(220,340);c.quadraticCurveTo(206,196,158,184);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(66,206);c.quadraticCurveTo(78,226,76,250);c.moveTo(190,206);c.quadraticCurveTo(178,226,180,250);c.moveTo(128,212);c.lineTo(128,330);c.moveTo(96,280);c.quadraticCurveTo(104,300,100,320);c.moveTo(160,280);c.quadraticCurveTo(152,300,156,320);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3.5;for(const y of[238,266,294])c.beginPath(),c.arc(128,y,5,0,T),c.fill(),c.stroke()
|
||||
c.fillStyle='#111';c.lineWidth=6;for(const d of[-1,1]){c.beginPath();c.moveTo(128,192);c.quadraticCurveTo(128+d*44,184,128+d*46,200);c.quadraticCurveTo(128+d*44,224,128+d*4,214);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2;c.setLineDash([4,4]);c.beginPath();c.moveTo(128+d*10,204);c.quadraticCurveTo(128+d*36,214,128+d*38,200);c.stroke();c.setLineDash([]);c.strokeStyle='#111'}
|
||||
c.fillStyle='#fff';c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(128,212),c.lineTo(128+d*16,204),c.lineTo(128+d*16,222),c.closePath(),c.fill(),c.stroke()
|
||||
c.fillStyle=DOT;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*3,212),c.lineTo(128+d*14,207),c.lineTo(128+d*14,219),c.closePath(),c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(128,212,5,0,T);c.fill();c.stroke()
|
||||
c.save();head();c.fillStyle='#fff';c.lineWidth=7
|
||||
c.beginPath();c.rect(116,166,24,22);c.fill();c.beginPath();c.moveTo(116,166);c.lineTo(116,188);c.moveTo(140,166);c.lineTo(140,188);c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(listen||clink){c.lineWidth=6;for(const x of[103,153])c.beginPath(),listen?(c.moveTo(x-14,142),c.quadraticCurveTo(x,152,x+14,142)):(c.moveTo(x-12,134),c.lineTo(x+8,142),c.lineTo(x-12,150)),c.stroke()}
|
||||
else{const[lx,ly]=shine?[0,0]:t<.32?[5,-6]:[3,4];for(const[x,d]of[[103,-1],[153,1]]){
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+lx,140+ly,10.5,14,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+lx-4,135+ly,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+lx+4,147+ly,2,0,T);c.fill()
|
||||
if(shine){c.save();c.translate(x+2,140);c.beginPath();for(let k=0;k<10;k++){const r=k%2?2.5:7,b=k*PI/5-PI/2;c.lineTo(cos(b)*r,sin(b)*r)}c.fill();c.restore()}
|
||||
c.restore();c.lineWidth=6;c.beginPath();c.moveTo(x-16,135);c.quadraticCurveTo(x,120,x+16,135);c.moveTo(x+d*14,132);c.lineTo(x+d*21,127);c.stroke()}}
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*42+k-3,166),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
c.lineWidth=4.5;c.beginPath();if(listen||shine)c.moveTo(116,166),c.quadraticCurveTo(128,182,140,166),c.closePath(),c.fillStyle='#111',c.fill();else if(t<.32)c.arc(128,170,5,0,T);else c.moveTo(121,169),c.quadraticCurveTo(128,173,135,169);c.stroke()
|
||||
// curly fringe
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(74,124);curls(c,128,120,58,66,9,9,1.06*PI,1.94*PI);c.lineTo(182,124);c.quadraticCurveTo(170,104,156,108);c.quadraticCurveTo(146,96,134,106);c.quadraticCurveTo(122,96,110,106);c.quadraticCurveTo(98,98,88,108);c.quadraticCurveTo(78,110,74,124);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3.5;for(const[x,y]of[[96,74],[128,62],[160,74],[110,90],[146,88]])c.beginPath(),c.arc(x,y,6,.2*PI,1.4*PI),c.stroke()
|
||||
c.restore();c.restore()
|
||||
// jingle marks while she shakes it
|
||||
if(listen){c.lineWidth=3.5;for(const d of[-1,1])for(const k of[0,1])c.beginPath(),c.arc(px,py,44+k*9,d>0?-.5:PI-.5,d>0?.5:PI+.5),c.stroke()
|
||||
for(const[o,dx]of[[0,-1],[.5,1]]){const p=(t*4+o)%1;c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.ellipse(px+dx*(40+8*p),py-30-26*p,6,6,0,0,T);c.fill();c.stroke();c.beginPath();c.arc(px+dx*(40+8*p),py-30-26*p,2,0,T);c.stroke()}}
|
||||
// sleeves from the fixed shoulders out to the hands
|
||||
for(const[[sx,sy],[hx,hy],d]of[[[72,172],L,-1],[[172,172],R,1]]){const mx=(sx+hx)/2+d*12,my=max(sy,hy)+22
|
||||
for(const[l,s]of[[26,'#111'],[15,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(sx,sy),c.quadraticCurveTo(mx,my,hx,hy),c.stroke()}
|
||||
c.strokeStyle='#111'
|
||||
// piggy bank
|
||||
c.save();c.translate(px,py);c.rotate(pa);c.scale(ps,ps)
|
||||
c.lineWidth=6;c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.moveTo(d*18,-26),c.lineTo(d*30,-46),c.lineTo(d*36,-20),c.closePath(),c.fill(),c.stroke()
|
||||
for(const d of[-1,1])c.beginPath(),c.roundRect(d*24-8,24,16,16,5),c.fill(),c.stroke()
|
||||
c.beginPath();c.ellipse(0,0,48,36,0,0,T);c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=DOT;for(const[x,y,r]of[[-30,-14,9],[30,-10,7],[24,20,10],[-26,18,7]])c.beginPath(),c.arc(x,y,r,0,T),c.fill();c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.roundRect(-14,-36,28,7,3);c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(0,6,15,11,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(d*5,6,2.5,4,0,0,T),c.fill()
|
||||
for(const d of[-1,1])c.beginPath(),c.arc(d*22,-8,4,0,T),c.fill()
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-4,0,4])c.beginPath(),c.moveTo(d*30+k-2,8),c.lineTo(d*30+k+2,2),c.stroke()
|
||||
c.restore()
|
||||
// the holed coin: pinched by the right hand, dropped through the slot, and a fresh one fetched at the end
|
||||
const held=t<.22||t>.9,f=held?0:ease((t-.22)/.12)
|
||||
if(f<1&&(t<.4||t>.9)){const cx=held?R[0]-26:128,cy=held?R[1]:150+24*f,w=held?13:13*(1-f*.8)
|
||||
c.save();if(f>0){c.beginPath();c.rect(0,0,256,170);c.clip()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(cx,cy,w,13,0,0,T);c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(cx,cy,w*.32,4,0,0,T);c.fill();c.lineWidth=3;c.stroke();c.restore()
|
||||
if(t<.22)spark(c,cx+14,cy-12,7*(.6+.4*sin(t*T*8)))}
|
||||
if(clink){c.lineWidth=4;for(const a of[-2.4,-1.9,-1.2,-.7])c.beginPath(),c.moveTo(128+cos(a)*18,170+sin(a)*18),c.lineTo(128+cos(a)*30,170+sin(a)*30),c.stroke()}
|
||||
// the hands holding the piggy
|
||||
c.lineWidth=6;c.fillStyle='#fff';for(const[[x,y],d]of[[L,-1],[R,1]])c.beginPath(),c.ellipse(x,y,13,15,d*.3*(1-g),0,T),c.fill(),c.stroke()
|
||||
if(shine)for(const[x,y,o]of[[80,186,0],[180,190,.5],[128,160,.25]])spark(c,x,y,10*(.6+.4*sin((t*6+o)*T)))
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// chibi with hatched high pigtails throwing a poka-poka tantrum: both little arms windmill-punching, tears at her squeezed eyes, steam puffs and an anger mark
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=60,rim=4
|
||||
const hatch=(c,x0,x1,y0,y1,s=5)=>{c.lineWidth=1.6;c.beginPath();for(let x=x0-(y1-y0);x<x1;x+=s)c.moveTo(x,y1),c.lineTo(x+(y1-y0),y0);c.stroke()}
|
||||
const hatched=(c,path)=>{path();c.fillStyle='#fff';c.fill();c.save();c.clip();hatch(c,20,240,10,200);c.restore();c.lineWidth=5;path();c.stroke()}
|
||||
const puff=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.fillStyle='#fff';c.lineWidth=4/s;c.beginPath();c.arc(-8,0,8,PI*.5,PI*1.6);c.arc(2,-5,9,PI*1.1,PI*1.95);c.arc(10,1,7,PI*1.4,PI*.5);c.closePath();c.fill();c.stroke();c.restore()}
|
||||
export default(c,t)=>{
|
||||
const b=2*max(0,sin(4*T*t)),stomp=sin(4*T*t)
|
||||
for(const o of[0,.33,.66]){const p=(t*2+o)%1;if(p<.85)puff(c,128+(o-.33)*70+sin(p*6)*6,40-p*34,.5+.6*p)}
|
||||
c.save();c.translate(0,-b)
|
||||
for(const d of[-1,1]){c.save();c.translate(128+d*54,58);c.rotate(-d*(.5+.12*sin(4*T*t+d)))
|
||||
hatched(c,()=>{c.beginPath();c.moveTo(0,0);c.bezierCurveTo(d*34,-8,d*40,40,d*24,70);c.quadraticCurveTo(d*16,62,d*8,70);c.quadraticCurveTo(d*14,30,-d*4,10);c.closePath()})
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(0,4,7,0,T);c.fill();c.stroke();c.restore()}
|
||||
c.lineWidth=5;c.fillStyle='#fff'
|
||||
for(const d of[-1,1]){const lift=d*stomp>0?6*abs(stomp):0;c.beginPath();c.roundRect(128+d*14-7,196-lift,14,30,6);c.fill();c.stroke();c.beginPath();c.ellipse(128+d*16,228-lift,13,8,0,0,T);c.fillStyle='#111';c.fill();c.fillStyle='#fff'}
|
||||
c.beginPath();c.moveTo(108,150);c.lineTo(148,150);c.lineTo(170,206);c.quadraticCurveTo(128,214,86,206);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const x of[104,118,132,146])c.moveTo(x,170),c.lineTo(x-4,206);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.ellipse(128,96,62,56,0,0,T);c.fill();c.stroke()
|
||||
for(const[x,d]of[[104,-1],[152,1]]){c.lineWidth=5.5;c.beginPath();c.moveTo(x-d*12,104);c.lineTo(x+d*8,112);c.lineTo(x-d*12,120);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3;const q=(t*4+(d>0?.5:0))%1;c.save();c.translate(x+d*(20+q*14),110-q*8+q*q*30);c.beginPath();c.moveTo(0,-7);c.quadraticCurveTo(5,0,4.5,3);c.arc(0,3,4.5,0,PI);c.quadraticCurveTo(-5,0,0,-7);c.fill();c.stroke();c.restore()}
|
||||
c.lineWidth=2.2;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*40+k-3,132),c.lineTo(128+d*40+k+3,124),c.stroke()
|
||||
const m=3*sin(8*T*t)
|
||||
c.fillStyle='#111';c.lineWidth=3;c.beginPath();c.moveTo(118,126);c.quadraticCurveTo(128,120,138,126);c.quadraticCurveTo(138,136+m,128,136+m);c.quadraticCurveTo(118,136+m,118,126);c.fill();c.stroke()
|
||||
hatched(c,()=>{c.beginPath();c.moveTo(68,112);c.bezierCurveTo(56,52,88,38,128,38);c.bezierCurveTo(168,38,200,52,188,112);c.quadraticCurveTo(186,88,178,80);c.lineTo(168,92);c.lineTo(162,76);c.lineTo(150,90);c.lineTo(142,74);c.lineTo(130,88);c.lineTo(122,72);c.lineTo(110,88);c.lineTo(102,74);c.lineTo(92,90);c.lineTo(84,78);c.quadraticCurveTo(72,90,68,112);c.closePath()})
|
||||
c.strokeStyle='#fff';c.lineWidth=6;c.beginPath();c.arc(128,90,50,PI*1.25,PI*1.42);c.stroke();c.beginPath();c.arc(128,90,50,PI*1.56,PI*1.74);c.stroke();c.strokeStyle='#111'
|
||||
for(const[x,d]of[[104,-1],[152,1]])for(const[l,st]of[[9,'#fff'],[4,'#111']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(x+d*14,88),c.lineTo(x-d*8,97),c.stroke()
|
||||
const ap=1+.15*sin(4*T*t);c.save();c.translate(170,52);c.scale(ap,ap);c.lineWidth=7;c.strokeStyle='#fff';c.beginPath();for(const[a,e]of[[0,1],[1,2],[2,3],[3,0]])c.moveTo(cos(a*PI/2+PI/4)*4,sin(a*PI/2+PI/4)*4),c.quadraticCurveTo(cos(a*PI/2+PI/2)*4,sin(a*PI/2+PI/2)*4,cos(a*PI/2+PI/4)*13,sin(a*PI/2+PI/4)*13);c.stroke();c.lineWidth=3.5;c.strokeStyle='#111';c.stroke();c.restore()
|
||||
for(const d of[-1,1]){const sx=128+d*34,sy=166,a=4*T*t*d*-1+(d>0?PI:0),fx=sx+cos(a)*36,fy=sy+sin(a)*30
|
||||
c.beginPath();c.ellipse(sx,sy,42,36,0,a+d*2.4,a+d*.5,d>0);for(const[l,st]of[[8,'#fff'],[3.5,'#111']])c.lineWidth=l,c.strokeStyle=st,c.stroke()
|
||||
for(const[l,st]of[[16,'#111'],[8,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(sx,sy),c.lineTo(fx,fy),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4.5;c.beginPath();c.arc(fx,fy,11,0,T);c.fill();c.stroke()}
|
||||
c.restore()
|
||||
}
|
||||
const abs=Math.abs
|
||||
@@ -0,0 +1,53 @@
|
||||
// manga rokurokubi girl in a toned kimono: her neck stretches up into a playful loop, head tilting with a teehee wink, then it slurps back down
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=20,delay=80,rim=4
|
||||
const TL=createCanvas(6,6),tx=TL.getContext('2d');tx.fillStyle='#111';for(const[a,b]of[[1.5,1.5],[4.5,4.5]])tx.beginPath(),tx.arc(a,b,1.05,0,T),tx.fill()
|
||||
const ease=x=>x<0?0:x>1?1:x*x*(3-2*x)
|
||||
// soft droopy eye with a long lower lash
|
||||
const eye=(c,x,y,d,tn)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,14,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+4,10.5,12,0,0,T);c.clip();c.fillStyle='#111';c.fillRect(x-12,y-10,24,26);c.fillStyle=tn;c.beginPath();c.ellipse(x,y+10,9,6,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y,4,0,T);c.fill();c.beginPath();c.arc(x+4,y+9,1.8,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-d*15,y-4);c.quadraticCurveTo(x,y-12,x+d*15,y-2);c.lineTo(x+d*19,y+3);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-d*6,y+20);c.lineTo(x+d*8,y+19);c.stroke()
|
||||
}
|
||||
// crosshatch inside whatever path is current
|
||||
const hatch=(c,x0,y0,w,h)=>{c.save();c.clip();c.lineWidth=1.6;c.beginPath();for(let k=-h;k<w;k+=6)c.moveTo(x0+k,y0+h),c.lineTo(x0+k+h,y0);for(let k=0;k<w+h;k+=6)c.moveTo(x0+k,y0+h),c.lineTo(x0+k-h,y0);c.stroke();c.restore()}
|
||||
const head=(c,tn,wink)=>{
|
||||
// bun with crossed kanzashi pins and a dangling flower charm
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.arc(150,40,26,0,T);c.fill();hatch(c,120,10,60,60);c.beginPath();c.arc(150,40,26,0,T);c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(118,20);c.lineTo(186,58);c.moveTo(186,18);c.lineTo(122,60);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(186,58);c.lineTo(190,78);c.stroke();c.fillStyle='#fff';c.lineWidth=3;for(let k=0;k<5;k++){const a=k*T/5;c.beginPath();c.arc(190+5*cos(a),84+5*sin(a),4,0,T);c.fill();c.stroke()}
|
||||
// hatched hair shell, then face, then blunt hatched bangs and side locks
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(70,150);c.bezierCurveTo(52,70,86,40,128,40);c.bezierCurveTo(170,40,204,70,186,150);c.closePath();c.fill();hatch(c,50,36,156,118);c.beginPath();c.moveTo(70,150);c.bezierCurveTo(52,70,86,40,128,40);c.bezierCurveTo(170,40,204,70,186,150);c.closePath();c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fillStyle='#fff';c.fill();c.stroke()
|
||||
if(wink){c.lineWidth=5;c.beginPath();c.moveTo(92,134);c.lineTo(112,141);c.lineTo(92,148);c.stroke();eye(c,153,138,1,tn)}
|
||||
else{eye(c,103,138,-1,tn);eye(c,153,138,1,tn)}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-3,166),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
if(wink){c.lineWidth=3.5;c.fillStyle='#111';c.beginPath();c.moveTo(118,166);c.quadraticCurveTo(128,176,138,166);c.closePath();c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.ellipse(133,174,5,6,.3,0,T);c.fill();c.stroke()}
|
||||
else{c.lineWidth=3.5;c.beginPath();c.arc(128,164,6,.2*PI,.8*PI);c.stroke()}
|
||||
const bangs=()=>{c.beginPath();c.moveTo(72,140);c.bezierCurveTo(60,80,90,46,128,46);c.bezierCurveTo(166,46,196,80,184,140);c.lineTo(178,108);c.lineTo(78,108);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';bangs();c.fill();bangs();hatch(c,56,40,144,104);bangs();c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#fff';for(const d of[-1,1]){c.beginPath();c.moveTo(128+d*52,104);c.quadraticCurveTo(128+d*62,140,128+d*56,170);c.lineTo(128+d*48,160);c.quadraticCurveTo(128+d*50,130,128+d*44,108);c.closePath();c.fill();c.stroke()}
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat')
|
||||
const e=ease((t-.08)/.3)-ease((t-.72)/.22),wb=e>.95?sin((t-.38)/.34*T*2)*.12:0
|
||||
const hx=128-34*e+20*wb,hy=160-96*e,tl=-.25*e+wb
|
||||
// kimono shoulders with a crossed white collar and toned sleeves
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(104,206);c.quadraticCurveTo(52,212,40,256);c.lineTo(216,256);c.quadraticCurveTo(204,212,152,206);c.closePath();c.fill()
|
||||
c.save();c.clip();c.fillStyle=tn;c.fillRect(30,200,196,60);c.restore();c.stroke()
|
||||
// the neck: a long tube from the collar up to her chin, curling into a loop as it stretches
|
||||
const bx=128,by=214,ex=hx+tl*-30,ey=hy+38
|
||||
// straight line plus a circling offset: once it's long enough the offset outruns it and ties a loop
|
||||
const B=26*e,neck=()=>{c.beginPath();for(let k=0;k<=40;k++){const s=k/40,a=T*s+PI;c.lineTo(bx+(ex-bx)*s+B*(cos(a)+1)*1.1,by+(ey-by)*s+B*sin(a)*1.25)}}
|
||||
for(const[l,s]of[[20,'#111'],[12,'#fff']])c.lineWidth=l,c.strokeStyle=s,neck(),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
if(e>.2&&e<.95){c.lineWidth=3;c.beginPath();for(const d of[-1,1])c.moveTo(bx+d*20,by-40*e),c.quadraticCurveTo(bx+d*30,by-56*e,bx+d*22,by-72*e);c.stroke()}
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(108,206);c.lineTo(128,240);c.lineTo(148,206);c.lineTo(138,206);c.lineTo(128,224);c.lineTo(118,206);c.closePath();c.fill();c.stroke()
|
||||
// head rides on top of the neck
|
||||
c.save();c.translate(hx,hy);c.rotate(tl);c.scale(.6,.6);c.translate(-128,-120);head(c,tn,e>.9);c.restore()
|
||||
// sparkles while she's up
|
||||
if(e>.9){c.fillStyle='#fff';c.lineWidth=2.5;for(const[x,y,r]of[[hx+56,hy-34,7],[hx-60,hy-10,5]]){const s=r*(.6+.4*sin(t*T*6));c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,q=k%2?s*.35:s;c.lineTo(x+q*cos(a),y+q*sin(a))}c.closePath();c.fill();c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// lab coat girl with crosshatched messy hair and goggles; her flask foams up and over, she goes white-eyed with a sweat drop, then the foam sinks back
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=20,delay=80,rim=4
|
||||
const tile=createCanvas(6,6),q=tile.getContext('2d');q.fillStyle='#111';for(const[x,y]of[[1.5,1.5],[4.5,4.5]])q.beginPath(),q.arc(x,y,1.1,0,T),q.fill()
|
||||
const xt=createCanvas(7,7),x7=xt.getContext('2d');x7.strokeStyle='#111';x7.lineWidth=1.3;for(const o of[-7,0,7])x7.beginPath(),x7.moveTo(o,7),x7.lineTo(o+7,0),x7.moveTo(o,0),x7.lineTo(o+7,7),x7.stroke()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const ease=f=>f<=0?0:f>=1?1:.5-.5*cos(PI*f)
|
||||
const BLOBS=[[0,-8,17],[-17,-22,15],[15,-26,16],[-4,-42,18],[18,-50,13],[-21,-52,12],[3,-68,14]]
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),hat=c.createPattern(xt,'repeat')
|
||||
const h=t<.3?0:t<.45?ease((t-.3)/.15):t<.7?1:1-ease((t-.7)/.3),s=h>.45
|
||||
c.save();c.translate(-24,0)
|
||||
// crosshatched messy hair behind
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(128,30);c.bezierCurveTo(200,30,214,96,204,158);c.lineTo(194,150);c.lineTo(190,170);c.lineTo(66,170);c.lineTo(62,150);c.lineTo(52,158);c.bezierCurveTo(42,96,56,30,128,30);toned(c,hat)
|
||||
// lab coat with lapels over a dotted turtleneck
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(58,196,48,252);c.lineTo(208,252);c.quadraticCurveTo(198,196,156,188);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(112,186);c.lineTo(144,186);c.lineTo(138,252);c.lineTo(118,252);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*16,186),c.lineTo(128+d*12,230),c.lineTo(128+d*30,206),c.lineTo(128+d*24,190),c.closePath(),c.fill(),c.stroke()
|
||||
c.beginPath();c.rect(70,212,26,22);c.stroke();c.lineWidth=3;for(const[x,cap]of[[76,1],[84,0],[90,1]])c.beginPath(),c.moveTo(x,212),c.lineTo(x,202),c.stroke()
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.rect(116,168,24,20);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,186);c.moveTo(140,168);c.lineTo(140,186);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
// eyes behind the goggles: curious and glancing at the flask, or white with shock
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
if(s){c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.ellipse(x,142,12,15,0,0,T);c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.arc(x+2,142,2.5,0,T);c.fill();continue}
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,142,12,15,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+4,144,8,12,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+1,139,3.2,0,T);c.fill();c.beginPath();c.arc(x+7,150,1.6,0,T);c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(x-12,132);c.quadraticCurveTo(x,124,x+12,131);c.stroke()
|
||||
}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<3;k++)c.beginPath(),c.moveTo(128+d*44+(k-1)*5-3,172),c.lineTo(128+d*44+(k-1)*5+3,164),c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();if(s)c.ellipse(128,172,6,8,0,0,T);else c.arc(128,166,7,.2*PI,.8*PI);c.stroke()
|
||||
// goggles: thick rims, a strap and a glassy shine
|
||||
c.lineWidth=7;for(const x of[103,153])c.beginPath(),c.arc(x,140,23,0,T),c.stroke()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(80,136);c.lineTo(68,132);c.moveTo(176,136);c.lineTo(188,132);c.moveTo(126,138);c.lineTo(130,138);c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;for(const x of[103,153])c.beginPath(),c.arc(x,140,17,1.1*PI,1.4*PI),c.stroke();c.strokeStyle='#111'
|
||||
// messy crosshatched bangs and an antenna strand
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(68,130);c.bezierCurveTo(54,70,88,34,128,34);c.bezierCurveTo(168,34,204,70,188,130)
|
||||
for(const[x,y]of[[182,112],[176,98],[168,114],[160,92],[150,110],[140,94],[130,112],[120,92],[110,110],[100,94],[90,112],[82,98],[74,114]])c.lineTo(x,y)
|
||||
c.closePath();toned(c,hat)
|
||||
c.strokeStyle='#fff';c.lineWidth=6;for(const[a,e]of[[1.28,1.38],[1.46,1.55],[1.63,1.72]])c.beginPath(),c.arc(128,106,54,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(126,36);c.quadraticCurveTo(116,14,132,10);c.stroke()
|
||||
if(s){c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(68,100);c.quadraticCurveTo(80,118,68,124);c.quadraticCurveTo(56,118,68,100);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.save();c.translate(40,58);c.rotate(-.25);c.beginPath();c.moveTo(-6,-22);c.lineTo(6,-22);c.lineTo(2,6);c.lineTo(-2,6);c.closePath();c.fill();c.beginPath();c.arc(0,14,4.5,0,T);c.fill();c.restore()}
|
||||
// arm reaching to hold the flask
|
||||
for(const[l,st]of[[30,'#111'],[23,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(166,206),c.quadraticCurveTo(196,224,214,186),c.stroke()
|
||||
c.strokeStyle='#111';c.restore()
|
||||
// erlenmeyer flask with dotted liquid and rising bubbles
|
||||
c.lineWidth=5;c.fillStyle='#fff';const fl=()=>{c.beginPath();c.moveTo(194,150);c.lineTo(194,176);c.lineTo(172,226);c.quadraticCurveTo(166,242,182,242);c.lineTo(230,242);c.quadraticCurveTo(246,242,240,226);c.lineTo(218,176);c.lineTo(218,150);c.closePath()}
|
||||
fl();c.fill();c.save();fl();c.clip();c.beginPath();c.rect(150,206+(s?-4:0),110,50);c.fillStyle=dot;c.fill();c.lineWidth=3;c.beginPath();c.moveTo(150,206+(s?-4:0));c.lineTo(250,206+(s?-4:0));c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=2.5;for(let k=0;k<5;k++){const p=(t*(2+k%2)+k*.23)%1,x=186+k*9,y=238-p*30;c.beginPath();c.arc(x,y,2.5+k%3,0,T);c.fill();c.stroke()}
|
||||
c.restore();fl();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.roundRect(188,144,36,8,3);c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(184,222);c.lineTo(198,190);c.stroke();c.strokeStyle='#111'
|
||||
// the foam: one union outline, puffs that grow and spill down the side
|
||||
if(h>.02){
|
||||
const B=BLOBS.map(([x,y,r],k)=>[206+x*h+(k%2?1:-1)*1.5*sin(T*2*t+k),148+y*h,r*(.35+.65*h)]),d=max(0,h-.6)/.4
|
||||
const all=()=>{for(const[x,y,r]of B)c.moveTo(x+r,y),c.arc(x,y,r,0,T);if(d>0)c.moveTo(196,156),c.ellipse(190,156+d*14,6,6+d*14,0,0,T),c.moveTo(230,156),c.ellipse(224,156+d*8,5,5+d*9,0,0,T)}
|
||||
c.lineWidth=11;c.beginPath();all();c.stroke();c.fillStyle='#fff';c.beginPath();all();c.fill()
|
||||
c.lineWidth=2;for(const[x,y,r]of B.slice(1))c.beginPath(),c.arc(x-r*.3,y-r*.25,r*.28,0,T),c.stroke()
|
||||
if(h>.9){c.lineWidth=2.5;for(let k=0;k<3;k++){const p=(t*4+k/3)%1,r=3+5*sin(PI*p);c.beginPath();c.arc(194+k*14,70-p*36,r,0,T);c.stroke()}}
|
||||
}
|
||||
// the hand gripping the neck
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(186,162,40,22,10);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const x of[198,208,218])c.beginPath(),c.moveTo(x,164),c.lineTo(x,174),c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// girl with round glasses and a white top bun sews a button on, the needle pulling thread up through the holes and back down, tongue out
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=20,delay=80,rim=4
|
||||
const tone=(c,s,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle='#111';for(const[a,b]of[[0,0],[s,0],[0,s],[s,s],[s/2,s/2]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return c.createPattern(k,'repeat')}
|
||||
const ease=u=>(u=max(0,min(1,u)),u*u*(3-2*u))
|
||||
const BX=124,BY=206,H=[[-8,-8],[8,-8],[8,8],[-8,8]]
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
// needle cycle, twice per loop: up out of one hole, pull high, back down into the next
|
||||
const p=(t*2)%1,n=Math.floor(t*2),up=H[n*2%4],dn=H[(n*2+1)%4]
|
||||
const rise=p<.45?ease(p/.45):p<.55?1:1-ease((p-.55)/.4),hole=p<.5?up:dn
|
||||
const nx=BX+hole[0]+46*rise,ny=BY+hole[1]-60*rise,ang=-.9+.3*rise
|
||||
// girl
|
||||
c.save();c.translate(128,96);c.scale(.74,.74);c.translate(-128,-130)
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(98,184);c.quadraticCurveTo(44,196,34,300);c.lineTo(222,300);c.quadraticCurveTo(212,196,158,184);c.closePath();c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=7;c.beginPath();c.moveTo(100,186);c.quadraticCurveTo(110,206,128,202);c.quadraticCurveTo(146,206,156,186);c.quadraticCurveTo(128,196,100,186);c.fill();c.stroke()
|
||||
c.beginPath();c.rect(116,166,24,22);c.fill();c.beginPath();c.moveTo(116,166);c.lineTo(116,186);c.moveTo(140,166);c.lineTo(140,186);c.stroke()
|
||||
// white hair pulled up into a top bun with an ink scrunchie
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.arc(128,26,26,0,T);c.fill();c.stroke();c.lineWidth=4;c.beginPath();c.arc(124,26,14,1.2*PI,1.9*PI);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(128,48,24,9,0,0,T);c.fill()
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(70,150);c.bezierCurveTo(50,74,88,46,128,46);c.bezierCurveTo(168,46,206,74,186,150);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
// focused eyes looking down at the button, behind round glasses
|
||||
const lx=(nx-150)*.05,ly=6
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,144,12,13,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,144,12,13,0,0,T);c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+lx,144+ly,9,11,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+lx-3,140+ly,3.5,4,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-14,138);c.quadraticCurveTo(x,130,x+14,138);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(x-12,124);c.quadraticCurveTo(x,118+d*2,x+12,122);c.stroke()}
|
||||
c.lineWidth=5;for(const x of[103,153])c.beginPath(),c.arc(x,144,21,0,T),c.stroke();c.beginPath();c.moveTo(122,140);c.quadraticCurveTo(128,134,134,140);c.stroke()
|
||||
const gl=sin(t*T)>.85;if(gl){c.strokeStyle='#111';c.lineWidth=3;for(const x of[103,153])c.beginPath(),c.moveTo(x+4,128),c.lineTo(x+14,138),c.moveTo(x-2,132),c.lineTo(x+10,146),c.stroke()}
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(118,170);c.quadraticCurveTo(128,174,138,170);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(130,171);c.quadraticCurveTo(140,182,144,172);c.stroke();c.lineWidth=3;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*44+k-3,170),c.lineTo(128+d*44+k+3,162),c.stroke()
|
||||
// white fringe in soft points
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(72,140);c.bezierCurveTo(60,80,96,52,128,52);c.bezierCurveTo(160,52,196,80,184,140);c.quadraticCurveTo(178,112,170,108);c.quadraticCurveTo(160,120,152,110);c.quadraticCurveTo(142,122,130,108);c.quadraticCurveTo(118,122,106,110);c.quadraticCurveTo(96,120,86,108);c.quadraticCurveTo(76,116,72,140);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const[a,b]of[[100,64],[156,64],[128,58]])c.beginPath(),c.moveTo(a,b),c.quadraticCurveTo(a+(a-128)*.2,b+18,a+(a-128)*.1,b+34),c.stroke()
|
||||
c.restore()
|
||||
// fabric patch with the button, and the stitches already done
|
||||
c.save();c.translate(BX,BY);c.rotate(-.06);c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(-74,-34);c.lineTo(70,-38);c.lineTo(76,40);c.lineTo(-70,44);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.lineWidth=3;c.setLineDash([6,5]);c.beginPath();c.moveTo(-64,-24);c.lineTo(60,-28);c.lineTo(66,32);c.lineTo(-60,34);c.closePath();c.stroke();c.setLineDash([]);c.restore()
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.arc(BX,BY,24,0,T);c.fill();c.stroke();c.lineWidth=3;c.beginPath();c.arc(BX,BY,17,0,T);c.stroke()
|
||||
c.fillStyle='#111';for(const[x,y]of H)c.beginPath(),c.arc(BX+x,BY+y,3.5,0,T),c.fill()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(BX-8,BY-8);c.lineTo(BX+8,BY+8);c.stroke()
|
||||
// hand holding the fabric
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.ellipse(48,214,16,20,.4,0,T);c.fill();c.stroke()
|
||||
for(const[l,s]of[[24,'#111'],[14,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(40,256),c.quadraticCurveTo(34,238,42,226),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// tomato pin cushion
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(216,232,26,18,0,0,T);c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.lineWidth=3;for(const a of[-.6,0,.6])c.beginPath(),c.moveTo(216,216),c.quadraticCurveTo(216+a*40,226,216+a*30,246),c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(216,212);for(let k=0;k<6;k++){const b=k*T/6;c.lineTo(216+cos(b)*(k%2?4:10),214+sin(b)*(k%2?3:6))}c.closePath();c.fill()
|
||||
c.lineWidth=2.5;for(const[a,b,r]of[[198,222,5],[232,220,5],[222,206,4]]){c.beginPath();c.moveTo(a,b);c.lineTo(a+(a-216)*.4,b-14);c.stroke();c.fillStyle='#fff';c.beginPath();c.arc(a+(a-216)*.4,b-16,r,0,T);c.fill();c.stroke()}
|
||||
// thread from the hole to the needle's eye, the needle, and the pinching fingers
|
||||
{const ex=nx+30*cos(ang),ey=ny+30*sin(ang)
|
||||
for(const[l,s]of[[5,'#111'],[2,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(BX+hole[0],BY+hole[1]),c.quadraticCurveTo((BX+hole[0]+ex)/2+6,(BY+hole[1]+ey)/2+10*(1-rise),ex,ey),c.stroke()
|
||||
for(const[l,s]of[[7,'#111'],[3,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(nx,ny),c.lineTo(ex,ey),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
{const fx=nx+20*cos(ang),fy=ny+20*sin(ang)
|
||||
for(const[l,s]of[[24,'#111'],[14,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(178,150),c.quadraticCurveTo(220,176,fx+12,fy+6),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(fx+6,fy+2,11,8,ang,0,T);c.fill();c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// freezing girl in a ribbed beanie with a dotted pompom hugging herself: shivering side to side, teeth chattering, shiver zigzags, little breath puffs
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=12,delay=70,rim=4
|
||||
const DOT=(()=>{const t=createCanvas(6,6),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(3,3,1.5,0,T);x.fill();return t})()
|
||||
const hatch=(c,x0,x1,y0,y1,s=5)=>{c.lineWidth=1.6;c.beginPath();for(let x=x0-(y1-y0);x<x1;x+=s)c.moveTo(x,y1),c.lineTo(x+(y1-y0),y0);c.stroke()}
|
||||
const hatched=(c,path,y0,y1)=>{path();c.fillStyle='#fff';c.fill();c.save();c.clip();c.strokeStyle='#111';hatch(c,40,220,y0,y1);c.restore();c.lineWidth=6;path();c.stroke()}
|
||||
const puff=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.fillStyle='#fff';c.lineWidth=3.5/s;c.beginPath();c.arc(-7,0,7,PI*.5,PI*1.6);c.arc(2,-4,8,PI*1.1,PI*1.95);c.arc(9,1,6,PI*1.4,PI*.5);c.closePath();c.fill();c.stroke();c.restore()}
|
||||
export default(c,t,i)=>{
|
||||
const dx=i%2?2:-2,open=i%2
|
||||
c.save();c.translate(dx,0)
|
||||
c.lineWidth=6;c.fillStyle='#fff'
|
||||
hatched(c,()=>{c.beginPath();c.moveTo(70,170);c.quadraticCurveTo(64,130,74,110);c.lineTo(182,110);c.quadraticCurveTo(192,130,186,170);c.lineTo(176,160);c.lineTo(170,178);c.lineTo(160,150);c.lineTo(96,150);c.lineTo(86,178);c.lineTo(80,160);c.closePath()},104,182)
|
||||
c.beginPath();c.moveTo(98,190);c.quadraticCurveTo(62,198,56,250);c.lineTo(200,250);c.quadraticCurveTo(194,198,158,190);c.closePath();c.fill();c.fillStyle=c.createPattern(DOT,'repeat');c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,26);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){const y=142;c.lineWidth=5;c.beginPath();c.moveTo(x-d*12,y-8);c.lineTo(x+d*8,y);c.lineTo(x-d*12,y+8);c.stroke()}
|
||||
hatched(c,()=>{c.beginPath();c.moveTo(78,110);c.lineTo(178,110);c.lineTo(178,126);c.lineTo(168,134);c.lineTo(162,122);c.lineTo(152,134);c.lineTo(144,122);c.lineTo(134,132);c.lineTo(126,122);c.lineTo(116,134);c.lineTo(108,122);c.lineTo(98,132);c.lineTo(90,122);c.lineTo(80,130);c.closePath()},104,140)
|
||||
c.lineWidth=2.2;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-3,165),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
c.beginPath();for(const k of[-4,0,4])c.moveTo(124+k,160),c.lineTo(128+k,153);c.stroke()
|
||||
const g=open?5:1.5
|
||||
c.fillStyle='#111';c.lineWidth=3;c.beginPath();c.roundRect(113,166-g,30,10+2*g,4);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(115,167-g,26,5);c.rect(115,172+g,26,5);c.fill()
|
||||
c.lineWidth=1.8;c.beginPath();for(const x of[121,128,135])c.moveTo(x,167-g),c.lineTo(x,172-g),c.moveTo(x,172+g),c.lineTo(x,177+g);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(70,112);c.bezierCurveTo(66,40,190,40,186,112);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.lineWidth=2.5;c.beginPath();for(let x=80;x<180;x+=10)c.moveTo(x,112),c.quadraticCurveTo(128+(x-128)*.6,70,128+(x-128)*.2,44);c.stroke();c.restore()
|
||||
c.beginPath();c.roundRect(64,98,128,24,10);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(let x=72;x<188;x+=7)c.moveTo(x,102),c.lineTo(x,118);c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();for(let k=0;k<18;k++){const a=k*T/18,r=k%2?17:21;c.lineTo(128+cos(a)*r,30+sin(a)*r)}c.closePath();c.fill();c.fillStyle=c.createPattern(DOT,'repeat');c.fill();c.stroke()
|
||||
for(const d of[-1,1]){c.lineWidth=36;c.strokeStyle='#111';c.beginPath();c.moveTo(128-d*62,214);c.quadraticCurveTo(128-d*30,250,128+d*46,208);c.stroke()
|
||||
c.lineWidth=24;c.strokeStyle='#fff';c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4.5;c.beginPath();c.ellipse(128+d*50,204,12,14,d*.4,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128+d*42,198);c.lineTo(128+d*48,206);c.moveTo(128+d*46,194);c.lineTo(128+d*53,202);c.stroke()}
|
||||
c.restore()
|
||||
c.strokeStyle='#111';c.lineWidth=3.5
|
||||
for(const d of[-1,1]){const o=(i%2?1:0)*4*d;c.beginPath();for(const y0 of[120,170]){c.moveTo(128+d*(108+o*.5),y0);for(let k=1;k<=4;k++)c.lineTo(128+d*(108+(k%2?8:0)+o*.5),y0+k*7)}c.stroke()}
|
||||
for(const o of[0,.5]){const p=(t+o)%1,s=.4+.7*p;if(p<.9)puff(c,108-p*46,166-p*58,s*(p<.1?p*10:1))}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// side profile of a tired girl with long ink hair letting out a huge sigh: a puff cloud drifts from her lips as her shoulders slump, then a deep breath back in
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=100,rim=4
|
||||
const DOT=(()=>{const t=createCanvas(6,6),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(3,3,1.3,0,T);x.fill();return t})()
|
||||
const ease=x=>x<=0?0:x>=1?1:x*x*(3-2*x)
|
||||
const cloud=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.lineWidth=4.5/s;c.fillStyle='#fff';c.beginPath();c.arc(-14,4,11,PI*.5,PI*1.5);c.arc(-2,-8,13,PI,PI*1.9);c.arc(14,-2,11,PI*1.3,PI*.3);c.arc(2,10,10,PI*.1,PI*.9);c.closePath();c.fill();c.stroke();c.restore()}
|
||||
export default(c,t)=>{
|
||||
const sl=t<.55?ease(t/.55):t<.72?1:1-ease((t-.72)/.28),sigh=t<.6,inh=t>.72
|
||||
c.lineWidth=6
|
||||
c.save();c.translate(0,6*sl)
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(108,198);c.quadraticCurveTo(80,206,76,252);c.lineTo(180,252);c.quadraticCurveTo(178,220,150,200);c.closePath();c.fill()
|
||||
c.fillStyle=c.createPattern(DOT,'repeat');c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(128,200);c.lineTo(150,200);c.lineTo(156,224);c.lineTo(132,214);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.translate(130,190);c.rotate(.1*sl);c.translate(-130,-190)
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(150,62);c.bezierCurveTo(120,34,66,48,62,100);c.bezierCurveTo(58,150,70,200,56,246);c.lineTo(104,246);c.quadraticCurveTo(110,200,118,160);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(120,176);c.lineTo(122,204);c.lineTo(146,204);c.lineTo(144,172);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(152,60);c.bezierCurveTo(168,72,172,96,170,112);c.quadraticCurveTo(172,122,178,130);c.lineTo(186,144);c.quadraticCurveTo(182,149,176,149);c.quadraticCurveTo(180,153,179,156);c.quadraticCurveTo(174,158,177,162);c.quadraticCurveTo(174,174,162,180);c.quadraticCurveTo(146,186,128,176);c.bezierCurveTo(104,150,104,90,152,60);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(154,66);c.bezierCurveTo(124,40,70,52,66,100);c.bezierCurveTo(64,130,76,150,96,160);c.quadraticCurveTo(110,150,118,126);c.quadraticCurveTo(128,112,140,116);c.lineTo(144,100);c.lineTo(152,110);c.lineTo(158,94);c.lineTo(164,104);c.quadraticCurveTo(168,82,154,66);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(92,62);c.quadraticCurveTo(110,54,128,58);c.moveTo(78,100);c.quadraticCurveTo(76,130,86,148);c.moveTo(70,178);c.lineTo(72,226);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.ellipse(118,92,9,5,-.5,0,T);c.fill();c.stroke()
|
||||
const lid=sl*5
|
||||
c.save();c.beginPath();c.moveTo(150,124+lid);c.quadraticCurveTo(160,120+lid,168,126);c.quadraticCurveTo(162,138,152,136);c.closePath();c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(161,131,5,7,0,0,T);c.fill();c.restore()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(148,124+lid);c.quadraticCurveTo(160,118+lid,170,126);c.lineTo(174,121);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(152,138);c.quadraticCurveTo(160,141,166,137);c.stroke()
|
||||
c.beginPath();c.moveTo(152,112+lid*.6);c.quadraticCurveTo(160,108+lid*.6,168,113+lid);c.stroke()
|
||||
c.lineWidth=2;for(const k of[0,5,10])c.beginPath(),c.moveTo(150+k,150),c.lineTo(154+k,143),c.stroke()
|
||||
c.restore()
|
||||
c.restore()
|
||||
const mx=182,my=156+6*sl+3*sl
|
||||
c.lineWidth=3;c.fillStyle='#111'
|
||||
if(sigh&&t>.05){c.beginPath();c.ellipse(mx-2,my,3,4,0,0,T);c.fill()}
|
||||
if(sigh){const p=min(1,t/.55);cloud(c,mx+12+28*p,my-8-30*p,.2+.8*p)
|
||||
c.lineWidth=3;for(const k of[0,1,2])c.beginPath(),c.moveTo(mx+6+k*4,my+4+k*5),c.quadraticCurveTo(mx+16+k*4+p*10,my+k*5,mx+24+k*4+p*16,my+6+k*5),c.stroke()}
|
||||
else if(t<.72){const q=(t-.6)/.12;cloud(c,222-6*q,118-10*q,1-q)}
|
||||
if(inh){const q=(t-.72)/.28;c.lineWidth=3;for(const[a,r]of[[-.5,30],[0,34],[.5,30]]){const x=mx+4+cos(a)*r*(1-q*.4),y=my+sin(a)*r*(1-q*.4)
|
||||
c.beginPath();c.moveTo(x,y);c.lineTo(x-cos(a)*12,y-sin(a)*12);c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// smug girl with gradient-tone long hair and side-swept bangs: L-shaped hand at her chin, half-lidded side glance, ufufu squiggles rising and sparkles twinkling
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=90,rim=4
|
||||
// screentone whose dots grow from tiny at y0 to fat at y1
|
||||
const grad=(c,path,y0,y1)=>{path();c.fillStyle='#fff';c.fill();c.save();c.clip();c.fillStyle='#111';c.beginPath()
|
||||
for(let y=y0,r=0;y<y1;y+=6,r++)for(let x=(r%2)*3;x<256;x+=6){const s=.35+2.5*(y-y0)/(y1-y0);c.moveTo(x+s,y);c.arc(x,y,s,0,T)}c.fill();c.restore();path();c.stroke()}
|
||||
const star=(c,x,y,r)=>{c.beginPath();for(let k=0;k<8;k++){const q=k%2?r*.28:r,a=k*PI/4-PI/2;c.lineTo(x+cos(a)*q,y+sin(a)*q)}c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t,i)=>{
|
||||
const fu=i%5<2
|
||||
c.lineWidth=6
|
||||
grad(c,()=>{c.beginPath();c.moveTo(70,90);c.bezierCurveTo(40,140,56,190,40,246);c.lineTo(216,246);c.bezierCurveTo(200,190,216,140,186,90);c.closePath()},60,250)
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,192);c.quadraticCurveTo(70,200,64,250);c.lineTo(192,250);c.quadraticCurveTo(186,200,156,192);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(108,192);c.lineTo(128,214);c.lineTo(148,192);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(128,214);c.lineTo(114,206);c.lineTo(116,226);c.closePath();c.moveTo(128,214);c.lineTo(142,206);c.lineTo(140,226);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,26);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){const y=142
|
||||
c.save();c.beginPath();c.ellipse(x,y,13,15,0,0,T);c.clip();c.beginPath();c.rect(x-20,y-6,40,30);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,15,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x-6,y+2,9,12,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-9,y-1,3,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-5);c.quadraticCurveTo(x,y-9,x+16,y-5);c.moveTo(x+d*15,y-5);c.lineTo(x+d*21,y-8);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-9,y+15);c.quadraticCurveTo(x,y+18,x+9,y+14);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(x-11,y-21);c.quadraticCurveTo(x,y-26-(d<0?4:0),x+11,y-21);c.stroke()}
|
||||
c.lineWidth=2.2;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+k-3,165),c.lineTo(128+d*44+k+3,158),c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128,156);c.lineTo(127,159);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath()
|
||||
if(fu)c.moveTo(118,168),c.quadraticCurveTo(126,176,136,166),c.quadraticCurveTo(126,170,118,168),c.fillStyle='#111',c.fill()
|
||||
else c.moveTo(117,170),c.quadraticCurveTo(127,174,137,165),c.moveTo(137,165),c.lineTo(140,162)
|
||||
c.stroke()
|
||||
c.lineWidth=6
|
||||
grad(c,()=>{c.beginPath();c.moveTo(66,158);c.bezierCurveTo(50,70,90,34,130,34);c.bezierCurveTo(172,34,212,74,196,172);c.quadraticCurveTo(190,142,176,126);c.lineTo(166,132);c.lineTo(160,116);c.lineTo(148,122);c.lineTo(140,106);c.lineTo(126,110);c.lineTo(114,98);c.lineTo(100,102);c.lineTo(90,94);c.lineTo(82,104);c.quadraticCurveTo(70,122,66,158);c.closePath()},34,170)
|
||||
c.strokeStyle='#fff';c.lineWidth=7;c.beginPath();c.arc(128,112,62,PI*1.25,PI*1.45);c.stroke();c.beginPath();c.arc(128,112,62,PI*1.55,PI*1.75);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(76,96,22,8,4);c.fill();c.stroke();c.beginPath();c.roundRect(80,108,18,7,3.5);c.fill();c.stroke()
|
||||
for(const[l,s]of[[30,'#111'],[19,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(208,252),c.quadraticCurveTo(206,226,186,206),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4.5
|
||||
c.beginPath();c.roundRect(172,172,10,30,5);c.fill();c.stroke()
|
||||
c.beginPath();c.roundRect(150,194,28,10,5);c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(182,204,14,12,-.3,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(174,212);c.lineTo(188,210);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=2.5
|
||||
for(let k=0;k<3;k++){const p=(t+k/3)%1,x=40+k*6-p*8,y=150-k*22-p*24,s=min(1,p*4,(1-p)*4)
|
||||
if(s>.1){c.save();c.translate(x,y);c.scale(s,s);c.beginPath();c.moveTo(-10,0);c.bezierCurveTo(-5,-8,0,8,5,0);c.bezierCurveTo(8,-5,12,-2,12,2);for(const[l,st]of[[8,'#fff'],[3,'#111']])c.lineWidth=l,c.strokeStyle=st,c.stroke();c.beginPath();c.arc(14,-8,2.5,0,T);c.fillStyle='#111';c.fill();c.restore()}}
|
||||
c.fillStyle='#fff';c.lineWidth=2.5
|
||||
for(const[x,y,o]of[[208,58,0],[226,96,.33],[36,70,.66]]){const r=3+7*max(0,sin(T*(t+o)))
|
||||
if(r>3.5)star(c,x,y,r)}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// chibi sprinter with inky twin pigtails holds "set" in the blocks, explodes off in dust, sprints with speed lines, then rewinds
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs,min,max}=Math,T=2*PI
|
||||
export const frames=20,delay=60,rim=4
|
||||
const tile=(s,col,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[s/2,s/2],[0,0],[s,0],[0,s],[s,s]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const DOT=tile(6,'#111',1.4),LITE=tile(5,'#fff',1)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u),lerp=(a,b,k)=>a+(b-a)*k
|
||||
const blend=(p,q,k)=>Array.isArray(p)?p.map((v,n)=>blend(v,q[n],k)):lerp(p,q,k)
|
||||
const limb=(c,p,w)=>{for(const[l,s]of[[w+8,'#111'],[w,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...p[0]);for(const q of p.slice(1))c.lineTo(...q);c.stroke()}c.strokeStyle='#111'}
|
||||
// pose: [hip, torso lean, near leg [knee,foot], far leg, near arm [elbow,hand], far arm], legs from the hip, arms from the shoulder
|
||||
const SET=[[88,194],1.2,[[24,14],[12,42]],[[-18,26],[-42,42]],[[4,30],[2,58]],[[-2,30],[-6,58]]]
|
||||
const GO=[[114,184],.8,[[28,4],[22,36]],[[-20,26],[-46,50]],[[-22,16],[-34,-4]],[[20,14],[32,-6]]]
|
||||
const run=f=>{const lg=s=>{const th=.95*sin(f+s),sh=th-.25-1.3*(sin(f+s+1.3)+1)/2;return[[30*sin(th),30*cos(th)],[30*sin(th)+30*sin(sh),30*cos(th)+30*cos(sh)]]}
|
||||
const am=s=>{const a=-.9*sin(f+s);return[[24*sin(a),24*cos(a)],[24*sin(a)+22*sin(a-1.4),24*cos(a)+22*cos(a-1.4)]]}
|
||||
return[[132,176+3*abs(sin(f))],.32,lg(0),lg(PI),am(PI),am(0)]}
|
||||
const eye=(c,x,y,d,lt)=>{
|
||||
c.save();c.beginPath();c.moveTo(x-17,y-6+d*4);c.lineTo(x+17,y-6-d*4);c.lineTo(x+17,y+22);c.lineTo(x-17,y+22);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,15,0,0,T);c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+6,y+3,9,12,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-4,y+7,22,10)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+4,y,3.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6+d*4);c.lineTo(x+16,y-6-d*4);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-14,y-26+d*6);c.lineTo(x+14,y-26-d*6);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const dt=c.createPattern(DOT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const f=(t-.4)/.42*T*2.5
|
||||
let P=t<.3?SET:t<.4?blend(SET,GO,ease((t-.3)/.1)):t<.5?blend(GO,run(f),ease((t-.4)/.1)):t<.8?run(f):blend(run(f),SET,ease((t-.8)/.2))
|
||||
if(t<.3)P=blend(P,P,0),P[0]=[P[0][0]+(t>.18?1.5*sin(t*T*24):0),P[0][1]]
|
||||
const[H,a,nl,fl,na,fa]=P,go=t>=.3&&t<.82
|
||||
c.save();c.translate(118,236);c.scale(1.07,1.07);c.translate(-116,-242)
|
||||
// starting blocks
|
||||
c.lineWidth=5
|
||||
c.fillStyle='#111';for(const x of[44,98])c.beginPath(),c.moveTo(x,240),c.lineTo(x+20,240),c.lineTo(x+4,222),c.closePath(),c.fill(),c.stroke()
|
||||
if(t>=.32&&t<.75){const k=(t-.32)/.43;c.fillStyle='#fff';c.lineWidth=4;for(const[x,r]of[[50,14],[74,18],[98,12],[32,10]])c.beginPath(),c.arc(x-8*k,232-6*k,r*(1-k)*(1+.6*k),0,T),c.fill(),c.stroke()}
|
||||
if(t>=.42&&t<.82){c.lineWidth=4;for(const[y,o]of[[110,0],[140,.4],[170,.2],[200,.7],[226,.5]]){const x=18+((o+t*5)%1)*32;c.beginPath();c.moveTo(x,y);c.lineTo(x+34,y);c.stroke()}}
|
||||
const S=[H[0]+44*sin(a),H[1]-44*cos(a)],F=[S[0]-6,S[1]-2]
|
||||
const leg=(l,w,dx)=>{const hp=[H[0]+dx,H[1]],kn=[hp[0]+l[0][0],hp[1]+l[0][1]],ft=[hp[0]+l[1][0],hp[1]+l[1][1]];limb(c,[hp,kn,ft],w)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(ft[0]+6,ft[1]+2,14,8,0,0,T);c.fill();c.stroke();c.fillStyle='#111';c.fillRect(ft[0]-6,ft[1]+4,24,4)}
|
||||
const arm=(o,l)=>{const e=[o[0]+l[0][0],o[1]+l[0][1]],w=[o[0]+l[1][0],o[1]+l[1][1]];limb(c,[o,e,w],12);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(...w,8,0,T);c.fill();c.stroke()}
|
||||
arm(F,fa);leg(fl,15,-4)
|
||||
// torso
|
||||
c.save();c.translate(...H);c.rotate(a)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-20,-8);c.lineTo(20,-8);c.lineTo(23,12);c.lineTo(-23,12);c.closePath();c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(-26,-8,52,22);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(-20,-4);c.lineTo(20,-4);c.lineTo(22,-48);c.quadraticCurveTo(0,-56,-20,-48);c.closePath();c.fill();c.save();c.clip();c.fillStyle='#111';c.fillRect(-21,-54,7,52);c.restore();c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.roundRect(-6,-38,24,20,2);c.stroke();c.fillStyle='#111';c.fillRect(0,-33,4,11);c.fillRect(8,-33,6,11)
|
||||
c.restore()
|
||||
leg(nl,15,4)
|
||||
// head
|
||||
c.save();c.translate(H[0]+90*sin(a)+4,H[1]-90*cos(a));c.rotate(a*.45);c.scale(.64,.64);c.translate(-128,-121)
|
||||
c.lineWidth=9;c.fillStyle='#111'
|
||||
const fl2=go?10*sin(t*T*8):3*sin(t*T*2)
|
||||
for(const[y,d]of[[96,0],[150,1]]){c.beginPath();c.moveTo(70,y-12);c.quadraticCurveTo(30,y-20+fl2*(d?-1:1),6,y+6+fl2);c.quadraticCurveTo(26,y+10,20,y+30+fl2*.6);c.quadraticCurveTo(46,y+18,72,y+14);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(72,y+1,9,12,0,0,T);c.fill();c.stroke();c.fillStyle='#111'}
|
||||
c.beginPath();c.moveTo(66,154);c.bezierCurveTo(46,74,88,36,130,36);c.bezierCurveTo(176,36,208,74,190,150);c.lineTo(160,154);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,114,140,-1,lt);eye(c,162,140,1,lt)
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const j of[-6,0,6])c.beginPath(),c.moveTo(138+d*42+j-3,168),c.lineTo(138+d*42+j+3,160),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath()
|
||||
if(go)c.roundRect(128,162,26,14,4),c.fill(),c.stroke(),c.beginPath(),c.moveTo(128,169),c.lineTo(154,169),c.moveTo(137,162),c.lineTo(137,176),c.moveTo(146,162),c.lineTo(146,176),c.stroke()
|
||||
else c.moveTo(132,170),c.lineTo(150,168),c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(72,126);c.lineTo(72,92);c.bezierCurveTo(86,50,174,50,188,94);c.lineTo(188,124);c.lineTo(176,104);c.lineTo(168,120);c.lineTo(156,100);c.lineTo(146,116);c.lineTo(134,98);c.lineTo(122,114);c.lineTo(110,98);c.lineTo(100,116);c.lineTo(90,102);c.lineTo(82,118);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=7;for(const[s,e]of[[1.18,1.3],[1.38,1.56],[1.64,1.76]])c.beginPath(),c.arc(130,124,60,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
if(t>=.16&&t<.3){c.lineWidth=4;c.beginPath();c.moveTo(170,104);c.lineTo(174,90);c.moveTo(188,110);c.lineTo(198,98);c.moveTo(152,106);c.lineTo(150,92);c.stroke()}
|
||||
arm(S,na)
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// extreme close-up panel of an unblinking stare: hatched shadow under her ink bangs, big eyes with tiny glinting pupils, focus lines, a sweat bead sliding, then one slow blink
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=20,delay=90,rim=4
|
||||
const F=[...Array(60)].map((_,k)=>[k/60*T+.035*sin(k*12.9),[124,140,132,146][k%4]])
|
||||
const panel=c=>{c.beginPath();c.roundRect(14,58,228,140,10)}
|
||||
const hatch=(c,y0,y1,s=4.5)=>{c.beginPath();for(let x=-40;x<300;x+=s)c.moveTo(x,y1),c.lineTo(x+(y1-y0)*.6,y0);c.stroke()}
|
||||
// eye pieces in local coords, scaled by d (mirror) and o (how open) about the lower lid
|
||||
const K=1.35,at=(c,x,y,d,o,f)=>{c.save();c.translate(x,y+26*K);c.scale(d*K,max(o,.04)*K);c.translate(0,-26);c.beginPath();f();c.setTransform(1,0,0,1,0,0)}
|
||||
export default(c,t)=>{
|
||||
const o=t>.7&&t<.92?1-sin(PI*(t-.7)/.22):1,g=3.5+3*(.5+.5*sin(2*T*t))
|
||||
c.fillStyle='#111'
|
||||
for(const[a,r]of F){const w=3.2/200;c.beginPath();c.moveTo(128+cos(a-w)*200,128+sin(a-w)*200);c.lineTo(128+cos(a)*r,128+sin(a)*r);c.lineTo(128+cos(a+w)*200,128+sin(a+w)*200);c.closePath();c.fill()}
|
||||
c.lineWidth=7;panel(c);c.fillStyle='#fff';c.fill()
|
||||
c.save();panel(c);c.clip()
|
||||
c.lineWidth=1.8;hatch(c,96,122)
|
||||
for(const[x,d]of[[80,-1],[176,1]]){const y=144
|
||||
at(c,x,y,d,o,()=>c.ellipse(0,0,25,28,0,0,T));c.fillStyle='#fff';c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+d*3,y+5,16,23,0,0,T);c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=1.5;c.beginPath();c.ellipse(x+d*3,y+5,11,16,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.arc(x+d*3,y+5,7,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<8;k++){const r=k%2?g*.3:g,a=k*PI/4;c.lineTo(x+d*3-3+cos(a)*r,y+1+sin(a)*r)}c.closePath();c.fill()
|
||||
c.strokeStyle='#111';c.lineWidth=1.6;hatch(c,y-40,y-16,4)
|
||||
c.restore()
|
||||
at(c,x,y,d,o,()=>{c.moveTo(-30,-2);c.bezierCurveTo(-24,-30,16,-36,32,-12)});c.lineWidth=8;c.stroke();c.restore()
|
||||
at(c,x,y,d,o,()=>{c.moveTo(30,-14);c.lineTo(40,-24);c.moveTo(31,-9);c.lineTo(41,-12)});c.lineWidth=4.5;c.stroke();c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-d*16,y+39);c.quadraticCurveTo(x+d*8,y+43,x+d*27,y+32);c.stroke()}
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(130,180);c.lineTo(127,188);c.stroke()
|
||||
c.fillStyle='#111'
|
||||
for(const d of[-1,1]){c.beginPath();c.moveTo(128+d*114,58);c.lineTo(128+d*114,198);c.lineTo(128+d*108,198);c.quadraticCurveTo(128+d*104,140,128+d*92,104);c.closePath();c.fill()}
|
||||
c.beginPath();c.moveTo(14,58);c.lineTo(242,58);c.lineTo(242,96);c.lineTo(228,90);c.lineTo(222,108);c.lineTo(206,86);c.lineTo(196,100);c.lineTo(178,84);c.lineTo(166,96);c.lineTo(150,78);c.lineTo(136,92);c.lineTo(122,76);c.lineTo(110,94);c.lineTo(94,80);c.lineTo(84,98);c.lineTo(66,84);c.lineTo(56,104);c.lineTo(42,88);c.lineTo(32,100);c.lineTo(14,94);c.closePath();c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;for(const[x0,y0,x1,y1]of[[60,64,70,76],[146,62,154,72],[198,64,206,76]])c.beginPath(),c.moveTo(x0,y0),c.lineTo(x1,y1),c.stroke()
|
||||
for(const d of[-1,1])for(const[l,s]of[[8,'#fff'],[3.5,'#111']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128+d*16,106),c.lineTo(128+d*62,98),c.stroke()
|
||||
const u=min(1,t/.75),ds=t<.08?t/.08:t>.75?max(0,1-(t-.75)/.15):1
|
||||
if(ds>.05){c.save();c.translate(226,112+48*u);c.scale(ds,ds);c.fillStyle='#fff';c.strokeStyle='#111';c.lineWidth=3.5;c.beginPath();c.moveTo(0,-15);c.quadraticCurveTo(10,-2,9,5);c.arc(0,5,9,0,PI);c.quadraticCurveTo(-10,-2,0,-15);c.fill();c.stroke();c.lineWidth=2;c.beginPath();c.arc(0,5,5,PI*.7,PI*1.05);c.stroke();c.restore()}
|
||||
c.restore()
|
||||
c.strokeStyle='#111';c.lineWidth=7;panel(c);c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// crouching girl with short ink pigtails holds out a treat to a stray cat, it licks, then rubs its cheek on her hand, hearts rising
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=20,delay=90,rim=4
|
||||
const tone=(c,s,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle='#111';for(const[a,b]of[[0,0],[s,0],[0,s],[s,s],[s/2,s/2]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return c.createPattern(k,'repeat')}
|
||||
const ease=u=>(u=max(0,min(1,u)),u*u*(3-2*u))
|
||||
const heart=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.beginPath();c.moveTo(0,8);c.bezierCurveTo(-13,-1,-9,-12,0,-5);c.bezierCurveTo(9,-12,13,-1,0,8);c.restore();c.fillStyle='#fff';c.fill();c.lineWidth=3;c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
const rub=ease((t-.48)/.08)*(1-ease((t-.88)/.08)),lick=t<.48||t>=.94,tg=lick&&sin(t*T*10)>0
|
||||
// ground
|
||||
c.fillStyle=FINE;c.beginPath();c.ellipse(132,240,112,9,0,0,T);c.fill()
|
||||
// girl squatting
|
||||
c.save();c.translate(80,104);c.scale(.6,.6);c.translate(-128,-130)
|
||||
c.lineWidth=10;c.fillStyle='#111';for(const x of[96,150])c.beginPath(),c.roundRect(x-18,348,40,18,8),c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(96,184);c.quadraticCurveTo(62,200,64,280);c.lineTo(70,330);c.lineTo(190,330);c.lineTo(196,280);c.quadraticCurveTo(196,200,160,184);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(102,186);c.quadraticCurveTo(128,214,154,186);c.stroke();for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*12,204),c.lineTo(128+d*14,236),c.stroke()
|
||||
// knees up in front, shorts
|
||||
c.lineWidth=10;c.fillStyle='#fff';for(const x of[100,156])c.beginPath(),c.ellipse(x,300,30,34,0,0,T),c.fill(),c.stroke()
|
||||
for(const x of[100,156])c.beginPath(),c.rect(x-12,328,24,24),c.fill(),c.stroke()
|
||||
// the arm resting on her knee
|
||||
c.beginPath();c.ellipse(96,270,16,14,0,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(rub>.5){c.lineWidth=9;for(const x of[103,153])c.beginPath(),c.moveTo(x-15,146),c.quadraticCurveTo(x,128,x+15,146),c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(114,166);c.quadraticCurveTo(128,184,142,166);c.stroke()}
|
||||
else{for(const[x,d]of[[103,-1],[153,1]]){
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,142,14,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,142,14,16,0,0,T);c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+6,146,11,13,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+2,140,4.5,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+10,152,2.2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=9;c.beginPath();c.moveTo(x-16,136);c.quadraticCurveTo(x,124,x+16,136);c.moveTo(x+d*14,134);c.lineTo(x+d*21,128);c.stroke()}
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(120,168);c.quadraticCurveTo(128,174,136,168);c.stroke()}
|
||||
c.lineWidth=5;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*42+k-3,170),c.lineTo(128+d*42+k+3,162),c.stroke()
|
||||
// short ink pigtails low on each side, round bangs
|
||||
c.lineWidth=10;c.fillStyle='#111';for(const d of[-1,1]){c.beginPath();c.moveTo(128+d*54,148);c.quadraticCurveTo(128+d*86,160,128+d*80,196);c.quadraticCurveTo(128+d*70,190,128+d*66,198);c.quadraticCurveTo(128+d*62,172,128+d*52,166);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(128+d*58,154,8,0,T);c.fill();c.stroke();c.fillStyle='#111'}
|
||||
c.beginPath();c.moveTo(70,150);c.bezierCurveTo(50,72,88,38,128,38);c.bezierCurveTo(168,38,206,72,186,150)
|
||||
c.quadraticCurveTo(184,118,176,106);c.quadraticCurveTo(160,118,146,110);c.quadraticCurveTo(132,120,116,110);c.quadraticCurveTo(100,120,86,110);c.quadraticCurveTo(76,124,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=10;c.beginPath();c.arc(128,104,52,1.28*PI,1.72*PI);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
// her reaching arm and the treat tube
|
||||
const hx=150,hy=188
|
||||
for(const[l,s]of[[24,'#111'],[14,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(104,152),c.quadraticCurveTo(122,186,hx,hy),c.stroke()
|
||||
c.strokeStyle='#111';c.save();c.translate(hx+8,hy+2);c.rotate(.25);c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(-4,-6);c.lineTo(24,-4);c.lineTo(30,0);c.lineTo(24,4);c.lineTo(-4,6);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.arc(30,0,2.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy,12,10,.2,0,T);c.fill();c.stroke()
|
||||
// the cat: body, tail, head moving between licking the tube and rubbing her hand
|
||||
const cx=200,cy=214,sw=sin(t*T*2)
|
||||
for(const[l,s]of[[13,'#111'],[5,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(cx+22,cy-6),c.bezierCurveTo(cx+46,cy-14,cx+40+8*sw,cy-50,cx+30+10*sw,cy-60),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(cx,cy,30,20,0,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(cx+10,cy-8,14,9,.3,0,T);c.fill()
|
||||
c.fillStyle='#fff';for(const x of[cx-18,cx+16])c.beginPath(),c.ellipse(x,cy+18,7,5,0,0,T),c.fill(),c.stroke()
|
||||
const kx=184-20*rub,ky=200-14*rub,ka=-.2-.35*rub+(rub>.5?.12*sin(t*T*6):0)
|
||||
c.save();c.translate(kx,ky);c.rotate(ka)
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(d*16,-8),c.lineTo(d*14,-26),c.lineTo(d*3,-14),c.closePath(),c.fill(),c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(14,-8);c.lineTo(14,-26);c.lineTo(4,-14);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(0,0,19,16,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3.5;if(rub>.5)for(const d of[-1,1])c.beginPath(),c.arc(d*7,-1,4.5,1.15*PI,1.85*PI),c.stroke()
|
||||
else{c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(d*7-3,-1,3,4.5,0,0,T),c.fill()}
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(-7,7);c.quadraticCurveTo(-4,10,-1,7);c.quadraticCurveTo(2,10,5,7);c.stroke()
|
||||
if(tg){c.fillStyle='#fff';c.beginPath();c.ellipse(-4,12,4,5,0,0,T);c.fill();c.stroke()}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-1,1])c.beginPath(),c.moveTo(d*12,4+k*3),c.lineTo(d*24,3+k*6),c.stroke()
|
||||
c.restore()
|
||||
if(lick&&tg){c.lineWidth=3;for(const k of[0,1])c.beginPath(),c.moveTo(168+k*6,180),c.lineTo(172+k*6,172),c.stroke()}
|
||||
// purr marks and hearts while it rubs
|
||||
if(rub>.5){c.lineWidth=3;for(const k of[0,1])c.beginPath(),c.arc(kx+4,ky-4,26+k*8,-1.1,-.4),c.stroke()
|
||||
for(const[o,x]of[[0,182],[.33,206],[.66,162]]){const p=(t*3+o)%1;heart(c,x+6*sin(p*T),150-p*60,.8+.5*sin(PI*p))}}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// cleaning duty chibi in a polka-dot kerchief and apron, dotted braid over her shoulder, humming while she sweeps side to side, dust puffing off the broom
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=16,delay=80,rim=4
|
||||
const tile=createCanvas(6,6),q=tile.getContext('2d');q.fillStyle='#111';for(const[x,y]of[[1.5,1.5],[4.5,4.5]])q.beginPath(),q.arc(x,y,1.1,0,T),q.fill()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const P=[158,150],LEN=96,BASE=-.5
|
||||
const ang=t=>BASE+.3*sin(T*2*t)
|
||||
const tip=t=>{const a=ang(t);return[P[0]+LEN*sin(a),P[1]+LEN*cos(a)]}
|
||||
const note=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.beginPath();c.ellipse(0,0,6,4.5,-.35,0,T);c.fill();c.lineWidth=2.5;c.beginPath();c.moveTo(5,-2);c.lineTo(5,-20);c.quadraticCurveTo(12,-15,12,-8);c.stroke();c.restore()}
|
||||
export default(c,t)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),a=ang(t),[tx,ty]=tip(t),lo=[P[0]+LEN*.42*sin(a),P[1]+LEN*.42*cos(a)]
|
||||
// dust puffs left at each end of a sweep, drifting off and shrinking
|
||||
c.lineWidth=3;c.fillStyle='#fff'
|
||||
for(const te of[1/8,3/8,5/8,7/8]){const age=(t-te+1)%1;if(age>.3)continue;const[x,y]=tip(te),d=te==1/8||te==5/8?1:-1,r=(1-age/.3)*10;for(const o of[0,1])c.beginPath(),c.arc(x+d*(8+age*70)+o*d*10,y-4-age*40-o*6,r*(1-o*.35),0,T),c.fill(),c.stroke()}
|
||||
// legs and shoes
|
||||
c.lineWidth=4;c.fillStyle='#fff';for(const x of[106,138]){c.beginPath();c.rect(x,190,14,36);c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.ellipse(x+7,232,12,7,0,0,T);c.fill();c.fillStyle='#fff'}
|
||||
// dotted dress under a white apron
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(98,120);c.lineTo(150,120);c.lineTo(170,196);c.lineTo(78,196);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(102,136);c.lineTo(146,136);c.lineTo(156,190);c.quadraticCurveTo(124,198,92,190);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.rect(112,160,24,16);c.stroke();c.beginPath();c.moveTo(102,136);c.lineTo(110,122);c.moveTo(146,136);c.lineTo(138,122);c.stroke()
|
||||
// dotted braid over her shoulder
|
||||
c.lineWidth=4;for(let k=0;k<5;k++){c.beginPath();c.ellipse(84-k,112+k*14,9-k*.6,9,.2,0,T);toned(c,dot)}
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(80,180,6,4,0,0,T);c.fill();c.stroke();c.beginPath();c.moveTo(78,184);c.lineTo(74,196);c.lineTo(84,196);c.closePath();toned(c,dot)
|
||||
// head: hair, face, kerchief
|
||||
c.lineWidth=6;c.beginPath();c.arc(124,74,48,0,T);toned(c,dot)
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(124,84,40,38,0,0,T);c.fill();c.stroke()
|
||||
for(const x of[106,142]){c.lineWidth=5;c.beginPath();c.arc(x,100,9,1.15*PI,1.85*PI);c.stroke();c.lineWidth=3;c.beginPath();c.moveTo(x+(x<124?-9:9),96);c.lineTo(x+(x<124?-13:13),92);c.stroke()}
|
||||
c.lineWidth=2.2;for(const d of[-1,1])for(let k=0;k<3;k++)c.beginPath(),c.moveTo(124+d*30+(k-1)*4-2,112),c.lineTo(124+d*30+(k-1)*4+2,106),c.stroke()
|
||||
const hm=.8+.2*sin(T*4*t);c.lineWidth=3;c.beginPath();c.ellipse(124,113,4*hm,5*hm,0,0,T);c.stroke()
|
||||
// bangs peeking under the kerchief
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(80,64);for(const[x,y]of[[86,82],[96,70],[106,84],[116,70],[126,82],[136,70],[146,84],[156,70],[164,82],[168,64]])c.lineTo(x,y);c.closePath();toned(c,dot)
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.arc(124,74,52,1.02*PI,1.98*PI);c.quadraticCurveTo(124,58,72,72);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(78,66);c.quadraticCurveTo(124,52,170,66);c.stroke()
|
||||
c.lineWidth=4;for(const[x,y,r]of[[178,40,-.6],[184,56,.5]])c.beginPath(),c.ellipse(x,y,11,6,r,0,T),c.fill(),c.stroke();c.beginPath();c.arc(172,50,5,0,T);c.fill();c.stroke()
|
||||
// broom: long handle, banded straw head, swinging from her top hand
|
||||
c.save();c.translate(...P);c.rotate(-a)
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.roundRect(-4,-12,8,LEN+2,3);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(-8,LEN-8);c.lineTo(8,LEN-8);c.lineTo(22,LEN+30);c.lineTo(-22,LEN+30);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const x of[-14,-7,0,7,14])c.beginPath(),c.moveTo(x*.4,LEN),c.lineTo(x,LEN+28),c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(-10,LEN-2);c.lineTo(10,LEN-2);c.stroke()
|
||||
c.restore()
|
||||
// arms to the two hands on the handle
|
||||
for(const[s0,h]of[[[96,128],lo],[[150,126],P]]){for(const[l,st]of[[16,'#111'],[10,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(...s0),c.lineTo(...h),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(h[0],h[1],8,0,T);c.fill();c.stroke()}
|
||||
// humming notes
|
||||
c.fillStyle='#111';c.strokeStyle='#111';for(const o of[0,.5]){const p=(t*2+o)%1;if(p<.85)note(c,176+p*40+6*sin(T*p),112-p*80,.8+.4*sin(PI*p))}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// manga zashiki-warashi: chibi child with an ink okappa bob and a flower-print kimono, bouncing an embroidered temari ball twice per loop
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs,max,min}=Math,T=2*PI
|
||||
export const frames=16,delay=80,rim=4
|
||||
const TL=createCanvas(6,6),tx=TL.getContext('2d');tx.fillStyle='#111';for(const[a,b]of[[1.5,1.5],[4.5,4.5]])tx.beginPath(),tx.arc(a,b,1.05,0,T),tx.fill()
|
||||
// round child eye: big ink iris, two big highlights, short lashes
|
||||
const eye=(c,x,y,d,lx)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,16,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+lx,y+2,10.5,13,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+lx-3,y-3,4.8,0,T);c.fill();c.beginPath();c.arc(x+lx+4,y+7,2.5,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-19,x+16,y-6);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(x+d*14,y-9);c.lineTo(x+d*19,y-14);c.stroke()
|
||||
}
|
||||
// temari: white ball with a toned band, crossing thread stars and a center flower
|
||||
const ball=(c,x,y,r,sq,tn,rot)=>{
|
||||
c.save();c.translate(x,y);c.scale(1+sq,1-sq);c.rotate(rot)
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(0,0,r,0,T);c.fill()
|
||||
// kiku stitching: alternating toned and white wedges, then a white ring of thread round the middle
|
||||
c.save();c.clip();c.fillStyle=tn;for(let k=0;k<8;k+=2){c.beginPath();c.moveTo(0,0);c.arc(0,0,r,k*PI/4,(k+1)*PI/4);c.fill()}
|
||||
c.lineWidth=2;c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4;c.moveTo(0,0);c.lineTo(r*cos(a),r*sin(a))}c.stroke()
|
||||
c.restore();c.beginPath();c.arc(0,0,r,0,T);c.stroke();c.lineWidth=2.5;c.fillStyle='#fff';c.beginPath();c.arc(0,0,r*.55,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();for(let k=0;k<10;k++){const a=k*PI/5,q=k%2?3:7;c.lineTo(q*cos(a),q*sin(a))}c.closePath();c.fill()
|
||||
c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat')
|
||||
// ball: hand on top at the peak, floor at the bottom, squashed on contact
|
||||
const ph=abs(sin(2*PI*t)),r=17,by=226-88*ph,sq=ph<.12?.18*(1-ph/.12):0,bx=184
|
||||
const hy=min(by-r-4,118+10*(1-ph)),look=(1-ph)*3
|
||||
// floor shadows
|
||||
c.fillStyle=tn;c.lineWidth=3;c.beginPath();c.ellipse(112,244,50,7,0,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(bx,244,12+8*(1-ph),4,0,0,T);c.fill();c.stroke()
|
||||
// little zori feet
|
||||
c.fillStyle='#fff';c.lineWidth=4;for(const x of[96,126])c.beginPath(),c.ellipse(x,240,13,6,0,0,T),c.fill(),c.stroke()
|
||||
// kimono: toned with a scatter of white five-petal flowers, white obi with a bow behind
|
||||
const kim=()=>{c.beginPath();c.moveTo(94,134);c.quadraticCurveTo(80,170,76,236);c.lineTo(146,236);c.quadraticCurveTo(142,170,130,134);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';kim();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(70,130,90,110)
|
||||
c.fillStyle='#fff';c.lineWidth=2;for(const[x,y]of[[90,200],[118,214],[132,184],[100,226],[86,170]]){for(let k=0;k<5;k++){const a=k*T/5;c.beginPath();c.arc(x+4*cos(a),y+4*sin(a),3,0,T);c.fill();c.stroke()}c.fillStyle='#111';c.beginPath();c.arc(x,y,1.5,0,T);c.fill();c.fillStyle='#fff'}
|
||||
c.restore();c.lineWidth=6;kim();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(98,136);c.lineTo(112,160);c.lineTo(126,136);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(84,166);c.lineTo(140,166);c.lineTo(141,180);c.lineTo(83,180);c.closePath();c.fill();c.stroke()
|
||||
// left sleeve hanging, hand tucked
|
||||
c.beginPath();c.moveTo(94,140);c.quadraticCurveTo(68,150,64,192);c.lineTo(88,194);c.quadraticCurveTo(90,168,98,156);c.closePath();c.fillStyle='#fff';c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(60,136,40,60);c.restore();c.stroke()
|
||||
// right arm reaching over to pat the ball, wide sleeve swinging under it
|
||||
const hx=bx-2
|
||||
for(const[l,s]of[[22,'#111'],[13,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128,146),c.quadraticCurveTo(160,148,hx-4,hy+2),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(134,144);c.quadraticCurveTo(150,150,152,176);c.quadraticCurveTo(140,180,130,168);c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(128,140,30,42);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy,10,7,0,0,T);c.fill();c.stroke()
|
||||
ball(c,bx,by,r,sq,tn,t*T)
|
||||
// head: ink okappa with a blunt fringe and a white ribbon
|
||||
c.save();c.translate(112,86);c.scale(.6,.6);c.translate(-128,-120)
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(62,170);c.bezierCurveTo(42,70,86,30,128,30);c.bezierCurveTo(170,30,214,70,194,170);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,136,-1,look);eye(c,153,136,1,look)
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-3,166),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
c.lineWidth=3.5;c.fillStyle='#111';c.beginPath();c.moveTo(118,164);c.quadraticCurveTo(128,176,138,164);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(66,140);c.bezierCurveTo(56,70,90,40,128,40);c.bezierCurveTo(166,40,200,70,190,140);c.lineTo(184,140);c.lineTo(182,108);c.lineTo(74,108);c.lineTo(72,140);c.closePath();c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(128,110,58,PI*1.25,PI*1.42);c.stroke();c.beginPath();c.arc(128,110,58,PI*1.52,PI*1.7);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(170,52),c.quadraticCurveTo(170+d*22,40-6,170+d*26,58),c.quadraticCurveTo(170+d*14,62,170,52),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(170,52,6,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// manga tengu girl with a white wolf cut, tokin cap and a crow mask on the side of her head, little black wings flapping as she waves her feather fan and gust curls blow off it
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI}=Math,T=2*PI
|
||||
export const frames=16,delay=80,rim=4
|
||||
const TL=createCanvas(6,6),tx=TL.getContext('2d');tx.fillStyle='#111';for(const[a,b]of[[1.5,1.5],[4.5,4.5]])tx.beginPath(),tx.arc(a,b,1.05,0,T),tx.fill()
|
||||
// sharp, confident eye: flat heavy lid, ink iris with a toned bottom
|
||||
const eye=(c,x,y,d,tn)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(x-15,y-5);c.lineTo(x+15,y-5);c.quadraticCurveTo(x+14,y+15,x,y+16);c.quadraticCurveTo(x-14,y+15,x-15,y-5);c.fill()
|
||||
c.save();c.clip();c.beginPath();c.ellipse(x-d*2,y+5,10,13,0,0,T);c.fillStyle='#111';c.fill();c.fillStyle=tn;c.beginPath();c.ellipse(x-d*2,y+11,8,6,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-d*2-4,y+1,3.5,4.5,-.4,0,T);c.fill();c.beginPath();c.arc(x-d*2+4,y+11,1.8,0,T);c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-d*16,y-3);c.lineTo(x+d*14,y-7);c.lineTo(x+d*21,y-11);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(x-d*14,y-18);c.lineTo(x+d*12,y-22);c.stroke()
|
||||
}
|
||||
// a wind curl: a short tail then a spiral rolling up and in
|
||||
const curl=(c,x,y,s)=>{c.beginPath();c.moveTo(x-2.4*s,y+s);c.lineTo(x,y+s);for(let k=1;k<=24;k++){const a=PI/2-k*.33,r=s*(1-.03*k);c.lineTo(x+r*cos(a),y+r*sin(a))}c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat'),fw=sin(2*T*t),wf=.13*sin(2*T*t+1)
|
||||
c.lineWidth=3.5
|
||||
for(let k=0;k<3;k++){const p=(2*t+k/3)%1,s=3+9*sin(PI*p);curl(c,200+32*p,112+k*32+6*sin(T*p),s)}
|
||||
c.save();c.translate(108,166);c.scale(.76,.76);c.translate(-128,-140)
|
||||
// little crow wings behind the shoulders
|
||||
for(const d of[-1,1]){
|
||||
c.save();c.translate(128+d*30,196);c.rotate(d*(wf-.05));c.scale(.8,.8)
|
||||
c.fillStyle='#111';c.lineWidth=5;c.beginPath();c.moveTo(0,0);c.bezierCurveTo(d*20,-60,d*56,-108,d*98,-122)
|
||||
for(const[x,y]of[[100,-92],[96,-64],[84,-40],[66,-20],[44,-6],[20,4]])c.quadraticCurveTo(d*(x+14),y-16,d*x,y)
|
||||
c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();for(const[x,y]of[[96,-92],[90,-64],[78,-40],[60,-20],[40,-6]])c.moveTo(d*x*.4,y*.5-24),c.lineTo(d*(x-6),y-8);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
}
|
||||
// white wolf-cut hair behind, layered spiky tips at the shoulders
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128,38);c.bezierCurveTo(60,38,52,100,62,150)
|
||||
for(const[x,y]of[[54,178],[72,170],[74,196],[94,184]])c.lineTo(x,y)
|
||||
c.lineTo(162,184);for(const[x,y]of[[182,196],[184,170],[202,178],[194,150]])c.lineTo(x,y)
|
||||
c.bezierCurveTo(204,100,196,38,128,38);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(const d of[-1,1])c.moveTo(128+d*58,130),c.lineTo(128+d*66,170),c.moveTo(128+d*48,150),c.lineTo(128+d*50,182);c.stroke()
|
||||
// yamabushi top: toned side panels, crossed collar, ink pom-poms
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,186);c.quadraticCurveTo(64,196,56,252);c.lineTo(200,252);c.quadraticCurveTo(192,196,156,186);c.closePath();c.fill()
|
||||
c.save();c.clip();c.fillStyle=tn;c.beginPath();c.moveTo(50,252);c.lineTo(100,188);c.lineTo(116,252);c.fill();c.beginPath();c.moveTo(206,252);c.lineTo(156,188);c.lineTo(140,252);c.fill();c.restore();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(104,188);c.lineTo(128,224);c.lineTo(152,188);c.stroke()
|
||||
for(const x of[108,148]){c.fillStyle='#111';c.lineWidth=3;c.beginPath();c.arc(x,232,10,0,T);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.arc(x-3,228,3,0,T);c.fill()}
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,22);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,166);c.lineTo(116,186);c.moveTo(140,166);c.lineTo(140,186);c.stroke()
|
||||
// face
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,103,138,-1,tn);eye(c,153,138,1,tn)
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+k-3,166),c.lineTo(128+d*44+k+3,158),c.stroke()
|
||||
// smirk with a fang
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(118,168);c.quadraticCurveTo(130,174,140,164);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(131,171);c.lineTo(134,177);c.lineTo(136,169);c.fill();c.stroke()
|
||||
// spiky white bangs
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(70,146);c.bezierCurveTo(52,74,88,40,128,40);c.bezierCurveTo(168,40,204,74,186,146)
|
||||
for(const[x,y]of[[180,102],[170,118],[160,94],[148,116],[138,92],[126,118],[114,92],[104,116],[94,96],[86,118],[78,102]])c.lineTo(x,y)
|
||||
c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(const[a,b,e,f]of[[104,56,96,92],[124,50,120,88],[150,52,158,90],[170,64,178,98]])c.moveTo(a,b),c.lineTo(e,f);c.stroke()
|
||||
// tokin cap on the forehead, tied on with a cord
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(108,60);c.quadraticCurveTo(84,70,78,90);c.moveTo(148,60);c.quadraticCurveTo(172,70,178,90);c.stroke()
|
||||
c.save();c.translate(128,52);c.fillStyle='#111';c.lineWidth=4;c.beginPath();c.moveTo(-15,9);c.lineTo(-17,-4);c.lineTo(-7,-15);c.lineTo(7,-15);c.lineTo(17,-4);c.lineTo(15,9);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2;c.beginPath();c.moveTo(-12,-2);c.lineTo(12,-2);c.moveTo(0,-12);c.lineTo(0,7);c.stroke();c.strokeStyle='#111';c.restore()
|
||||
// crow mask pushed to the side of her head
|
||||
c.save();c.translate(70,90);c.rotate(-.4)
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(0,0,16,20,0,0,T);c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(-16,-20,32,40);c.restore();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(-6,0);c.lineTo(-32,12);c.lineTo(-4,13);c.closePath();c.fill();c.stroke()
|
||||
for(const x of[-5,7])c.beginPath(),c.ellipse(x,-6,4,2.5,0,0,T),c.fill()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(-12,-14);c.lineTo(-1,-10);c.moveTo(13,-14);c.lineTo(3,-10);c.stroke()
|
||||
c.restore()
|
||||
// fan arm: wide sleeve out to the hand, a fan of seven long feathers pivoting at the wrist
|
||||
for(const[l,s]of[[30,'#111'],[20,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(160,212),c.quadraticCurveTo(196,212,204,178),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
c.save();c.translate(204,170);c.rotate(.5+.45*fw)
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(0,8);c.lineTo(0,-22);c.stroke()
|
||||
for(let k=-3;k<=3;k++){
|
||||
c.save();c.translate(0,-20);c.rotate(k*.2);c.fillStyle='#fff';c.lineWidth=4
|
||||
c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(-13,-40,0,-74);c.quadraticCurveTo(13,-40,0,0);c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(-14,-76,28,22);c.restore();c.stroke()
|
||||
c.lineWidth=2;c.beginPath();c.moveTo(0,-4);c.lineTo(0,-64);c.stroke();c.restore()
|
||||
}
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(0,-20,7,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(204,174,12,11,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(198,170);c.lineTo(208,168);c.moveTo(198,176);c.lineTo(208,174);c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// side-view chibi transfer student with ink low twin tails bowing deeply by the chalkboard with her name on it, then popping up with a happy sparkle
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=20,delay=80,rim=4
|
||||
const tile=createCanvas(6,6),q=tile.getContext('2d');q.fillStyle='#111';for(const[x,y]of[[1.5,1.5],[4.5,4.5]])q.beginPath(),q.arc(x,y,1.1,0,T),q.fill()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const ease=f=>f<=0?0:f>=1?1:.5-.5*cos(PI*f)
|
||||
const H=[132,182],C=[136,90]
|
||||
const spark=(c,x,y,s)=>{c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,r=k%2?s*.35:s;c.lineTo(x+r*cos(a),y+r*sin(a))}c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const dot=c.createPattern(tile,'repeat')
|
||||
const b=t<.1?0:t<.3?ease((t-.1)/.2):t<.5?1:t<.65?1-ease((t-.5)/.15):0,a=.85*b,shut=t>=.15&&t<.58,happy=t>=.62&&t<.95
|
||||
// chalkboard behind with her name, 山田, in chalk
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.rect(8,34,88,94);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.rect(15,41,74,80);c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(35,54);c.lineTo(35,78);c.moveTo(24,63);c.lineTo(24,78);c.lineTo(46,78);c.lineTo(46,63);c.rect(57,56,22,22);c.moveTo(68,56);c.lineTo(68,78);c.moveTo(57,67);c.lineTo(79,67);c.stroke()
|
||||
c.beginPath();c.moveTo(28,92);c.lineTo(76,92);c.stroke();c.beginPath();c.moveTo(52,104);c.bezierCurveTo(44,96,38,106,52,114);c.bezierCurveTo(66,106,60,96,52,104);c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.rect(10,128,84,8);c.fill();c.stroke()
|
||||
// legs and shoes stay planted
|
||||
c.lineWidth=4;for(const[x0,x1]of[[124,122],[140,144]]){c.fillStyle='#fff';c.beginPath();c.moveTo(x0-6,196);c.lineTo(x1-6,230);c.lineTo(x1+6,230);c.lineTo(x0+6,196);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(x1-6,216);c.lineTo(x1+6,216);c.stroke();c.fillStyle='#111';c.beginPath();c.ellipse(x1+5,235,13,7,0,0,T);c.fill()}
|
||||
c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.ellipse(124,232,12,4,0,0,T);c.fill();c.lineWidth=4
|
||||
// pleated dotted skirt, tipping a little with the bow
|
||||
c.save();c.translate(...H);c.rotate(a*.2);c.translate(-H[0],-H[1])
|
||||
c.beginPath();c.moveTo(118,178);c.lineTo(150,178);c.lineTo(162,208);c.lineTo(106,208);c.closePath();toned(c,dot)
|
||||
c.lineWidth=2.5;for(const x of[124,134,144])c.beginPath(),c.moveTo(x,180),c.lineTo(x+(x-134)*.4,206),c.stroke()
|
||||
c.restore()
|
||||
// upper body bows from the hips
|
||||
c.save();c.translate(...H);c.rotate(a);c.translate(-H[0],-H[1])
|
||||
// twin tails dangle with gravity
|
||||
for(const[x,y,o]of[[114,112,-6],[118,116,4]]){c.save();c.translate(x,y);c.rotate(-a*.85+.05*sin(T*2*t+o));c.lineWidth=5;c.fillStyle='#111';c.beginPath();c.moveTo(-6,0);c.quadraticCurveTo(-18+o,30,-8+o*.5,62);c.quadraticCurveTo(0,52,6,0);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(-4,10);c.quadraticCurveTo(-10+o,30,-6+o*.5,46);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(0,2,9,6,0,0,T);c.fill();c.stroke();c.restore()}
|
||||
// sailor top with the square collar at the back and a ribbon in front
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.roundRect(118,126,32,58,8);c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(118,128);c.lineTo(138,128);c.lineTo(132,156);c.lineTo(114,152);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(148,138);c.lineTo(160,132);c.lineTo(158,146);c.closePath();c.fill();c.beginPath();c.moveTo(150,140);c.lineTo(156,160);c.lineTo(146,156);c.closePath();c.fill()
|
||||
for(const[l,s]of[[16,'#111'],[10,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(136,136),c.quadraticCurveTo(140,160,152,174),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(154,176,8,7,0,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.rect(130,118,10,10);c.fill()
|
||||
// profile head
|
||||
c.lineWidth=6;c.beginPath();c.arc(...C,40,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(175,96);c.lineTo(179,101);c.lineTo(175,103);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.arc(C[0],C[1],44,-.1*PI,.65*PI,true);c.lineTo(122,112);c.quadraticCurveTo(122,86,140,74)
|
||||
for(const[x,y]of[[146,86],[152,74],[160,90],[166,76],[174,90]])c.lineTo(x,y);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(C[0],C[1],34,1.15*PI,1.45*PI);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(126,102,7,9,0,0,T);c.fill();c.stroke();c.lineWidth=2;c.beginPath();c.arc(127,102,4,.6*PI,1.5*PI);c.stroke()
|
||||
// eye: open, squeezed shut while bowing, or a happy arc
|
||||
if(shut){c.lineWidth=4.5;c.beginPath();c.moveTo(153,96);c.lineTo(165,100);c.lineTo(155,105);c.stroke()}
|
||||
else if(happy){c.lineWidth=4.5;c.beginPath();c.arc(160,102,7,1.1*PI,1.9*PI);c.stroke()}
|
||||
else{c.fillStyle='#fff';c.beginPath();c.ellipse(161,100,7,11,0,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.ellipse(163,101,5,9,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(162,97,2.2,0,T);c.fill()
|
||||
c.lineWidth=4.5;c.beginPath();c.moveTo(153,90);c.quadraticCurveTo(162,86,170,91);c.lineTo(174,87);c.stroke()}
|
||||
c.lineWidth=2.2;for(let k=0;k<3;k++)c.beginPath(),c.moveTo(150+k*5,114),c.lineTo(154+k*5,108),c.stroke()
|
||||
c.lineWidth=3;c.beginPath();if(happy){c.fillStyle='#111';c.moveTo(163,114);c.quadraticCurveTo(170,124,175,113);c.closePath();c.fill()}else c.moveTo(166,116),c.quadraticCurveTo(170,118,173,115);c.stroke()
|
||||
// nervous sweat drop while she holds the bow
|
||||
if(t>=.3&&t<.52){c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(166,40);c.quadraticCurveTo(176,54,166,58);c.quadraticCurveTo(156,54,166,40);c.fill();c.stroke()}
|
||||
c.restore()
|
||||
if(happy){c.fillStyle='#fff';c.lineWidth=3;const s=1+.4*sin(T*(t-.62)*6);for(const[x,y,r]of[[186,34,10],[214,66,7],[110,24,8]])spark(c,x,y,r*s)}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// sports day tug of war: side-view girl with short hatched hair and a headband leaning way back on the rope, heels dug in, yanking twice with dust puffs
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=16,delay=80,rim=4
|
||||
const tile=createCanvas(6,6),q=tile.getContext('2d');q.fillStyle='#111';for(const[x,y]of[[1.5,1.5],[4.5,4.5]])q.beginPath(),q.arc(x,y,1.1,0,T),q.fill()
|
||||
const ht=createCanvas(6,6),h6=ht.getContext('2d');h6.strokeStyle='#111';h6.lineWidth=1.4;for(const o of[-6,0,6])h6.beginPath(),h6.moveTo(o,6),h6.lineTo(o+6,0),h6.stroke()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const H=[112,180]
|
||||
export default(c,t)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),hat=c.createPattern(ht,'repeat'),s=(1-cos(4*PI*t))/2,L=.6+.16*s
|
||||
const W=([x,y])=>{const dx=x-H[0],dy=y-H[1];return[H[0]+dx*cos(-L)-dy*sin(-L),H[1]+dx*sin(-L)+dy*cos(-L)]}
|
||||
const sh=W([118,132]),hand=[sh[0]+52,sh[1]+16+2*s],end=[234,hand[1]-14]
|
||||
c.translate(14,0)
|
||||
// dust kicked up at the front heel on each yank
|
||||
c.lineWidth=3;c.fillStyle='#fff';for(const[x,y,r]of[[170,230,9],[184,222,7],[162,220,6]]){const k=r*s;if(k>1.5)c.beginPath(),c.arc(x+6*s,y,k,0,T),c.fill(),c.stroke()}
|
||||
// legs: front one braced straight, back one bent; sneakers
|
||||
c.lineWidth=4;c.fillStyle='#fff'
|
||||
for(const[a,b,e]of[[[106,186],[92,210],[88,228]],[[118,186],[134,206],[152,226]]]){for(const[l,st]of[[16,'#111'],[9,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(...a),c.lineTo(...b),c.lineTo(...e),c.stroke()}
|
||||
c.strokeStyle='#111';c.lineWidth=4;c.fillStyle='#111'
|
||||
c.save();c.translate(156,232);c.rotate(-.45);c.beginPath();c.ellipse(4,0,15,7,0,0,T);c.fill();c.fillStyle='#fff';c.fillRect(-10,4,28,3);c.restore()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(92,234,14,7,0,0,T);c.fill();c.fillStyle='#fff';c.fillRect(80,238,26,3)
|
||||
// the rope off to the right, twisted, wobbling with the pull
|
||||
const mid=[(hand[0]+end[0])/2,(hand[1]+end[1])/2+4-6*s]
|
||||
for(const[l,st]of[[13,'#111'],[8,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(hand[0]-30,hand[1]+18),c.lineTo(...hand),c.quadraticCurveTo(...mid,...end),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=2;for(let k=1;k<9;k++){const f=k/9,x=(1-f)*(1-f)*hand[0]+2*f*(1-f)*mid[0]+f*f*end[0],y=(1-f)*(1-f)*hand[1]+2*f*(1-f)*mid[1]+f*f*end[1];c.beginPath();c.moveTo(x-3,y-4);c.lineTo(x+3,y+4);c.stroke()}
|
||||
// upper body leaning back from the hips
|
||||
c.save();c.translate(...H);c.rotate(-L);c.translate(-H[0],-H[1])
|
||||
c.lineWidth=5;c.beginPath();c.roundRect(98,170,32,22,6);toned(c,dot)
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(98,124,32,52,9);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.rect(110,138,14,10);c.stroke();c.lineWidth=2;c.beginPath();c.moveTo(112,143);c.lineTo(122,143);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.rect(110,112,12,14);c.fill()
|
||||
// head turned a little back toward the rope
|
||||
c.translate(116,116);c.rotate(L*.55);c.translate(-116,-116)
|
||||
c.lineWidth=6;c.beginPath();c.arc(118,84,34,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.arc(118,84,38,-.12*PI,.62*PI,true);c.lineTo(104,104);c.quadraticCurveTo(104,76,124,64);for(const[x,y]of[[130,76],[138,64],[144,78],[152,68],[154,80]])c.lineTo(x,y);c.closePath();toned(c,hat)
|
||||
c.lineWidth=2.5;for(const a of[1.05,1.25,1.45])c.beginPath(),c.arc(118,84,30,a*PI,(a+.12)*PI),c.stroke()
|
||||
// headband with the knot and tails flying behind
|
||||
for(const[l,st]of[[13,'#111'],[8,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.arc(118,84,36,1.02*PI,1.98*PI),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#111';c.beginPath();c.arc(150,68,4,0,T);c.fill()
|
||||
c.lineWidth=4;c.fillStyle='#fff';for(const o of[0,1]){const w=sin(T*4*t+o*2)*5;c.beginPath();c.moveTo(84,76);c.quadraticCurveTo(70,70+o*10+w,56,62+o*16+w);c.lineTo(58,70+o*16+w);c.quadraticCurveTo(70,78+o*10+w,84,82);c.closePath();c.fill();c.stroke()}
|
||||
c.beginPath();c.arc(84,79,6,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(108,92,6,8,0,0,T);c.fill();c.stroke()
|
||||
// fierce eye, gritted teeth, sweat flying
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(138,92,6,9,0,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.ellipse(140,93,4,7,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(139,90,1.8,0,T);c.fill()
|
||||
c.lineWidth=4.5;c.beginPath();c.moveTo(130,82);c.lineTo(147,88);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.rect(140,104,12,8);c.fill();c.stroke();c.lineWidth=2;c.beginPath();c.moveTo(146,104);c.lineTo(146,112);c.moveTo(140,108);c.lineTo(152,108);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(152,95);c.lineTo(156,100);c.lineTo(152,101);c.stroke()
|
||||
c.lineWidth=2;for(let k=0;k<3;k++)c.beginPath(),c.moveTo(126+k*4,104),c.lineTo(129+k*4,99),c.stroke()
|
||||
if(s>.5){c.fillStyle='#fff';c.lineWidth=3;for(const[x,y]of[[78,50],[70,92]]){const o=(s-.5)*20;c.beginPath();c.moveTo(x-o,y-8);c.quadraticCurveTo(x+6-o,y+2,x-o,y+5);c.quadraticCurveTo(x-6-o,y+2,x-o,y-8);c.fill();c.stroke()}}
|
||||
c.restore()
|
||||
// arms straight out to the rope, fists wrapped round it
|
||||
for(const[o,lw]of[[0,1],[10,1]]){for(const[l,st]of[[15,'#111'],[9,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(sh[0],sh[1]),c.lineTo(hand[0]-o,hand[1]+o*.35),c.stroke()}
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;for(const o of[12,0])c.beginPath(),c.roundRect(hand[0]-o-8,hand[1]+o*.35-9,17,17,7),c.fill(),c.stroke()
|
||||
for(const[l,st]of[[18,'#111'],[12,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(...W([116,132])),c.lineTo(...W([120,148])),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// chibi volleyball girl with hatched hair and a headband crouches, leaps, cocks her arm and spikes the ball away in a burst
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,hypot}=Math,T=2*PI
|
||||
export const frames=18,delay=70,rim=4
|
||||
const tile=(s,col,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[s/2,s/2],[0,0],[s,0],[0,s],[s,s]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const DOT=tile(6,'#111',1.4),LITE=tile(5,'#fff',1)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u),lerp=(a,b,k)=>a+(b-a)*k,mix=(a,b,k)=>a.map((v,n)=>lerp(v,b[n],k))
|
||||
// keyframe lookup: list of [t,value] with smooth steps between
|
||||
const key=(ks,t)=>{for(let n=1;n<ks.length;n++)if(t<=ks[n][0]){const[a,va]=ks[n-1],[b,vb]=ks[n],k=ease((t-a)/(b-a));return Array.isArray(va)?mix(va,vb,k):lerp(va,vb,k)}return ks.at(-1)[1]}
|
||||
const limb=(c,p,w)=>{for(const[l,s]of[[w+8,'#111'],[w,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...p[0]);for(const q of p.slice(1))c.lineTo(...q);c.stroke()}c.strokeStyle='#111'}
|
||||
// knee for a two-bone leg, always bent toward +x (she faces right)
|
||||
const knee=(h,f,L)=>{const dx=f[0]-h[0],dy=f[1]-h[1],d=hypot(dx,dy)/2,o=d<L?Math.sqrt(L*L-d*d)/(2*d):0;return[(h[0]+f[0])/2+dy*o,(h[1]+f[1])/2-dx*o]}
|
||||
const hatch=(c,x0,y0,x1,y1)=>{c.lineWidth=3.5;c.beginPath();for(let x=x0-(y1-y0);x<x1;x+=12)c.moveTo(x,y1),c.lineTo(x+(y1-y0),y0);c.stroke()}
|
||||
const ball=(c,x,y,a)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(0,0,19,0,T);c.fill();c.save();c.clip();c.lineWidth=3.5
|
||||
for(let j=0;j<3;j++){c.rotate(T/3);c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(12,-2,16,14);c.moveTo(-6,-8);c.quadraticCurveTo(14,-12,22,6);c.stroke()}c.restore();c.lineWidth=5;c.beginPath();c.arc(0,0,19,0,T);c.stroke();c.restore()}
|
||||
const eye=(c,x,y,d,lt)=>{
|
||||
c.save();c.beginPath();c.moveTo(x-17,y-6+d*4);c.lineTo(x+17,y-6-d*4);c.lineTo(x+17,y+22);c.lineTo(x-17,y+22);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,15,0,0,T);c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+5,y-2,9,11,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-6,y+2,22,10)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+3,y-5,3.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-16,y-6+d*4);c.lineTo(x+16,y-6-d*4);c.lineTo(x+d*21,y-12-d*d*2);c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-14,y-24+d*7);c.lineTo(x+14,y-24-d*7);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const dt=c.createPattern(DOT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const k=key([[0,0],[.18,1],[.3,0],[.62,0],[.7,1],[.92,0],[1,0]],t),h=key([[0,0],[.22,0],[.4,1],[.48,1],[.68,0],[1,0]],t)
|
||||
// arm keyframes as [elbow offset, hand offset] from the shoulder
|
||||
const sp=key([[0,[4,22,0,44]],[.18,[-20,16,-36,30]],[.32,[-24,-30,-46,-52]],[.4,[-30,-38,-62,-40]],[.44,[30,-36,56,-64]],[.5,[30,0,52,26]],[.68,[14,18,16,42]],[1,[4,22,0,44]]],t)
|
||||
const fp=key([[0,[-4,22,-4,44]],[.18,[-24,12,-40,26]],[.32,[6,-26,16,-54]],[.44,[8,-28,22,-52]],[.54,[6,22,12,44]],[1,[-4,22,-4,44]]],t)
|
||||
c.save();c.translate(128,242);c.scale(.98,.98);c.translate(-128,-242)
|
||||
const H=[100+8*k,180+14*k-16*h],lean=.12*k-.08*h
|
||||
c.fillStyle=dt;c.beginPath();c.ellipse(104,240,46*(1-.6*h),7*(1-.6*h),0,0,T);c.fill()
|
||||
const sh=(x,y)=>[H[0]+x*cos(lean)-y*sin(lean),H[1]+x*sin(lean)+y*cos(lean)],S=sh(10,-46),F=sh(-12,-46)
|
||||
const arm=(o,p)=>{const e=[o[0]+p[0],o[1]+p[1]],w=[o[0]+p[2],o[1]+p[3]];limb(c,[o,e,w],15);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(...w,9,0,T);c.fill();c.stroke();return w}
|
||||
arm(F,fp)
|
||||
for(const d of[-1,1]){const hp=[H[0]+d*9,H[1]],ft=h>.05?[hp[0]-16+d*12,hp[1]+46]:[100+d*16,236],kn=knee(hp,ft,30)
|
||||
limb(c,[hp,kn,ft],17);c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(kn[0]-10,kn[1]-7,20,14,5);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(ft[0]+5,ft[1]+3,14,8,h*.5,0,T);c.fill();c.stroke()}
|
||||
c.save();c.translate(...H);c.rotate(lean)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-24,-8);c.lineTo(24,-8);c.lineTo(28,14);c.lineTo(2,14);c.lineTo(0,6);c.lineTo(-2,14);c.lineTo(-28,14);c.closePath();c.fill();c.save();c.clip();c.fillStyle=dt;c.fillRect(-30,-8,60,24);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(-24,-4);c.lineTo(26,-4);c.lineTo(28,-50);c.quadraticCurveTo(2,-58,-24,-50);c.closePath();c.fill();c.save();c.clip();c.fillStyle='#111';c.fillRect(-26,-52,7,50);c.fillRect(20,-52,8,50);c.restore();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(-2,-40);c.lineTo(6,-44);c.lineTo(9,-44);c.lineTo(9,-16);c.lineTo(3,-16);c.lineTo(3,-36);c.lineTo(-2,-34);c.fill()
|
||||
c.restore()
|
||||
arm(S,sp)
|
||||
const shout=t>=.38&&t<.54
|
||||
c.save();c.translate(...sh(6,-94));c.rotate(lean-.08*h);c.scale(.68,.68);c.translate(-128,-121)
|
||||
c.lineWidth=9;c.fillStyle='#fff'
|
||||
const back=()=>{c.beginPath();c.moveTo(66,160);c.bezierCurveTo(46,74,88,34,130,34);c.bezierCurveTo(176,34,210,74,190,150);c.lineTo(178,170);c.lineTo(172,150);c.lineTo(84,150);c.lineTo(78,172);c.closePath()}
|
||||
back();c.fill();c.save();c.clip();hatch(c,30,20,230,190);c.restore();c.lineWidth=9;back();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
eye(c,112,140,-1,lt);eye(c,160,140,1,lt)
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const j of[-6,0,6])c.beginPath(),c.moveTo(136+d*42+j-3,168),c.lineTo(136+d*42+j+3,160),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath()
|
||||
if(shout)c.moveTo(124,164),c.lineTo(154,164),c.quadraticCurveTo(150,186,139,186),c.quadraticCurveTo(128,186,124,164),c.fill(),c.stroke()
|
||||
else c.moveTo(128,168),c.quadraticCurveTo(140,176,152,166),c.stroke()
|
||||
const fr=()=>{c.beginPath();c.moveTo(72,124);c.lineTo(72,92);c.bezierCurveTo(86,52,172,52,186,92);c.lineTo(188,126);c.lineTo(178,108);c.lineTo(170,120);c.lineTo(160,100);c.lineTo(148,118);c.lineTo(138,98);c.lineTo(126,116);c.lineTo(114,98);c.lineTo(102,118);c.lineTo(92,100);c.lineTo(84,116);c.closePath()}
|
||||
fr();c.fill();c.save();c.clip();hatch(c,60,40,200,130);c.restore();c.lineWidth=9;fr();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(70,94);c.quadraticCurveTo(128,62,186,94);c.lineTo(186,80);c.quadraticCurveTo(128,48,70,80);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(86,82);c.quadraticCurveTo(128,62,170,82);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
// ball drops in from the top right, meets the hand at the top of the swing, then rockets away
|
||||
const hp=[S[0]+56,S[1]-64],bb=t<.44?[lerp(196,hp[0]+12,min(1,t/.44)),lerp(-24,hp[1]-10,ease(min(1,t/.44)))]:[lerp(hp[0]+12,320,(t-.44)/.14),lerp(hp[1]-10,300,(t-.44)/.14)]
|
||||
if(t>=.44&&t<.58){c.lineWidth=4;for(const d of[-12,0,12])c.beginPath(),c.moveTo(bb[0]-60+d,bb[1]-56-d),c.lineTo(bb[0]-24+d,bb[1]-20-d),c.stroke()}
|
||||
if(t<.58)ball(c,...bb,t*14)
|
||||
if(t>=.42&&t<.5){const x=hp[0]+12,y=hp[1]-10;c.fillStyle='#fff';c.lineWidth=4;c.beginPath();for(let j=0;j<16;j++){const r=j%2?13:27,b=j*PI/8;c.lineTo(x+cos(b)*r,y+sin(b)*r)}c.closePath();c.fill();c.stroke()}
|
||||
if(t>=.42&&t<.52){c.lineWidth=4;for(let j=0;j<3;j++)c.beginPath(),c.arc(S[0],S[1],62+j*7,-2.6+j*.12,-1.3),c.stroke()}
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// seen from outside in the rain: a girl draws a heart in her fogged-up window with one finger and peeks through it, then the fog creeps back
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=20,delay=90,rim=4
|
||||
const tone=(c,s,r)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.fillStyle='#111';for(const[a,b]of[[0,0],[s,0],[0,s],[s,s],[s/2,s/2]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return c.createPattern(k,'repeat')}
|
||||
const ease=u=>(u=max(0,min(1,u)),u*u*(3-2*u))
|
||||
const HX=152,HY=146,hp=u=>{const a=PI+u*T,s=sin(a);return[HX-2.4*16*s*s*s,HY-2.4*(13*cos(a)-5*cos(2*a)-2*cos(3*a)-cos(4*a))]}
|
||||
const F=createCanvas(256,256),f=F.getContext('2d')
|
||||
const D=[[46,0,1],[84,.4,1.5],[120,.75,1],[188,.2,1.5],[214,.6,.5],[70,.15,1]]
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),HAIR=tone(c,5,1.1)
|
||||
const s=ease(t/.55),done=t>=.58,back=ease((t-.84)/.14),[px,py]=hp(s),peek=done&&t<.9
|
||||
// the room side: the girl, close to the glass
|
||||
c.save();c.beginPath();c.rect(26,22,204,204);c.clip();c.fillStyle='#fff';c.fill()
|
||||
c.save();c.translate(120,140);c.scale(.96,.96);c.translate(-128,-130)
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(56,190);c.bezierCurveTo(40,90,80,34,128,34);c.bezierCurveTo(176,34,216,90,200,190);c.quadraticCurveTo(206,214,192,224);c.quadraticCurveTo(186,206,176,212);c.lineTo(80,212);c.quadraticCurveTo(70,206,64,224);c.quadraticCurveTo(50,214,56,190);c.closePath();c.fill();c.fillStyle=HAIR;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(96,186);c.quadraticCurveTo(52,198,44,280);c.lineTo(212,280);c.quadraticCurveTo(204,198,160,186);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(104,188);c.quadraticCurveTo(128,214,152,188);c.stroke()
|
||||
c.beginPath();c.rect(116,166,24,24);c.fill();c.beginPath();c.moveTo(116,166);c.lineTo(116,188);c.moveTo(140,166);c.lineTo(140,188);c.stroke()
|
||||
c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
const lx=done?0:max(-5,min(5,(px-128)*.08)),ly=done?-2:max(-4,min(5,(py-150)*.08))
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+lx,140+ly,10.5,14,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+lx-4,135+ly,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+lx+4,147+ly,2,0,T);c.fill()
|
||||
if(peek){c.save();c.translate(x+lx+4,138+ly);c.beginPath();for(let k=0;k<8;k++){const r=k%2?1.4:5,b=k*PI/4;c.lineTo(cos(b)*r,sin(b)*r)}c.fill();c.restore()}
|
||||
c.restore();c.lineWidth=6;c.beginPath();c.moveTo(x-16,135);c.quadraticCurveTo(x,120,x+16,135);c.moveTo(x+d*14,132);c.lineTo(x+d*21,127);c.stroke()}
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*42+k-3,166),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
c.lineWidth=4.5;c.beginPath();if(peek)c.moveTo(116,166),c.quadraticCurveTo(128,180,140,166),c.closePath(),c.fillStyle='#111',c.fill();else c.moveTo(121,169),c.quadraticCurveTo(128,173,135,169);c.stroke()
|
||||
// wavy toned fringe and a white headband
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(70,140);c.bezierCurveTo(56,76,92,48,128,48);c.bezierCurveTo(164,48,200,76,186,140)
|
||||
c.quadraticCurveTo(184,112,172,106);c.quadraticCurveTo(164,118,152,112);c.quadraticCurveTo(144,100,134,112);c.quadraticCurveTo(124,100,114,112);c.quadraticCurveTo(104,100,96,112);c.quadraticCurveTo(84,104,78,118);c.quadraticCurveTo(72,128,70,140);c.closePath();c.fill();c.fillStyle=HAIR;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(72,80);c.quadraticCurveTo(128,40,184,80);c.lineTo(182,94);c.quadraticCurveTo(128,56,74,94);c.closePath();c.fill();c.stroke()
|
||||
c.restore();c.restore()
|
||||
// fog over the glass, wiped away along the heart (and its middle once it's drawn), creeping back at the end
|
||||
f.clearRect(0,0,256,256);f.globalCompositeOperation='source-over';f.fillStyle='rgba(255,255,255,.72)';f.fillRect(26,22,204,204)
|
||||
f.fillStyle='rgba(255,255,255,.95)';for(let k=0;k<40;k++){const x=30+(k*53)%196,y=26+(k*37)%196;f.beginPath();f.arc(x,y,1.5+(k%3),0,T);f.fill()}
|
||||
// water beads up along the wiped edge, so the heart keeps a soft gray rim
|
||||
const path=(e,cl)=>{f.beginPath();for(let k=0;k<=60;k++){const u=min(e,k/60);const[x,y]=hp(u);k?f.lineTo(x,y):f.moveTo(x,y)}if(cl)f.closePath()}
|
||||
f.globalAlpha=1-back;f.lineCap=f.lineJoin='round';f.strokeStyle='rgba(120,120,120,.85)';f.lineWidth=21;path(s);f.stroke()
|
||||
f.globalCompositeOperation='destination-out';f.lineCap=f.lineJoin='round';f.lineWidth=15;f.beginPath()
|
||||
for(let k=0;k<=60;k++){const u=min(s,k/60);const[x,y]=hp(u);k?f.lineTo(x,y):f.moveTo(x,y)}f.stroke()
|
||||
if(done){f.globalAlpha=(1-back)*ease((t-.58)/.08);f.beginPath();for(let k=0;k<=60;k++){const[x,y]=hp(k/60);f.lineTo(x,y)}f.fill()}
|
||||
f.globalAlpha=1;c.drawImage(F,0,0)
|
||||
// the wiping finger, close to the glass
|
||||
if(!done||t<.62){const go=done?ease((t-.58)/.04):0,fx=px+8*go,fy=py+30*go
|
||||
for(const[l,s]of[[24,'#111'],[14,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(fx+40,262),c.quadraticCurveTo(fx+30,fy+50,fx+14,fy+26),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(fx+14,fy+26,15,13,-.5,0,T);c.fill();c.stroke()
|
||||
for(const[l,s]of[[13,'#111'],[5,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(fx+8,fy+16),c.lineTo(fx,fy),c.stroke();c.strokeStyle='#111'}
|
||||
// rain running down the outside of the pane
|
||||
c.save();c.beginPath();c.rect(26,22,204,204);c.clip()
|
||||
for(const[x,o,v]of D){const y=10+((t*v*2+o)%1)*230
|
||||
for(const[l,s]of[[6,'#111'],[2,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(x,y-50),c.quadraticCurveTo(x+3,y-24,x,y-8),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.moveTo(x,y-10);c.quadraticCurveTo(x+6,y,x,y+5);c.quadraticCurveTo(x-6,y,x,y-10);c.fill();c.stroke()}
|
||||
c.restore()
|
||||
// window frame and sill
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.rect(12,8,232,232);c.rect(26,22,204,204);c.fill('evenodd');c.stroke();c.fillStyle=DOT;c.beginPath();c.rect(12,8,232,232);c.rect(26,22,204,204);c.fill('evenodd')
|
||||
c.beginPath();c.rect(26,22,204,204);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(4,230,248,18,4);c.fill();c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// manga wolf girl with shaggy crosshatched hair: ears perk, she tips her head back and howls at a toned crescent moon, sound arcs rolling up, fluffy tail swishing
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=12,delay=110,rim=4
|
||||
const TL=createCanvas(6,6),tx=TL.getContext('2d');tx.fillStyle='#111';for(const[a,b]of[[1.5,1.5],[4.5,4.5]])tx.beginPath(),tx.arc(a,b,1.05,0,T),tx.fill()
|
||||
const ease=x=>x<0?0:x>1?1:x*x*(3-2*x)
|
||||
const hatch=(c,x0,y0,w,h,g)=>{c.save();c.clip();c.lineWidth=1.6;c.beginPath();for(let k=-h;k<w;k+=g)c.moveTo(x0+k,y0+h),c.lineTo(x0+k+h,y0);for(let k=0;k<w+h;k+=g)c.moveTo(x0+k,y0+h),c.lineTo(x0+k-h,y0);c.stroke();c.restore()}
|
||||
// sharp wolf eye: slanted, ink iris with a slit-ish pupil highlight
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(x-d*16,y+2);c.quadraticCurveTo(x,y-14,x+d*16,y-6);c.quadraticCurveTo(x+d*12,y+16,x,y+15);c.quadraticCurveTo(x-d*12,y+14,x-d*16,y+2);c.fill()
|
||||
c.save();c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,9,12,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-3,y,2.5,5,0,0,T);c.fill();c.beginPath();c.arc(x+3,y+9,1.6,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-d*16,y+2);c.quadraticCurveTo(x,y-14,x+d*18,y-8);c.lineTo(x+d*22,y-13);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat')
|
||||
// breathe in, then a long howl, then settle
|
||||
const h=ease((t-.18)/.14)-ease((t-.78)/.14),pre=t<.18?sin(t/.18*PI):0,ang=-.38*h+.06*pre,sw=sin(t*T*2)
|
||||
// toned crescent moon sticker with two twinkles
|
||||
const moon=()=>{c.beginPath();c.arc(204,54,34,.523*PI,1.851*PI);c.arc(222,42,30,1.917*PI,.634*PI,true);c.closePath()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';moon();c.fill();c.save();moon();c.clip();c.fillStyle=tn;c.fillRect(160,10,90,90);c.restore();moon();c.stroke()
|
||||
c.lineWidth=2.5;for(const[x,y,r]of[[160,24,6],[240,104,5]]){const s=r*(.7+.3*sin(t*T*4+x));c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,q=k%2?s*.35:s;c.lineTo(x+q*cos(a),y+q*sin(a))}c.closePath();c.fill();c.stroke()}
|
||||
// fluffy hatched tail swishing behind her
|
||||
c.save();c.translate(62,232);c.rotate(-.3+.22*sw)
|
||||
const tail=()=>{c.beginPath();c.moveTo(0,0);c.bezierCurveTo(-30,-30,-34,-90,-6,-118);c.quadraticCurveTo(4,-100,10,-112);c.quadraticCurveTo(12,-92,22,-98);c.bezierCurveTo(26,-60,22,-24,0,0)}
|
||||
c.lineWidth=6;c.fillStyle='#fff';tail();c.fill();tail();hatch(c,-40,-124,70,130,7);c.fillStyle='#fff';c.beginPath();c.moveTo(-6,-118);c.quadraticCurveTo(-14,-96,4,-90);c.quadraticCurveTo(16,-98,22,-98);c.quadraticCurveTo(12,-92,10,-112);c.quadraticCurveTo(4,-100,-6,-118);c.fill();tail();c.stroke()
|
||||
c.restore()
|
||||
// toned jacket with a fluffy white ruff round the neck
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(92,176);c.quadraticCurveTo(48,186,40,256);c.lineTo(188,256);c.quadraticCurveTo(180,186,136,176);c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(30,170,170,90);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(102,150,24,30);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(102,150);c.lineTo(102,176);c.moveTo(126,150);c.lineTo(126,176);c.stroke()
|
||||
c.lineWidth=5;c.beginPath();for(let k=0;k<9;k++){const a=PI*(.05+k*.1125),x=114+44*cos(a),y=180+14*sin(a);c.arc(x,y,9,a-1.4,a+1.4)}c.closePath();c.fill();c.stroke()
|
||||
c.save();c.translate(114,166);c.rotate(ang);c.translate(-114,-166)
|
||||
c.save();c.translate(114,108);c.scale(.74,.74);c.translate(-128,-128)
|
||||
// wolf ears, hatched outside, white inside, twitching up when she howls
|
||||
for(const d of[-1,1]){c.save();c.translate(128+d*40,62);c.rotate(d*(.2-.18*h))
|
||||
const ear=()=>{c.beginPath();c.moveTo(-22,8);c.lineTo(d*4,-48);c.lineTo(22,8);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';ear();c.fill();ear();hatch(c,-26,-50,52,60,6);ear();c.stroke()
|
||||
c.beginPath();c.moveTo(-10,4);c.lineTo(d*3,-28);c.lineTo(10,4);c.closePath();c.fill();c.lineWidth=3;c.stroke();c.restore()}
|
||||
// shaggy crosshatched hair behind
|
||||
const back=()=>{c.beginPath();c.moveTo(128,40);c.bezierCurveTo(62,40,52,100,58,150);for(const[x,y]of[[50,186],[68,174],[66,204],[86,186]])c.lineTo(x,y);c.lineTo(170,186);for(const[x,y]of[[190,204],[188,174],[206,186],[198,150]])c.lineTo(x,y);c.bezierCurveTo(204,100,194,40,128,40)}
|
||||
c.lineWidth=6;c.fillStyle='#fff';back();c.fill();back();hatch(c,44,36,168,172,7);back();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
if(h>.3){c.lineWidth=5;for(const x of[103,153])c.beginPath(),c.arc(x,140,12,PI*1.1,PI*1.9),c.stroke()}
|
||||
else{eye(c,103,138,-1);eye(c,153,138,1)}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+k-3,168),c.lineTo(128+d*44+k+3,160),c.stroke()
|
||||
// mouth: a little closed smile, then a big round howl with fangs
|
||||
c.lineWidth=4;if(h>.3){c.fillStyle='#111';c.beginPath();c.ellipse(128,172,10,8+6*h,0,0,T);c.fill();c.stroke();c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*8,164),c.lineTo(128+d*5,172),c.lineTo(128+d*3,164),c.fill()}
|
||||
else{c.beginPath();c.moveTo(118,168);c.quadraticCurveTo(123,172,128,168);c.quadraticCurveTo(133,172,138,168);c.stroke()}
|
||||
// shaggy bangs
|
||||
const bang=()=>{c.beginPath();c.moveTo(68,150);c.bezierCurveTo(52,76,88,42,128,42);c.bezierCurveTo(168,42,204,76,188,150);for(const[x,y]of[[180,104],[170,122],[160,98],[146,120],[136,96],[124,120],[112,98],[100,122],[90,100],[80,120],[74,106]])c.lineTo(x,y);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';bang();c.fill();bang();hatch(c,50,38,156,92,7);bang();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(128,110,58,PI*1.3,PI*1.45);c.stroke();c.beginPath();c.arc(128,110,58,PI*1.55,PI*1.7);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
c.restore()
|
||||
// howl sound arcs rolling up toward the moon
|
||||
if(h>.3){c.lineWidth=4;for(let k=0;k<3;k++){const q=(t*4+k/3)%1,r=14+30*q;c.beginPath();c.arc(126,88,r+24*k*.3,-PI*.62,-PI*.18);c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// lunch rush winner: spiky white-haired girl with a cheek bandage hoisting the last yakisoba bread, star eyes, fang grin, victory lines, classmates' hands still grabbing from the corners
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=12,delay=90,rim=4
|
||||
const tile=createCanvas(6,6),q=tile.getContext('2d');q.fillStyle='#111';for(const[x,y]of[[1.5,1.5],[4.5,4.5]])q.beginPath(),q.arc(x,y,1.1,0,T),q.fill()
|
||||
const ht=createCanvas(6,6),h6=ht.getContext('2d');h6.strokeStyle='#111';h6.lineWidth=1.4;for(const o of[-6,0,6])h6.beginPath(),h6.moveTo(o,6),h6.lineTo(o+6,0),h6.stroke()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const star=(c,x,y,s)=>{c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4-PI/2,r=k%2?s*.35:s;c.lineTo(x+r*cos(a),y+r*sin(a))}c.closePath();c.fill()}
|
||||
export default(c,t)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),hat=c.createPattern(ht,'repeat'),b=Math.round(4*sin(T*2*t)),w=.14*sin(T*4*t),tw=1+.35*sin(T*2*t)
|
||||
c.save();c.translate(-20,16)
|
||||
// spiky white hair behind
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(60,150);for(const[x,y]of[[44,120],[56,104],[40,78],[64,70],[60,42],[90,46],[104,22],[128,40],[150,20],[166,46],[194,40],[192,70],[216,80],[200,104],[212,124],[196,150]])c.lineTo(x,y);c.closePath();c.fill();c.stroke()
|
||||
// sweater vest over a shirt and tie
|
||||
c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(62,196,54,236);c.lineTo(202,236);c.quadraticCurveTo(194,196,156,188);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(84,198);c.lineTo(108,190);c.lineTo(128,226);c.lineTo(148,190);c.lineTo(172,198);c.lineTo(176,236);c.lineTo(80,236);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(123,194);c.lineTo(133,194);c.lineTo(136,216);c.lineTo(128,224);c.lineTo(120,216);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.rect(116,168,24,24);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
|
||||
// star eyes, fang grin, bandage
|
||||
for(const[x,d]of[[103,-1],[153,1]]){c.fillStyle='#fff';c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.ellipse(x,141,10.5,15,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';star(c,x-1,138,9*tw);c.beginPath();c.arc(x+5,150,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-d*12,129);c.quadraticCurveTo(x,118,x+d*15,130);c.lineTo(x+d*21,124);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(x-12,112+d*-3);c.quadraticCurveTo(x,106,x+12,110+d*3);c.stroke()}
|
||||
c.lineWidth=3.5;c.fillStyle='#111';c.beginPath();c.moveTo(110,162);c.quadraticCurveTo(128,192,146,162);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle='#fff';c.beginPath();c.ellipse(128,180,9,5,0,0,T);c.fill();c.beginPath();c.moveTo(136,162);c.lineTo(141,162);c.lineTo(139,170);c.closePath();c.fill();c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=3;c.save();c.translate(164,160);c.rotate(-.4);c.beginPath();c.roundRect(-12,-5,24,10,4);c.fill();c.stroke();c.lineWidth=1.5;for(const x of[-3,3])c.beginPath(),c.moveTo(x,-3),c.lineTo(x,3),c.stroke();c.restore()
|
||||
// spiky bangs with hatched shade
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(68,140);c.bezierCurveTo(52,74,88,38,128,38);c.bezierCurveTo(168,38,204,74,188,140)
|
||||
for(const[x,y]of[[182,108],[172,118],[166,96],[154,116],[146,92],[132,114],[124,90],[110,114],[102,92],[90,116],[84,98],[74,116]])c.lineTo(x,y);c.closePath();c.fillStyle='#fff';c.fill()
|
||||
c.save();c.clip();c.fillStyle=hat;c.fillRect(60,86,140,40);c.restore();c.stroke()
|
||||
c.lineWidth=2.5;for(const[x,y]of[[104,52],[128,46],[152,52]])c.beginPath(),c.moveTo(x,y),c.lineTo(x+(x-128)*.2,y+28),c.stroke()
|
||||
// arm raised high with the prize
|
||||
const hx=206,hy=52+b
|
||||
for(const[l,s]of[[28,'#111'],[21,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(178,214),c.quadraticCurveTo(212,150,hx,hy+12),c.stroke()
|
||||
c.strokeStyle='#111';c.save();c.translate(hx,hy);c.rotate(w)
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.roundRect(-40,-18,80,22,11);toned(c,dot)
|
||||
c.lineWidth=3;c.beginPath();for(let k=0;k<=16;k++){const x=-34+k*4.25;c.lineTo(x,-20-4*sin(k*1.7)-3)}c.stroke();c.beginPath();for(let k=0;k<=12;k++){const x=-30+k*5;c.lineTo(x,-15-3*cos(k*2.1))}c.stroke()
|
||||
c.fillStyle='#111';for(const x of[-20,-4,14,26])c.beginPath(),c.arc(x,-24,2.6,0,T),c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(-12,-6,24,20,8);c.fill();c.stroke()
|
||||
c.restore()
|
||||
c.restore()
|
||||
// victory lines fanning off the bread, and twinkles
|
||||
const v=1+.12*sin(T*2*t);c.lineCap='round';c.lineWidth=4;for(const a of[-2.5,-2.15,-1.8,-1.35,-1,-.65])c.beginPath(),c.moveTo(186+44*v*cos(a),68+b+36*v*sin(a)),c.lineTo(186+62*v*cos(a),68+b+52*v*sin(a)),c.stroke()
|
||||
c.fillStyle='#fff';c.strokeStyle='#111';c.lineWidth=2.5;for(const[x,y,o]of[[118,40,0],[244,94,.5],[226,130,.25]]){const s=8*Math.abs(sin(PI*(t*2+o)));if(s>2){c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,r=k%2?s*.3:s;c.lineTo(x+r*cos(a),y+r*sin(a))}c.closePath();c.fill();c.stroke()}}
|
||||
// classmates' hands grabbing at the bottom
|
||||
for(const[x0,y0,x1,y1,o]of[[-6,262,22,206,0],[262,262,232,212,1]]){
|
||||
for(const[l,s]of[[26,'#111'],[20,'#fff'],[20,dot]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(x0,y0),c.lineTo(x1,y1),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=3.5;const g=3*sin(T*4*t+o*PI)
|
||||
for(const k of[-1.5,-.5,.5,1.5]){c.save();c.translate(x1,y1);c.rotate(k*.35+(o?-.3:.3));c.beginPath();c.roundRect(-3.5,-24-g*(k%1?1:-1),7,18,3.5);c.fill();c.stroke();c.restore()}
|
||||
c.beginPath();c.arc(x1,y1,11,0,T);c.fill();c.stroke()}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user