Add a manga section of 50 black-and-white anime girls after a third divider
Black ink, screentone and white only: reaction tropes (gaan shock, gloom corner, menacing aura, crow silence, petrify), comedy and action (harisen, iai draw, kendo, kyudo, miko), school life (locker letters, zero test, rooftop wind, shared umbrella), town and home (omikuji, shrine prayer, tanabata, floating lantern, alarm smash) and fantasy (yurei, oni, dragon, android, summoning circle, knight, pirate, detective, princess). gen files can now export rim=<px> for a white sticker border, so solid black ink still shows on the black page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@@ -0,0 +1,60 @@
|
||||
// bedhead manga girl under the blanket smashes her ringing alarm clock flat, dozes off happily, then it springs back up ringing
|
||||
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 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)),lerp=(a,b,u)=>a+(b-a)*ease(u)
|
||||
const zz=(c,x,y,s)=>{c.lineWidth=4;c.beginPath();c.moveTo(x-s,y-s);c.lineTo(x+s,y-s);c.lineTo(x-s,y+s);c.lineTo(x+s,y+s);c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
const ring=t<.4||t>=.85,hit=t>=.4&&t<.47,flat=t>=.4&&t<.75,sleep=t>=.47&&t<.8
|
||||
const sy=flat?.5:t<.85?.5+.5*ease((t-.75)/.06)+.18*sin(min(1,(t-.75)/.1)*PI)*(t<.85?1:0):1,rot=ring?.14*sin(t*T*8):0
|
||||
// nightstand
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.rect(166,186,78,62);c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.rect(176,198,58,20);c.fill();c.stroke();c.beginPath();c.arc(205,208,3,0,T);c.stroke()
|
||||
// alarm clock
|
||||
c.save();c.translate(205,184);c.scale(1,sy);c.rotate(rot);c.translate(-205,-184)
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(186,176);c.lineTo(180,186);c.moveTo(224,176);c.lineTo(230,186);c.stroke()
|
||||
for(const d of[-1,1]){c.save();c.translate(205+d*19,126);c.rotate(d*.6);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(0,0,12,PI,0);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke();c.restore()}
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(205,118);c.lineTo(205+(ring?6*sin(t*T*16):0),108);c.stroke();c.fillStyle='#111';c.beginPath();c.arc(205+(ring?6*sin(t*T*16):0),106,4,0,T);c.fill()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.arc(205,152,28,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.arc(205,152,21,0,T);c.stroke();c.fillStyle='#111';for(let k=0;k<12;k++){const b=k*PI/6;c.beginPath();c.arc(205+cos(b)*17,152+sin(b)*17,k%3?1.2:2.2,0,T);c.fill()}
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(205,152);c.lineTo(205,138);c.moveTo(205,152);c.lineTo(214,156);c.stroke()
|
||||
if(flat){c.lineWidth=3;c.beginPath();for(const d of[-1,1])c.moveTo(205+d*9,146),c.lineTo(205+d*3,150),c.moveTo(205+d*9,150),c.lineTo(205+d*3,146);c.stroke()}
|
||||
c.restore()
|
||||
if(ring){c.lineWidth=4;for(const d of[-1,1])for(const k of[0,1])c.beginPath(),c.arc(205,140,40+k*11,d<0?PI*.82:-PI*.18,d<0?PI*1.18:PI*.18),c.stroke()
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const k of[0,1,2]){const b=-PI/2+d*(.3+k*.28);c.beginPath();c.moveTo(205+cos(b)*42,128+sin(b)*30);c.lineTo(205+cos(b)*52,128+sin(b)*38);c.stroke()}}
|
||||
if(hit){c.fillStyle='#fff';c.lineWidth=5;c.beginPath();for(let k=0;k<16;k++){const r=k%2?18:34,b=k*PI/8;c.lineTo(205+cos(b)*r*1.3,150+sin(b)*r*.8)}c.closePath();c.fill();c.stroke()}
|
||||
// pillow and the bedhead girl's head
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.roundRect(12,112,124,62,26);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(26,124);c.lineTo(34,132);c.moveTo(122,124);c.lineTo(114,132);c.stroke()
|
||||
c.save();c.translate(70,112);c.rotate(-.28);c.scale(.6,.6);c.translate(-128,-128)
|
||||
c.lineWidth=10;c.fillStyle='#111';c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,210,96,200,150);c.lineTo(56,150);c.bezierCurveTo(46,96,60,36,128,36);c.fill();c.stroke()
|
||||
c.lineWidth=10;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(sleep){c.lineWidth=8;for(const x of[103,153])c.beginPath(),c.arc(x,138,13,.15*PI,.85*PI),c.stroke();c.lineWidth=6;c.beginPath();c.arc(128,164,9,.1*PI,.9*PI);c.stroke();c.lineWidth=4;c.beginPath();c.moveTo(138,170);c.quadraticCurveTo(142,182,138,190);c.stroke()}
|
||||
else if(hit){c.lineWidth=9;for(const d of[-1,1]){const x=128+d*25;c.beginPath();c.moveTo(x-d*13,126);c.lineTo(x+d*11,138);c.lineTo(x-d*13,150);c.stroke()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.rect(112,160,32,14);c.fill();c.stroke();c.lineWidth=3;c.beginPath();for(const x of[120,128,136])c.moveTo(x,160),c.lineTo(x,174);c.stroke()}
|
||||
else{c.lineWidth=8;c.beginPath();c.moveTo(88,140);c.lineTo(118,140);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(153,142,13,10,0,0,T);c.fill();c.save();c.clip();c.fillStyle='#111';c.beginPath();c.arc(155,148,9,0,T);c.fill();c.restore();c.lineWidth=8;c.beginPath();c.moveTo(138,136);c.lineTo(168,136);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(114,168);c.lineTo(120,163);c.lineTo(126,168);c.lineTo(132,163);c.lineTo(138,168);c.lineTo(144,163);c.stroke()}
|
||||
c.lineWidth=4;for(const d of[-1,1])for(const k of[-7,0,7])c.beginPath(),c.moveTo(128+d*42+k-3,168),c.lineTo(128+d*42+k+3,158),c.stroke()
|
||||
// ink black bedhead with strands sticking out
|
||||
c.lineWidth=10;c.fillStyle='#111';c.beginPath();c.moveTo(70,140)
|
||||
c.bezierCurveTo(54,70,88,38,128,38);c.bezierCurveTo(168,38,202,70,186,140)
|
||||
c.lineTo(178,106);c.lineTo(168,116);c.lineTo(160,94);c.lineTo(146,110);c.lineTo(138,90);c.lineTo(126,108);c.lineTo(114,88);c.lineTo(104,108);c.lineTo(94,94);c.lineTo(86,114);c.lineTo(78,104);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=12;c.beginPath();c.moveTo(110,44);c.quadraticCurveTo(92,14,70,22);c.moveTo(140,40);c.quadraticCurveTo(150,8,176,14);c.moveTo(180,64);c.quadraticCurveTo(212,52,222,68);c.moveTo(70,70);c.quadraticCurveTo(40,60,34,76);c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=8;for(const[s,e]of[[1.26,1.38],[1.46,1.56],[1.64,1.74]])c.beginPath(),c.arc(128,106,52,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
if(ring&&!sleep){c.lineWidth=7;c.save();c.translate(170,62);c.scale(1+.15*sin(t*T*4),1+.15*sin(t*T*4));for(let k=0;k<4;k++){c.rotate(PI/2);c.beginPath();c.moveTo(4,-14);c.quadraticCurveTo(4,-4,14,-4);c.stroke()}c.restore()}
|
||||
c.restore()
|
||||
// blanket mound
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(14,248);c.lineTo(14,178);c.quadraticCurveTo(40,158,92,166);c.quadraticCurveTo(140,150,172,186);c.quadraticCurveTo(186,214,176,248);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(14,178);c.quadraticCurveTo(40,158,92,166);c.quadraticCurveTo(140,150,172,186);c.lineTo(166,196);c.quadraticCurveTo(136,166,92,178);c.quadraticCurveTo(40,172,14,192);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(60,200);c.quadraticCurveTo(90,214,96,240);c.moveTo(120,196);c.quadraticCurveTo(140,214,140,240);c.stroke()
|
||||
// her arm out of the blanket: creeping, smashing, resting, retracting
|
||||
const[hx,hy]=t<.38?[lerp(150,200,t/.38),lerp(176,72,t/.38)]:t<.4?[200,72]:t<.75?[204,lerp(72,140,(t-.4)/.04)]:t<.85?[lerp(204,150,(t-.75)/.1),lerp(140,176,(t-.75)/.1)]:[150,176]
|
||||
for(const[l,st]of[[18,'#111'],[10,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(140,184),c.quadraticCurveTo((140+hx)/2,min(184,hy)-10,hx,hy),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy,11,10,0,0,T);c.fill();c.stroke();c.lineWidth=3;c.beginPath();c.moveTo(hx-6,hy-3);c.lineTo(hx+6,hy-3);c.moveTo(hx-6,hy+3);c.lineTo(hx+6,hy+3);c.stroke()
|
||||
if(hit){c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(hx+d*18,hy-30),c.lineTo(hx+d*14,hy-12),c.stroke()}
|
||||
if(sleep)for(let k=0;k<3;k++){const p=((t-.47)/.33*1.5+k/3)%1;c.globalAlpha=min(1,(1-p)*3);zz(c,104+p*20,40-p*26+k*3,4+p*5)}
|
||||
c.globalAlpha=1
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// manga android girl booting up: progress bar fills with eyes shut, a scan line sweeps her eyes open, lens irises focus, antenna blinks, then she powers down
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=20,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()
|
||||
// camera-lens eye: rings, crosshair ticks, a pupil that focuses down to size f
|
||||
const eye=(c,x,y,d,f,tn)=>{
|
||||
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=tn;c.fillRect(x-12,y-12,24,30);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.5,14,0,0,T);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x,y+3,7.5,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=1.8;c.beginPath();for(const a of[0,PI/2,PI,PI*1.5])c.moveTo(x+cos(a)*5.5,y+3+sin(a)*5.5),c.lineTo(x+cos(a)*7.5,y+3+sin(a)*7.5);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.arc(x,y+3,f,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-5,y-4,3,0,T);c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-17,y-6);c.quadraticCurveTo(x,y-19,x+17,y-6);c.moveTo(x+d*16,y-7);c.lineTo(x+d*22,y-11);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat'),on=t>=.35&&t<.9,sc=t>=.35&&t<.5?(t-.35)/.15:-1
|
||||
const prog=t<.35?t/.3:t<.9?1:0,f=on?max(3,8-(t-.35)*40):0,ant=on&&t>=.5&&(t*20|0)%4<2
|
||||
c.save();c.translate(128,124);c.scale(.95,.95);c.translate(-128,-120)
|
||||
// bodysuit with panel lines and a status bar
|
||||
const body=()=>{c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(60,198,52,256);c.lineTo(204,256);c.quadraticCurveTo(196,198,156,190);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';body();c.fill()
|
||||
c.save();c.clip();c.fillStyle=tn;c.beginPath();c.moveTo(40,200);c.lineTo(104,200);c.lineTo(96,260);c.lineTo(40,260);c.moveTo(216,200);c.lineTo(152,200);c.lineTo(160,260);c.lineTo(216,260);c.fill();c.restore();body();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(104,196);c.lineTo(96,256);c.moveTo(152,196);c.lineTo(160,256);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.rect(104,222,48,14);c.fill();c.stroke();if(prog>0){c.fillStyle='#111';c.fillRect(107,225,42*min(1,prog),8)}
|
||||
if(on){c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(118,244);c.lineTo(126,250);c.lineTo(140,238);c.stroke()}
|
||||
// segmented neck
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(114,166,28,28);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(114,166);c.lineTo(114,192);c.moveTo(142,166);c.lineTo(142,192);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const y of[176,184])c.moveTo(114,y),c.lineTo(142,y);c.stroke()
|
||||
// ink black bob, back
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(128,34);c.bezierCurveTo(196,34,208,100,200,168);c.lineTo(56,168);c.bezierCurveTo(48,100,60,34,128,34);c.fill();c.stroke()
|
||||
// face
|
||||
c.lineWidth=6;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()
|
||||
// cheek panel seams
|
||||
c.lineWidth=2.5;c.setLineDash([5,4]);c.beginPath();c.moveTo(166,152);c.lineTo(160,174);c.moveTo(90,152);c.lineTo(96,174);c.stroke();c.setLineDash([])
|
||||
c.beginPath();c.arc(163,162,2,0,T);c.stroke()
|
||||
if(on){eye(c,103,140,-1,f,tn);eye(c,153,140,1,f,tn)}
|
||||
else{c.lineWidth=6;for(const x of[103,153])c.beginPath(),c.moveTo(x-15,144),c.quadraticCurveTo(x,149,x+15,144),c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const x of[103,153])for(const k of[-8,0,8])c.moveTo(x+k,148),c.lineTo(x+k*1.2,154);c.stroke()}
|
||||
c.lineWidth=3.5;c.beginPath();if(on)c.arc(128,166,6,.2*PI,.8*PI);else c.moveTo(123,170),c.lineTo(133,170);c.stroke()
|
||||
// scan line sweeping down across the eyes
|
||||
if(sc>=0){const y=110+sc*60;c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.rect(68,y-4,120,8);c.fill();c.stroke()
|
||||
c.lineWidth=1.8;for(const k of[1,2])c.beginPath(),c.moveTo(72,y-4-k*5),c.lineTo(184,y-4-k*5),c.stroke()}
|
||||
// blunt bangs with a shine band
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(70,150);c.bezierCurveTo(52,72,88,38,128,38);c.bezierCurveTo(168,38,204,72,186,150);c.lineTo(182,108);c.lineTo(74,108);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=5;for(const[a,e]of[[1.2,1.34],[1.42,1.56],[1.64,1.78]])c.beginPath(),c.arc(128,106,54,PI*a,PI*e),c.stroke()
|
||||
c.lineWidth=2.5;for(const x of[96,116,140,160])c.beginPath(),c.moveTo(x,90),c.lineTo(x,106),c.stroke();c.strokeStyle='#111'
|
||||
// ear modules and antenna
|
||||
for(const d of[-1,1]){c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(128+d*58,128,13,20,0,0,T);c.fill();c.stroke();c.lineWidth=2.5;c.beginPath();c.ellipse(128+d*58,128,6,11,0,0,T);c.stroke()
|
||||
c.save();c.beginPath();c.ellipse(128+d*58,128,6,11,0,0,T);c.clip();c.fillStyle=tn;c.fillRect(128+d*58-8,114,16,28);c.restore()}
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(192,112);c.lineTo(214,58);c.stroke()
|
||||
c.fillStyle=ant?'#fff':'#111';c.beginPath();c.arc(216,52,8,0,T);c.fill();c.stroke()
|
||||
if(ant){c.lineWidth=3;c.beginPath();for(const a of[-2.4,-1.6,-.8,0])c.moveTo(216+cos(a)*13,52+sin(a)*13),c.lineTo(216+cos(a)*20,52+sin(a)*20);c.stroke()}
|
||||
// boot dots while loading
|
||||
if(!on&&t<.35){c.fillStyle='#111';for(let k=0;k<3;k++)if(k<=(t*30|0)%4)c.beginPath(),c.arc(110+k*18,26,4,0,T),c.fill()}
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// menacing girl with crosshatched hair: her face sinks into shadow with glinting eyes, black aura flames rise behind her and menace symbols drift up
|
||||
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 DK=(()=>{const t=createCanvas(5,5),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(2.5,2.5,1.2,0,T);x.fill();return t})()
|
||||
const xh=(c,x0,x1,y0,y1)=>{c.lineWidth=1.4;c.beginPath();for(let x=x0-(y1-y0);x<x1;x+=5)c.moveTo(x,y1),c.lineTo(x+(y1-y0),y0),c.moveTo(x,y0),c.lineTo(x+(y1-y0),y1);c.stroke()}
|
||||
const go=(c,x,y,s,a)=>{c.save();c.translate(x,y);c.scale(s,s);c.rotate(a);c.transform(1,0,-.25,1,0,0);c.lineWidth=6;c.lineJoin='miter'
|
||||
c.beginPath();c.moveTo(-11,-12);c.lineTo(10,-12);c.lineTo(10,12);c.lineTo(-11,12);c.stroke();c.lineWidth=3.5;c.beginPath();c.moveTo(15,-22);c.lineTo(17,-15);c.moveTo(21,-23);c.lineTo(23,-16);c.stroke();c.restore()}
|
||||
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)}
|
||||
const hair=c=>{c.beginPath();c.moveTo(128,34);c.bezierCurveTo(198,34,210,100,206,170);c.quadraticCurveTo(212,214,220,244);c.lineTo(36,244);c.quadraticCurveTo(44,214,50,170);c.bezierCurveTo(46,100,58,34,128,34);c.closePath()}
|
||||
export default(c,t,i)=>{
|
||||
c.fillStyle='#111'
|
||||
for(let x=24;x<=232;x+=16){const dx=abs(x-128),b=dx<56?84:84+(dx-56)*1.7,h=(dx<70?58:40)+14*sin(2*T*t+x*.7)+8*sin(T*t*3+x),sw=6*sin(T*t*2+x*.3),tip=max(10,b-h)
|
||||
c.beginPath();c.moveTo(x-14,b+30);c.quadraticCurveTo(x-12,(b+tip)/2,x+sw,tip);c.quadraticCurveTo(x+12,(b+tip)/2,x+14,b+30);c.closePath();c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=2;c.beginPath();c.moveTo(x-4,b+10);c.quadraticCurveTo(x-3,(b+tip)/2+8,x+sw*.6,tip+16);c.stroke();c.strokeStyle='#111'}
|
||||
c.lineWidth=6;hair(c);c.fillStyle='#fff';c.fill();c.save();c.clip();xh(c,30,226,30,250);c.restore();c.lineWidth=6;hair(c);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(64,198,58,246);c.lineTo(198,246);c.quadraticCurveTo(192,198,156,190);c.closePath();c.fill()
|
||||
c.save();c.clip();c.fillStyle=c.createPattern(DK,'repeat');c.fillRect(50,186,160,64);c.fillStyle='#fff';c.beginPath();c.moveTo(108,190);c.lineTo(128,240);c.lineTo(148,190);c.fill();c.restore();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(108,190);c.lineTo(122,244);c.moveTo(148,190);c.lineTo(134,244);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(122,206);c.lineTo(134,206);c.lineTo(137,236);c.lineTo(128,244);c.lineTo(119,236);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.fillStyle='#fff';face(c);c.fill();c.stroke()
|
||||
c.save();face(c);c.clip();c.fillStyle='#111';c.fillRect(60,40,140,104);c.lineWidth=2.4;for(let x=76;x<182;x+=4){c.beginPath();c.moveTo(x,140);c.lineTo(x,150+((x*13)%7)*2.4);c.stroke()}c.restore()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){const y=138
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(x-d*15,y-6);c.quadraticCurveTo(x,y-10,x+d*15,y+2);c.quadraticCurveTo(x,y+8,x-d*15,y-6);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.arc(x+d,y,2.6,0,T);c.fill()}
|
||||
const g=[1,.6,.35,.8][i%4];c.fillStyle='#fff';for(const[x,y]of[[87,128],[169,128]]){c.beginPath();c.moveTo(x,y-11*g);c.quadraticCurveTo(x+1.5,y-1.5,x+11*g,y);c.quadraticCurveTo(x+1.5,y+1.5,x,y+11*g);c.quadraticCurveTo(x-1.5,y+1.5,x-11*g,y);c.quadraticCurveTo(x-1.5,y-1.5,x,y-11*g);c.fill()}
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(114,168);c.quadraticCurveTo(128,172,142,164);c.stroke();c.fillStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(134,169);c.lineTo(137,176);c.lineTo(139,167);c.fill();c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(72,150);c.bezierCurveTo(56,74,88,36,128,36);c.bezierCurveTo(168,36,200,74,184,150);c.quadraticCurveTo(184,116,176,102);c.quadraticCurveTo(162,112,152,100);c.quadraticCurveTo(140,114,128,104);c.quadraticCurveTo(116,114,104,100);c.quadraticCurveTo(94,112,80,102);c.quadraticCurveTo(72,116,72,150);c.closePath()
|
||||
c.fillStyle='#fff';c.fill();c.save();c.clip();xh(c,50,206,30,160);c.strokeStyle='#fff';c.lineWidth=8;c.beginPath();c.arc(128,108,58,PI*1.25,PI*1.45);c.stroke();c.beginPath();c.arc(128,108,58,PI*1.55,PI*1.75);c.stroke();c.restore()
|
||||
c.strokeStyle='#111';c.lineWidth=6;c.stroke()
|
||||
c.strokeStyle='#111';for(const[x,o]of[[34,0],[222,.33],[30,.66],[226,.85]]){const p=(t+o)%1;{const k=1.15*sin(PI*p);if(k>.15)go(c,x+4*sin(T*p*2),214-p*160,k,.15*sin(T*p))}}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// girl with screentone twin tails holding out a tamagoyaki on chopsticks, "say aah", over her open bento box
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI}=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 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()}
|
||||
const tama=(c,x,y,s,dot)=>{c.save();c.translate(x,y);c.scale(s,s);c.lineWidth=4
|
||||
c.beginPath();c.moveTo(-12,-16);c.lineTo(20,-16);c.quadraticCurveTo(28,-16,26,-6);c.lineTo(24,10);c.quadraticCurveTo(22,18,14,18);c.lineTo(-12,18);c.closePath();c.fillStyle='#fff';c.fill();c.fillStyle=dot;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(-12,1,13,17,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(let a=0;a<T*2.2;a+=.2){const r=1+a*1.6;c.lineTo(-12+r*cos(a)*.75,1+r*sin(a))}c.stroke();c.restore()}
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),p=(1-cos(t*T))/2,aah=p>.45,b=Math.round(2*sin(t*T*2))
|
||||
// twin tails, swinging a little
|
||||
for(const d of[-1,1]){
|
||||
c.save();c.translate(118+d*58,70)
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(0,-6);c.bezierCurveTo(d*58,-14,d*74,62,d*58,122);c.quadraticCurveTo(d*52,146,d*40,158);c.quadraticCurveTo(d*44,136,d*34,124);c.quadraticCurveTo(d*30,140,d*20,146);c.bezierCurveTo(d*40,86,d*22,26,0,10);c.closePath();toned(c,dot)
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(d*20,4);c.bezierCurveTo(d*44,14,d*54,50,d*52,84);c.stroke();c.strokeStyle='#111';c.lineWidth=2;c.beginPath();c.moveTo(d*14,20);c.bezierCurveTo(d*36,50,d*40,90,d*32,128);c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
c.save();c.translate(-10,0)
|
||||
// dark sailor uniform with white stripes
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,56,246);c.lineTo(200,246);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2.5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*32,194),c.lineTo(128+d*6,232),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(110,191);c.lineTo(128,220);c.lineTo(146,191);c.closePath();c.fill();c.stroke()
|
||||
for(const d of[-1,1])c.beginPath(),c.moveTo(128,218),c.quadraticCurveTo(128+d*18,214,128+d*20,226),c.quadraticCurveTo(128+d*10,228,128,218),c.fill(),c.stroke()
|
||||
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()
|
||||
// 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()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
if(!aah){c.lineWidth=6;c.beginPath();c.moveTo(x-13,146);c.quadraticCurveTo(x,130,x+13,146);c.stroke();continue}
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+3,141,10.5,15,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x,135,4.5,0,T);c.fill();c.beginPath();c.arc(x+7,149,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-d*12,130);c.quadraticCurveTo(x,119,x+d*15,131);c.lineTo(x+d*21,125);c.stroke()
|
||||
}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<4;k++)c.beginPath(),c.moveTo(128+d*40+(k-1.5)*5-3,170),c.lineTo(128+d*40+(k-1.5)*5+3,161),c.stroke()
|
||||
c.lineWidth=3.5;c.fillStyle='#111';c.beginPath()
|
||||
if(aah){c.ellipse(128,172,9,11,0,0,T);c.fill();c.stroke();c.save();c.clip();c.fillStyle='#fff';c.beginPath();c.ellipse(128,182,7,5,0,0,T);c.fill();c.restore()}
|
||||
else{c.moveTo(119,166);c.quadraticCurveTo(128,178,137,166);c.closePath();c.fill();c.stroke()}
|
||||
// screentone bangs, ribbons on the tails
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(70,146);c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,146)
|
||||
c.quadraticCurveTo(186,116,178,102);c.quadraticCurveTo(170,104,164,112);c.quadraticCurveTo(160,98,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,124,90);c.quadraticCurveTo(118,102,110,108);c.quadraticCurveTo(106,96,96,94);c.quadraticCurveTo(90,104,86,112)
|
||||
c.quadraticCurveTo(80,104,78,102);c.quadraticCurveTo(70,120,70,146);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<=30;k++){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?52:48)*cos(a),104+(k%2?52:48)*sin(a))}for(let k=30;k>=0;k--){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?44:40)*cos(a),104+(k%2?44:40)*sin(a))}c.fill()
|
||||
c.fillStyle='#111';c.lineWidth=4;for(const d of[-1,1]){const x=128+d*62;for(const e of[-1,1])c.beginPath(),c.moveTo(x,74),c.lineTo(x+e*16,62),c.lineTo(x+e*16,86),c.closePath(),c.fill(),c.stroke();c.beginPath();c.arc(x,74,5,0,T);c.fill()}
|
||||
c.restore()
|
||||
// open bento held in her left hand
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.roundRect(30,200,124,48,8);c.fill();c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(90,204);c.lineTo(90,244);c.moveTo(90,224);c.lineTo(150,224);c.stroke()
|
||||
c.fillStyle=dot;c.beginPath();c.roundRect(36,206,50,36,4);c.fill();c.fillStyle='#fff';for(const[x,y]of[[50,216],[66,228],[76,214],[44,232]])c.beginPath(),c.ellipse(x,y,4,2.5,.4,0,T),c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.arc(61,222,7,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(59,219,2,0,T);c.fill()
|
||||
c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.roundRect(96,206,24,15,4);c.fill();c.stroke();c.lineWidth=2;c.beginPath();c.arc(103,213,4,0,T*.8);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.arc(136,212,6,0,T);c.fillStyle=dot;c.fill();c.stroke();c.beginPath();c.arc(130,215,5,0,T);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(112,236,12,7,0,PI,0);c.lineTo(124,236);c.fill();c.stroke();for(let k=0;k<4;k++)c.beginPath(),c.moveTo(101+k*7,236),c.quadraticCurveTo(100+k*7,244,104+k*7,244),c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.arc(108,231,1.5,0,T);c.arc(116,231,1.5,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=4.5;c.beginPath();c.ellipse(28,226,10,13,.3,0,T);c.fill();c.stroke()
|
||||
// chopsticks bring the tamagoyaki forward
|
||||
const s=1+.35*p,tx=154+12*p,ty=192+6*p,hx=216,hy=216+b
|
||||
for(const o of[-5,5])for(const[l,st]of[[8,'#111'],[3,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(hx+14,hy+16+o*.3),c.lineTo(tx+10*s,ty+o*s*.9),c.stroke()
|
||||
c.strokeStyle='#111';tama(c,tx,ty,s,dot)
|
||||
c.fillStyle='#fff';c.lineWidth=4.5;c.beginPath();c.ellipse(hx,hy,12,13,-.4,0,T);c.fill();c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(hx-8,hy-2);c.quadraticCurveTo(hx-2,hy+2,hx+2,hy-4);c.stroke()
|
||||
// sparkle on the egg and a heart
|
||||
c.fillStyle='#fff';c.lineWidth=3;const sp=7*p;if(sp>2){c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,r=k%2?sp*.3:sp;c.lineTo(tx+30*s+r*cos(a),ty-20*s+r*sin(a))}c.closePath();c.fill();c.stroke()}
|
||||
const hp=(t+.25)%1;c.fillStyle='#111';heart(c,214,104-hp*40,.9+.4*sin(PI*hp));c.fill()
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// side view: girl on tiptoe stretching to write a cursive line high on the chalkboard, chalk squeaking, dust puffs, then the line dusts away
|
||||
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()
|
||||
// prolate cycloid reads as looped handwriting
|
||||
const W=(u,x0,y0,w)=>{const th=u*12*PI;return[x0+w*u-5*sin(th),y0-5*cos(th)+5]}
|
||||
const line=(c,x0,y0,w,u)=>{c.beginPath();for(let k=0;k<=u*120;k++){const[x,y]=W(k/120,x0,y0,w);c.lineTo(x,y)}c.stroke()}
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),wr=t<.8,u=min(t/.8,1),b=i%2,fade=wr?1:1-(t-.8)/.2
|
||||
// chalkboard
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.rect(124,18,124,160);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.fillRect(132,26,108,144)
|
||||
c.strokeStyle='#fff';c.lineWidth=3;line(c,150,40,70,1);c.beginPath();c.moveTo(146,58);c.lineTo(228,58);c.stroke()
|
||||
c.lineWidth=2.5;for(const y of[120,146])line(c,178,y,52,1)
|
||||
c.lineWidth=3;c.beginPath();for(let k=0;k<=10;k++){const a=k*PI/5-PI/2,r=k%2?5:12;c.lineTo(156+r*cos(a),136+r*sin(a))}c.closePath();c.stroke()
|
||||
c.globalAlpha=fade;c.lineWidth=3;line(c,140,82,64,u);c.globalAlpha=1
|
||||
if(!wr)for(let k=0;k<7;k++){const p=(t-.8)/.2,x=144+k*9,y=86-p*(14+k%3*6)-(k%2)*4;c.fillStyle='#fff';c.beginPath();c.arc(x+3*sin(k+p*6),y,1.6+k%2,0,T);c.fill()}
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.rect(126,172,120,8);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.rect(206,164,22,8);c.fillStyle=dot;c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.rect(184,167,14,5);c.fill();c.stroke()
|
||||
// where the chalk is
|
||||
const[ex,ey]=W(1,140,82,64),[tx,ty]=wr?W(u,140,82,64):[ex+(140-ex)*(t-.8)/.2,ey+10*sin(PI*(t-.8)/.2)]
|
||||
const y=b?-1:0
|
||||
c.save();c.translate(0,y)
|
||||
// ponytail behind
|
||||
c.lineWidth=5;c.fillStyle='#fff';const sw=6*sin(t*T*2)
|
||||
c.beginPath();c.moveTo(56,92);c.bezierCurveTo(30+sw,96,18+sw,140,26+sw*1.5,176);c.quadraticCurveTo(34+sw,166,40+sw,170);c.bezierCurveTo(38+sw,140,52,118,66,104);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const k of[0,1])c.beginPath(),c.moveTo(50-k*6,104),c.quadraticCurveTo(30+sw-k*2,130,32+sw*1.3-k*3,160),c.stroke()
|
||||
// far arm: under her chin, elbow out, up to the chalk
|
||||
const ox=134+(tx-140)*.3
|
||||
c.beginPath();c.moveTo(98,176);c.lineTo(ox,160);c.lineTo(tx-4,ty+10)
|
||||
for(const[l,s2]of[[21,'#fff'],[14,'#111'],[8,'#fff'],[8,dot]])c.lineWidth=l,c.strokeStyle=s2,c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=12;c.beginPath();c.arc(tx-3,ty+8,8,0,T);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(tx-3,ty+8,8,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.rect(tx-2,ty-3,5,8);c.fill();c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(tx-9,ty+5);c.lineTo(tx-4,ty+5);c.moveTo(tx-9,ty+10);c.lineTo(tx-4,ty+10);c.stroke()
|
||||
// legs on tiptoe and skirt
|
||||
for(const[x,d]of[[70,0],[86,1]]){c.lineWidth=12;c.beginPath();c.moveTo(x,214);c.lineTo(x+2,240);c.stroke();c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(x,214);c.lineTo(x+2,240);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle=d?'#111':'#fff';c.lineWidth=4;c.beginPath();c.ellipse(x+5,241,9,4.5,1.05,0,T);c.fill();c.stroke()}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])c.beginPath(),c.arc(79+d*22,236,6,d<0?.6*PI:-.4*PI,d<0?1.4*PI:.4*PI),c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(62,196);c.lineTo(100,196);c.lineTo(108,222);c.lineTo(54,222);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const x of[68,80,92])c.beginPath(),c.moveTo(x+(x-81)*.1,198),c.lineTo(x+(x-81)*.35,221),c.stroke()
|
||||
// cardigan body and the arm resting on the tray
|
||||
c.lineWidth=5;c.fillStyle=dot;c.beginPath();c.moveTo(70,160);c.quadraticCurveTo(60,180,62,200);c.lineTo(102,200);c.quadraticCurveTo(102,176,94,160);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(80,162);c.lineTo(86,178);c.lineTo(92,162);c.fill();c.stroke()
|
||||
c.lineWidth=13;c.beginPath();c.moveTo(78,170);c.quadraticCurveTo(96,196,120,182);c.stroke();c.strokeStyle=dot;c.lineWidth=7;c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(123,181,6.5,0,T);c.fill();c.stroke()
|
||||
// head in profile
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.arc(80,124,40,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(82,98);c.quadraticCurveTo(116,98,118,124);c.lineTo(124,140);c.lineTo(118,144);c.quadraticCurveTo(120,152,116,156);c.quadraticCurveTo(110,168,92,166);c.quadraticCurveTo(80,164,76,150);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.arc(80,140,7,-.5*PI,.6*PI);c.stroke()
|
||||
// eye looking up at the board
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(105,130,7,12,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(108,127,5.5,9.5,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(109,123,2.6,0,T);c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(98,120);c.quadraticCurveTo(106,114,114,118);c.moveTo(99,121);c.lineTo(93,116);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.ellipse(114,153,3,2.5+b,0,0,T);c.stroke()
|
||||
c.lineWidth=2.5;for(let k=0;k<3;k++)c.beginPath(),c.moveTo(97+k*4,150),c.lineTo(100+k*4,144),c.stroke()
|
||||
// white hair: crown, bangs, scrunchie
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(42,132);c.bezierCurveTo(34,82,80,74,104,90);c.quadraticCurveTo(118,100,120,118)
|
||||
c.quadraticCurveTo(112,112,110,118);c.quadraticCurveTo(104,106,98,114);c.quadraticCurveTo(92,104,86,112);c.quadraticCurveTo(80,104,78,128);c.quadraticCurveTo(70,120,60,138);c.quadraticCurveTo(52,128,42,132);c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const[a,b2,e]of[[[52,98],[70,88],[86,96]],[[50,112],[64,100],[80,104]],[[96,96],[108,100],[114,112]]])c.beginPath(),c.moveTo(...a),c.quadraticCurveTo(...b2,...e),c.stroke()
|
||||
c.fillStyle=dot;c.lineWidth=4;c.beginPath();c.ellipse(56,94,9,12,.5,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
// squeak marks and chalk dust at the tip
|
||||
if(wr){
|
||||
c.strokeStyle='#fff';c.lineWidth=2.5;if(i%3==0)for(const a of[-.9,-.4,.1])c.beginPath(),c.moveTo(tx+10*cos(a),ty-6+10*sin(a)),c.lineTo(tx+16*cos(a),ty-6+16*sin(a)),c.stroke()
|
||||
c.fillStyle='#fff';for(let k=0;k<3;k++){const p=(t*5+k/3)%1;c.beginPath();c.arc(tx+10+k*3,ty+2+p*18,1.4+p*1.4,0,T);c.fill()}
|
||||
c.strokeStyle='#111'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// cheerleader with an ink-black high ponytail shaking her pompoms, crouching and leaping into a spread jump, sparkles at the top
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=16,delay=75,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 pom=(c,x,y,r,a)=>{c.save();c.translate(x,y);c.rotate(a);c.lineWidth=4;c.fillStyle='#fff';c.beginPath();for(let k=0;k<28;k++){const b=k*T/28,rr=k%2?r*.78:r;c.lineTo(rr*cos(b),rr*sin(b))}c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;for(let k=0;k<14;k++){const b=k*T/14+.2;c.beginPath();c.moveTo(r*.2*cos(b),r*.2*sin(b));c.lineTo(r*.7*cos(b+.15),r*.7*sin(b+.15));c.stroke()}c.restore()}
|
||||
const limb=(c,pts,w,core)=>{c.beginPath();c.moveTo(...pts[0]);c.quadraticCurveTo(...pts[1],...pts[2]);for(const[l,s]of[[w,'#111'],[w-6,core]])c.lineWidth=l,c.strokeStyle=s,c.stroke();c.strokeStyle='#111'}
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat')
|
||||
const air=t>=.2&&t<.6,p=air?(t-.2)/.4:0,oy=t<.1?0:t<.2?8*(t-.1)/.1:air?8*(1-p)-32*sin(PI*p):t<.72?8*sin(PI*(t-.6)/.12):0
|
||||
const kb=air?0:max(0,oy)/8,up=air?sin(PI*p):0,sh=air?0:(i%2?4:-4),vel=air?-cos(PI*p):0
|
||||
c.translate(0,4)
|
||||
// shadow on the ground
|
||||
c.lineWidth=3;c.fillStyle=dot;c.beginPath();c.ellipse(128,240,40-14*up,6-2*up,0,0,T);c.fill()
|
||||
// ponytail lags the jump
|
||||
c.save();c.translate(152,52+oy);c.rotate(-.15+.4*vel)
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath();c.moveTo(-8,-4);c.bezierCurveTo(44,-16,66,30,58,84);c.quadraticCurveTo(50,72,42,82);c.quadraticCurveTo(40,66,30,70);c.bezierCurveTo(40,34,20,12,-8,10);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(12,2);c.bezierCurveTo(36,6,50,34,48,64);c.stroke();c.strokeStyle='#111';c.restore()
|
||||
// legs: planted and bending, or spread wide in the air
|
||||
for(const d of[-1,1]){
|
||||
const hx=128+d*10,hy=190+oy,fx=air?128+d*(14+30*up):128+d*12,fy=air?hy+40-8*up:234,kx=(hx+fx)/2+d*(10*kb+6*up),ky=(hy+fy)/2
|
||||
limb(c,[[hx,hy],[kx,ky],[fx,fy-4]],13,'#fff')
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(fx+d*3,fy,10,6,d*(air?.5*up:0),0,T);c.fill();c.stroke();c.beginPath();c.moveTo(fx+d*3-9,fy+3);c.lineTo(fx+d*3+9,fy+3);c.stroke()
|
||||
}
|
||||
// pleated skirt flares in the jump
|
||||
const fl=8*up
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath();c.moveTo(110,168+oy);c.lineTo(146,168+oy);c.lineTo(158+fl,194+oy-fl*.4);c.lineTo(98-fl,194+oy-fl*.4);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2.5;for(const k of[-2,-1,0,1,2])c.beginPath(),c.moveTo(128+k*7,172+oy),c.lineTo(128+k*(11+fl/3),192+oy-fl*.4),c.stroke()
|
||||
c.beginPath();c.moveTo(101-fl,189+oy-fl*.4);c.lineTo(155+fl,189+oy-fl*.4);c.stroke();c.strokeStyle='#111'
|
||||
// top with a star
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(110,132+oy);c.lineTo(146,132+oy);c.lineTo(148,170+oy);c.lineTo(108,170+oy);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(114,133+oy);c.lineTo(128,150+oy);c.lineTo(142,133+oy);c.lineTo(136,133+oy);c.lineTo(128,143+oy);c.lineTo(120,133+oy);c.closePath();c.fill()
|
||||
c.lineWidth=2.5;c.beginPath();for(let k=0;k<10;k++){const a=k*PI/5-PI/2,r=k%2?3.5:8;c.lineTo(128+r*cos(a),160+oy+r*sin(a))}c.closePath();c.fillStyle=dot;c.fill();c.stroke()
|
||||
// arms: pompoms shaking at the hips, or thrown up in a V
|
||||
for(const d of[-1,1]){
|
||||
const sx=128+d*17,sy=138+oy,hx=128+d*(40+12*up),hy=(1-up)*(176+d*sh)+up*82+oy,ex=(sx+hx)/2+d*12,ey=(sy+hy)/2
|
||||
limb(c,[[sx,sy],[ex,ey],[hx,hy]],12,'#fff');pom(c,hx+d*4,hy-4*up,17+3*up,t*T*2*d)
|
||||
}
|
||||
// head
|
||||
c.save();c.translate(128,92+oy);c.scale(.62,.62);c.translate(-128,-120)
|
||||
c.lineWidth=8;c.fillStyle='#111';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.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]]){
|
||||
if(air){c.lineWidth=8;c.beginPath();c.moveTo(x-14,146);c.quadraticCurveTo(x,128,x+14,146);c.stroke();continue}
|
||||
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';c.beginPath();c.arc(x-3,135,5,0,T);c.fill();c.beginPath();c.arc(x+4,149,2.2,0,T);c.fill()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-d*12,130);c.quadraticCurveTo(x,119,x+d*15,131);c.lineTo(x+d*21,125);c.stroke()
|
||||
}
|
||||
c.lineWidth=3;for(const d of[-1,1])for(let k=0;k<4;k++)c.beginPath(),c.moveTo(128+d*42+(k-1.5)*6-3,172),c.lineTo(128+d*42+(k-1.5)*6+3,161),c.stroke()
|
||||
c.lineWidth=4.5;c.fillStyle='#111';c.beginPath();c.moveTo(114,163);c.quadraticCurveTo(128,186,142,163);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle='#fff';c.beginPath();c.ellipse(128,182,8,5,0,0,T);c.fill();c.restore()
|
||||
// swept bangs, white shine, ribbon at the tie
|
||||
c.lineWidth=8;c.fillStyle='#111';c.beginPath();c.moveTo(70,140);c.bezierCurveTo(56,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,140)
|
||||
c.quadraticCurveTo(184,112,170,100);c.quadraticCurveTo(160,112,146,112);c.quadraticCurveTo(140,98,128,92);c.quadraticCurveTo(116,110,98,112);c.quadraticCurveTo(90,104,86,98);c.quadraticCurveTo(72,114,70,140);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<=30;k++){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?52:48)*cos(a),100+(k%2?52:48)*sin(a))}for(let k=30;k>=0;k--){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?44:40)*cos(a),100+(k%2?44:40)*sin(a))}c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=6;for(const e of[-1,1])c.beginPath(),c.moveTo(166,44),c.lineTo(166+e*22,30),c.lineTo(166+e*20,58),c.closePath(),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(166,44,7,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
// sparkles at the top of the jump
|
||||
if(up>.3){c.fillStyle='#fff';c.lineWidth=3;for(const[x,y,o]of[[46,62,0],[212,52,1],[198,132,2],[56,140,3]]){const s=(6+5*sin(o+t*T*4))*up;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+oy*.4+r*sin(a))}c.closePath();c.fill();c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// manga girl with screentone twin tails chomps a big crepe, gets cream on her nose, chews happily, then licks it off cross-eyed
|
||||
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 eye=(c,x,y,d,lx=0)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.clip();c.beginPath();c.ellipse(x+lx,y+2,10.5,14,0,0,T);c.fillStyle='#111';c.fill();c.fillStyle='#777';c.beginPath();c.ellipse(x+lx,y+13,10,6,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+lx-4,y-4,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+lx+4,y+8,2,0,T);c.fill()
|
||||
c.save();c.translate(x+lx+5,y-6);c.beginPath();for(let k=0;k<8;k++){const r=k%2?1.2:4,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,y-5);c.quadraticCurveTo(x,y-20,x+16,y-5);c.moveTo(x+d*14,y-8);c.lineTo(x+d*21,y-13);c.moveTo(x+d*15,y-3);c.lineTo(x+d*21,y-4);c.stroke()
|
||||
}
|
||||
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 L=createCanvas(256,256),lc=L.getContext('2d')
|
||||
const crepe=(x,y,DOT,FINE)=>{
|
||||
const c=lc;c.clearRect(0,0,256,256);c.lineCap=c.lineJoin='round';c.strokeStyle='#111';c.save();c.translate(x,y);c.scale(1.1,1.1);c.translate(-x,-y)
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(x-30,y-14);c.quadraticCurveTo(x,y-30,x+30,y-14);c.lineTo(x+4,y+44);c.quadraticCurveTo(x,y+50,x-4,y+44);c.closePath();c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
const C=[[x-20,y-24,15],[x,y-30,17],[x+20,y-24,15],[x-10,y-46,14],[x+12,y-46,13],[x+1,y-60,11]]
|
||||
c.lineWidth=12;for(const[a,b,r]of C)c.beginPath(),c.arc(a,b,r,0,T),c.stroke()
|
||||
c.fillStyle='#fff';for(const[a,b,r]of C)c.beginPath(),c.arc(a,b,r,0,T),c.fill()
|
||||
c.lineWidth=3;for(const[a,b,r]of C)c.beginPath(),c.arc(a+2,b+2,r*.6,.1*PI,.7*PI),c.stroke()
|
||||
c.lineWidth=4;for(const[a,b,r]of[[x+22,y-40,-.4],[x-24,y-34,.5]]){c.save();c.translate(a,b);c.rotate(r);c.fillStyle='#fff';c.beginPath();c.moveTo(0,12);c.quadraticCurveTo(-13,0,-9,-9);c.quadraticCurveTo(0,-14,9,-9);c.quadraticCurveTo(13,0,0,12);c.fill();c.fillStyle=DOT;c.fill();c.stroke();c.fillStyle='#fff';for(const[p,q]of[[-4,-3],[4,-3],[0,4]])c.beginPath(),c.ellipse(p,q,1.5,2.2,0,0,T),c.fill();c.restore()}
|
||||
c.save();c.translate(x+8,y-62);c.rotate(.35);c.fillStyle='#111';c.beginPath();c.roundRect(-4,-26,8,30,3);c.fill();c.strokeStyle='#fff';c.lineWidth=2;c.beginPath();c.moveTo(-1,-22);c.lineTo(-1,-4);c.stroke();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-30,y-14);c.lineTo(x-24,y-6);c.lineTo(x-16,y-12);c.lineTo(x-8,y-4);c.lineTo(x,y-11);c.lineTo(x+8,y-4);c.lineTo(x+16,y-12);c.lineTo(x+24,y-6);c.lineTo(x+30,y-14);c.fillStyle='#fff';c.fill();c.stroke()
|
||||
c.globalCompositeOperation='destination-out';for(const[a,b,r]of[[x-26,y-50,13],[x-14,y-62,10]])c.beginPath(),c.arc(a,b,r,0,T),c.fill()
|
||||
c.globalCompositeOperation='source-atop';c.lineWidth=10;for(const[a,b,r]of[[x-26,y-50,13],[x-14,y-62,10]])c.beginPath(),c.arc(a,b,r,0,T),c.stroke()
|
||||
c.globalCompositeOperation='source-over';c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8),HAIR=tone(c,5,1.1)
|
||||
const go=t<.25?0:t<.33?ease((t-.25)/.08):t<.4?1:1-ease((t-.4)/.08),bite=t>=.3&&t<.42,chew=t>=.42&&t<.75,lick=t>=.75&&t<.94,cream=t>=.37&&t<.9
|
||||
// twin tails
|
||||
for(const d of[-1,1]){const w=0;c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128+d*60,72);c.bezierCurveTo(128+d*112,84,128+d*104+w,170,128+d*92+w,236);c.quadraticCurveTo(128+d*80+w,214,128+d*72,226);c.bezierCurveTo(128+d*82,170,128+d*76,110,128+d*52,92);c.closePath();c.fill();c.fillStyle=HAIR;c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(128+d*76,92);c.bezierCurveTo(128+d*96,120,128+d*94,160,128+d*88+w,200);c.stroke();c.strokeStyle='#111'}
|
||||
// striped top
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,252);c.lineTo(202,252);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill()
|
||||
c.save();c.clip();c.fillStyle='#111';for(let y=204;y<252;y+=16)c.fillRect(40,y,180,8);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(102,190);c.quadraticCurveTo(128,212,154,190);c.lineTo(150,186);c.lineTo(106,186);c.fill();c.lineWidth=5;c.stroke()
|
||||
// face
|
||||
c.lineWidth=6;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(chew){c.lineWidth=5;for(const x of[103,153])c.beginPath(),c.arc(x,144,12,PI*1.15,PI*1.85),c.stroke()
|
||||
const p=1+.25*Math.abs(sin(t*T*6));c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*42,164,12*p,d<0?PI*.5:-PI*.5,d<0?PI*1.5:PI*.5),c.fill(),c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(121,170);c.quadraticCurveTo(124,166,128,170);c.quadraticCurveTo(132,166,135,170);c.stroke()}
|
||||
else if(bite){c.lineWidth=5;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()}}
|
||||
else{const lx=lick?[6,-6]:t<.25?[5,5]:[0,0];eye(c,103,140,-1,lx[0]);eye(c,153,140,1,lx[1])
|
||||
c.lineWidth=4.5;c.fillStyle='#111';c.beginPath();c.moveTo(116,166);c.quadraticCurveTo(128,186,140,166);c.closePath();c.fill();c.stroke()
|
||||
if(lick){const u=ease((t-.75)/.08)*(1-ease((t-.88)/.06));c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(120,170);c.bezierCurveTo(116,164-10*u,120,160-14*u,128,160-14*u);c.bezierCurveTo(136,160-14*u,140,164-10*u,136,170);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(128,168);c.lineTo(128,164-12*u);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(cream){c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(124,152,6,0,T);c.arc(131,150,5,0,T);c.stroke();c.beginPath();c.arc(124,152,6,0,T);c.arc(131,150,5,0,T);c.fill()}
|
||||
// screentone bangs with a white shine
|
||||
c.lineWidth=6;c.fillStyle='#fff';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(172,104,166,112);c.quadraticCurveTo(160,98,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,124,90);c.quadraticCurveTo(118,102,110,108);c.quadraticCurveTo(106,96,96,94);c.quadraticCurveTo(90,104,86,112)
|
||||
c.quadraticCurveTo(80,104,78,102);c.quadraticCurveTo(70,122,70,150);c.closePath();c.fill();c.fillStyle=HAIR;c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=7;for(const[s,e]of[[1.24,1.36],[1.44,1.54],[1.62,1.74]])c.beginPath(),c.arc(128,108,54,PI*s,PI*e),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.roundRect(128+d*58-7,66,14,16,4),c.fill()
|
||||
if(chew)for(let k=0;k<3;k++){const p=(t*4+k/3)%1;flower(c,[40,214,30][k],[70,64,140][k]-p*20,8+4*sin(PI*p),p*3)}
|
||||
if(t<.25){c.fillStyle='#fff';c.lineWidth=3;for(const[x,y,s]of[[232,112,9],[190,96,6]]){const r=s*(1+.3*sin(t*T*8));c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x,y,x+r,y);c.quadraticCurveTo(x,y,x,y+r);c.quadraticCurveTo(x,y,x-r,y);c.quadraticCurveTo(x,y,x,y-r);c.fill();c.stroke()}}
|
||||
// crepe in her hand
|
||||
const x=208-go*54,y=190+go*34
|
||||
for(const[l,st]of[[24,'#111'],[15,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(x-6,y+16),c.lineTo(x+12,262),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
crepe(x,y,DOT,FINE);c.drawImage(L,0,0)
|
||||
if(bite){c.lineWidth=5;for(const[a,b]of[[-1,-1],[-1,1],[1,-1]]){c.beginPath();c.moveTo(x-30+a*30,y-70+b*24);c.lineTo(x-30+a*40,y-70+b*32);c.stroke()}}
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(x-6,y+14,15,13,.3,0,T);c.fill();c.stroke();c.lineWidth=3;c.beginPath();c.moveTo(x-15,y+9);c.lineTo(x+2,y+7);c.moveTo(x-15,y+17);c.lineTo(x+2,y+15);c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// deadpan girl with an ink-black pixie cut staring with dot eyes in awkward silence while ellipsis dots appear and a crow flaps across
|
||||
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 DK=(()=>{const t=createCanvas(5,5),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(2.5,2.5,1.2,0,T);x.fill();return t})()
|
||||
const crow=(c,x,y,w)=>{
|
||||
c.save();c.translate(x,y);c.fillStyle='#111';c.lineWidth=3
|
||||
c.beginPath();c.moveTo(-14,0);c.lineTo(-30,-4);c.lineTo(-31,6);c.closePath();c.fill()
|
||||
c.beginPath();c.ellipse(0,0,16,9,-.1,0,T);c.fill();c.beginPath();c.arc(15,-5,8,0,T);c.fill()
|
||||
c.beginPath();c.moveTo(21,-8);c.lineTo(33,-3);c.lineTo(21,-1);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(17,-7,2,0,T);c.fill();c.fillStyle='#111'
|
||||
c.beginPath();c.moveTo(-8,-4);c.lineTo(8,-4);c.lineTo(-2+6*w,-4-26*w);c.lineTo(-10+4*w,-6-22*w);c.lineTo(-14,-4-14*w);c.closePath();c.fill()
|
||||
c.restore()
|
||||
}
|
||||
export default(c,t,i)=>{
|
||||
const cx=-50+t*356,cy=38+5*sin(T*t*2),blink=i==13
|
||||
c.save();c.translate(128,250);c.scale(.84,.84);c.translate(-128,-250)
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,186);c.quadraticCurveTo(62,196,56,248);c.lineTo(200,248);c.quadraticCurveTo(194,196,156,186);c.closePath();c.fill()
|
||||
c.save();c.clip();c.fillStyle=c.createPattern(DK,'repeat');c.fillRect(50,180,160,70);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.roundRect(108,170,40,30,10);c.fill();c.save();c.clip();c.lineWidth=2.5;for(const y of[178,186,194])c.beginPath(),c.moveTo(106,y),c.lineTo(150,y),c.stroke();c.restore();c.lineWidth=5;c.beginPath();c.roundRect(108,170,40,30,10);c.stroke()
|
||||
c.lineWidth=6;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()
|
||||
c.fillStyle='#111';c.lineWidth=5
|
||||
for(const[x,d]of[[106,-1],[150,1]]){if(blink){c.beginPath();c.moveTo(x-7,142);c.lineTo(x+7,142);c.stroke()}else{c.beginPath();c.arc(x,142,5,0,T);c.fill()}c.lineWidth=3.5;c.beginPath();c.moveTo(x+d*9,138);c.lineTo(x+d*14,134);c.stroke();c.lineWidth=5}
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(94,124);c.lineTo(116,124);c.moveTo(140,124);c.lineTo(162,124);c.stroke()
|
||||
c.beginPath();c.moveTo(120,168);c.lineTo(136,168);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(68,168);c.bezierCurveTo(48,76,84,38,128,38);c.bezierCurveTo(172,38,208,76,188,168)
|
||||
c.lineTo(180,160);c.lineTo(176,170);c.lineTo(172,118);c.lineTo(164,124);c.lineTo(160,104);c.lineTo(150,116);c.lineTo(146,98);c.lineTo(136,114);c.lineTo(128,98);c.lineTo(120,114);c.lineTo(110,98);c.lineTo(104,116);c.lineTo(94,104);c.lineTo(90,124);c.lineTo(84,118);c.lineTo(80,170);c.lineTo(76,160);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.56],[1.64,1.76]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.save();c.translate(86,80);c.rotate(-.5);for(const d of[-1,1])c.beginPath(),c.moveTo(0,0),c.lineTo(d*16,-9),c.lineTo(d*16,9),c.closePath(),c.fill(),c.stroke();c.beginPath();c.arc(0,0,5,0,T);c.fill();c.stroke();c.restore()
|
||||
c.restore()
|
||||
c.fillStyle='#111';for(let k=0;k<3;k++)if(t>=.12+k*.1&&t<.92)c.beginPath(),c.arc(178+k*18,104,6,0,T),c.fill()
|
||||
c.save();c.translate(cx,cy);c.scale(1.35,1.35);crow(c,0,0,sin(T*t*6));c.restore()
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// girl with a screentone side tail daydreaming of manga meat, drooling with starry eyes while the thought bubble bobs and sparkles
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=16,delay=90,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.1,0,T);x.fill();return t})()
|
||||
const star=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r);for(const[a,b]of[[1,0],[0,1],[-1,0],[0,-1]])c.quadraticCurveTo(x+(a+b)*r*.14,y+(b-a)*r*.14,x+a*r,y+b*r);c.fill()}
|
||||
const tone=(c,f)=>{f();c.fillStyle='#fff';c.fill();c.save();c.clip();c.fillStyle=c.createPattern(DK,'repeat');c.fillRect(0,0,256,256);c.restore();f();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const dr=.5+.5*sin(T*t),by=3*sin(T*t*2)
|
||||
c.save();c.translate(96,248);c.scale(.8,.8);c.translate(-128,-250)
|
||||
c.save();c.translate(186,74);c.rotate(.1+.06*sin(T*t));c.lineWidth=6;tone(c,()=>{c.beginPath();c.moveTo(-8,0);c.bezierCurveTo(40,0,56,70,40,130);c.quadraticCurveTo(34,150,20,156);c.quadraticCurveTo(30,90,0,20);c.closePath()})
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(10,14);c.bezierCurveTo(30,40,38,80,32,124);c.stroke();c.fillStyle='#fff';c.lineWidth=4.5;c.beginPath();c.ellipse(0,4,9,12,.4,0,T);c.fill();c.stroke();c.restore()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,56,248);c.lineTo(200,248);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(104,192);c.quadraticCurveTo(128,206,152,192);c.stroke();c.fillStyle='#111';c.beginPath();c.ellipse(128,228,16,10,0,0,T);c.fill();c.fillStyle='#fff';star(c,128,228,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.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=142
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,17,0,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.ellipse(x+3,y-3,10,13,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';star(c,x+1,y-5,7+2*sin(T*t*2+d));c.beginPath();c.arc(x+7,y+5,2.2,0,T);c.fill();c.beginPath();c.arc(x-3,y+6,1.5,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-15,y-7);c.quadraticCurveTo(x,y-19,x+15,y-7);c.moveTo(x+d*14,y-8);c.lineTo(x+d*20,y-13);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.arc(x,y-14,18,PI*1.3,PI*1.7);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,166),c.lineTo(128+d*42+k+3,159),c.stroke()
|
||||
c.lineWidth=3.5;c.fillStyle='#111';c.beginPath();c.moveTo(114,166);c.quadraticCurveTo(128,170,144,164);c.quadraticCurveTo(140,182,126,180);c.quadraticCurveTo(114,178,114,166);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(130,176,7,3.5,-.1,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=3;const dl=6+16*dr;c.beginPath();c.moveTo(141,170);c.quadraticCurveTo(147,170+dl*.5,146,170+dl);c.arc(144,170+dl,3.5+dr*1.5,0,PI);c.quadraticCurveTo(140,170+dl*.5,139,172);c.fill();c.stroke()
|
||||
c.lineWidth=6;tone(c,()=>{c.beginPath();c.moveTo(70,144);c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,144)
|
||||
c.quadraticCurveTo(184,116,176,104);c.quadraticCurveTo(166,108,160,116);c.quadraticCurveTo(156,100,146,94);c.quadraticCurveTo(138,108,128,112);c.quadraticCurveTo(124,98,114,92);c.quadraticCurveTo(106,106,96,112);c.quadraticCurveTo(92,102,84,100);c.quadraticCurveTo(72,114,70,144);c.closePath()})
|
||||
c.strokeStyle='#fff';c.lineWidth=7;for(const[a,e]of[[1.24,1.36],[1.44,1.56],[1.64,1.76]])c.beginPath(),c.arc(128,106,56,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
c.save();c.translate(0,by)
|
||||
for(const[x,y,r]of[[150,122,4],[162,106,6.5]]){c.lineWidth=3.5;c.fillStyle='#fff';c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke()}
|
||||
const cb=[[190,52,34],[160,58,20],[220,58,22],[176,30,20],[208,30,20],[190,80,20],[164,78,14],[216,78,16]]
|
||||
c.lineWidth=11;for(const[x,y,r]of cb)c.beginPath(),c.arc(x,y,r,0,T),c.stroke()
|
||||
c.fillStyle='#fff';for(const[x,y,r]of cb)c.beginPath(),c.arc(x,y,r,0,T),c.fill()
|
||||
c.save();c.translate(190,56);c.rotate(-.35+.12*sin(T*t))
|
||||
c.lineWidth=4.5;c.fillStyle='#fff';for(const d of[-1,1])for(const e of[-1,1])c.beginPath(),c.arc(d*38,e*5,5.5,0,T),c.fill(),c.stroke()
|
||||
c.beginPath();c.roundRect(-40,-4,80,8,4);c.fill();c.stroke();for(const d of[-1,1])for(const e of[-1,1])c.beginPath(),c.arc(d*38,e*5,3.5,0,T),c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(-28,-4);c.bezierCurveTo(-30,-26,22,-30,28,-8);c.bezierCurveTo(34,14,8,26,-8,22);c.bezierCurveTo(-26,18,-30,8,-28,-4);c.closePath();c.fill()
|
||||
c.save();c.clip();c.lineWidth=2;c.beginPath();for(let k=-40;k<40;k+=5)c.moveTo(k,26),c.lineTo(k+16,4);c.stroke();c.fillStyle='#fff';c.beginPath();c.ellipse(-6,-6,20,12,-.2,0,T);c.fill();c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(-10,-12,8,3.5,-.4,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#111';for(const[x,y,o]of[[154,30,0],[228,40,.33],[222,94,.66]])star(c,x,y,7*Math.abs(sin(T*(t+o))))
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// manga chibi detective in a deerstalker sweeping her magnifying glass along a trail of footprints, then lifting it so her eye looms huge through the lens
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min,hypot}=Math,T=2*PI
|
||||
export const frames=20,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)
|
||||
const lerp=(a,b,u)=>a.map((v,i)=>v+(b[i]-v)*u)
|
||||
const xh=(c,f,g=7)=>{c.save();f();c.clip();c.lineWidth=1.8;c.beginPath();for(let k=-256;k<256;k+=g)c.moveTo(k,0),c.lineTo(k+256,256),c.moveTo(k+256,0),c.lineTo(k,256);c.stroke();c.restore()}
|
||||
const eye=(c,x,y,d,lx,ly,tn,bl)=>{
|
||||
if(bl){c.lineWidth=6;c.beginPath();c.moveTo(x-15,y+2);c.quadraticCurveTo(x,y+8,x+15,y+2);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,14,17,0,0,T);c.clip();c.beginPath();c.ellipse(x+lx,y+2+ly,11,14,0,0,T);c.save();c.clip();c.fillStyle=tn;c.fillRect(x-20,y-20,40,40);c.fillStyle='#111';c.fillRect(x-20,y-14+ly,40,11);c.restore();c.lineWidth=2.5;c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+lx,y+3+ly,4.5,7,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x+lx-4,y-4+ly,4,5,-.4,0,T);c.fill();c.beginPath();c.arc(x+lx+4,y+8+ly,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-17,y-6);c.quadraticCurveTo(x,y-20,x+17,y-6);c.moveTo(x+d*16,y-7);c.lineTo(x+d*22,y-12);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-8,y+19);c.lineTo(x+8,y+19);c.stroke()
|
||||
}
|
||||
// shoe print: sole and heel in dotted tone
|
||||
const print=(c,x,y,a,tn)=>{c.save();c.translate(x,y);c.rotate(a);c.lineWidth=3
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(0,-15);c.bezierCurveTo(9,-15,9,-1,6,3);c.quadraticCurveTo(0,6,-6,3);c.bezierCurveTo(-9,-1,-9,-15,0,-15);c.fill();c.stroke();c.lineWidth=2;c.beginPath();for(const v of[-10,-5,0])c.moveTo(-4,v),c.lineTo(4,v);c.stroke();c.lineWidth=3
|
||||
c.beginPath();c.ellipse(0,11,5.5,5,0,0,T);c.fill();c.stroke();c.lineWidth=2;c.beginPath();c.moveTo(-3,11);c.lineTo(3,11);c.stroke()
|
||||
c.restore()}
|
||||
const PR=[[232,236,-1.75],[206,222,-1.4],[180,236,-1.75],[154,222,-1.4]]
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat')
|
||||
// lens path: sweep along the prints, rise to her eye, hold, drop back
|
||||
const A=[226,226],B=[164,226],E=[132,99]
|
||||
const L=t<.4?lerp(A,B,ease(t/.4)):t<.55?lerp(B,E,ease((t-.4)/.15)):t<.82?E:lerp(E,A,ease((t-.82)/.18))
|
||||
const up=t>=.55&&t<.82,R=up?28:22,look=up?[0,0]:[5,6],bl=t>.68&&t<.73
|
||||
const scene=()=>{
|
||||
for(const[x,y,a]of PR)print(c,x,y,a,tn)
|
||||
// short legs and boots
|
||||
c.lineWidth=4;c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.rect(118+d*10-5,186,10,18),c.fill(),c.stroke(),c.beginPath(),c.ellipse(118+d*10+3,208,10,6,0,0,T),c.fillStyle='#111',c.fill(),c.stroke(),c.fillStyle='#fff'
|
||||
// coat and inverness cape in tone
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(102,128);c.quadraticCurveTo(88,160,90,192);c.lineTo(146,192);c.quadraticCurveTo(148,160,134,128);c.closePath();c.fill();xh(c,()=>{c.beginPath();c.rect(88,126,60,70)},9);c.beginPath();c.moveTo(102,128);c.quadraticCurveTo(88,160,90,192);c.lineTo(146,192);c.quadraticCurveTo(148,160,134,128);c.closePath();c.stroke()
|
||||
const cp=()=>{c.beginPath();c.moveTo(104,124);c.quadraticCurveTo(78,134,72,166);c.quadraticCurveTo(118,176,164,166);c.quadraticCurveTo(158,134,132,124);c.closePath()}
|
||||
c.fillStyle='#fff';cp();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(60,120,120,60);c.restore();cp();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(118,128);c.lineTo(106,122);c.lineTo(106,134);c.closePath();c.moveTo(118,128);c.lineTo(130,122);c.lineTo(130,134);c.closePath();c.fill();c.stroke()
|
||||
// head
|
||||
c.save();c.translate(118,88);c.scale(.55,.55);c.translate(-128,-120)
|
||||
// crosshatched bob
|
||||
const bb=()=>{c.beginPath();c.moveTo(128,40);c.bezierCurveTo(194,40,206,100,198,170);c.lineTo(58,170);c.bezierCurveTo(50,100,62,40,128,40)}
|
||||
c.lineWidth=7;c.fillStyle='#fff';bb();c.fill();xh(c,bb,9);bb();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,140,-1,...look,tn,bl);eye(c,153,140,1,...look,tn,bl)
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(88,112);c.lineTo(114,116);c.moveTo(142,110);c.quadraticCurveTo(156,100,168,108);c.stroke()
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-5,1])c.beginPath(),c.moveTo(128+d*42+k-3,168),c.lineTo(128+d*42+k+3,160),c.stroke()
|
||||
c.lineWidth=4;c.beginPath();if(up)c.arc(128,168,7,.15*PI,.85*PI);else c.moveTo(120,172),c.quadraticCurveTo(128,168,136,173);c.stroke()
|
||||
const bg=()=>{c.beginPath();c.moveTo(72,140);c.bezierCurveTo(60,84,90,60,128,60);c.bezierCurveTo(166,60,196,84,184,140);c.quadraticCurveTo(178,112,170,104);c.lineTo(156,116);c.lineTo(150,98);c.lineTo(136,112);c.lineTo(126,96);c.lineTo(114,112);c.lineTo(104,98);c.lineTo(94,114);c.lineTo(84,104);c.quadraticCurveTo(76,118,72,140);c.closePath()}
|
||||
c.lineWidth=7;c.fillStyle='#fff';bg();c.fill();xh(c,bg,9);bg();c.stroke()
|
||||
// deerstalker: checked cap, front and back bills, ear flaps tied on top
|
||||
const cap=()=>{c.beginPath();c.moveTo(70,86);c.bezierCurveTo(66,24,190,24,186,86);c.quadraticCurveTo(128,70,70,86);c.closePath()}
|
||||
c.fillStyle='#fff';cap();c.fill();c.save();cap();c.clip();c.fillStyle='#111';for(let x=60;x<200;x+=16)for(let y=20;y<90;y+=16)c.fillRect(x+((y/16|0)%2)*8,y,8,8);c.restore();cap();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(150,82);c.quadraticCurveTo(190,74,212,94);c.quadraticCurveTo(184,96,156,90);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(106,80);c.quadraticCurveTo(70,72,50,88);c.quadraticCurveTo(76,92,100,88);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(128,34);c.quadraticCurveTo(110,14,104,30);c.quadraticCurveTo(116,34,128,34);c.quadraticCurveTo(146,14,152,30);c.quadraticCurveTo(140,34,128,34);c.fill();c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
scene()
|
||||
// magnified view inside the lens
|
||||
c.save();c.beginPath();c.arc(...L,R,0,T);c.clip();c.fillStyle='#fff';c.fillRect(0,0,256,256);c.translate(...L);c.scale(2,2);c.translate(-L[0],-L[1]);c.lineWidth=7;c.strokeStyle='#111';scene();c.restore()
|
||||
c.lineWidth=3;c.strokeStyle='#fff';c.beginPath();c.arc(...L,R-6,PI*1.1,PI*1.4);c.stroke();c.strokeStyle='#111'
|
||||
// handle points back toward her hand, arm from the shoulder
|
||||
const S=[140,150],dx=S[0]-L[0],dy=S[1]-L[1],dl=hypot(dx,dy),u=[dx/dl,dy/dl],h0=[L[0]+u[0]*(R+4),L[1]+u[1]*(R+4)],h1=[L[0]+u[0]*(R+28),L[1]+u[1]*(R+28)]
|
||||
for(const[l,s]of[[13,'#111'],[6,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(...h0),c.lineTo(...h1),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
for(const[l,s]of[[16,'#111'],[8,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(134,140),c.quadraticCurveTo((134+h1[0])/2+6,(140+h1[1])/2+10,...h1),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=3.5;c.fillStyle='#fff';c.beginPath();c.arc(...h1,7,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.arc(...L,R,0,T);c.stroke();c.lineWidth=2;c.strokeStyle='#fff';c.beginPath();c.arc(...L,R-3.5,0,T);c.stroke();c.strokeStyle='#111'
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// graduating girl with light screentone hair and a big bow pulls the cap off her diploma tube, pop, cherry petals burst out
|
||||
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 lt=createCanvas(7,7),l=lt.getContext('2d');l.fillStyle='#111';l.beginPath();l.arc(3.5,3.5,1,0,T);l.fill()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const petal=(c,x,y,a,s)=>{c.save();c.translate(x,y);c.rotate(a);c.scale(s,s);c.beginPath();c.moveTo(0,-9);c.quadraticCurveTo(9,-4,0,9);c.quadraticCurveTo(-9,-4,0,-9);c.fill();c.stroke();c.beginPath();c.moveTo(0,9);c.lineTo(0,3);c.stroke();c.restore()}
|
||||
const P=[...Array(10)].map((_,k)=>[-110+k*17+(k%2?6:-6),58+k%3*14,k*1.7])
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),lite=c.createPattern(lt,'repeat')
|
||||
const pull=t<.3?t/.3:0,open=t>=.3&&t<.85,cy=open?-34*sin(PI*min(1,(t-.3)/.12)/2)*(t<.75?1:1-(t-.75)/.1):-3*pull,sh=t<.3&&i%2?1:0
|
||||
// long light-toned hair behind
|
||||
c.save();c.translate(-18,0)
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,208,100,204,170);c.lineTo(208,240);c.lineTo(48,240);c.lineTo(52,170);c.bezierCurveTo(48,100,60,36,128,36);toned(c,lite)
|
||||
// blazer, ribbon, corsage
|
||||
c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(64,196,58,244);c.lineTo(198,244);c.quadraticCurveTo(192,196,156,188);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(108,189);c.lineTo(128,228);c.lineTo(148,189);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.moveTo(128,206),c.lineTo(128+d*14,198),c.lineTo(128+d*14,214),c.closePath(),c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=3;for(const d of[-1,1])c.beginPath(),c.moveTo(88+d*4,214),c.lineTo(88+d*9,240),c.lineTo(88+d*2,236),c.closePath(),c.fill(),c.stroke()
|
||||
for(let k=0;k<5;k++){const a=k*T/5-PI/2;c.beginPath();c.ellipse(88+9*cos(a),210+9*sin(a),7,5,a,0,T);c.fill();c.stroke()}
|
||||
c.beginPath();c.arc(88,210,5,0,T);c.fillStyle=dot;c.fill();c.stroke()
|
||||
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()
|
||||
// face: straining while pulling, then delighted
|
||||
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]]){
|
||||
if(!open){c.lineWidth=5;c.beginPath();c.moveTo(x-d*12,132);c.lineTo(x+d*6,141);c.lineTo(x-d*12,150);c.stroke();continue}
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+3,140,10.5,15,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,r=k%2?2.5:7;c.lineTo(x+r*cos(a),135+r*sin(a))}c.fill();c.beginPath();c.arc(x+8,149,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-d*12,130);c.quadraticCurveTo(x,119,x+d*15,131);c.lineTo(x+d*21,125);c.stroke()
|
||||
}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<4;k++)c.beginPath(),c.moveTo(128+d*40+(k-1.5)*5-3,170),c.lineTo(128+d*40+(k-1.5)*5+3,161),c.stroke()
|
||||
c.lineWidth=3.5;c.fillStyle='#111';c.beginPath()
|
||||
if(open){c.moveTo(116,165);c.quadraticCurveTo(128,184,140,165);c.closePath();c.fill();c.stroke()}else{c.moveTo(118,170);c.lineTo(138,170);c.stroke();c.fillStyle='#fff';c.beginPath();c.ellipse(132,173,4,4,0,0,PI);c.fill();c.stroke()}
|
||||
// straight bangs and a big black bow
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(68,170);c.bezierCurveTo(48,74,88,36,128,36);c.bezierCurveTo(168,36,208,74,188,170)
|
||||
c.quadraticCurveTo(182,130,178,106);c.lineTo(166,110);c.lineTo(156,104);c.lineTo(146,112);c.lineTo(134,104);c.lineTo(122,112);c.lineTo(110,104);c.lineTo(98,112);c.lineTo(88,104);c.lineTo(78,108);c.quadraticCurveTo(72,140,68,170);c.closePath();toned(c,lite)
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<=30;k++){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?52:48)*cos(a),102+(k%2?52:48)*sin(a))}for(let k=30;k>=0;k--){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?44:40)*cos(a),102+(k%2?44:40)*sin(a))}c.fill()
|
||||
c.fillStyle='#111';c.lineWidth=5;for(const e of[-1,1])c.beginPath(),c.moveTo(128,34),c.quadraticCurveTo(128+e*26,10,128+e*34,24),c.quadraticCurveTo(128+e*30,40,128,34),c.fill(),c.stroke()
|
||||
c.beginPath();c.arc(128,32,7,0,T);c.fill()
|
||||
c.restore()
|
||||
// diploma tube with the cap
|
||||
const X=184+sh
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath();c.roundRect(X-13,150,26,92,4);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.fillRect(X-13,196,26,8);c.strokeRect(X-13,196,26,8);c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(X-6,160);c.lineTo(X-6,190);c.moveTo(X-6,210);c.lineTo(X-6,232);c.stroke();c.strokeStyle='#111'
|
||||
if(open){c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(X,150,13,4,0,0,T);c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.ellipse(X,150,8,2,0,0,T);c.fill()}
|
||||
c.fillStyle='#111';c.lineWidth=5;c.beginPath();c.roundRect(X-15,128+cy,30,24,4);c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(X-7,134+cy);c.lineTo(X-7,146+cy);c.stroke();c.strokeStyle='#111'
|
||||
// hands on cap and tube
|
||||
c.fillStyle='#fff';c.lineWidth=4.5;c.beginPath();c.ellipse(X+14,140+cy,10,12,0,0,T);c.fill();c.stroke();c.beginPath();c.ellipse(X-14,222,10,12,0,0,T);c.fill();c.stroke()
|
||||
// strain marks, then the pop burst and petals
|
||||
if(t<.3){c.lineWidth=3;for(const d of[-1,1])c.beginPath(),c.moveTo(X+d*26,150),c.lineTo(X+d*32,146),c.moveTo(X+d*26,158),c.lineTo(X+d*33,158),c.stroke()}
|
||||
const tau=t-.3
|
||||
if(tau>=0&&tau<.14){const r=10+tau*220;c.lineWidth=3.5;for(let k=0;k<8;k++){const a=-PI/2+(k-3.5)*.32;c.beginPath();c.moveTo(X+r*.55*cos(a),146+r*.55*sin(a));c.lineTo(X+r*cos(a),146+r*sin(a));c.stroke()}}
|
||||
if(tau>=0){c.fillStyle='#fff';c.lineWidth=2.5
|
||||
for(const[dx,h,o]of P){const s=min(1,tau*12)*max(0,1-max(0,tau-.45)/.25);if(s<=.05)continue
|
||||
const e=1-Math.exp(-tau*11),x=X+dx*e+6*sin(o+tau*14),y=146-h*e+70*tau;petal(c,x,y,o+tau*9,s)}}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// manga dragon girl with screentone twin buns and curled horns blowing smoke rings that drift off and break up, scaly tail swishing
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min}=Math,T=2*PI
|
||||
export const frames=12,delay=100,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()
|
||||
// relaxed half-lidded eye
|
||||
const eye=(c,x,y,d,tn)=>{
|
||||
c.save();c.beginPath();c.rect(x-20,y-6,40,30);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,15,0,0,T);c.fill()
|
||||
c.beginPath();c.ellipse(x+3,y+4,10,13,0,0,T);c.save();c.clip();c.fillStyle=tn;c.fillRect(x-10,y-12,26,30);c.fillStyle='#111';c.fillRect(x-10,y-12,26,14);c.restore();c.lineWidth=2.5;c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+3,y+5,4,6,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-1,y+1,3.5,4,-.4,0,T);c.fill();c.beginPath();c.arc(x+7,y+10,1.8,0,T);c.fill()
|
||||
c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-4);c.quadraticCurveTo(x,y-9,x+16,y-4);c.moveTo(x+d*15,y-5);c.lineTo(x+d*21,y-9);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-14,y-14);c.quadraticCurveTo(x,y-19,x+14,y-13);c.moveTo(x-6,y+20);c.lineTo(x+6,y+20);c.stroke()
|
||||
}
|
||||
const ring=(c,x,y,r,p)=>{
|
||||
// the outline breaks into dashes as the ring fades out
|
||||
const g=max(0,(p-.55)/.45)
|
||||
if(g>.98)return
|
||||
c.setLineDash(g>0?[22*(1-g)+4,4+14*g]:[]);c.lineDashOffset=0
|
||||
c.fillStyle='#fff';c.lineWidth=3.5-1.5*g
|
||||
c.beginPath();c.ellipse(x,y,r,r*.72,-.5,0,T);c.ellipse(x,y,r*.55,r*.36,-.5,0,T,true);if(!g)c.fill('evenodd');c.stroke()
|
||||
c.setLineDash([])
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat'),sw=sin(t*T)
|
||||
// tail behind: the base stays put, the tip swishes; spikes ride the outer edge
|
||||
const bz=(p,u)=>p[0].map((_,i)=>(1-u)**3*p[0][i]+3*(1-u)**2*u*p[1][i]+3*(1-u)*u*u*p[2][i]+u**3*p[3][i])
|
||||
const O=[[84,238],[8,228],[4+4*sw,150],[24+12*sw,100]],tip=O[3]
|
||||
for(let k=1;k<8;k++){const u=.12+k*.1,[x,y]=bz(O,u),[x2,y2]=bz(O,u+.02),l=Math.hypot(x2-x,y2-y),nx=(y2-y)/l,ny=-(x2-x)/l,h=11-k*.6
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(x-(x2-x)/l*5,y-(y2-y)/l*5);c.lineTo(x+nx*h+(x2-x)/l*3,y+ny*h+(y2-y)/l*3);c.lineTo(x+(x2-x)/l*8,y+(y2-y)/l*8);c.closePath();c.fill();c.stroke()}
|
||||
const tail=()=>{c.beginPath();c.moveTo(...O[0]);c.bezierCurveTo(...O[1],...O[2],...tip);c.quadraticCurveTo(tip[0]+20,tip[1]-12,tip[0]+24,tip[1]+4);c.quadraticCurveTo(tip[0]+14,tip[1]+2,tip[0]+14,tip[1]+14);c.bezierCurveTo(30+8*sw,170,50,214,112,236);c.closePath()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';tail();c.fill();c.save();c.clip();c.lineWidth=2;for(let y=80;y<250;y+=9)for(let x=(y/9%2?4.5:0);x<120;x+=9)c.beginPath(),c.arc(x,y,4.5,0,PI),c.stroke();c.restore();tail();c.stroke()
|
||||
const H=f=>{c.save();c.translate(116,104);c.scale(.78,.78);c.translate(-128,-120);f();c.restore()}
|
||||
// twin buns and long side hair in screentone
|
||||
const hairTone=f=>{c.lineWidth=6;c.fillStyle='#fff';f();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(0,0,256,256);c.restore();f();c.stroke()}
|
||||
H(()=>{
|
||||
for(const d of[-1,1]){
|
||||
// curled dragon horns
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(128+d*34,50);c.bezierCurveTo(128+d*44,20,128+d*78,10,128+d*96,24);c.bezierCurveTo(128+d*80,22,128+d*62,30,128+d*56,58);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(const u of[.3,.5,.7])c.moveTo(128+d*(40+u*36),44-u*24),c.lineTo(128+d*(50+u*34),48-u*20);c.stroke()
|
||||
hairTone(()=>{c.beginPath();c.moveTo(128+d*40,80);c.bezierCurveTo(128+d*76,90,128+d*72,170,128+d*66,214);c.quadraticCurveTo(128+d*56,206,128+d*48,214);c.quadraticCurveTo(128+d*52,150,128+d*40,120);c.closePath()})
|
||||
}
|
||||
})
|
||||
// qipao with a high collar
|
||||
c.lineWidth=5;const body=()=>{c.beginPath();c.moveTo(100,170);c.quadraticCurveTo(64,178,54,252);c.lineTo(178,252);c.quadraticCurveTo(170,178,134,170);c.closePath()}
|
||||
c.fillStyle='#fff';body();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(40,186,160,70);c.restore();body();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(104,158);c.lineTo(104,178);c.quadraticCurveTo(117,184,130,178);c.lineTo(130,158);c.fill();c.lineWidth=4;c.stroke()
|
||||
c.beginPath();c.moveTo(117,182);c.quadraticCurveTo(136,194,150,214);c.stroke()
|
||||
c.lineWidth=3;for(const[x,y]of[[134,196],[144,206]])c.beginPath(),c.moveTo(x-6,y),c.lineTo(x+6,y),c.stroke(),c.beginPath(),c.arc(x+7,y,2.5,0,T),c.stroke()
|
||||
// smoke rings drifting up and right from her mouth
|
||||
const R=[]
|
||||
for(let k=0;k<3;k++){const p=(t+k/3)%1;if(p>.05)R.push([136+92*p,147-90*p**1.5,6+20*p,p])}
|
||||
// head
|
||||
const pf=max(0,1-((t*3)%1)/.3)
|
||||
H(()=>{
|
||||
c.lineWidth=6;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,140,-1,tn);eye(c,153,140,1,tn)
|
||||
c.lineWidth=2.5;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()
|
||||
// pursed lips, cheek puffs right after each ring leaves
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.ellipse(134,172,5+2*pf,5+pf,0,0,T);c.fill();c.stroke()
|
||||
if(pf>.3){c.lineWidth=3;c.beginPath();c.arc(170,166,8,-.9,.9);c.stroke()}
|
||||
// bangs and buns
|
||||
hairTone(()=>{c.beginPath();c.moveTo(72,140);c.bezierCurveTo(56,70,88,40,128,40);c.bezierCurveTo(168,40,200,70,184,140)
|
||||
c.quadraticCurveTo(178,112,170,104);c.quadraticCurveTo(160,110,154,118);c.quadraticCurveTo(150,100,140,94);c.quadraticCurveTo(132,108,124,114)
|
||||
c.quadraticCurveTo(120,100,108,94);c.quadraticCurveTo(102,108,94,114);c.quadraticCurveTo(90,104,84,104);c.quadraticCurveTo(76,118,72,140);c.closePath()})
|
||||
c.strokeStyle='#fff';c.lineWidth=4;for(const[a,e]of[[1.3,1.42],[1.5,1.6],[1.66,1.72]])c.beginPath(),c.arc(128,110,54,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
for(const d of[-1,1]){hairTone(()=>{c.beginPath();c.arc(128+d*46,52,20,0,T)});c.lineWidth=2.5;c.beginPath();c.arc(128+d*46,52,11,d<0?PI*1.1:PI*1.9,d<0?PI*1.6:PI*.4,d>0);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(128+d*30,66);c.lineTo(128+d*24,78);c.lineTo(128+d*36,74);c.closePath();c.fill();c.stroke()}
|
||||
})
|
||||
for(const[x,y,r,p]of R)ring(c,x,y,r,p)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// manga elf girl with wavy white hair: long ears twitch twice, droop as she blushes hard, then spring back up
|
||||
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)
|
||||
const eye=(c,x,y,d,tn)=>{
|
||||
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=tn;c.fillRect(x-12,y-14,24,32);c.fillStyle='#111';c.fillRect(x-12,y-14,24,12);c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke();c.beginPath();c.ellipse(x,y+3,7,10,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,4,6.5,0,0,T);c.fill()
|
||||
// star highlight
|
||||
c.fillStyle='#fff';c.save();c.translate(x-4,y-4);c.beginPath();for(let k=0;k<8;k++){const r=k%2?1.8:5.5,a=k*PI/4;c.lineTo(r*cos(a),r*sin(a))}c.fill();c.restore();c.beginPath();c.arc(x+5,y+9,2,0,T);c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-17,y-6);c.quadraticCurveTo(x,y-21,x+17,y-6);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const k of[0,1,2])c.moveTo(x+d*(13+k*2),y-9+k*4),c.lineTo(x+d*(21+k*2),y-13+k*5);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-8,y+19);c.lineTo(x+8,y+19);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat')
|
||||
const tw=.26*(max(0,sin((t-.04)/.08*PI))*(t>.04&&t<.12)+max(0,sin((t-.16)/.08*PI))*(t>.16&&t<.24))
|
||||
const dr=ease((t-.34)/.1)-ease((t-.72)/.08),sp=t>.8?.22*sin((t-.8)/.2*T*1.5)*(1-(t-.8)/.2):0
|
||||
const ang=-tw+.95*dr-sp,bl=ease((t-.32)/.08)*(1-ease((t-.76)/.08)),sh=bl>.5
|
||||
c.save();c.translate(128,118);c.scale(.82,.82);c.translate(-128,-120)
|
||||
// long wavy hair behind
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128,36);c.bezierCurveTo(56,36,46,110,58,170)
|
||||
for(const[x,y]of[[44,196],[62,216],[46,240],[78,252]])c.quadraticCurveTo(x-14,y-12,x,y)
|
||||
c.lineTo(178,252);for(const[x,y]of[[210,240],[194,216],[212,196],[198,170]])c.quadraticCurveTo(x+14,y-12,x,y)
|
||||
c.bezierCurveTo(210,110,200,36,128,36);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(const d of[-1,1])for(const[a,b,e,f]of[[62,120,52,180],[72,150,66,226],[60,200,54,234]])c.moveTo(128+d*(a-128),b),c.quadraticCurveTo(128+d*(a-138),(b+f)/2,128+d*(e-128),f);c.stroke()
|
||||
// ears
|
||||
for(const d of[-1,1]){
|
||||
c.save();c.translate(128+d*50,132);c.rotate(d*ang)
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(0,-14);c.quadraticCurveTo(d*40,-26,d*80,-40);c.quadraticCurveTo(d*50,-6,d*6,16);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(d*8,-4);c.quadraticCurveTo(d*36,-16,d*62,-30);c.stroke()
|
||||
if(bl>0){c.lineWidth=2;c.globalAlpha=1;for(let k=0;k<4*bl;k++)c.beginPath(),c.moveTo(d*(16+k*9),-4-k*4),c.lineTo(d*(22+k*9),4-k*4),c.stroke()}
|
||||
c.restore()
|
||||
}
|
||||
// tunic with a leafy collar
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,186);c.quadraticCurveTo(66,196,58,252);c.lineTo(198,252);c.quadraticCurveTo(190,196,156,186);c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(50,210,160,50);c.restore();c.stroke()
|
||||
c.lineWidth=4;for(const[x,a]of[[100,-.5],[116,-.2],[140,.2],[156,.5]])c.save(),c.translate(x,200),c.rotate(a),c.beginPath(),c.ellipse(0,0,9,16,0,0,T),c.fill(),c.stroke(),c.beginPath(),c.moveTo(0,-12),c.lineTo(0,12),c.lineWidth=2,c.stroke(),c.lineWidth=4,c.restore()
|
||||
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.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(sh){c.lineWidth=6;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*13,132),c.lineTo(128+d*35,140),c.lineTo(128+d*13,148),c.stroke()}
|
||||
else{eye(c,103,140,-1,tn);eye(c,153,140,1,tn)}
|
||||
// blush hatching spreads across the cheeks and nose
|
||||
const n=3+round6(bl)
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<n;k++){const x=128+d*(46-k*5);c.beginPath();c.moveTo(x-3,168);c.lineTo(x+3,158);c.stroke()}
|
||||
if(sh){c.lineWidth=3.5;c.fillStyle='#fff';c.beginPath();c.moveTo(116,172);for(let k=0;k<6;k++)c.lineTo(120+k*4,k%2?168:174);c.stroke()
|
||||
// steam puffs
|
||||
for(const[x,y,r]of[[182,60,7],[200,42,5.5]]){c.lineWidth=3;c.beginPath();c.arc(x-r,y-4*bl,r*.8,PI*.5,PI*1.5);c.arc(x,y-4*bl-r*.5,r,PI,0);c.arc(x+r,y-4*bl,r*.8,PI*1.5,PI*.5);c.closePath();c.fill();c.stroke()}}
|
||||
else{c.lineWidth=3.5;c.beginPath();c.arc(128,166,6,.2*PI,.8*PI);c.stroke()}
|
||||
// bangs, wavy white with strand lines
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(70,150);c.bezierCurveTo(52,76,86,40,128,40);c.bezierCurveTo(170,40,204,76,186,150)
|
||||
c.quadraticCurveTo(184,118,174,104);c.quadraticCurveTo(168,114,160,116);c.quadraticCurveTo(156,98,144,92);c.quadraticCurveTo(140,108,128,114);c.quadraticCurveTo(122,98,108,92)
|
||||
c.quadraticCurveTo(102,110,94,116);c.quadraticCurveTo(88,112,82,104);c.quadraticCurveTo(72,120,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(const[a,b,e,f]of[[100,54,88,90],[120,48,114,84],[146,50,154,84],[164,60,176,96]])c.moveTo(a,b),c.quadraticCurveTo((a+e)/2+6,(b+f)/2,e,f);c.stroke()
|
||||
// braided crown with a leaf circlet gem
|
||||
c.lineWidth=4;for(let k=0;k<7;k++){const a=PI*(1.18+k*.107);c.beginPath();c.ellipse(128+64*cos(a),112+64*sin(a),8,6,a+PI/2,0,T);c.fill();c.stroke()}
|
||||
c.save();c.translate(128,52);c.rotate(PI/4);c.beginPath();c.rect(-8,-8,16,16);c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(-8,-8,16,16);c.restore();c.stroke();c.restore()
|
||||
c.restore()
|
||||
}
|
||||
const round6=x=>Math.round(x*5)
|
||||
@@ -0,0 +1,42 @@
|
||||
// shoujo heroine with wavy screentone hair swoons backward, back of her hand to her forehead, as petals scatter
|
||||
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 dots=(col,r,s=6)=>{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 TONE=dots('#111',1.25)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u)
|
||||
const petal=(c,x,y,a,s)=>{c.save();c.translate(x,y);c.rotate(a);c.scale(s,s);c.beginPath();c.moveTo(0,9);c.bezierCurveTo(-9,3,-8,-8,-2,-9);c.lineTo(0,-6);c.lineTo(2,-9);c.bezierCurveTo(8,-8,9,3,0,9);c.restore();c.fill();c.stroke()}
|
||||
const rose=(c,x,y,r)=>{c.lineWidth=4;c.fillStyle='#fff';c.beginPath();for(let k=0;k<5;k++){const a=k*T/5-PI/2;c.moveTo(x+cos(a)*r*.4,y+sin(a)*r*.4);c.arc(x+cos(a)*r*.62,y+sin(a)*r*.62,r*.42,a-1.4,a+1.4)}c.fill();c.stroke()
|
||||
c.beginPath();c.arc(x,y,r*.45,0,T);c.fill();c.stroke();c.lineWidth=3;c.beginPath();c.arc(x+1,y+1,r*.25,-.5,PI*1.3);c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TONE,'repeat')
|
||||
const k=t<.4?ease(t/.4):t<.75?1:1-ease((t-.75)/.25),tilt=-.3*k+.02*sin(t*T*2)*(1-k),sw=k>.9
|
||||
rose(c,26,226,20);rose(c,230,222,22);rose(c,52,244,13);rose(c,206,246,12)
|
||||
c.save();c.translate(128,262);c.rotate(tilt);c.translate(-128,-262)
|
||||
const hair=()=>{c.beginPath();c.moveTo(128,38);c.bezierCurveTo(200,38,214,96,206,140);for(let j=0;j<4;j++)c.quadraticCurveTo(222-(j%2)*20,154+j*22,206-(j%2)*4,168+j*22);c.quadraticCurveTo(184,256,160,246);c.lineTo(96,246);c.quadraticCurveTo(72,256,50,256);for(let j=3;j>=0;j--)c.quadraticCurveTo(34+(j%2)*20,168+j*22,50+(j%2)*4,154+j*22);c.bezierCurveTo(42,96,56,38,128,38);c.closePath()}
|
||||
c.lineWidth=7;c.fillStyle='#fff';hair();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const d of[-1,1])for(const o of[14]){c.moveTo(128+d*(56+o),150);for(let j=0;j<4;j++)c.quadraticCurveTo(128+d*(70+o-(j%2)*14),130+j*28,128+d*(60+o),144+j*28)}c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,178);c.quadraticCurveTo(62,188,56,260);c.lineTo(200,260);c.quadraticCurveTo(194,188,156,178);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();for(let j=0;j<7;j++){const a=PI*(.15+.7*j/6),x=128-cos(a)*34,y=184+sin(a)*16;c.moveTo(x+6,y);c.arc(x,y,6,0,PI)}c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(128,208,7,9,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(126,205,2.5,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.rect(114,146,28,40);c.fill();c.beginPath();c.moveTo(114,146);c.lineTo(114,182);c.moveTo(142,146);c.lineTo(142,182);c.stroke()
|
||||
c.save();c.translate(128,106);c.rotate(-.12*k);c.scale(.9,.9);c.translate(-128,-128)
|
||||
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()
|
||||
for(const[x,d]of[[104,-1],[152,1]]){c.lineWidth=9;c.beginPath();c.moveTo(x-17,138);c.quadraticCurveTo(x,155,x+17,138);c.moveTo(x+d*17,138);c.lineTo(x+d*26,132);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();for(const j of[-8,0,8])c.moveTo(x+j,147-Math.abs(j)*.3),c.lineTo(x+j*1.5,154);c.stroke()}
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const j of[-7,-1,5])c.beginPath(),c.moveTo(128+d*44+j-3,170),c.lineTo(128+d*44+j+3,162),c.stroke()
|
||||
c.lineWidth=4.5;c.fillStyle='#111';c.beginPath();if(sw)c.ellipse(128,174,5,7,0,0,T),c.fill();else c.moveTo(122,172),c.quadraticCurveTo(128,176,134,172),c.stroke()
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(72,140);c.bezierCurveTo(56,70,88,38,128,38);c.bezierCurveTo(168,38,200,70,184,140)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(172,108,166,114);c.quadraticCurveTo(160,100,148,94);c.quadraticCurveTo(144,106,136,112)
|
||||
c.quadraticCurveTo(130,100,120,94);c.quadraticCurveTo(116,106,108,112);c.quadraticCurveTo(102,98,94,96);c.quadraticCurveTo(90,106,86,114);c.quadraticCurveTo(80,106,78,104);c.quadraticCurveTo(70,124,72,140);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const[x0,y0,x1,y1]of[[110,52,98,90],[132,48,128,88],[152,54,150,88],[92,64,82,100]])c.moveTo(x0,y0),c.quadraticCurveTo((x0+x1)/2+4,(y0+y1)/2,x1,y1);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;const fx=186,fy=74;c.beginPath();for(let j=0;j<5;j++){const a=j*T/5;c.moveTo(fx,fy);c.ellipse(fx+cos(a)*12,fy+sin(a)*12,11,8,a,0,T)}c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.arc(fx,fy,6,0,T);c.fill()
|
||||
c.restore()
|
||||
const hx=82+14*k,hy=284-220*k,ex=74-22*k,ey=244-104*k
|
||||
for(const[l,s]of[[22,'#111'],[13,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(86,202),c.lineTo(ex,ey),c.lineTo(hx,hy),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(hx+2,hy-4,13,9,-.5-.6*k,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const j of[-4,2,8])c.moveTo(hx-6+j,hy-10+j*.4),c.lineTo(hx+2+j,hy-6+j*.4);c.stroke()
|
||||
c.restore()
|
||||
if(t>=.38){const u=(t-.38)/.62;c.fillStyle='#fff';c.lineWidth=3.5
|
||||
for(let j=0;j<9;j++){const a=-PI*(.05+.9*j/8),r=40+120*u*(.7+.3*(j%3)/2),s=1.3*(1-u)+.2;petal(c,124+cos(a)*r*1.1,96+sin(a)*r*.75+50*u*u,j+u*T*(j%2?1:-1),s)}}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// chibi with ink-black braids sulking with her knees hugged, gloom lines on her face, a dark aura wavering up and mushrooms sprouting beside her
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,PI,min,max}=Math,T=2*PI
|
||||
export const frames=12,delay=100,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.1,0,T);x.fill();return t})()
|
||||
const shroom=(c,x,s,dot)=>{
|
||||
if(s<=0)return
|
||||
c.save();c.translate(x,236);c.scale(s,s);c.lineWidth=3.5;c.fillStyle='#fff'
|
||||
c.beginPath();c.roundRect(-5,-18,10,18,4);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(-15,-16);c.quadraticCurveTo(-14,-34,0,-34);c.quadraticCurveTo(14,-34,15,-16);c.closePath();c.fill()
|
||||
if(dot){c.save();c.clip();c.fillStyle=c.createPattern(DK,'repeat');c.fillRect(-16,-36,32,22);c.restore()}else{c.fillStyle='#111';for(const[a,b,r]of[[-6,-25,3],[5,-27,2.5],[8,-20,2]])c.beginPath(),c.arc(a,b,r,0,T),c.fill()}
|
||||
c.stroke();c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
c.lineWidth=3.5
|
||||
for(const x0 of[34,58,198,222]){c.beginPath();for(let y=40;y<=200;y+=6)c.lineTo(x0+5*sin(y*.09+T*t*2+x0),y);c.stroke()}
|
||||
c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.ellipse(128,238,104,12,0,0,T);c.fill();c.save();c.clip();c.fillStyle=c.createPattern(DK,'repeat');c.fillRect(20,224,216,30);c.restore();c.stroke()
|
||||
for(const[x,o,d]of[[40,0,1],[66,.3,0],[192,.55,1],[216,.8,0]]){const a=(t+o)%1;shroom(c,x,a<.18?a/.18:a>.88?(1-a)/.12:1+.04*sin(T*a*3),d)}
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(76,234);c.bezierCurveTo(70,186,86,160,128,158);c.bezierCurveTo(170,160,186,186,180,234);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(128,164);c.lineTo(128,230);c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*20,238,16,7,0,0,T),c.fill()
|
||||
c.save();c.translate(128,124);c.scale(.82,.82);c.translate(-128,-124)
|
||||
for(const d of[-1,1]){c.lineWidth=5;for(let k=0;k<5;k++){const x=128+d*(60+k*1.5),y=128+k*20;c.fillStyle='#111';c.beginPath();c.ellipse(x,y,11-k*.6,12,d*.2,0,T);c.fill();c.strokeStyle='#fff';c.lineWidth=2;c.beginPath();c.arc(x,y-3,8-k*.5,PI*.2,PI*.8);c.stroke();c.strokeStyle='#111'}
|
||||
c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.roundRect(128+d*67-7,224,14,6,2);c.fill();c.stroke()}
|
||||
c.lineWidth=6;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=142
|
||||
c.save();c.beginPath();c.ellipse(x,y,13,15,0,0,T);c.clip();c.fillStyle='#fff';c.fill();c.fillStyle='#111';c.beginPath();c.ellipse(x,y+6,9,11,0,0,T);c.fill();c.fillStyle=c.createPattern(DK,'repeat');c.fillRect(x-14,y-16,28,13);c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-15,y-3);c.lineTo(x+15,y-3);c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(x-9,y+18);c.quadraticCurveTo(x,y+21,x+9,y+18);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=2.2;for(let x=82;x<176;x+=6){c.beginPath();c.moveTo(x,80);c.lineTo(x,112+((x*7)%4)*4);c.stroke()}c.restore()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(118,172);c.quadraticCurveTo(121,169,124,172);c.quadraticCurveTo(127,175,130,172);c.quadraticCurveTo(133,169,137,172);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(70,150);c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,150)
|
||||
c.quadraticCurveTo(184,118,178,104);c.quadraticCurveTo(166,108,160,118);c.quadraticCurveTo(152,104,140,100);c.quadraticCurveTo(134,112,128,118);c.quadraticCurveTo(120,104,110,100);c.quadraticCurveTo(100,112,96,118);c.quadraticCurveTo(88,106,78,104);c.quadraticCurveTo(72,118,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.56],[1.64,1.76]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
for(const d of[-1,1]){c.lineWidth=24;c.beginPath();c.moveTo(128+d*52,176);c.quadraticCurveTo(128+d*46,208,128+d*12,206);c.stroke();c.lineWidth=14;c.strokeStyle='#fff';c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4.5;c.beginPath();c.ellipse(128+d*8,204,9,8,0,0,T);c.fill();c.stroke()}
|
||||
if(t>=.5&&t<.85){const p=(t-.5)/.35,x=150+p*30,y=166-p*24,r=4+p*5;c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.arc(x,y,r,0,T);c.arc(x+r*.9,y-r*.5,r*.7,0,T);c.fill();c.beginPath();c.arc(x,y,r,0,T);c.stroke();c.beginPath();c.arc(x+r*.9,y-r*.5,r*.7,-PI*.9,PI*.6);c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// eager girl at her desk shooting her hand up and waving it, sparkly star eyes, bouncing in her seat
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=70,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 spark=(c,x,y,r)=>{c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,rr=k%2?r*.3:r;c.lineTo(x+rr*cos(a),y+rr*sin(a))}c.closePath();c.fill()}
|
||||
const toned=(c,dot)=>{c.fillStyle='#fff';c.fill();c.fillStyle=dot;c.fill();c.stroke()}
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),b=-6*abs(sin(t*T*2)),w=.35*sin(t*T*2),tw=1+.3*sin(t*T*4)
|
||||
c.save();c.translate(18,b)
|
||||
// hair behind, screentone bob
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(198,34,212,100,204,176);c.quadraticCurveTo(202,184,192,184);c.lineTo(64,184);c.quadraticCurveTo(54,184,52,176);c.bezierCurveTo(44,100,58,34,128,34);toned(c,dot)
|
||||
// black blazer, shirt and striped tie
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(66,196,58,236);c.lineTo(198,236);c.quadraticCurveTo(190,196,156,188);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(108,189);c.lineTo(128,226);c.lineTo(148,189);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(122,200);c.lineTo(134,200);c.lineTo(138,226);c.lineTo(118,226);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.lineWidth=3;for(const y of[204,212,220])c.beginPath(),c.moveTo(114,y+4),c.lineTo(142,y-4),c.stroke();c.restore()
|
||||
c.beginPath();c.moveTo(118,190);c.lineTo(128,200);c.lineTo(138,190);c.stroke()
|
||||
// raised arm, waving from the wrist
|
||||
c.save();c.translate(90,204);c.rotate(-.5+.06*sin(t*T*2))
|
||||
c.lineCap='butt';for(const[l,s]of[[32,'#fff'],[25,'#111']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(0,0),c.lineTo(0,-104),c.stroke()
|
||||
c.lineCap='round';c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.rect(-12,-118,24,14);c.fill();c.stroke()
|
||||
c.translate(0,-118);c.rotate(w)
|
||||
for(const[a,l]of[[-.5,22],[-.17,27],[.17,27],[.5,22],[1.25,18]]){const x=sin(a)*(l+8),y=-14-cos(a)*(l+8);for(const[lw,st]of[[12,'#111'],[5,'#fff']])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(0,-14),c.lineTo(x,y),c.stroke()}
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(0,-12,13,14,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=2;c.beginPath();c.arc(1,-8,7,1.1*PI,1.7*PI);c.stroke()
|
||||
c.restore()
|
||||
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()
|
||||
// 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()
|
||||
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';spark(c,x-3,136,8*tw);c.beginPath();c.arc(x+4,149,2.2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-d*12,130);c.quadraticCurveTo(x,119,x+d*15,131);c.lineTo(x+d*21,125);c.stroke()
|
||||
}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<4;k++)c.beginPath(),c.moveTo(128+d*42+(k-1.5)*5-3,170),c.lineTo(128+d*42+(k-1.5)*5+3,161),c.stroke()
|
||||
c.lineWidth=3.5;c.fillStyle='#111';c.beginPath();c.moveTo(114,164);c.quadraticCurveTo(128,190,142,164);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle='#fff';c.beginPath();c.ellipse(128,180,8,5,0,0,T);c.fill();c.restore()
|
||||
// side-swept bangs, shine band, crossed pins
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(66,178)
|
||||
c.bezierCurveTo(48,74,88,36,128,36);c.bezierCurveTo(168,36,208,74,190,178)
|
||||
c.quadraticCurveTo(186,130,176,104);c.quadraticCurveTo(170,108,166,116);c.quadraticCurveTo(160,96,148,90);c.quadraticCurveTo(142,104,132,112)
|
||||
c.quadraticCurveTo(126,96,112,92);c.quadraticCurveTo(108,104,96,114);c.quadraticCurveTo(92,104,82,104);c.quadraticCurveTo(70,140,66,178);c.closePath();toned(c,dot)
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<=30;k++){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?52:48)*cos(a),104+(k%2?52:48)*sin(a))}for(let k=30;k>=0;k--){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?44:40)*cos(a),104+(k%2?44:40)*sin(a))}c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(160,74);c.lineTo(182,88);c.moveTo(164,90);c.lineTo(180,70);c.stroke()
|
||||
c.restore()
|
||||
// "!" marks and sparkles by the hand, they pop on each bounce
|
||||
const p=.75+.35*abs(sin(t*T*2))
|
||||
c.fillStyle='#111';c.lineWidth=3
|
||||
for(const[x,y,a]of[[20,122,-.5],[238,62,.35]]){c.save();c.translate(x,y+b);c.rotate(a);c.scale(p,p);c.beginPath();c.moveTo(-5,-18);c.lineTo(5,-18);c.lineTo(2,4);c.lineTo(-2,4);c.closePath();c.fill();c.beginPath();c.arc(0,11,3.6,0,T);c.fill();c.restore()}
|
||||
c.fillStyle='#fff';c.strokeStyle='#111';c.lineWidth=3
|
||||
for(const[x,y,o]of[[16,70,0],[96,92,.5]]){const s=9*abs(sin(PI*(t*2+o)));if(s>2){c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,rr=k%2?s*.3:s;c.lineTo(x+rr*cos(a),y+b+rr*sin(a))}c.closePath();c.fill();c.stroke()}}
|
||||
// desk in front, it doesn't bounce
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(26,214);c.lineTo(230,214);c.lineTo(242,230);c.lineTo(14,230);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const[x,y,l]of[[50,220,40],[120,224,60],[170,219,36]])c.beginPath(),c.moveTo(x,y),c.quadraticCurveTo(x+l/2,y-3,x+l,y),c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.rect(18,230,220,16);toned(c,dot)
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(186,214,14,9,0,PI,0);c.fill();c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// tsukkomi girl with white twin tails winds up a paper fan, trembling with an anger mark, then smacks it down into a burst
|
||||
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 dots=(col,r)=>{const k=createCanvas(6,6),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[3,3],[0,0],[6,0],[0,6],[6,6]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const TONE=dots('#111',1.3),LITE=dots('#fff',1.3)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u)
|
||||
const eye=(c,x,y,d,L)=>{
|
||||
c.save();c.beginPath();c.moveTo(x-16,y-6+d*3);c.lineTo(x+16,y-6-d*3);c.lineTo(x+16,y+20);c.lineTo(x-16,y+20);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,12,13,0,0,T);c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+3,y+2,10,13,0,0,T);c.fill();c.fillStyle=L;c.fillRect(x-6,y+4,20,12)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+1,y-2,3.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-14,y-6+d*3);c.lineTo(x+14,y-6-d*3);c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(x-12,y-21+d*5);c.lineTo(x+12,y-21-d*5);c.stroke()
|
||||
}
|
||||
const fan=c=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(-7,16);c.lineTo(7,16);c.lineTo(8,-18);c.lineTo(20,-74);c.lineTo(-20,-74);c.lineTo(-8,-18);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.fillRect(-8,-2,16,10)
|
||||
c.lineWidth=2.5;c.beginPath();for(let k=-3;k<=3;k++)c.moveTo(k*1.2,-18),c.lineTo(k*6,-74);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(-20,-74);c.lineTo(20,-74);c.stroke()
|
||||
}
|
||||
const burst=(c,x,y,R,n=12)=>{c.beginPath();for(let k=0;k<n*2;k++){const r=k%2?R*.5:R*(k%4?1:.8),a=k*PI/n;c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.closePath()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TONE,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const a=t<.4?-1.25:t<.5?-1.25+2.6*ease((t-.4)/.1):t<.75?1.35:1.35-2.6*ease((t-.75)/.25),k=(a+1.25)/2.6,hit=t>=.5&&t<.75
|
||||
const sh=t<.4?1.5*sin(t*T*10):0,hx=170+sh-22*k,hy=78+50*k,front=1
|
||||
const arm=()=>{
|
||||
for(const[l,s]of[[26,'#111'],[15,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(116,200),c.quadraticCurveTo(150+10*k,150-10*k,hx,hy+8),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.save();c.translate(hx,hy);c.rotate(a);fan(c);c.restore()
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy+4,11,10,a,0,T);c.fill();c.stroke()
|
||||
}
|
||||
if(t>=.4&&t<.5){c.lineWidth=3;for(const r of[52,64,76])c.beginPath(),c.arc(hx,hy,r,-PI/2-1.25,-PI/2+a-.3),c.stroke()}
|
||||
if(hit){
|
||||
const p=(t-.5)/.25,R=22*(1.1-.4*p),bx=hx+70*sin(a),by=hy-70*cos(a)
|
||||
c.lineWidth=4;for(let j=0;j<8;j++){const b=j*PI/4+.4;c.beginPath();c.moveTo(bx+cos(b)*(R+5),by+sin(b)*(R+5));c.lineTo(bx+cos(b)*(R+13-5*p),by+sin(b)*(R+13-5*p));c.stroke()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';burst(c,bx,by,R);c.fill();c.stroke();c.fillStyle=tn;burst(c,bx,by,R*.55,8);c.fill()
|
||||
}
|
||||
c.save();c.translate(80,128);c.scale(.86,.86);c.translate(-128,-128)
|
||||
c.lineWidth=6;c.fillStyle='#fff'
|
||||
for(const d of[-1,1]){const w=d*4*sin(t*T+d);c.beginPath();c.moveTo(128+d*54,64);c.bezierCurveTo(128+d*84,60,128+d*(84+w),140,128+d*(72+w),218);c.quadraticCurveTo(128+d*(60+w),206,128+d*58,160);c.quadraticCurveTo(128+d*58,110,128+d*50,80);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128+d*64,84);c.quadraticCurveTo(128+d*(76+w),150,128+d*(70+w),204);c.moveTo(128+d*72,80);c.quadraticCurveTo(128+d*(80+w),130,128+d*(78+w),170);c.stroke();c.lineWidth=6}
|
||||
c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,276);c.lineTo(202,276);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=tn;c.beginPath();c.moveTo(102,190);c.quadraticCurveTo(72,198,62,234);c.lineTo(104,226);c.lineTo(128,240);c.lineTo(152,226);c.lineTo(194,234);c.quadraticCurveTo(184,198,154,190);c.lineTo(128,226);c.closePath();c.fill();c.lineWidth=5;c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(70,228);c.lineTo(104,220);c.lineTo(128,233);c.lineTo(152,220);c.lineTo(186,228);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(128,226);c.lineTo(110,252);c.lineTo(122,258);c.lineTo(128,236);c.lineTo(134,258);c.lineTo(146,252);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(116,168);c.lineTo(116,194);c.lineTo(128,224);c.lineTo(140,194);c.lineTo(140,168);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,192);c.lineTo(128,224);c.lineTo(140,192);c.lineTo(140,168);c.stroke()
|
||||
c.restore()
|
||||
c.save();c.translate(80,128);c.scale(.86,.86);c.translate(-128,-128)
|
||||
c.lineWidth=6;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,106,140,-1,lt);eye(c,154,140,1,lt)
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const j of[-6,0,6])c.beginPath(),c.moveTo(128+d*42+j-3,166),c.lineTo(128+d*42+j+3,158),c.stroke()
|
||||
const m=hit?1:.6+.1*sin(t*T*10);c.lineWidth=4;c.fillStyle='#111';c.beginPath();c.moveTo(122,163);c.quadraticCurveTo(136,161,146,163);c.quadraticCurveTo(142,163+16*m,132,163+16*m);c.quadraticCurveTo(122,163+12*m,122,163);c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=lt;c.beginPath();c.ellipse(134,166+16*m,10,6,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(126,163,14,4);c.fill()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(70,150)
|
||||
c.bezierCurveTo(54,74,88,40,128,40);c.bezierCurveTo(168,40,202,74,186,150)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(172,108,166,114);c.quadraticCurveTo(160,100,148,94);c.quadraticCurveTo(144,106,136,112)
|
||||
c.quadraticCurveTo(130,100,120,94);c.quadraticCurveTo(116,106,108,112);c.quadraticCurveTo(102,98,94,96);c.quadraticCurveTo(90,106,86,114)
|
||||
c.quadraticCurveTo(80,106,78,104);c.quadraticCurveTo(70,124,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(const[x0,y0,x1,y1]of[[110,52,98,90],[132,48,128,88],[152,54,150,88],[92,64,82,100],[170,70,172,100]])c.moveTo(x0,y0),c.quadraticCurveTo((x0+x1)/2+4,(y0+y1)/2,x1,y1);c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*54,70,8,13,d*.4,0,T),c.fill()
|
||||
const v=1+.18*sin(t*T*4)*(t<.5?1:0)+(hit?.25:0);c.save();c.translate(80,62);c.scale(v,v);c.lineWidth=4.5
|
||||
for(let j=0;j<4;j++){c.rotate(PI/2);c.beginPath();c.moveTo(3,-13);c.quadraticCurveTo(3,-3,13,-3);c.stroke()}
|
||||
c.restore()
|
||||
c.restore()
|
||||
if(front)arm()
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// chibi karate girl with hatched odango buns bounces in her stance, then snaps a high kick through a screentone swoosh
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=70,rim=4
|
||||
const dots=(col,r)=>{const k=createCanvas(6,6),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[3,3],[0,0],[6,0],[0,6],[6,6]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const TONE=dots('#111',1.4),LITE=dots('#fff',1.2)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u)
|
||||
const hatch=(c,x0,y0,x1,y1)=>{c.lineWidth=3.5;c.beginPath();for(let x=x0-(y1-y0);x<x1;x+=9)c.moveTo(x,y1),c.lineTo(x+(y1-y0),y0);c.stroke()}
|
||||
const limb=(c,pts,w)=>{for(const[l,s]of[[w+8,'#111'],[w,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(...pts[0]);for(const p of pts.slice(1))c.lineTo(...p);c.stroke()}c.strokeStyle='#111'}
|
||||
const eye=(c,x,y,d,lt,sh)=>{
|
||||
c.save();c.beginPath();c.moveTo(x-17,y-6+d*3);c.lineTo(x+17,y-6-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+3,y+4,10,13,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-8,y+7,22,12)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x,y,4,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-15,y-6+d*3);c.lineTo(x+15,y-6-d*3);c.lineTo(x+d*21,y-12);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TONE,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const k=t<.3?0:t<.42?ease((t-.3)/.12):t<.66?1:1-ease((t-.66)/.34),kick=t>=.3&&t<.66,b=t<.3?3*abs(sin(t*T*5/1.5)):0
|
||||
const L=-.22*k,hx=112,hy=190-b,th0=PI/2-.45,th=th0-k*(th0+1.05),bend=.75*(1-k)+1.5*sin(PI*k)*(t<.5?1:.6),sa=th+bend
|
||||
const kn=[hx+8+27*cos(th),hy+2+27*sin(th)],ft=[kn[0]+28*cos(sa),kn[1]+28*sin(sa)]
|
||||
c.fillStyle=tn;c.beginPath();c.ellipse(106,240,44,7,0,0,T);c.fill()
|
||||
if(k>.15){const e=th,R0=40,R1=62,s=.95;c.fillStyle=tn;c.beginPath();c.arc(hx+8,hy+2,R1,s,e,true);c.arc(hx+8,hy+2,R0,e+.3,s,false);c.closePath();c.fill()
|
||||
c.lineWidth=3;for(const r of[R0,(R0+R1)/2,R1])c.beginPath(),c.arc(hx+8,hy+2,r,s-.15,e+(r==R0?.3:0),true),c.stroke()}
|
||||
limb(c,[[hx-8,hy],[hx-18,hy+24],[hx-16,234]],22)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(hx-12,236,16,8,0,0,T);c.fill();c.stroke()
|
||||
c.save();c.translate(hx,hy);c.rotate(L)
|
||||
const af=k,ar=[[-22,-40],[-36-12*af,-24+6*af],[-20-30*af,-52+38*af]],fr=[[22,-40],[38,-28],[26+6*af,-56+4*af]]
|
||||
limb(c,ar,15);c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.arc(...ar[2],10,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(-8,-50);c.lineTo(-28,-44);c.quadraticCurveTo(-34,-16,-31,12);c.lineTo(31,12);c.quadraticCurveTo(34,-16,28,-44);c.lineTo(8,-50);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(-10,-50);c.lineTo(12,-12);c.moveTo(10,-50);c.lineTo(-4,-26);c.stroke()
|
||||
c.fillStyle='#111';c.fillRect(-32,-12,64,11);const w=kick?5*sin(t*T*8):1.5*sin(t*T*2);c.beginPath();c.moveTo(6,-6);c.lineTo(2+w,18);c.lineTo(10+w,19);c.lineTo(11,-4);c.lineTo(19+w,16);c.lineTo(26+w,12);c.lineTo(15,-6);c.fill()
|
||||
c.beginPath();c.ellipse(11,-6,7,6,0,0,T);c.fill()
|
||||
c.restore()
|
||||
limb(c,[[hx+8,hy+2],kn,ft],22)
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.save();c.translate(...ft);c.rotate(sa-PI/2);c.beginPath();c.ellipse(3,5,9,15,0,0,T);c.fill();c.stroke();c.restore()
|
||||
if(t>=.42&&t<.58){c.lineWidth=4;const p=(t-.42)/.16;for(let j=0;j<6;j++){const a=j*PI/3+.3;c.beginPath();c.moveTo(ft[0]+cos(a)*(18+6*p),ft[1]+sin(a)*(18+6*p));c.lineTo(ft[0]+cos(a)*(28+6*p),ft[1]+sin(a)*(28+6*p));c.stroke()}}
|
||||
c.save();c.translate(hx,hy);c.rotate(L);c.translate(-2,-100);c.scale(.8,.8);c.translate(-128,-120)
|
||||
c.lineWidth=7;c.fillStyle='#fff'
|
||||
for(const d of[-1,1]){c.beginPath();c.arc(128+d*46,48,26,0,T);c.fill();c.save();c.clip();hatch(c,90+d*46,20,166+d*46,76);c.restore();c.lineWidth=7;c.beginPath();c.arc(128+d*46,48,26,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(128+d*38,66,12,7,d*.5,0,T);c.fill();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,138,-1,lt);eye(c,154,138,1,lt)
|
||||
c.lineWidth=7;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*12,112),c.lineTo(128+d*40,118-8),c.stroke()
|
||||
c.lineWidth=3;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.fillStyle='#111';c.beginPath()
|
||||
if(kick)c.moveTo(114,162),c.lineTo(144,162),c.quadraticCurveTo(140,184,128,184),c.quadraticCurveTo(116,184,114,162),c.fill(),c.stroke()
|
||||
else c.moveTo(118,166),c.lineTo(138,164),c.stroke()
|
||||
const hair=()=>{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,118,178,104);c.quadraticCurveTo(170,100,160,110);c.quadraticCurveTo(152,96,140,94);c.quadraticCurveTo(134,108,122,112)
|
||||
c.quadraticCurveTo(116,98,104,94);c.quadraticCurveTo(96,106,88,112);c.quadraticCurveTo(80,104,78,104);c.quadraticCurveTo(70,124,70,150);c.closePath()}
|
||||
c.lineWidth=7;c.fillStyle='#fff';hair();c.fill();c.save();c.clip();hatch(c,40,30,220,160);c.restore();c.lineWidth=7;hair();c.stroke()
|
||||
c.restore()
|
||||
c.save();c.translate(hx,hy);c.rotate(L)
|
||||
limb(c,fr,15);c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.arc(...fr[2],10,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// chibi kendo girl in full armor raises her shinai overhead, then stomps and strikes with a shout and an impact flash
|
||||
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 dots=(col,r,s=6)=>{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 TONE=dots('#111',1.9),SOFT=dots('#111',1.2),LITE=dots('#fff',1.2)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u)
|
||||
const burst=(c,x,y,R,n)=>{c.beginPath();for(let k=0;k<n*2;k++){const r=k%2?R*.5:R,a=k*PI/n;c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.closePath()}
|
||||
const shinai=(c,tn)=>{
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.roundRect(-16,-6,44,12,5);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(30,-5);c.lineTo(84,-4);c.lineTo(84,4);c.lineTo(30,5);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(29,0,3.5,9,0,0,T);c.fill();c.fillRect(80,-5,8,10)
|
||||
c.lineWidth=2;c.beginPath();c.moveTo(48,-4);c.lineTo(48,4);c.moveTo(64,-3);c.lineTo(64,3);c.stroke()
|
||||
}
|
||||
const kote=(c,x,y,a,tn)=>{c.save();c.translate(x,y);c.rotate(a);c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.ellipse(0,0,14,12,0,0,T);c.fill();c.fillStyle=tn;c.fill();c.stroke();c.restore()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TONE,'repeat'),sf=c.createPattern(SOFT,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const k=t<.375?0:t<.4375?.5:t<.6875?1:1-ease((t-.6875)/.3125),hit=t>=.4375&&t<.6875,tr=t<.35?1.2*sin(t*T*8):0
|
||||
const a=-3.24+3.14*k**.5,gx=118+20*k+tr,gy=max(24+104*k**.5,12-86*sin(a)),lean=.12*k
|
||||
c.fillStyle=sf;c.beginPath();c.ellipse(118,240,62,8,0,0,T);c.fill()
|
||||
if(hit){c.lineWidth=4;for(const x of[60,72,166,178])c.beginPath(),c.moveTo(x,236),c.lineTo(x+(x<120?-8:8),224),c.stroke()}
|
||||
const bx=118,by=150
|
||||
c.save();c.translate(bx,240);c.rotate(lean*.5);c.translate(-bx,-240)
|
||||
c.lineWidth=5;c.fillStyle='#fff';const st=hit?10:0
|
||||
c.beginPath();c.ellipse(96,236,14,6,0,0,T);c.fill();c.stroke();c.beginPath();c.ellipse(146+st,236,14,6,0,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=6;c.fillStyle=tn;c.beginPath();c.moveTo(90,170);c.lineTo(74,234);c.lineTo(118,234);c.lineTo(120,206);c.lineTo(124,234);c.lineTo(162+st,234);c.lineTo(146,170);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.strokeStyle='#fff';c.beginPath();for(const x of[94,106,134,146])c.moveTo(x,180),c.lineTo(x+(x<120?-6:6),228);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
c.save();c.translate(bx,by+40);c.rotate(lean);c.translate(-bx,-by-40)
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(84,166);c.lineTo(80,198);c.lineTo(156,198);c.lineTo(152,166);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();for(const x of[99,118,137])c.moveTo(x,170),c.lineTo(x+(x-118)*.08,198);c.stroke()
|
||||
c.fillStyle=tn;for(const x of[86,104,123,141])c.fillRect(x,174,10,20)
|
||||
c.lineWidth=6;c.fillStyle=tn;c.beginPath();c.moveTo(92,122);c.quadraticCurveTo(76,126,78,150);c.lineTo(80,170);c.lineTo(156,170);c.lineTo(158,150);c.quadraticCurveTo(160,126,144,122);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(84,132);c.quadraticCurveTo(118,120,152,132);c.lineTo(154,166);c.quadraticCurveTo(118,174,82,166);c.closePath();c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(92,140);c.quadraticCurveTo(110,134,124,136);c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(86,150);c.quadraticCurveTo(118,142,150,150);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
const sh=[[92+4*k,130],[144+4*k,130]],g1=[gx+cos(a)*-6,gy+sin(a)*-6],g2=[gx+cos(a)*14,gy+sin(a)*14]
|
||||
const arm=(s,g)=>{for(const[l,st]of[[22,'#111'],[14,tn]])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(...s),c.quadraticCurveTo((s[0]+g[0])/2+(k<.5?-6:0),(s[1]+g[1])/2+8,...g),c.stroke();c.strokeStyle='#111'}
|
||||
const fr=gy>110
|
||||
if(!fr){arm(sh[0],g1);arm(sh[1],g2);if(gy>40)kote(c,...g1,a,tn),kote(c,...g2,a,tn)}
|
||||
c.save();c.translate(100+6*k,82+4*k);c.rotate(lean*.6);c.scale(.62,.62);c.translate(-128,-128)
|
||||
c.lineWidth=8;c.fillStyle=tn;c.beginPath();c.moveTo(128,30);c.bezierCurveTo(206,30,214,110,214,160);c.lineTo(226,200);c.quadraticCurveTo(196,212,178,200);c.lineTo(176,170);c.lineTo(80,170);c.lineTo(78,200);c.quadraticCurveTo(60,212,30,200);c.lineTo(42,160);c.bezierCurveTo(42,110,50,30,128,30);c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.setLineDash([6,6]);c.beginPath();for(const y of[60,80])c.moveTo(70,y+20),c.quadraticCurveTo(128,y-20,186,y+20);for(const x of[54,202])c.moveTo(x,172),c.lineTo(x+(x<128?-4:4),198);c.stroke();c.setLineDash([]);c.strokeStyle='#111'
|
||||
c.lineWidth=7;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,72,160,64,128,64);c.bezierCurveTo(96,64,74,72,74,96);c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=sf;c.fillRect(70,60,120,40);c.restore()
|
||||
for(const[x,d]of[[106,-1],[154,1]]){const y=136
|
||||
c.save();c.beginPath();c.moveTo(x-16,y-4+d*4);c.lineTo(x+16,y-4-d*4);c.lineTo(x+16,y+20);c.lineTo(x-16,y+20);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,14,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+6,20,10)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+3,y-1,3.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-15,y-4+d*4);c.lineTo(x+15,y-4-d*4);c.lineTo(x+d*20,y-8);c.stroke()}
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath();if(hit)c.ellipse(132,170,10,9,0,0,T),c.fill();else c.moveTo(124,170),c.lineTo(140,168),c.stroke()
|
||||
c.lineWidth=8;c.strokeStyle='#111';c.beginPath();c.roundRect(68,92,120,100,30);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(128,94);c.lineTo(128,190);for(const y of[114,160,176])c.moveTo(72,y),c.quadraticCurveTo(128,y+6,184,y);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle=tn;c.beginPath();c.moveTo(96,192);c.lineTo(160,192);c.lineTo(154,226);c.lineTo(102,226);c.closePath();c.fill();c.stroke()
|
||||
const fl=hit?8*sin(t*T*8):3*sin(t*T*2);c.lineWidth=5;c.beginPath();c.moveTo(70,90);c.quadraticCurveTo(40,100,22,120+fl);c.moveTo(70,96);c.quadraticCurveTo(46,112,34,140+fl);c.strokeStyle='#fff';c.lineWidth=9;c.stroke();c.strokeStyle='#111';c.lineWidth=3;c.stroke()
|
||||
c.restore()
|
||||
c.save();c.translate(gx,gy);c.rotate(a);shinai(c,tn);c.restore()
|
||||
if(fr){arm(sh[0],g1);arm(sh[1],g2)}
|
||||
if(fr||gy<=40)kote(c,...g1,a,tn),kote(c,...g2,a,tn)
|
||||
if(k==.5){c.lineWidth=3;for(const r of[56,68,80])c.beginPath(),c.arc(gx,gy,r,-1.0,-.2),c.stroke()}
|
||||
if(hit){const p=(t-.4375)/.25,tx=gx+cos(a)*88,ty=gy+sin(a)*88,R=20*(1.15-.45*p);c.lineWidth=4;c.fillStyle='#fff';burst(c,tx,ty,R,10);c.fill();c.stroke()
|
||||
for(let j=0;j<7;j++){const b=j*T/7+.3;c.beginPath();c.moveTo(tx+cos(b)*(R+4),ty+sin(b)*(R+4));c.lineTo(tx+cos(b)*(R+11),ty+sin(b)*(R+11));c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// manga knight girl raising her sword to her face in salute with a glint, then sweeping it down; cape and helm plume blowing
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,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()
|
||||
const ease=x=>x<0?0:x>1?1:x*x*(3-2*x)
|
||||
const eye=(c,x,y,d,tn)=>{
|
||||
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,14,0,0,T);c.clip();c.fillStyle=tn;c.fillRect(x-12,y-12,24,30);c.fillStyle='#111';c.fillRect(x-12,y-12,24,10);c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11,14,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,4.5,6.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-4,y-3,3.5,4.5,-.4,0,T);c.fill();c.beginPath();c.arc(x+4,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-17,y-4);c.quadraticCurveTo(x,y-15,x+17,y-4);c.moveTo(x+d*16,y-5);c.lineTo(x+d*22,y-9);c.stroke()
|
||||
// determined brows
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(x-d*14,y-20);c.lineTo(x+d*12,y-25);c.stroke()
|
||||
}
|
||||
const star=(c,x,y,s)=>{c.beginPath();for(let k=0;k<8;k++){const r=k%2?s*.22:s,a=k*PI/4;c.lineTo(x+r*cos(a),y+r*sin(a))}c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat'),w=sin(t*T*2),s=ease((t-.05)/.15)-ease((t-.55)/.15)
|
||||
const hx=150-26*s,hy=206-48*s,a=-.75-(PI/2-.75)*s
|
||||
// cape behind, ink black, blowing out to the left
|
||||
const cp=()=>{c.beginPath();c.moveTo(90,118);c.quadraticCurveTo(60,170,34+6*w,236);c.quadraticCurveTo(50,230,62+4*w,246);c.quadraticCurveTo(76,236,92,250);c.lineTo(168,250);c.quadraticCurveTo(170,170,150,118);c.closePath()}
|
||||
c.lineWidth=5;c.fillStyle='#111';cp();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(80,150);c.quadraticCurveTo(62,196,48+5*w,232);c.stroke();c.strokeStyle='#111'
|
||||
// screentone ponytail streaming left
|
||||
const pt=()=>{c.beginPath();c.moveTo(92,52);c.bezierCurveTo(60,48,30,70,16+4*w,96+6*w);c.quadraticCurveTo(40,88,46+3*w,104);c.quadraticCurveTo(58,84,88,80);c.closePath()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';pt();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(0,40,100,80);c.restore();pt();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(84,64);c.quadraticCurveTo(56,64,30+4*w,90+5*w);c.moveTo(84,74);c.quadraticCurveTo(62,78,46+3*w,96);c.stroke()
|
||||
// breastplate and pauldrons
|
||||
const bp=()=>{c.beginPath();c.moveTo(96,124);c.quadraticCurveTo(84,170,92,232);c.lineTo(150,232);c.quadraticCurveTo(158,170,144,124);c.closePath()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';bp();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(132,120,30,120);c.restore();bp();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(120,150);c.lineTo(120,190);c.moveTo(106,166);c.lineTo(134,166);c.stroke()
|
||||
c.beginPath();c.moveTo(92,206);c.quadraticCurveTo(120,214,150,206);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;for(const[x,d]of[[94,-1],[146,1]]){c.beginPath();c.ellipse(x,130,20,14,d*.3,PI,0);c.lineTo(x+d*16,142);c.quadraticCurveTo(x,148,x-d*16,140);c.closePath();c.fill();c.stroke();c.beginPath();c.ellipse(x,134,13,7,d*.3,PI,0);c.stroke()}
|
||||
const arms=()=>{
|
||||
// arms out to the gauntlets on the hilt
|
||||
for(const sx of[96,146])for(const[l,st]of[[18,'#111'],[10,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(sx,140),c.quadraticCurveTo((sx+hx)/2+(sx<120?-10:14),(140+hy)/2+16,hx,hy),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// sword
|
||||
c.save();c.translate(hx,hy);c.rotate(a)
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(16,-6);c.lineTo(98,-5);c.lineTo(110,0);c.lineTo(98,5);c.lineTo(16,6);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;c.beginPath();c.moveTo(20,0);c.lineTo(94,0);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.rect(10,-18,7,36);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.rect(-16,-4,26,8);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.arc(-20,0,6,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
for(const o of[-6,6]){c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.ellipse(hx+o*cos(a),hy+o*sin(a),9,9,0,0,T);c.fill();c.stroke()}
|
||||
if(s>.95){c.fillStyle='#fff';c.lineWidth=2.5;const g=1-Math.abs((t-.38)/.12);if(g>0)star(c,hx+112*cos(a),hy+112*sin(a)+2,14*g)}
|
||||
}
|
||||
if(s<.5)arms()
|
||||
// head
|
||||
c.save();c.translate(118,84);c.scale(.6,.6);c.translate(-128,-120)
|
||||
c.lineWidth=7;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,142,-1,tn);eye(c,153,142,1,tn)
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-5,1])c.beginPath(),c.moveTo(128+d*44+k-3,168),c.lineTo(128+d*44+k+3,160),c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(120,172);c.lineTo(136,172);c.stroke()
|
||||
// screentone side locks framing the face
|
||||
for(const d of[-1,1]){const sl=()=>{c.beginPath();c.moveTo(128+d*50,96);c.quadraticCurveTo(128+d*64,150,128+d*58,196);c.quadraticCurveTo(128+d*50,184,128+d*44,190);c.quadraticCurveTo(128+d*48,140,128+d*40,104);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';sl();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(0,0,256,256);c.restore();sl();c.stroke()}
|
||||
// screentone bangs
|
||||
const bg=()=>{c.beginPath();c.moveTo(72,150);c.bezierCurveTo(56,80,90,48,128,48);c.bezierCurveTo(166,48,200,80,184,150);c.quadraticCurveTo(180,118,172,108);c.quadraticCurveTo(162,114,156,120);c.quadraticCurveTo(150,104,138,98)
|
||||
c.quadraticCurveTo(132,112,122,116);c.quadraticCurveTo(116,102,104,98);c.quadraticCurveTo(98,112,90,118);c.quadraticCurveTo(84,110,80,104);c.quadraticCurveTo(72,124,72,150);c.closePath()}
|
||||
c.lineWidth=7;c.fillStyle='#fff';bg();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(0,0,256,256);c.restore();bg();c.stroke()
|
||||
// half helm with a cheek guard line and a blowing plume
|
||||
c.fillStyle='#fff';c.lineWidth=7;c.beginPath();c.moveTo(72,92);c.bezierCurveTo(70,36,186,36,184,92);c.quadraticCurveTo(128,72,72,92);c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(80,80);c.quadraticCurveTo(128,62,176,80);c.stroke()
|
||||
c.beginPath();c.moveTo(128,40);c.lineTo(128,72);c.stroke()
|
||||
const pl=()=>{c.beginPath();c.moveTo(124,42);c.bezierCurveTo(110,4,72,-4,36+8*w,8+6*w);c.quadraticCurveTo(66,14,74,22);c.quadraticCurveTo(46,26,30+6*w,40+6*w);c.quadraticCurveTo(80,40,118,50);c.closePath()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';pl();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(118,40);c.quadraticCurveTo(84,14,50+6*w,12+4*w);c.moveTo(112,46);c.quadraticCurveTo(80,32,46+5*w,36+5*w);c.stroke()
|
||||
c.restore()
|
||||
if(s>=.5)arms()
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// archer with a screentone bob draws a tall kyudo bow to her cheek, lets the arrow streak off, and the string twangs
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min,exp}=Math,T=2*PI
|
||||
export const frames=20,delay=80,rim=4
|
||||
const dots=(col,r,s=6)=>{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 TONE=dots('#111',1.4),LITE=dots('#fff',1.2)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u)
|
||||
const arrow=(c,x,y,tn)=>{
|
||||
for(const[l,s]of[[7,'#111'],[2.5,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(x-2,y),c.lineTo(x+92,y),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=3;c.fillStyle='#111';c.beginPath();c.moveTo(x+90,y-4);c.lineTo(x+100,y);c.lineTo(x+90,y+4);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';for(const d of[-1,1]){c.beginPath();c.moveTo(x+4,y);c.lineTo(x+8,y+d*8);c.lineTo(x+26,y+d*8);c.lineTo(x+24,y);c.closePath();c.fill();c.save();c.clip();c.fillStyle='#111';for(const z of[9,15,21])c.fillRect(x+z,y-9,3,18);c.restore();c.stroke()}
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TONE,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const draw=t<.4?ease(t/.4):t<.55?1:0,rel=t>=.55,u=rel?(t-.55)/.45:0,d=t<.55?.35+.65*draw:0
|
||||
const G=[176,150],Tt=[160-10*d,14+4*d],Bt=[164-8*d,244-4*d]
|
||||
const rest=Tt[0]+(Bt[0]-Tt[0])*(128-Tt[1])/(Bt[1]-Tt[1]),vib=rel?9*exp(-u*6)*sin(u*T*9):0
|
||||
const sx=rel?rest+vib:140-44*draw+(t>=.4?.8*sin(t*T*12):0),sy=128
|
||||
const back=t<.55?0:t<.82?1:1-ease((t-.82)/.12),hand=rel?[(64+32*max(0,1-u*4))*back+140*(1-back),sy-6*back]:[sx,sy]
|
||||
const hasArrow=!rel||t>=.94
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,176);c.quadraticCurveTo(50,184,40,252);c.lineTo(160,252);c.quadraticCurveTo(152,184,122,176);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(118,184);c.quadraticCurveTo(146,190,148,228);c.lineTo(122,232);c.closePath();c.fill()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(100,176);c.lineTo(116,208);c.lineTo(124,178);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.rect(100,156,22,24);c.fill();c.beginPath();c.moveTo(100,156);c.lineTo(100,178);c.moveTo(122,156);c.lineTo(122,178);c.stroke()
|
||||
const hx=hand[0],hy=hand[1]
|
||||
for(const[l,s]of[[22,'#111'],[13,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(84,184),c.lineTo(46,140),c.lineTo(hx-6,hy),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
c.save();c.translate(108,100);c.scale(.62,.62);c.translate(-128,-128)
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(60,170);c.bezierCurveTo(40,90,80,34,128,34);c.bezierCurveTo(176,34,216,90,196,170);c.quadraticCurveTo(160,180,128,176);c.quadraticCurveTo(96,180,60,170);c.closePath();c.fill();c.fillStyle=tn;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()
|
||||
for(const[x,dd]of[[108,-1],[158,1]]){const y=140
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,15,0,0,T);c.fill();c.save();c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+6,y+3,9,13,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-2,y+6,20,12);c.fillStyle='#fff';c.beginPath();c.arc(x+3,y-2,3.5,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-15,y-6);c.quadraticCurveTo(x,y-14,x+15,y-6);c.lineTo(x+dd*20,y-11);c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(x-10,y-26);c.quadraticCurveTo(x,y-30-(rel?4:0),x+10,y-26);c.stroke()}
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(134,172);c.lineTo(148,171);c.stroke()
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(66,140);c.bezierCurveTo(50,70,90,38,128,38);c.bezierCurveTo(166,38,206,70,190,140)
|
||||
c.quadraticCurveTo(186,110,176,96);c.lineTo(160,108);c.lineTo(156,92);c.lineTo(136,104);c.lineTo(130,88);c.lineTo(110,104);c.lineTo(104,90);c.lineTo(86,106);c.lineTo(80,96);c.quadraticCurveTo(70,112,66,140);c.closePath();c.fill();c.fillStyle=tn;c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=6;c.beginPath();for(const[a,b]of[[1.22,1.34],[1.44,1.54],[1.64,1.76]])c.arc(128,100,54,PI*a,PI*b);c.stroke();c.strokeStyle='#111'
|
||||
c.restore()
|
||||
for(const[l,s]of[[22,'#111'],[13,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(126,184),c.quadraticCurveTo(150,168,G[0]-6,G[1]+2),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(...Tt);c.lineTo(sx,sy);c.lineTo(...Bt);c.stroke()
|
||||
if(rel&&u<.25){c.lineWidth=3;for(const y of[60,100,170,200]){c.beginPath();c.moveTo(rest-14,y);c.quadraticCurveTo(rest-20,y+8,rest-14,y+16);c.stroke()}}
|
||||
const bow=()=>{c.beginPath();c.moveTo(...Tt);c.quadraticCurveTo(G[0]+4,40,G[0],G[1]-16);c.lineTo(G[0],G[1]+14);c.quadraticCurveTo(G[0]+4,224,...Bt)}
|
||||
for(const[l,s]of[[11,'#111'],[4.5,'#fff']])c.lineWidth=l,c.strokeStyle=s,bow(),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#111';c.fillRect(G[0]-6,G[1]-16,12,30)
|
||||
if(hasArrow)arrow(c,!rel?sx:140,sy,tn)
|
||||
if(rel&&u<.25){const ax=96+u/.25*300;c.lineWidth=3;for(const[y,o]of[[121,0],[135,24]]){const a0=max(ax-60+o,186),a1=min(ax+90,246);if(a1-a0>8)c.beginPath(),c.moveTo(a0,y),c.lineTo(a1,y),c.stroke()};if(ax+100<250)arrow(c,ax,sy,tn)}
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(G[0],G[1]-2,10,12,0,0,T);c.fill();c.stroke()
|
||||
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy,11,10,0,0,T);c.fill();c.fillStyle=tn;c.fill();c.stroke()
|
||||
if(rel&&back>.5){c.lineWidth=3;c.beginPath();for(const a of[-.5,0,.5])c.moveTo(hx-14*cos(a),hy-14*sin(a)),c.lineTo(hx-22*cos(a),hy-22*sin(a));c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// manga girl in a polka-dot yukata sets a glowing paper lantern on dark water, it drifts away bobbing with ripples while fireflies blink
|
||||
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 hatch=(c,s)=>{const k=createCanvas(s,s),x=k.getContext('2d');x.strokeStyle='#111';x.lineWidth=1.4;for(const o of[-s,0,s])x.beginPath(),x.moveTo(o,s),x.lineTo(o+s,0),x.stroke();return c.createPattern(k,'repeat')}
|
||||
const ease=u=>(u=max(0,min(1,u)),u*u*(3-2*u)),lerp=(a,b,u)=>a+(b-a)*ease(u)
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.clip();c.beginPath();c.ellipse(x+3,y+4,10.5,14,0,0,T);c.fillStyle='#111';c.fill();c.fillStyle='#777';c.beginPath();c.ellipse(x+3,y+15,10,6,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-1,y,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+7,y+11,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-3);c.quadraticCurveTo(x,y-16,x+16,y-3);c.moveTo(x+d*14,y-6);c.lineTo(x+d*21,y-10);c.stroke()
|
||||
}
|
||||
const lantern=(c,x,y,s,g)=>{
|
||||
c.save();c.translate(x,y);c.scale(s,s)
|
||||
c.lineWidth=3;for(let k=0;k<8;k++){const b=k*PI/4+PI/8,r=24+g*4;c.beginPath();c.moveTo(cos(b)*r,-8+sin(b)*r*.9);c.lineTo(cos(b)*(r+7+g*3),-8+sin(b)*(r+7+g*3)*.9);c.stroke()}
|
||||
c.lineWidth=4.5;c.fillStyle='#fff';c.beginPath();c.moveTo(-22,8);c.lineTo(22,8);c.lineTo(18,16);c.lineTo(-18,16);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.rect(-15,-28,30,36);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(-15,-10);c.lineTo(15,-10);c.moveTo(0,-28);c.lineTo(0,8);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(-6,2);c.quadraticCurveTo(-7,-6,0,-14);c.quadraticCurveTo(7,-6,6,2);c.closePath();c.fill()
|
||||
c.lineWidth=4.5;c.beginPath();c.moveTo(-18,-28);c.lineTo(18,-28);c.stroke();c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const DARK=tone(c,5,1.7),DOT=tone(c,6,1.3),HAT=hatch(c,5)
|
||||
const held=t<.2||t>=.95,calm=t>=.28&&t<.9
|
||||
// girl behind the water: polka-dot yukata and obi
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(84,150);c.quadraticCurveTo(44,158,36,214);c.lineTo(162,214);c.quadraticCurveTo(156,158,116,150);c.closePath();c.fill()
|
||||
c.save();c.clip();c.fillStyle='#111';for(let y=150;y<216;y+=14)for(let x=30+(y/14%2)*7;x<170;x+=14)c.beginPath(),c.arc(x,y,3.2,0,T),c.fill();c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(84,150);c.lineTo(104,184);c.lineTo(116,150);c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.fillRect(40,186,120,16);c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(42,194);c.lineTo(158,194);c.stroke();c.strokeStyle='#111'
|
||||
// dark water in front with ripples and reflections
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(14,206);c.bezierCurveTo(14,186,80,184,130,184);c.bezierCurveTo(200,184,244,186,244,206);c.bezierCurveTo(244,246,220,248,128,248);c.bezierCurveTo(36,248,14,246,14,206);c.fill();c.fillStyle=DARK;c.fill();c.stroke()
|
||||
c.save();c.clip()
|
||||
if(t>=.16&&t<.7){const u=(t-.16)/.54;c.strokeStyle='#fff';for(const k of[0,1]){const r=u*80-k*24;if(r>4){c.globalAlpha=1-u;c.lineWidth=4;c.beginPath();c.ellipse(100,222,r,r*.25,0,0,T);c.stroke()}}c.globalAlpha=1}
|
||||
c.strokeStyle='#fff';c.lineWidth=3;for(const[x,y,w]of[[40,234,24],[150,240,40],[220,200,20]])c.beginPath(),c.moveTo(x-w/2+4*sin(t*T*2),y),c.lineTo(x+w/2+4*sin(t*T*2),y),c.stroke()
|
||||
c.restore();c.strokeStyle='#111'
|
||||
// lantern drifting off to the right
|
||||
const lu=t<.2?0:t<.95?(t-.2)/.75:0,lx=t<.2?lerp(100,100,t/.2):100+lu*112,ly=t<.2?lerp(196,214,t/.2):214-lu*8+2*sin(t*T*3),ls=1.25-lu*.45,la=t>=.95?(t-.95)/.05:t>=.84?1-(t-.84)/.11:1
|
||||
c.globalAlpha=max(0,la)
|
||||
if(!held){c.strokeStyle='#fff';c.lineWidth=5*ls;c.beginPath();for(let k=0;k<3;k++)c.moveTo(lx-10*ls+k%2*4,ly+20*ls+k*6),c.lineTo(lx+10*ls-k%2*4,ly+20*ls+k*6);c.stroke();c.strokeStyle='#111'}
|
||||
lantern(c,lx,ly,ls,(sin(t*T*4)+1)/2);c.globalAlpha=1
|
||||
// fireflies
|
||||
for(let k=0;k<4;k++){const a=t*T+k*1.7,[x,y]=[196+[0,30,-22,14][k]+10*cos(a),96+[0,-26,26,44][k]+8*sin(2*a)],b=(sin(t*T*2+k*2)+1)/2
|
||||
c.fillStyle='#fff';c.lineWidth=2.5;c.beginPath();c.arc(x,y,3+b*2,0,T);c.fill();c.stroke();if(b>.5){c.lineWidth=2;for(let j=0;j<4;j++){const g=j*PI/2;c.beginPath();c.moveTo(x+cos(g)*8,y+sin(g)*8);c.lineTo(x+cos(g)*12,y+sin(g)*12);c.stroke()}}}
|
||||
// arms: holding the lantern, then resting together
|
||||
const H=held?[[lx-26*ls,ly-8],[lx+26*ls,ly-8]]:calm?[[92,184],[108,184]]:t<.28?[[lerp(lx-30,92,(t-.2)/.08),lerp(ly-8,184,(t-.2)/.08)],[lerp(lx+30,108,(t-.2)/.08),lerp(ly-8,184,(t-.2)/.08)]]:[[lerp(92,lx-26,(t-.9)/.05),lerp(184,ly-8,(t-.9)/.05)],[lerp(108,lx+26,(t-.9)/.05),lerp(184,ly-8,(t-.9)/.05)]]
|
||||
for(const[[sx,sy],[hx,hy]]of[[[70,164],H[0]],[[130,164],H[1]]]){for(const[l,st]of[[22,'#111'],[13,'#fff'],[13,DOT]])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(sx,sy),c.lineTo(hx,hy-6),c.stroke();c.strokeStyle='#111'}
|
||||
for(const[hx,hy]of H){c.lineWidth=4.5;c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy,8,9,0,0,T);c.fill();c.stroke()}
|
||||
// head, looking down at the water
|
||||
c.save();c.translate(100,108);c.rotate(.06);c.scale(.68,.68);c.translate(-128,-128)
|
||||
c.lineWidth=10;c.fillStyle='#fff';c.beginPath();c.arc(118,34,30,0,T);c.fill();c.fillStyle=HAT;c.fill();c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(146,30);c.lineTo(178,12);c.moveTo(150,40);c.lineTo(184,30);c.stroke();c.fillStyle='#fff';c.beginPath();c.arc(182,10,6,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=10;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(calm){c.lineWidth=8;for(const x of[103,153])c.beginPath(),c.arc(x,142,13,.15*PI,.85*PI),c.stroke()}else{eye(c,103,140,-1);eye(c,153,140,1)}
|
||||
c.lineWidth=4;for(const d of[-1,1])for(const k of[-7,0,7])c.beginPath(),c.moveTo(128+d*40+k-3,168),c.lineTo(128+d*40+k+3,158),c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.arc(128,166,7,.2*PI,.8*PI);c.stroke()
|
||||
c.lineWidth=10;c.fillStyle='#fff';c.beginPath();c.moveTo(72,140)
|
||||
c.bezierCurveTo(56,72,88,40,128,40);c.bezierCurveTo(168,40,200,72,184,140)
|
||||
c.quadraticCurveTo(182,116,176,104);c.quadraticCurveTo(160,104,146,92);c.quadraticCurveTo(138,104,124,108);c.quadraticCurveTo(110,104,100,94);c.quadraticCurveTo(92,108,80,110);c.quadraticCurveTo(74,124,72,140);c.closePath();c.fill();c.fillStyle=HAT;c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=10;for(const[s,e]of[[1.26,1.38],[1.46,1.56],[1.64,1.74]])c.beginPath(),c.arc(128,108,52,PI*s,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=7;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*56,120),c.quadraticCurveTo(128+d*64,150,128+d*58,176),c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// manga girl in a bandana and apron pins a bedsheet to the clothesline while gusts of wind billow it, then smiles at the job done
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=16,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)),lerp=(a,b,u)=>a+(b-a)*ease(u)
|
||||
const ly=x=>62+4*x/256+16*(x/128)*(2-x/128)
|
||||
const spark=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x,y,x+r,y);c.quadraticCurveTo(x,y,x,y+r);c.quadraticCurveTo(x,y,x-r,y);c.quadraticCurveTo(x,y,x,y-r);c.fill();c.stroke()}
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.clip();c.beginPath();c.ellipse(x-3,y-2,10.5,14,0,0,T);c.fillStyle='#111';c.fill();c.fillStyle='#777';c.beginPath();c.ellipse(x-3,y+9,10,6,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-7,y-6,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+1,y+6,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-5);c.quadraticCurveTo(x,y-20,x+16,y-5);c.moveTo(x+d*14,y-8);c.lineTo(x+d*21,y-13);c.stroke()
|
||||
}
|
||||
const pin=(c,x,y)=>{c.lineWidth=3.5;c.fillStyle='#fff';c.beginPath();c.roundRect(x-4,y-9,8,20,2);c.fill();c.stroke();c.beginPath();c.moveTo(x,y-9);c.lineTo(x,y+11);c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
const w=.5+.5*sin(t*T*2),push=t<.45,done=t>=.55&&t<.9
|
||||
// clothesline
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(8,ly(8));for(let x=16;x<=248;x+=8)c.lineTo(x,ly(x));c.stroke()
|
||||
// the sheet, billowing
|
||||
const x1=30,x2=156,bl=[34+22*w,198-14*w],br=[150+20*w,198-10*w],hold=[lerp(160,162,0)+6*w,132]
|
||||
const sheet=()=>{c.beginPath();c.moveTo(x1,ly(x1));for(let x=x1;x<=x2;x+=6)c.lineTo(x,ly(x)+2);c.quadraticCurveTo(x2+18*w,96,hold[0],hold[1]);c.quadraticCurveTo(br[0]+8*w,170,br[0],br[1])
|
||||
for(let k=1;k<=4;k++){const u=k/4,x=br[0]+(bl[0]-br[0])*u,y=br[1]+(bl[1]-br[1])*u;c.quadraticCurveTo(x+(br[0]-bl[0])/8,y+(k%2?10:-6)*(.4+w),x,y)}
|
||||
c.quadraticCurveTo(x1+16*w,130,x1,ly(x1))}
|
||||
sheet();c.lineWidth=6;c.fillStyle='#fff';c.fill();c.save();c.clip()
|
||||
c.fillStyle=DOT;c.beginPath();c.moveTo(70+12*w,70);c.quadraticCurveTo(90+30*w,140,80+24*w,210);c.lineTo(104+24*w,210);c.quadraticCurveTo(108+30*w,140,92+12*w,70);c.fill()
|
||||
c.lineWidth=3;for(const o of[0,46])c.beginPath(),c.moveTo(52+o+8*w,76),c.quadraticCurveTo(64+o+26*w,140,58+o+22*w,196),c.stroke()
|
||||
c.restore();sheet();c.lineWidth=6;c.stroke()
|
||||
pin(c,x1+6,ly(x1+6));pin(c,93,ly(93))
|
||||
// wind streaks
|
||||
c.lineWidth=4;for(let k=0;k<3;k++){const p=(t*2+k/3)%1,x=10+p*120,y=[30,112,200][k];c.globalAlpha=sin(PI*p);c.beginPath();c.moveTo(x,y);c.quadraticCurveTo(x+18,y-8,x+34,y);c.quadraticCurveTo(x+44,y+6,x+40,y+12);c.stroke()}c.globalAlpha=1
|
||||
// laundry basket
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(70,212);c.quadraticCurveTo(80,206,86,198);c.lineTo(102,204);c.lineTo(118,196);c.lineTo(132,206);c.quadraticCurveTo(144,204,150,212);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(62,210);c.lineTo(158,210);c.lineTo(150,246);c.lineTo(70,246);c.closePath();c.fill();c.save();c.clip();c.lineWidth=2.5;for(let k=-8;k<14;k++)c.beginPath(),c.moveTo(56+k*10,210),c.lineTo(76+k*10,246),c.moveTo(76+k*10,210),c.lineTo(56+k*10,246),c.stroke();c.restore();c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(60,212);c.lineTo(160,212);c.stroke()
|
||||
// girl: skirt, apron, legs
|
||||
const G=[204,96]
|
||||
for(const x of[194,214]){c.lineWidth=12;c.beginPath();c.moveTo(x,212);c.lineTo(x,234);c.stroke();c.lineWidth=5;c.strokeStyle='#fff';c.stroke();c.strokeStyle='#111';c.lineWidth=4.5;c.fillStyle='#fff';c.beginPath();c.ellipse(x+(x<204?-4:4),240,11,6,0,0,T);c.fill();c.fillStyle='#111';c.fill()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(184,184);c.lineTo(226,184);c.lineTo(240,216);c.lineTo(170,216);c.closePath();c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(186,138);c.quadraticCurveTo(176,150,176,190);c.lineTo(234,190);c.quadraticCurveTo(234,150,224,138);c.closePath();c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(190,150);c.lineTo(220,150);c.lineTo(228,210);c.quadraticCurveTo(205,216,182,210);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(188,166);c.lineTo(222,166);c.stroke();c.beginPath();c.rect(196,176,18,14);c.stroke()
|
||||
// arms: one pinning the corner, one holding the edge down
|
||||
const[px,py]=push?[160+2*sin(t*T*10),ly(160)+4]:t<.55?[lerp(160,224,(t-.45)/.1),lerp(ly(160)+4,140,(t-.45)/.1)]:done?[224,140]:[lerp(224,160,(t-.9)/.1),lerp(140,ly(160)+4,(t-.9)/.1)]
|
||||
for(const[[sx,sy],[hx,hy]]of[[[188,146],[px,py+8]],[[190,150],hold]]){for(const[l,st]of[[15,'#111'],[8,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(sx,sy),c.lineTo(hx,hy),c.stroke();c.strokeStyle='#111'}
|
||||
pin(c,push?px:160,ly(160)+4);for(const[hx,hy]of[[px,py+8],hold]){c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.arc(hx,hy,7,0,T);c.fill();c.stroke()}
|
||||
if(t>=.45&&t<.6){c.fillStyle='#fff';c.lineWidth=3;spark(c,148,ly(148)-14,8);spark(c,172,ly(172)-10,5)}
|
||||
// head with bandana, hair and tails blowing right
|
||||
c.save();c.translate(...G);c.scale(.52,.52);c.translate(-128,-128)
|
||||
const f=10*sin(t*T*4)
|
||||
c.lineWidth=11;c.fillStyle='#fff';for(const k of[0,1]){c.beginPath();c.moveTo(184,74);c.quadraticCurveTo(208,70+k*30+f,218+k*4,84+k*34+f);c.lineTo(200,96+k*28+f);c.closePath();c.fill();c.stroke()}
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(128,40);c.bezierCurveTo(196,40,210,100,204,166);c.lineTo(52,166);c.bezierCurveTo(46,100,60,40,128,40);c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
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(done){c.lineWidth=10;for(const x of[103,153])c.beginPath(),c.arc(x,144,13,PI*1.15,PI*1.85),c.stroke()}
|
||||
else if(push){c.lineWidth=10;for(const d of[-1,1]){const x=128+d*25;c.beginPath();c.moveTo(x-d*13,128);c.lineTo(x+d*11,140);c.lineTo(x-d*13,152);c.stroke()}}
|
||||
else{eye(c,103,140,-1);eye(c,153,140,1)}
|
||||
c.lineWidth=5;for(const d of[-1,1])for(const k of[-7,0,7])c.beginPath(),c.moveTo(128+d*40+k-3,168),c.lineTo(128+d*40+k+3,158),c.stroke()
|
||||
c.lineWidth=8;c.beginPath();if(push)c.moveTo(116,170),c.lineTo(140,170);else c.arc(128,164,10,.15*PI,.85*PI);c.stroke()
|
||||
c.lineWidth=11;c.fillStyle='#fff';c.beginPath();c.moveTo(72,140);c.bezierCurveTo(60,100,80,80,128,80);c.bezierCurveTo(176,80,196,100,184,140);c.quadraticCurveTo(178,112,168,104);c.lineTo(156,114);c.lineTo(146,100);c.lineTo(130,112);c.lineTo(118,98);c.lineTo(104,112);c.lineTo(94,102);c.lineTo(84,114);c.quadraticCurveTo(76,124,72,140);c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(66,94);c.bezierCurveTo(66,30,190,30,190,94);c.quadraticCurveTo(128,74,66,94);c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle='#111';for(const[x,y]of[[96,56],[128,46],[160,56],[112,74],[146,72],[80,78],[176,80]])c.beginPath(),c.arc(x,y,6,0,T),c.fill();c.restore()
|
||||
c.restore()
|
||||
if(push){c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(178,72);c.quadraticCurveTo(186,86,178,90);c.quadraticCurveTo(170,86,178,72);c.fill();c.stroke()}
|
||||
if(done){c.fillStyle='#fff';c.lineWidth=3;for(let k=0;k<2;k++){const p=((t-.55)/.35*2+k/2)%1;spark(c,[176,236][k],[54,70][k]-p*8,3+6*sin(PI*p))}}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// flustered girl at her open shoe locker while love letters keep pouring out onto a pile
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI}=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 env=(c,x,y,a,s=1)=>{
|
||||
c.save();c.translate(x,y);c.rotate(a);c.scale(s,s);c.lineWidth=3.5;c.fillStyle='#fff'
|
||||
c.beginPath();c.rect(-15,-10,30,20);c.fill();c.stroke();c.beginPath();c.moveTo(-15,-10);c.lineTo(0,1);c.lineTo(15,-10);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(0,8);c.bezierCurveTo(-8,2,-5,-4,0,0);c.bezierCurveTo(5,-4,8,2,0,8);c.fill();c.restore()
|
||||
}
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),j=i%2?1.2:-1.2,dw=4*sin(t*T*2)
|
||||
// locker unit with the open door
|
||||
c.lineWidth=5;c.fillStyle=dot;c.beginPath();c.moveTo(150,46);c.lineTo(160,36);c.lineTo(246,36);c.lineTo(238,46);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(150,46,88,200);c.fill();c.stroke()
|
||||
c.lineWidth=3.5
|
||||
for(let r=0;r<4;r++)for(let k=0;k<2;k++){
|
||||
const x=154+k*42,y=50+r*48
|
||||
if(r==1&&!k){c.fillStyle='#111';c.fillRect(x,y,38,44);continue}
|
||||
c.beginPath();c.rect(x,y,38,44);c.stroke();c.fillStyle=dot;c.fillRect(x+12,y+6,14,6);c.strokeRect(x+12,y+6,14,6)
|
||||
}
|
||||
c.lineWidth=4.5;c.fillStyle='#fff';c.beginPath();c.moveTo(192,98);c.lineTo(218+dw,90);c.lineTo(218+dw,150);c.lineTo(192,142);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=dot;c.fillRect(203+dw/2,104,8,5)
|
||||
// girl
|
||||
c.save();c.translate(-46,0)
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(128,36);c.bezierCurveTo(192,36,204,100,200,170);c.lineTo(202,244);c.lineTo(54,244);c.lineTo(56,170);c.bezierCurveTo(52,100,64,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(64,196,60,244);c.lineTo(196,244);c.quadraticCurveTo(192,196,156,188);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4
|
||||
for(const d of[-1,1]){c.fillStyle=dot;c.beginPath();c.moveTo(128-d*28,189);c.lineTo(128-d*46,196);c.lineTo(128-d*8,236);c.lineTo(128,226);c.closePath();c.fill();c.stroke();c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(128-d*38,196);c.lineTo(128-d*8,228);c.stroke();c.strokeStyle='#111';c.lineWidth=4}
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.moveTo(128,226),c.lineTo(128+d*16,244),c.lineTo(128+d*4,244),c.closePath(),c.fill()
|
||||
c.beginPath();c.ellipse(128,226,8,6,0,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;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()
|
||||
// big ink eyes, trembling
|
||||
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+j,141,10.5,15,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+j-3,136,4.5,0,T);c.fill();c.beginPath();c.arc(x+j+4,149,2,0,T);c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=1.5;c.beginPath();c.arc(x+j,145,7,.2*PI,.8*PI);c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-d*12,130);c.quadraticCurveTo(x,119,x+d*15,131);c.lineTo(x+d*21,125);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-5,158);c.lineTo(x+5,158);c.stroke()
|
||||
}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<5;k++)c.beginPath(),c.moveTo(128+d*36+(k-2)*4.5-3,170),c.lineTo(128+d*36+(k-2)*4.5+3,161),c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(116,170);for(let k=1;k<=6;k++)c.lineTo(116+k*4,170+(k%2?-3:3)*(i%2?1:.6));c.stroke()
|
||||
// hime-style bangs, ink black with a shine band
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(66,178)
|
||||
c.bezierCurveTo(48,74,88,36,128,36);c.bezierCurveTo(168,36,208,74,190,178)
|
||||
c.quadraticCurveTo(184,140,180,110);c.lineTo(170,112);c.quadraticCurveTo(166,106,158,112);c.quadraticCurveTo(150,106,142,112)
|
||||
c.quadraticCurveTo(134,106,126,112);c.quadraticCurveTo(118,106,110,112);c.quadraticCurveTo(102,106,94,112);c.quadraticCurveTo(88,106,84,112)
|
||||
c.lineTo(76,110);c.quadraticCurveTo(72,140,66,178);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<=36;k++){const a=PI*(1.17+k*.0185);c.lineTo(128+(k%2?50:47)*cos(a),100+(k%2?50:47)*sin(a))}for(let k=36;k>=0;k--){const a=PI*(1.17+k*.0185);c.lineTo(128+(k%2?44:40)*cos(a),100+(k%2?44:40)*sin(a))}c.fill()
|
||||
c.strokeStyle='#fff'
|
||||
c.lineWidth=2;for(const x of[70,186])c.beginPath(),c.moveTo(x,130),c.quadraticCurveTo(x+(x<128?-2:2),190,x+(x<128?-6:6),236),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
// sweat and fluster marks
|
||||
c.fillStyle='#fff'
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(196,72);c.quadraticCurveTo(206,90,196,96);c.quadraticCurveTo(186,90,196,72);c.fill();c.stroke()
|
||||
for(const[a,l]of[[-2.45,13],[-2.05,15],[-1.65,13]]){const s=1+.2*(i%2);c.beginPath();c.moveTo(128+62*cos(a),92+62*sin(a));c.lineTo(128+(62+l*s)*cos(a),92+(62+l*s)*sin(a));c.stroke()}
|
||||
c.restore()
|
||||
// the pile, then letters streaming out of the locker
|
||||
for(const[x,y,a]of[[118,236,-.3],[150,240,.15],[184,236,-.1],[136,226,.4],[170,228,-.35]])env(c,x,y,a)
|
||||
for(let k=0;k<5;k++){const p=(t+k/5)%1;env(c,176-56*p,116-36*p+150*p*p,k*1.3+5*p,.65+.35*p)}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// manga mage girl floating over a spinning summoning circle, hatched twin tails lifting in the updraft, spellbook pages fluttering, light rising
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min,round}=Math,T=2*PI
|
||||
export const frames=12,delay=100,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 eye=(c,x,y,d,tn)=>{
|
||||
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=tn;c.fillRect(x-12,y-14,24,32);c.fillStyle='#111';c.fillRect(x-12,y-14,24,11);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,4.5,7,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-4,y-5,4,5,-.4,0,T);c.fill();c.save();c.translate(x+4,y+8);c.beginPath();for(let k=0;k<8;k++){const r=k%2?1:3.5,a=k*PI/4;c.lineTo(r*cos(a),r*sin(a))}c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-17,y-6);c.quadraticCurveTo(x,y-21,x+17,y-6);c.moveTo(x+d*16,y-7);c.lineTo(x+d*22,y-12);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-8,y+19);c.lineTo(x+8,y+19);c.stroke()
|
||||
}
|
||||
const hatch=(c,f,g=6)=>{c.save();f();c.clip();c.lineWidth=2;c.beginPath();for(let k=-256;k<256;k+=g)c.moveTo(k,0),c.lineTo(k+256,256);c.stroke();c.restore()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat'),w=sin(t*T),fl=round(3*sin(t*T))
|
||||
const cx=128,cy=214,RX=110,RY=28,P=(r,a)=>[cx+RX*r*cos(a),cy+RY*r*sin(a)]
|
||||
// summoning circle: double ring, rune ticks spinning one way, hexagram the other
|
||||
c.lineWidth=4;for(const r of[1,.86])c.beginPath(),c.ellipse(cx,cy,RX*r,RY*r,0,0,T),c.stroke()
|
||||
c.lineWidth=2.5;for(let k=0;k<12;k++){const a=-t*T/12+k*T/12,[x,y]=P(.93,a),[x2,y2]=P(.93,a+.16);c.beginPath();if(k%3==0)c.ellipse(x,y,3.5,2,0,0,T);else c.moveTo(x,y),c.lineTo(x2,y2);c.stroke()}
|
||||
c.lineWidth=3;for(const o of[0,PI/3]){c.beginPath();for(let k=0;k<3;k++){const[x,y]=P(.86,t*PI/3+o+k*T/3);k?c.lineTo(x,y):c.moveTo(x,y)}c.closePath();c.stroke()}
|
||||
c.beginPath();c.ellipse(cx,cy,RX*.42,RY*.42,0,0,T);c.stroke()
|
||||
// rising light streaks
|
||||
c.lineWidth=2.5;c.fillStyle='#fff';for(let k=0;k<4;k++){const p=(t+k/4)%1,[x]=P(.72,k*1.9+.4),y=cy-10-p*110,s=6*sin(p*PI);if(s>1.5){c.beginPath();for(let j=0;j<8;j++){const r=j%2?s*.3:s,a=j*PI/4;c.lineTo(x+r*cos(a),y+r*sin(a))}c.closePath();c.fill();c.stroke()}}
|
||||
c.save();c.translate(0,fl-4)
|
||||
// long hatched hair fanning up and out in the updraft, locks waving
|
||||
c.save();c.translate(128,70);c.scale(.52,.52);c.translate(-128,-120)
|
||||
for(const d of[-1,1]){const X=x=>128+d*x,v=k=>8*sin(t*T+k)
|
||||
const hr=()=>{c.beginPath();c.moveTo(128,40);c.quadraticCurveTo(X(44),18,X(64+v(0)),8+v(1));c.quadraticCurveTo(X(70),34,X(84),44);c.quadraticCurveTo(X(112),34,X(130+v(2)),34+v(0))
|
||||
c.quadraticCurveTo(X(100),64,X(96),78);c.quadraticCurveTo(X(124),86,X(136+v(1)),104+v(2));c.quadraticCurveTo(X(100),118,X(56),156);c.lineTo(128,156);c.closePath()}
|
||||
c.lineWidth=7;c.fillStyle='#fff';hr();c.fill();hatch(c,hr,8);hr();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(X(70),70);c.quadraticCurveTo(X(96),56,X(112),46);c.moveTo(X(78),110);c.quadraticCurveTo(X(104),96,X(120),96);c.stroke()}
|
||||
c.restore()
|
||||
// cloak: ink black with a white star trim
|
||||
const ck=()=>{c.beginPath();c.moveTo(112,96);c.quadraticCurveTo(84,116,78,170);c.quadraticCurveTo(96,178,104,172);c.quadraticCurveTo(116,180,128,174);c.quadraticCurveTo(140,180,152,172);c.quadraticCurveTo(160,178,178,170);c.quadraticCurveTo(172,116,144,96);c.closePath()}
|
||||
c.lineWidth=4;c.fillStyle='#111';ck();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=2;c.beginPath();c.moveTo(84,164);c.quadraticCurveTo(96,170,104,166);c.quadraticCurveTo(116,173,128,168);c.quadraticCurveTo(140,173,152,166);c.quadraticCurveTo(160,170,172,164);c.stroke()
|
||||
c.fillStyle='#fff';for(const[x,y]of[[94,148],[164,140],[100,124]]){c.beginPath();for(let k=0;k<8;k++){const r=k%2?1.2:4,a=k*PI/4;c.lineTo(x+r*cos(a),y+r*sin(a))}c.fill()}
|
||||
c.strokeStyle='#111'
|
||||
// legs dangling
|
||||
c.lineWidth=4;c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.rect(128+d*12-5,172,10,16),c.fill(),c.stroke(),c.beginPath(),c.ellipse(128+d*12,192,8,6,0,0,T),c.fillStyle='#111',c.fill(),c.stroke(),c.fillStyle='#fff'
|
||||
// open spellbook with a page fluttering
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(128,140);c.quadraticCurveTo(110,132,94,138);c.lineTo(96,160);c.quadraticCurveTo(112,154,128,162);c.quadraticCurveTo(144,154,160,160);c.lineTo(162,138);c.quadraticCurveTo(146,132,128,140);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(128,140);c.lineTo(128,162);c.stroke()
|
||||
c.lineWidth=2;for(const d of[-1,1])for(const y of[144,149,154])c.beginPath(),c.moveTo(128+d*8,y),c.lineTo(128+d*26,y-2),c.stroke()
|
||||
const pg=(t*2)%1,pa=PI*min(1,pg/.6),px=cos(pa)*30
|
||||
if(pg<.6){c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.moveTo(128,140);c.quadraticCurveTo(128+px*.5,132-14*sin(pa),128+px,138-10*sin(pa));c.lineTo(128+px,158-10*sin(pa));c.quadraticCurveTo(128+px*.5,152-14*sin(pa),128,162);c.closePath();c.fill();c.stroke()}
|
||||
for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*34,154,6,7,0,0,T),c.fill(),c.stroke()
|
||||
// glow sparkles above the book
|
||||
c.fillStyle='#fff';c.lineWidth=2.5;for(const[x,y,o]of[[118,120,0],[140,114,.5]]){const s=5*max(0,sin((t*2+o)*PI));if(s>1){c.beginPath();for(let k=0;k<8;k++){const r=k%2?s*.3:s,a=k*PI/4;c.lineTo(x+r*cos(a),y+r*sin(a))}c.closePath();c.fill();c.stroke()}}
|
||||
// head
|
||||
c.save();c.translate(128,70);c.scale(.52,.52);c.translate(-128,-120)
|
||||
c.lineWidth=7;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,140,-1,tn);eye(c,153,140,1,tn)
|
||||
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;c.beginPath();c.ellipse(128,170,6,5,0,0,T);c.stroke()
|
||||
const bg=()=>{c.beginPath();c.moveTo(70,150);c.bezierCurveTo(52,72,88,36,128,36);c.bezierCurveTo(168,36,204,72,186,150);c.quadraticCurveTo(182,114,174,102)
|
||||
c.quadraticCurveTo(166,112,158,114);c.quadraticCurveTo(152,96,140,92);c.quadraticCurveTo(136,108,126,112);c.quadraticCurveTo(120,96,106,92);c.quadraticCurveTo(100,108,92,112);c.quadraticCurveTo(86,108,82,102);c.quadraticCurveTo(72,118,70,150);c.closePath()}
|
||||
c.lineWidth=7;c.fillStyle='#fff';bg();c.fill();hatch(c,bg,8);bg();c.stroke()
|
||||
c.restore()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// shrine maiden with long ink-black hair waves her gohei side to side, paper streamers fluttering and sparkles popping
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,abs}=Math,T=2*PI
|
||||
export const frames=16,delay=80,rim=4
|
||||
const dots=(col,r,s=6)=>{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 TONE=dots('#111',1.6)
|
||||
const spark=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x+r*.15,y-r*.15,x+r,y);c.quadraticCurveTo(x+r*.15,y+r*.15,x,y+r);c.quadraticCurveTo(x-r*.15,y+r*.15,x-r,y);c.quadraticCurveTo(x-r*.15,y-r*.15,x,y-r);c.fill();c.stroke()}
|
||||
const shide=(c,a)=>{c.save();c.rotate(a);c.lineJoin='miter';for(const[l,s]of[[15,'#111'],[8,'#fff']]){c.lineWidth=l;c.strokeStyle=s;c.beginPath();c.moveTo(0,0);for(let k=1;k<=4;k++)c.lineTo(k%2?14:0,k*13);c.stroke()}c.restore();c.strokeStyle='#111';c.lineJoin='round'}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TONE,'repeat')
|
||||
const a=.27+.28*sin(t*T),hx=160,hy=200,L=100,tx=hx+L*sin(a),ty=hy-L*cos(a),lag=-.7*cos(t*T)
|
||||
c.fillStyle='#fff';c.lineWidth=3.5
|
||||
for(let j=0;j<5;j++){const p=(t*2+j/5)%1,s=8*sin(PI*p),b=-.3+.9*j/4,r=128+(j%2)*18;if(s>1.5)spark(c,hx+r*sin(b)+6,hy-r*cos(b)-6,s)}
|
||||
c.save();c.translate(-10,0)
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(60,96);c.quadraticCurveTo(36,170,44,250);c.lineTo(140,250);c.lineTo(140,140);c.quadraticCurveTo(150,100,124,60);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(78,178);c.quadraticCurveTo(40,190,36,252);c.lineTo(164,252);c.quadraticCurveTo(160,190,124,178);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle=tn;c.beginPath();c.rect(52,232,104,20);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(48,232);c.lineTo(156,232);c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(80,178);c.lineTo(102,224);c.lineTo(122,178);c.moveTo(88,178);c.lineTo(102,206);c.lineTo(114,178);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.rect(90,156,22,26);c.fill();c.beginPath();c.moveTo(90,156);c.lineTo(90,180);c.moveTo(112,156);c.lineTo(112,180);c.stroke()
|
||||
c.save();c.translate(100,102);c.scale(.7,.7);c.translate(-128,-128)
|
||||
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()
|
||||
for(const[x,d]of[[104,-1],[152,1]]){c.lineWidth=7;c.beginPath();c.moveTo(x-15,140);c.quadraticCurveTo(x,152,x+15,140);c.stroke();c.lineWidth=4;c.beginPath();for(const j of[-8,0,8])c.moveTo(x+j,148+(j?-1:1)),c.lineTo(x+j*1.3,156);c.moveTo(x+d*15,140);c.lineTo(x+d*21,136);c.stroke()}
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const j of[-6,0,6])c.beginPath(),c.moveTo(128+d*44+j-3,168),c.lineTo(128+d*44+j+3,160),c.stroke()
|
||||
c.lineWidth=4.5;c.beginPath();c.moveTo(118,170);c.quadraticCurveTo(128,178,138,170);c.stroke()
|
||||
c.lineWidth=8;c.fillStyle='#111';c.beginPath();c.moveTo(64,186);c.lineTo(60,104);c.bezierCurveTo(56,50,92,34,128,34);c.bezierCurveTo(164,34,200,50,196,104);c.lineTo(192,186);c.lineTo(176,186);c.lineTo(178,116);c.lineTo(80,116);c.lineTo(80,186);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=6;c.beginPath();for(const[aa,b]of[[1.2,1.32],[1.42,1.52],[1.62,1.76]])c.arc(128,110,60,PI*aa,PI*b);c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=5;for(const x of[70,186])c.beginPath(),c.roundRect(x-11,150,22,12,3),c.fill(),c.stroke()
|
||||
c.restore();c.restore()
|
||||
c.save();c.translate(tx,ty);shide(c,lag*.8-.5);c.scale(-1,1);shide(c,-lag*.8-.5);c.restore()
|
||||
c.save();c.translate(hx,hy);c.rotate(a)
|
||||
for(const[l,s]of[[10,'#111'],[4,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(0,22),c.lineTo(0,-L),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(-8,-L-2);c.lineTo(0,-L-14);c.lineTo(8,-L-2);c.closePath();c.fill();c.stroke()
|
||||
c.restore()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128,184);c.quadraticCurveTo(150,186,hx-10,hy+4);c.lineTo(hx+12,hy+20);c.quadraticCurveTo(hx+30,232,hx+22,252);c.lineTo(hx-30,252);c.quadraticCurveTo(hx-38,224,124,216);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(hx-2,hy+22);c.quadraticCurveTo(hx+8,236,hx,250);c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.ellipse(hx,hy,11,10,a,0,T);c.fill();c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// exorcist girl with a white pixie cut flicks three paper talismans that spin out across the panel and flash
|
||||
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 dots=(col,r,s=6)=>{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 TONE=dots('#111',1.5),LITE=dots('#fff',1.2)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u),out=u=>1-(1-u)**2
|
||||
const card=(c,x,y,a,sx=1,s=1)=>{c.save();c.translate(x,y);c.rotate(a);c.scale(sx*s,s)
|
||||
c.lineWidth=3.5;c.fillStyle='#fff';c.beginPath();c.rect(-8,-20,16,40);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.arc(0,-11,4,0,T);c.moveTo(0,-5);c.lineTo(0,15);for(const y of[-1,5,11])c.moveTo(-4,y),c.lineTo(4,y-1.5);c.stroke();c.restore()}
|
||||
const burst=(c,x,y,R,n)=>{c.beginPath();for(let k=0;k<n*2;k++){const r=k%2?R*.45:R,a=k*PI/n+.2;c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.closePath()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TONE,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const k=t<.3?0:t<.38?ease((t-.3)/.08):t<.6?1:1-ease((t-.6)/.3),hold=t<.34||t>=.9
|
||||
const hx=132+64*k,hy=112+14*k,fl=t>=.34&&t<.9
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(70,176);c.quadraticCurveTo(26,186,22,252);c.lineTo(150,252);c.quadraticCurveTo(146,186,104,176);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(72,176);c.lineTo(88,226);c.lineTo(102,176);c.closePath();c.fill();c.lineWidth=4;c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(87,184);c.lineTo(82,214);c.lineTo(88,222);c.lineTo(94,214);c.lineTo(89,184);c.fill()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(66,180);c.lineTo(84,236);c.moveTo(108,180);c.lineTo(92,236);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.rect(76,154,22,26);c.fill();c.beginPath();c.moveTo(76,154);c.lineTo(76,176);c.moveTo(98,154);c.lineTo(98,176);c.stroke()
|
||||
c.save();c.translate(88,100);c.scale(.7,.7);c.translate(-128,-128)
|
||||
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()
|
||||
for(const[x,d]of[[106,-1],[156,1]]){const y=138
|
||||
c.save();c.beginPath();c.moveTo(x-17,y-5+d*3);c.lineTo(x+17,y-5-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+3,13,15,0,0,T);c.fill();c.clip();c.fillStyle='#111';c.beginPath();c.ellipse(x+5,y+4,9.5,13,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-4,y+8,22,12)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+2,y-1,4,0,T);c.fill();c.beginPath();c.arc(x+9,y+10,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(x-15,y-5+d*3);c.lineTo(x+15,y-5-d*3);c.lineTo(x+d*21,y-10);c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(x-12,y-24+d*4);c.lineTo(x+12,y-24-d*4);c.stroke()}
|
||||
c.lineWidth=4.5;c.beginPath();c.moveTo(130,170);c.quadraticCurveTo(140,174,150,166);c.stroke()
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const j of[-6,0,6])c.beginPath(),c.moveTo(131+d*44+j-3,166),c.lineTo(131+d*44+j+3,158),c.stroke()
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(70,178);c.quadraticCurveTo(52,150,60,120);c.bezierCurveTo(40,70,90,30,132,32);c.bezierCurveTo(180,34,212,74,196,124);c.quadraticCurveTo(204,154,188,178);c.quadraticCurveTo(184,150,182,130)
|
||||
c.lineTo(186,112);c.lineTo(174,118);c.lineTo(170,96);c.lineTo(152,110);c.lineTo(146,90);c.lineTo(128,108);c.lineTo(118,90);c.lineTo(102,110);c.lineTo(94,96);c.lineTo(80,116);c.lineTo(76,104);c.quadraticCurveTo(72,140,70,178);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const[x0,y0,x1,y1]of[[100,48,84,84],[126,42,118,82],[152,44,156,86],[174,58,180,98]])c.moveTo(x0,y0),c.quadraticCurveTo((x0+x1)/2+6,(y0+y1)/2,x1,y1);c.stroke()
|
||||
c.save();c.translate(176,52);c.rotate(.4);c.lineWidth=6;c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.moveTo(0,0),c.quadraticCurveTo(d*22,-20,d*30,-2),c.quadraticCurveTo(d*22,14,0,0),c.fill(),c.stroke();c.beginPath();c.moveTo(-4,4);c.lineTo(-14,30);c.lineTo(-4,26);c.lineTo(2,34);c.lineTo(4,6);c.fill();c.beginPath();c.arc(0,0,7,0,T);c.fill();c.restore()
|
||||
c.restore()
|
||||
if(fl){const u=(t-.34)/.56
|
||||
for(const[j,ex,ey,o]of[[0,218,34,0],[1,236,110,.06],[2,224,190,.12]]){const v=min(1,max(0,(u-o)/.62)),x=hx+(ex-hx)*out(v),y=hy-20+(ey-hy+20)*out(v)
|
||||
if(v<=0)continue
|
||||
if(v<.95){c.lineWidth=2.5;const b=Math.atan2(ey-hy,ex-hx);for(const d of[-6,6])c.beginPath(),c.moveTo(x-cos(b)*30+d*sin(b),y-sin(b)*30-d*cos(b)),c.lineTo(x-cos(b)*12+d*sin(b),y-sin(b)*12-d*cos(b)),c.stroke();const q=cos(v*T*2);card(c,x,y,v*T*1.5,Math.sign(q||1)*max(.3,Math.abs(q)),1.25)}
|
||||
else{const f=(u-o-.62*.95)/.25;if(f<1){c.lineWidth=4;c.fillStyle='#fff';burst(c,ex,ey,18*(1.2-.6*f),8);c.fill();c.stroke();c.fillStyle=lt;c.fillStyle='#111';c.beginPath();c.arc(ex,ey,4*(1-f),0,T);c.fill()}}}}
|
||||
for(const[l,s]of[[22,'#111'],[12,'#111']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(118,186),c.quadraticCurveTo(140+20*k,170-30*k,hx-4,hy+12),c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(hx-14,hy+8);c.lineTo(hx+2,hy+20);c.stroke();c.strokeStyle='#111'
|
||||
if(hold)for(const a of[-.35,0,.35])card(c,hx+2+sin(a)*22,hy-20-cos(a)*18,a,1,.9)
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy+2,10,10,0,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.roundRect(hx-4,hy-20,7,18,3);c.fill();c.stroke();c.beginPath();c.roundRect(hx+4,hy-18,7,17,3);c.fill();c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// manga girl in a furisode nervously unrolls a shrine fortune slip, it reads 大吉 (great luck), her eyes turn to stars and sparkles burst
|
||||
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 star=(c,x,y,r,k=.4)=>{c.beginPath();for(let i=0;i<10;i++){const q=i%2?r*k:r,b=i*PI/5-PI/2;c.lineTo(x+cos(b)*q,y+sin(b)*q)}c.closePath()}
|
||||
const spark=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x,y,x+r,y);c.quadraticCurveTo(x,y,x,y+r);c.quadraticCurveTo(x,y,x-r,y);c.quadraticCurveTo(x,y,x,y-r);c.fill();c.stroke()}
|
||||
const eye=(c,x,y,d,joy)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,10.5,14,0,0,T);c.clip();c.fillStyle='#111';c.fill();c.fillStyle='#777';c.beginPath();c.ellipse(x,y+13,10,6,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#fff';if(joy){c.lineWidth=0;star(c,x,y+1,9,.45);c.fill();c.beginPath();c.arc(x+5,y+10,1.8,0,T);c.fill()}else{c.beginPath();c.ellipse(x-4,y-4,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+4,y+8,2,0,T);c.fill()}
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-5);c.quadraticCurveTo(x,y-20,x+16,y-5);c.moveTo(x+d*14,y-8);c.lineTo(x+d*21,y-13);c.stroke()
|
||||
}
|
||||
const kanji=(c,x,y)=>{
|
||||
c.lineWidth=5;c.beginPath()
|
||||
c.moveTo(x-34,y-6);c.lineTo(x-6,y-6);c.moveTo(x-20,y-17);c.lineTo(x-20,y-6);c.quadraticCurveTo(x-22,y+8,x-34,y+15);c.moveTo(x-19,y-2);c.quadraticCurveTo(x-14,y+9,x-5,y+15)
|
||||
c.moveTo(x+6,y-14);c.lineTo(x+34,y-14);c.moveTo(x+20,y-19);c.lineTo(x+20,y-5);c.moveTo(x+11,y-6);c.lineTo(x+29,y-6)
|
||||
c.moveTo(x+9,y);c.lineTo(x+9,y+16);c.moveTo(x+9,y);c.lineTo(x+31,y);c.lineTo(x+31,y+16);c.moveTo(x+9,y+14);c.lineTo(x+31,y+14);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
const w=16+152*(t<.3?0:t<.5?ease((t-.3)/.2):t<.88?1:1-ease((t-.88)/.12)),joy=t>=.5&&t<.9,nerv=t<.3
|
||||
// long white wavy hair behind
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128,36);c.bezierCurveTo(200,36,214,100,206,150)
|
||||
for(let k=0;k<4;k++)c.quadraticCurveTo(222-(k%2)*18,166+k*20,210-(k%2?0:10),182+k*20)
|
||||
c.lineTo(46,246);for(let k=3;k>=0;k--)c.quadraticCurveTo(34+(k%2)*18,166+k*20,46+(k%2?0:10),166+k*20)
|
||||
c.lineTo(50,150);c.bezierCurveTo(42,100,56,36,128,36);c.fill();c.stroke()
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const k of[0,1,2])c.beginPath(),c.moveTo(128+d*(70+k*8),110+k*14),c.bezierCurveTo(128+d*(82+k*6),150+k*10,128+d*(70+k*8),190,128+d*(78+k*6),226-k*6),c.stroke()
|
||||
// furisode: crossed collar, sleeves with flower pattern
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(102,188);c.quadraticCurveTo(60,196,52,252);c.lineTo(204,252);c.quadraticCurveTo(196,196,154,188);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle='#fff';c.lineWidth=3;for(const[x,y]of[[72,226],[184,214],[100,246],[170,244],[64,250]])for(let k=0;k<5;k++){const b=k*T/5;c.beginPath();c.ellipse(x+cos(b)*6,y+sin(b)*6,5,3.5,b,0,T);c.fill();c.stroke()}c.restore()
|
||||
c.lineWidth=5;for(const[a,b,x,y]of[[100,188,136,246],[156,188,118,246]])for(const[l,st]of[[17,'#111'],[8,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.lineCap='butt',c.beginPath(),c.moveTo(a,b),c.lineTo(x,y),c.stroke()
|
||||
c.lineCap='round';c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#111';c.beginPath();c.moveTo(58,236);c.lineTo(198,236);c.lineTo(202,252);c.lineTo(54,252);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(60,244);c.lineTo(196,244);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.fillRect(116,166,24,22);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.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,140,-1,joy);eye(c,153,140,1,joy)
|
||||
c.lineWidth=4;c.beginPath();if(nerv)c.moveTo(88,116),c.lineTo(112,122),c.moveTo(168,116),c.lineTo(144,122);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128,155);c.lineTo(126,158);c.stroke()
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*40+k-3,166),c.lineTo(128+d*40+k+3,158),c.stroke()
|
||||
c.lineWidth=4.5;c.beginPath()
|
||||
if(joy)c.moveTo(112,164),c.quadraticCurveTo(128,190,144,164),c.closePath(),c.fillStyle='#111',c.fill(),c.stroke(),c.save(),c.clip(),c.fillStyle='#fff',c.beginPath(),c.ellipse(128,180,8,5,0,0,T),c.fill(),c.restore()
|
||||
else if(nerv)c.moveTo(117,170),c.quadraticCurveTo(121,165,125,170),c.quadraticCurveTo(129,175,133,170),c.quadraticCurveTo(137,165,140,170),c.stroke()
|
||||
else c.ellipse(128,170,6,7,0,0,T),c.fillStyle='#111',c.fill(),c.stroke()
|
||||
// white bangs with strands
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(66,156)
|
||||
c.bezierCurveTo(50,74,88,36,128,36);c.bezierCurveTo(168,36,206,74,190,156)
|
||||
c.quadraticCurveTo(186,122,178,104);c.quadraticCurveTo(174,110,168,114);c.quadraticCurveTo(160,98,148,94);c.quadraticCurveTo(144,106,138,112)
|
||||
c.quadraticCurveTo(130,96,120,92);c.quadraticCurveTo(116,104,108,110);c.quadraticCurveTo(100,98,92,98);c.quadraticCurveTo(88,108,84,114)
|
||||
c.quadraticCurveTo(80,108,78,104);c.quadraticCurveTo(70,124,66,156);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3.5;for(const[a,b,x,y]of[[100,52,90,84],[124,44,116,78],[150,48,154,80],[174,64,176,92]])c.beginPath(),c.moveTo(a,b),c.quadraticCurveTo((a+x)/2+5,(b+y)/2,x,y),c.stroke()
|
||||
// kanzashi flower with dangling beads
|
||||
for(const[x,y,r]of[[70,72,11],[84,58,9],[62,56,8]]){c.fillStyle='#fff';c.lineWidth=3.5;for(let k=0;k<5;k++){const b=k*T/5;c.beginPath();c.ellipse(x+cos(b)*r*.6,y+sin(b)*r*.6,r*.55,r*.38,b,0,T);c.fill();c.stroke()}c.fillStyle='#111';c.beginPath();c.arc(x,y,r*.28,0,T);c.fill()}
|
||||
const sw=3*sin(t*T*2);c.lineWidth=2.5;for(const k of[0,1]){const x=64+k*10;c.beginPath();c.moveTo(x,80);c.lineTo(x+sw,104+k*6);c.stroke();c.fillStyle=k?'#111':'#fff';c.beginPath();c.arc(x+sw,108+k*6,4,0,T);c.fill();c.stroke()}
|
||||
if(nerv){c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(192,96);c.quadraticCurveTo(202,112,192,116);c.quadraticCurveTo(182,112,192,96);c.fill();c.stroke()}
|
||||
// reveal lines and sparkles
|
||||
if(t>=.48&&t<.62){const u=(t-.48)/.14;c.lineWidth=4;for(let k=0;k<10;k++){const b=PI+k*PI/9,r=60+u*30;c.beginPath();c.moveTo(128+cos(b)*r*1.4,212+sin(b)*r*.9);c.lineTo(128+cos(b)*(r+16)*1.4,212+sin(b)*(r+16)*.9);c.stroke()}}
|
||||
if(joy){c.fillStyle='#fff';c.lineWidth=3;for(let k=0;k<4;k++){const p=(t*3+k/4)%1,[x,y]=[[30,180],[224,176],[40,120],[216,120]][k];spark(c,x,y-p*14,4+9*sin(PI*p))}}
|
||||
// the fortune slip, rolled ends in her hands
|
||||
const L=128-w/2,R=128+w/2
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.rect(L,192,w,40);c.fill();c.stroke()
|
||||
c.save();c.beginPath();c.rect(L+2,194,w-4,36);c.clip();c.lineWidth=2;c.strokeRect(54,197,148,30);kanji(c,128,212)
|
||||
c.lineWidth=2.5;for(const x of[64,192])for(const y of[202,210,218])c.beginPath(),c.moveTo(x-4,y),c.lineTo(x+4,y),c.stroke();c.restore()
|
||||
for(const x of[L,R]){c.lineWidth=4.5;c.fillStyle='#fff';c.beginPath();c.roundRect(x-6,186,12,52,5);c.fill();c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(x-3,190);c.lineTo(x-3,234);c.stroke()}
|
||||
for(const d of[-1,1]){const x=d<0?L-8:R+8;c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(x,214,11,14,0,0,T);c.fill();c.stroke();c.lineWidth=3;c.beginPath();c.moveTo(x-d*4,204);c.lineTo(x-d*9,206);c.moveTo(x-d*4,214);c.lineTo(x-d*10,215);c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// manga oni girl with messy white hair, little horns and a fang grin, playfully swinging a studded kanabo club over her shoulder
|
||||
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()
|
||||
const eye=(c,x,y,d,tn)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,11,14,0,0,T);c.clip();c.fillStyle=tn;c.fillRect(x-12,y-14,24,30);c.fillStyle='#111';c.fillRect(x-12,y-14,24,11);c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11,14,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,4,7,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-4,y-5,4,5,-.4,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-7);c.quadraticCurveTo(x,y-20,x+16,y-7);c.moveTo(x+d*15,y-8);c.lineTo(x+d*22,y-14);c.moveTo(x+d*16,y-4);c.lineTo(x+d*21,y-5);c.stroke()
|
||||
}
|
||||
// tiger stripes: tapered black wedges coming in from both sides of a shape
|
||||
const stripes=(c,x0,x1,y0,y1)=>{c.fillStyle='#111';for(let y=y0,k=0;y<y1;y+=17,k++)for(const[e,d]of[[x0,1],[x1,-1]]){const l=26+(k*7+(d>0?0:11))%18;c.beginPath();c.moveTo(e-d*4,y-5);c.quadraticCurveTo(e+d*l*.6,y-2,e+d*l,y+3);c.quadraticCurveTo(e+d*l*.5,y+3,e-d*4,y+6);c.fill()}}
|
||||
const club=(c,x,y,a,tn)=>{
|
||||
c.save();c.translate(x,y);c.rotate(a)
|
||||
const sh=()=>{c.beginPath();c.moveTo(-14,-5);c.lineTo(20,-7);c.lineTo(92,-14);c.quadraticCurveTo(106,-14,106,0);c.quadraticCurveTo(106,14,92,14);c.lineTo(20,7);c.lineTo(-14,5);c.closePath()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';sh();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(20,2,90,16);c.restore();sh();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(20,-7);c.lineTo(20,7);c.stroke()
|
||||
c.lineWidth=2.5;c.fillStyle='#fff';for(const[u,v]of[[36,-3],[52,4],[68,-6],[84,5],[98,-4],[60,-9],[90,-9],[44,7],[76,0],[98,7]])c.beginPath(),c.arc(u,v,3.6,0,T),c.fill(),c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat'),a=-1.32+.44*sin(t*T),da=cos(t*T),up=sin(t*T)>.55
|
||||
const P=[176,202]
|
||||
const H=f=>{c.save();c.translate(110,104);c.scale(.8,.8);c.translate(-128,-120);f();c.restore()}
|
||||
// long messy hair behind, then the body, then the club, so the club rests in front of the hair
|
||||
H(()=>{
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128,40);c.bezierCurveTo(60,40,54,120,58,170);c.lineTo(50,208);c.lineTo(70,196);c.lineTo(72,222);c.lineTo(88,200);c.lineTo(100,190)
|
||||
c.lineTo(156,190);c.lineTo(168,200);c.lineTo(184,222);c.lineTo(186,196);c.lineTo(206,208);c.lineTo(198,170);c.bezierCurveTo(202,120,196,40,128,40);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(const d of[-1,1])c.moveTo(128+d*62,130),c.lineTo(128+d*68,196),c.moveTo(128+d*52,150),c.lineTo(128+d*48,206);c.stroke()
|
||||
})
|
||||
// body: tiger-print haori over a white shirt
|
||||
c.save();c.translate(-14,0)
|
||||
c.lineWidth=5;const body=()=>{c.beginPath();c.moveTo(102,168);c.quadraticCurveTo(64,176,52,252);c.lineTo(196,252);c.quadraticCurveTo(186,176,146,168);c.closePath()}
|
||||
c.fillStyle='#fff';body();c.fill();c.save();c.clip();stripes(c,52,196,182,252);c.restore();body();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(108,168);c.lineTo(124,252);c.lineTo(132,252);c.lineTo(142,168);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(118,170);c.lineTo(126,190);c.lineTo(134,170);c.stroke()
|
||||
// rope belt knot
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(94,234);c.quadraticCurveTo(128,242,176,232);c.stroke();c.beginPath();c.arc(150,238,6,0,T);c.fillStyle='#fff';c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(114,150,24,24);c.fill();c.lineWidth=4;c.beginPath();c.moveTo(114,150);c.lineTo(114,168);c.moveTo(138,150);c.lineTo(138,168);c.stroke()
|
||||
c.restore()
|
||||
// swing trail behind the club tip
|
||||
if(Math.abs(da)>.5){c.lineWidth=3;const s=Math.sign(da);for(const[r,l]of[[112,.5],[98,.38],[84,.28]])c.beginPath(),c.arc(P[0],P[1],r,a-s*(l+.08),a-s*.1,s<0),c.stroke()}
|
||||
club(c,P[0],P[1],a,tn)
|
||||
// arm up to the fist holding the club
|
||||
for(const[l,s]of[[22,'#111'],[13,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(146,192),c.quadraticCurveTo(178,234,P[0],P[1]),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
c.save();c.translate(P[0],P[1]);c.rotate(a);c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.ellipse(-2,0,11,12,0,0,T);c.fill();c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(-4,-8);c.lineTo(-4,8);c.stroke();c.restore()
|
||||
// head
|
||||
c.save();c.translate(110,104);c.scale(.8,.8);c.translate(-128,-120)
|
||||
// horns
|
||||
for(const d of[-1,1]){c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(128+d*22,46);c.quadraticCurveTo(128+d*26,20,128+d*40,4);c.quadraticCurveTo(128+d*42,26,128+d*38,48);c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(128+d*36-8,0,16,50);c.restore();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128+d*24,34);c.lineTo(128+d*39,34);c.moveTo(128+d*28,22);c.lineTo(128+d*39,21);c.stroke()}
|
||||
c.lineWidth=6;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(up){c.lineWidth=6;for(const x of[103,153])c.beginPath(),c.moveTo(x-14,144),c.quadraticCurveTo(x,128,x+14,144),c.stroke()}
|
||||
else{eye(c,103,140,-1,tn);eye(c,153,140,1,tn)}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*44+k-3,168),c.lineTo(128+d*44+k+3,160),c.stroke()
|
||||
// wide grin with one fang
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(110,164);c.quadraticCurveTo(128,170,148,162);c.quadraticCurveTo(142,184,128,184);c.quadraticCurveTo(114,184,110,164);c.closePath()
|
||||
c.save();c.fillStyle='#111';c.fill();c.clip();c.fillStyle='#fff';c.beginPath();c.ellipse(130,186,12,7,0,0,T);c.fill();c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(134,166);c.lineTo(140,165);c.lineTo(137,174);c.closePath();c.fill();c.stroke()
|
||||
// messy white hair with strand lines
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(70,148)
|
||||
c.bezierCurveTo(50,96,64,52,104,40);c.lineTo(114,30);c.lineTo(122,38);c.lineTo(138,28);c.lineTo(144,40)
|
||||
c.bezierCurveTo(196,66,202,110,186,150);c.lineTo(180,120);c.lineTo(170,128);c.lineTo(168,104);c.lineTo(156,116);c.lineTo(148,92);c.lineTo(136,112);c.lineTo(126,90);c.lineTo(114,112)
|
||||
c.lineTo(104,94);c.lineTo(96,118);c.lineTo(86,106);c.lineTo(80,130);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(const[x0,y0,x1,y1]of[[100,52,92,86],[122,46,118,80],[146,48,152,82],[166,62,174,98],[84,70,78,104]])c.moveTo(x0,y0),c.lineTo(x1,y1);c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// flustered girl with white space buns overheating: hatched blush across her face, hands on her cheeks, shaking while steam blasts out of her head
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=16,delay=70,rim=4
|
||||
const puff=(c,x,y,r)=>{const b=[[0,0,1],[-.8,.3,.7],[.8,.3,.7],[-.4,-.5,.75],[.45,-.45,.7]];c.lineWidth=7;for(const[a,e,k]of b)c.beginPath(),c.arc(x+a*r,y+e*r,k*r,0,T),c.stroke();c.fillStyle='#fff';for(const[a,e,k]of b)c.beginPath(),c.arc(x+a*r,y+e*r,k*r,0,T),c.fill()}
|
||||
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)=>{
|
||||
for(let k=0;k<4;k++){const p=(2*t+k/4)%1,r=(10+p*18)*sin(PI*Math.min(1,p*1.2+.08));if(r>5)puff(c,128+[-1,1,-.4,.4][k]*(16+p*52),76-p*50,r)}
|
||||
c.save();c.translate(128,250);c.scale(.8,.8);c.translate(-128,-250);c.translate(i%2?2:-2,i%4<2?1:-1)
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,56,248);c.lineTo(200,248);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(108,192);c.lineTo(128,212);c.lineTo(148,192);c.stroke();c.lineWidth=2.5;for(const y of[222,238])c.beginPath(),c.arc(128,y,3,0,T),c.stroke()
|
||||
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()
|
||||
for(const d of[-1,1]){const x=128+d*38;c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.arc(x,40,22,0,T);c.fill();c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(x-16,30);c.quadraticCurveTo(x,40,x+18,34);c.moveTo(x-18,44);c.quadraticCurveTo(x,54,x+16,48);c.stroke()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';face(c);c.fill();c.stroke()
|
||||
c.save();face(c);c.clip();c.lineWidth=2;c.beginPath();for(let x=70;x<200;x+=4.5)c.moveTo(x,166),c.lineTo(x+12,146);c.stroke();c.restore()
|
||||
c.lineWidth=6;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=2.5;c.fillStyle='#fff';for(const d of[-1,1]){const y=150+(i%4)*3;c.beginPath();c.moveTo(128+d*40,y);c.quadraticCurveTo(128+d*44,y+8,128+d*40,y+10);c.quadraticCurveTo(128+d*36,y+8,128+d*40,y);c.fill();c.stroke()}
|
||||
c.lineWidth=3.5;c.fillStyle='#fff';c.beginPath();c.moveTo(114,168);for(let k=1;k<=7;k++)c.lineTo(114+k*4,168+(k%2?-3:3));c.lineTo(142,174);c.quadraticCurveTo(128,182,114,174);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(70,148);c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,148)
|
||||
c.quadraticCurveTo(186,118,178,104);c.quadraticCurveTo(172,106,166,114);c.quadraticCurveTo(160,100,150,94);c.quadraticCurveTo(146,106,138,110);c.quadraticCurveTo(134,98,126,92)
|
||||
c.quadraticCurveTo(120,104,112,108);c.quadraticCurveTo(108,98,98,94);c.quadraticCurveTo(92,104,88,112);c.quadraticCurveTo(82,106,78,104);c.quadraticCurveTo(70,122,70,148);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const[a,b,e,f]of[[98,48,90,82],[128,42,126,74],[158,48,168,82]])c.beginPath(),c.moveTo(a,b),c.quadraticCurveTo((a+e)/2+5,(b+f)/2,e,f),c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*30,55,13,5.5,d*.55,0,T),c.fill()
|
||||
for(const d of[-1,1]){c.save();c.translate(128+d*50,168);c.rotate(-d*.25);c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(-12,14);c.lineTo(-13,-10)
|
||||
for(let k=0;k<4;k++)c.arc(-9+k*6,-12-(k%3?3:0),3.2,PI,0);c.lineTo(12,14);c.quadraticCurveTo(0,20,-12,14);c.fill();c.stroke();c.lineWidth=2.5;c.beginPath();for(const k of[-3,3])c.moveTo(k,-10),c.lineTo(k,0);c.stroke();c.restore()}
|
||||
c.lineWidth=4;for(const d of[-1,1])for(let k=0;k<3;k++){const a=-PI/2+d*(.45+k*.32),r1=88+(i%2)*4;c.beginPath();c.moveTo(128+cos(a)*r1,112+sin(a)*r1);c.lineTo(128+cos(a)*(r1+14),112+sin(a)*(r1+14));c.stroke()}
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// girl with crosshatched double buns throws a paper airplane that loops around and glides back into her hand
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,atan2,hypot,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 xt=createCanvas(6,6),h=xt.getContext('2d');h.strokeStyle='#111';h.lineWidth=1.2;for(const o of[-6,0,6])h.beginPath(),h.moveTo(o,6),h.lineTo(o+6,0),h.moveTo(o,0),h.lineTo(o+6,6),h.stroke()
|
||||
const toned=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
const CX=192,CY=114,R=44
|
||||
const at=u=>[CX+R*sin(T*u),CY+R*cos(T*u),atan2(-sin(T*u),cos(T*u))]
|
||||
const plane=(c,x,y,a)=>{c.save();c.translate(x,y);c.rotate(a);c.lineWidth=3.5;c.fillStyle='#fff'
|
||||
c.beginPath();c.moveTo(22,0);c.lineTo(-18,-12);c.lineTo(-10,0);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(22,0);c.lineTo(-10,0);c.lineTo(-16,9);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(22,0);c.lineTo(-14,4);c.stroke();c.restore()}
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),hat=c.createPattern(xt,'repeat')
|
||||
const fl=t>=.15&&t<.9,lin=fl?(t-.15)/.75:0,u=(1-cos(PI*lin))/2
|
||||
const pull=t<.15?sin(PI*t/.15):0,hx=CX-4-16*pull+(fl?6:0),hy=CY+R+8+4*pull-(fl?3:0)
|
||||
const[px,py,pa]=fl?at(u):[hx+4,hy-6,-.15*pull]
|
||||
c.save();c.translate(-34,46);c.scale(.82,.82)
|
||||
// double buns and back hair
|
||||
c.lineWidth=6;for(const d of[-1,1])c.beginPath(),c.arc(128+d*44,46,20,0,T),toned(c,hat)
|
||||
c.beginPath();c.moveTo(128,36);c.bezierCurveTo(194,36,206,96,198,150);c.lineTo(58,150);c.bezierCurveTo(50,96,62,36,128,36);toned(c,hat)
|
||||
// gym shirt with a name patch
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,58,244);c.lineTo(198,244);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(106,191);c.quadraticCurveTo(128,212,150,191);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.rect(90,214,30,18);c.stroke();c.lineWidth=2;c.beginPath();c.moveTo(95,223);c.lineTo(115,223);c.stroke()
|
||||
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()
|
||||
// eyes follow the plane
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
const dx=px+34-.82*x,dy=py-46-.82*140,n=hypot(dx,dy)||1,ox=3.5*dx/n,oy=3.5*dy/n
|
||||
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+ox,141+oy,10,14,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+ox-3,135+oy,4.5,0,T);c.fill();c.beginPath();c.arc(x+ox+4,148+oy,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-d*12,130);c.quadraticCurveTo(x,119,x+d*15,131);c.lineTo(x+d*21,125);c.stroke()
|
||||
}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(let k=0;k<4;k++)c.beginPath(),c.moveTo(128+d*40+(k-1.5)*5-3,170),c.lineTo(128+d*40+(k-1.5)*5+3,161),c.stroke()
|
||||
c.lineWidth=3.5;c.fillStyle='#111';c.beginPath();c.moveTo(117,165);c.quadraticCurveTo(128,182,139,165);c.closePath();c.fill();c.stroke()
|
||||
// crosshatched bangs with a white shine
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(70,146);c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,146)
|
||||
c.quadraticCurveTo(184,114,174,104);c.quadraticCurveTo(168,112,160,112);c.quadraticCurveTo(156,98,146,94);c.quadraticCurveTo(140,106,128,110)
|
||||
c.quadraticCurveTo(122,98,110,94);c.quadraticCurveTo(106,106,96,112);c.quadraticCurveTo(88,112,82,104);c.quadraticCurveTo(72,114,70,146);c.closePath();toned(c,hat)
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<=30;k++){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?52:48)*cos(a),104+(k%2?52:48)*sin(a))}for(let k=30;k>=0;k--){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?44:40)*cos(a),104+(k%2?44:40)*sin(a))}c.fill()
|
||||
c.restore()
|
||||
// throwing arm
|
||||
c.lineWidth=16;c.beginPath();c.moveTo(92,212);c.quadraticCurveTo(hx-46,hy+22,hx,hy);c.stroke();c.strokeStyle='#fff';c.lineWidth=9;c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(104,206,14,12,-.5,0,T);c.fill();c.stroke()
|
||||
// trail while it flies
|
||||
if(fl){c.lineWidth=3;for(let k=1;k<=3;k++){const v=u-k*.045;if(v<.02)break;const[a,b]=at(v),[a2,b2]=at(v-.02);c.beginPath();c.moveTo(a,b);c.lineTo(a2,b2);c.stroke()}}
|
||||
if(!fl)plane(c,px,py,pa)
|
||||
c.fillStyle='#fff';c.lineWidth=4.5;c.beginPath();c.ellipse(hx,hy,11,12,0,0,T);c.fill();c.stroke()
|
||||
if(fl)plane(c,px,py,pa)
|
||||
// a sparkle when it lands back
|
||||
if(t>=.9){const s=10*sin(PI*(t-.9)/.1);c.fillStyle='#fff';c.lineWidth=3;c.beginPath();for(let k=0;k<8;k++){const a=k*PI/4,r=k%2?s*.3:s;c.lineTo(hx+22+r*cos(a),hy-24+r*sin(a))}c.closePath();c.fill();c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// chibi girl turns to stone from the feet up, cracks spread, she crumbles into a pile, then rewinds back together and the stone fades off
|
||||
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 cl=v=>min(1,max(0,v)),rnd=k=>{const v=sin(k*127.1+311.7)*43758.5;return v-Math.floor(v)}
|
||||
const XS=[50,90,128,166,206],YS=[30,64,96,128,160,192,222,250]
|
||||
const pt=(a,b)=>[XS[a]+(a%4?(rnd(a*9+b)-.5)*18:0),YS[b]+(b%7?(rnd(a+b*13)-.5)*16:0)]
|
||||
const girl=(c,shock)=>{
|
||||
c.lineWidth=4.5;c.fillStyle='#fff'
|
||||
c.beginPath();c.moveTo(106,140);c.lineTo(150,140);c.lineTo(170,208);c.quadraticCurveTo(128,216,86,208);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(112,140);c.quadraticCurveTo(128,156,144,140);c.lineTo(140,138);c.quadraticCurveTo(128,148,116,138);c.closePath();c.stroke()
|
||||
c.lineWidth=2;for(const x of[104,118,138,152])c.beginPath(),c.moveTo(x,186),c.lineTo(x+(x-128)*.25,209),c.stroke()
|
||||
c.lineWidth=4.5;for(const d of[-1,1]){c.beginPath();c.moveTo(128+d*12,212);c.lineTo(128+d*12,234);c.stroke();c.fillStyle='#111';c.beginPath();c.ellipse(128+d*14,238,10,6,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(128+d*22,146);c.quadraticCurveTo(128+d*36,170,128+d*30,190);c.lineTo(128+d*22,186);c.quadraticCurveTo(128+d*26,170,128+d*16,154);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.arc(128+d*27,192,6,0,T);c.fill();c.stroke()}
|
||||
c.save();c.translate(128,90);c.scale(.62,.62);c.translate(-128,-118)
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128,36);c.bezierCurveTo(196,36,212,100,204,166);for(let k=0;k<5;k++)c.quadraticCurveTo(196-k*30,186,186-k*30-8,170+(k%2)*6);c.lineTo(52,166);c.bezierCurveTo(44,100,60,36,128,36);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()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){const y=136;c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,18,0,0,T);c.fill()
|
||||
if(shock){c.lineWidth=3.5;c.stroke()}else{c.fillStyle='#111';c.beginPath();c.ellipse(x,y+2,11,15,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+4,y+8,2.5,0,T);c.fill()}
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-8);c.quadraticCurveTo(x,y-21,x+16,y-8);c.moveTo(x+d*15,y-9);c.lineTo(x+d*22,y-14);c.stroke()}
|
||||
c.lineWidth=4.5;c.beginPath();if(shock){c.fillStyle='#fff';c.ellipse(128,170,7,9,0,0,T);c.fill()}else c.arc(128,162,9,.2*PI,.8*PI);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(72,140);c.bezierCurveTo(56,74,88,36,128,36);c.bezierCurveTo(168,36,200,74,184,140);c.quadraticCurveTo(180,108,168,98);c.quadraticCurveTo(160,110,150,112)
|
||||
c.quadraticCurveTo(148,98,138,92);c.quadraticCurveTo(130,106,118,110);c.quadraticCurveTo(116,98,104,94);c.quadraticCurveTo(98,106,88,110);c.quadraticCurveTo(86,100,82,98);c.quadraticCurveTo(74,112,72,140);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=12;c.beginPath();c.arc(128,112,62,PI*1.2,PI*1.8);c.stroke();c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.arc(128,112,62,PI*1.3,PI*1.4);c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=3;for(const[a,b,e,f]of[[98,52,92,84],[150,50,160,86]])c.beginPath(),c.moveTo(a,b),c.quadraticCurveTo((a+e)/2+5,(b+f)/2,e,f),c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
export default(c,t,i)=>{
|
||||
const o=createCanvas(256,256),x=o.getContext('2d')
|
||||
Object.assign(x,{lineWidth:7,lineCap:'round',lineJoin:'round',strokeStyle:'#111',fillStyle:'#fff'})
|
||||
const ys=t<.12?250:t<.36?250-(t-.12)/.24*226:t<.9?24:24+(t-.9)/.1*226,ck=t<.32?0:t<.9?cl((t-.32)/.12):0
|
||||
const fall=t<.48?0:t<.66?(t-.48)/.18:t<.74?1:t<.9?1-(t-.74)/.16:0
|
||||
girl(x,t>=.12&&t<.9)
|
||||
if(ys<250){x.save();x.globalCompositeOperation='source-atop';x.beginPath();x.rect(0,ys,256,256);x.clip()
|
||||
x.fillStyle='rgba(17,17,17,.14)';x.fillRect(0,0,256,256);x.lineWidth=1.1;x.strokeStyle='rgba(17,17,17,.6)';x.beginPath();for(let k=-260;k<260;k+=10)x.moveTo(k,256),x.lineTo(k+256,0),x.moveTo(k,0),x.lineTo(k+256,256);x.stroke()
|
||||
x.lineWidth=4;x.strokeStyle='#111';x.lineJoin='miter';x.restore()}
|
||||
if(ck){x.save();x.globalCompositeOperation='source-atop';x.lineWidth=3;x.lineJoin='miter'
|
||||
for(const[sx,sy,n]of[[128,60,7],[96,150,5],[150,176,6],[118,214,4]]){x.beginPath();x.moveTo(sx,sy);let px=sx,py=sy;for(let k=0;k<n*ck;k++){px+=(rnd(sx+k)-.5)*30;py+=12+rnd(sy+k)*8;x.lineTo(px,py)}x.stroke()}x.restore()}
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(128,244,70,7,0,0,T);c.fill();c.stroke()
|
||||
if(!fall){c.save();c.translate(t>=.4&&t<.48?(i%2?2:-2):0,0);c.drawImage(o,0,0);c.restore();return}
|
||||
const f=fall*fall*(3-2*fall)
|
||||
for(let a=0;a<4;a++)for(let b=0;b<7;b++){
|
||||
const q=[pt(a,b),pt(a+1,b),pt(a+1,b+1),pt(a,b+1)],cx=q.reduce((s,p)=>s+p[0],0)/4,cy=q.reduce((s,p)=>s+p[1],0)/4,k=a*7+b
|
||||
const d=cl(f*1.5-(1-cy/250)*.5),ex=(cx-128)*.55+(rnd(k)-.5)*30,ey=max(0,226-cy-rnd(k+5)*18-(6-b)*2),r=(rnd(k+9)-.5)*2.4*d
|
||||
c.save();c.translate(cx+ex*d,cy+ey*d*d);c.rotate(r);c.translate(-cx,-cy);c.beginPath();q.forEach(([px,py],n)=>n?c.lineTo(px,py):c.moveTo(px,py));c.closePath();c.clip();c.drawImage(o,0,0);c.restore()}
|
||||
if(fall>.5&&t<.74){const p=(t-.57)/.17;c.fillStyle='#fff';c.lineWidth=3;for(const d of[-1,1]){const r=5+6*sin(PI*cl(p));c.beginPath();c.arc(128+d*(60+p*14),234-p*8,r,0,T);c.fill();c.stroke()}}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// manga girl with messy white hair squeezes a pillow over her blushing face and rocks side to side kicking her feet, hearts popping
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max,abs}=Math,T=2*PI
|
||||
export const frames=12,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 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 DOT=tone(c,6,1.3),a=.2*sin(t*T*2),sq=1+.04*abs(sin(t*T*4))
|
||||
// hearts popping out to the sides
|
||||
c.lineWidth=3.5;for(let k=0;k<4;k++){const p=(t*2+k/4)%1,d=k%2?1:-1,s=1.3*sin(PI*p);if(s>.15){c.fillStyle=k%3?'#fff':'#111';heart(c,128+d*(70+p*30),120-p*70+(k>1?40:0),s)}}
|
||||
c.save();c.translate(128,248);c.rotate(a);c.translate(-128,-248)
|
||||
// kicking feet behind the pillow
|
||||
for(const d of[-1,1]){const k=8*sin(t*T*4+(d>0?PI:0));c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(128+d*30,236-max(0,k),13,9,d*.3,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;for(const j of[-5,0,5])c.beginPath(),c.arc(128+d*30+j,229-max(0,k),2,PI,0),c.stroke()}
|
||||
// messy white hair behind
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128,30);c.bezierCurveTo(196,30,212,92,204,140);c.lineTo(214,160);c.lineTo(198,162);c.lineTo(204,184);c.lineTo(52,184);c.lineTo(58,162);c.lineTo(42,160);c.lineTo(52,140);c.bezierCurveTo(44,92,60,30,128,30);c.fill();c.stroke()
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(const k of[0,1])c.beginPath(),c.moveTo(128+d*(66+k*8),96+k*16),c.quadraticCurveTo(128+d*(80+k*6),130,128+d*(70+k*10),170),c.stroke()
|
||||
// pajama shoulders with stars
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,176);c.quadraticCurveTo(62,184,56,228);c.lineTo(200,228);c.quadraticCurveTo(194,184,156,176);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
// face, half buried
|
||||
c.lineWidth=6;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()
|
||||
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=2.5;for(const d of[-1,1])for(let k=-3;k<=3;k++){const x=128+d*44+k*6;c.beginPath();c.moveTo(x+3,146);c.lineTo(x-4,162);c.stroke()}c.restore()
|
||||
c.lineWidth=7;for(const d of[-1,1]){const x=128+d*25;c.beginPath();c.moveTo(x-d*13,124);c.lineTo(x+d*11,136);c.lineTo(x-d*13,148);c.stroke()}
|
||||
c.lineWidth=5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*16,112),c.lineTo(128+d*36,104),c.stroke()
|
||||
// messy bangs with stray strands
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(70,142)
|
||||
c.bezierCurveTo(52,70,88,36,128,36);c.bezierCurveTo(168,36,204,70,186,142)
|
||||
c.lineTo(178,104);c.lineTo(168,114);c.lineTo(162,92);c.lineTo(150,106);c.lineTo(140,88);c.lineTo(128,104);c.lineTo(116,86);c.lineTo(106,104);c.lineTo(96,92);c.lineTo(88,112);c.lineTo(78,104);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(118,38);c.quadraticCurveTo(104,16,90,22);c.moveTo(140,38);c.quadraticCurveTo(152,12,170,20);c.moveTo(184,72);c.quadraticCurveTo(206,64,212,76);c.stroke()
|
||||
c.lineWidth=3.5;for(const[x0,y0,x1,y1]of[[104,50,96,76],[130,46,132,74],[156,52,164,78]])c.beginPath(),c.moveTo(x0,y0),c.quadraticCurveTo((x0+x1)/2+5,(y0+y1)/2,x1,y1),c.stroke()
|
||||
// pillow squeezed in her arms
|
||||
c.save();c.translate(128,190);c.scale(sq,2-sq);c.translate(-128,-190)
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(70,160);c.quadraticCurveTo(128,148,186,160);c.quadraticCurveTo(196,196,186,234);c.quadraticCurveTo(128,244,70,234);c.quadraticCurveTo(60,196,70,160);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.setLineDash([5,5]);c.beginPath();c.moveTo(78,168);c.quadraticCurveTo(128,158,178,168);c.quadraticCurveTo(186,196,178,226);c.quadraticCurveTo(128,234,78,226);c.quadraticCurveTo(70,196,78,168);c.stroke();c.setLineDash([])
|
||||
c.fillStyle=DOT;c.lineWidth=4;c.beginPath();c.moveTo(128,218);c.bezierCurveTo(100,200,108,176,128,188);c.bezierCurveTo(148,176,156,200,128,218);c.fill();c.stroke()
|
||||
c.lineWidth=5;for(const[x,y,d]of[[70,160,-1],[186,160,1],[70,234,-1],[186,234,1]])c.beginPath(),c.moveTo(x,y),c.lineTo(x+d*8,y+(y<190?-6:6)),c.moveTo(x,y),c.lineTo(x+d*10,y+(y<190?2:-2)),c.stroke()
|
||||
c.restore()
|
||||
// arms wrapped around the pillow
|
||||
for(const d of[-1,1]){for(const[l,st]of[[22,'#111'],[13,'#fff'],[13,DOT]])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(128+d*64,190),c.quadraticCurveTo(128+d*60,218,128+d*26,210),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=4.5;c.fillStyle='#fff';c.beginPath();c.ellipse(128+d*20,208,9,8,0,0,T);c.fill();c.stroke()}
|
||||
c.restore()
|
||||
// wiggle lines
|
||||
c.lineWidth=4;for(const d of[-1,1]){const o=a*d>0?1:.4;c.beginPath();c.moveTo(128+d*96,170);c.quadraticCurveTo(128+d*104,180,128+d*96,190);c.quadraticCurveTo(128+d*88,200,128+d*96,210);c.globalAlpha=o;c.stroke();c.globalAlpha=1}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// manga pirate captain with an eye patch and wavy ink hair: lifts her spyglass to her eye, extends it, scans, spots something, collapses and lowers it; coat and hair whipping in the wind
|
||||
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)
|
||||
const eye=(c,x,y,d,tn)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x+2,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=tn;c.fillRect(x-12,y-14,28,32);c.fillStyle='#111';c.fillRect(x-12,y-14,28,13);c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x+2,y+2,11.5,15,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+2,y+3,4.5,7,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-2,y-5,4.5,5.5,-.4,0,T);c.fill();c.beginPath();c.arc(x+6,y+8,2.2,0,T);c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-17,y-6);c.quadraticCurveTo(x,y-21,x+17,y-6);c.moveTo(x+d*16,y-7);c.lineTo(x+d*23,y-12);c.moveTo(x+d*17,y-2);c.lineTo(x+d*23,y-3);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-8,y+19);c.lineTo(x+8,y+19);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat'),w=sin(t*T*2),spot=t>.5&&t<.66
|
||||
// lift: spyglass from her chest up to her eye and back; ex: segments slide out only while it's up
|
||||
const lf=ease((t-.08)/.12)-ease((t-.8)/.12),ex=ease((t-.22)/.12)-ease((t-.68)/.1),sa0=-.28+.1*sin((t-.34)/.34*T)*(t>.34&&t<.68)
|
||||
const H=f=>{c.save();c.translate(108,98);c.scale(.62,.62);c.translate(-128,-120);f();c.restore()}
|
||||
// wavy ink hair streaming left behind
|
||||
H(()=>{c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(128,50);c.bezierCurveTo(60,40,40,100,36,150)
|
||||
for(const[x,y,k]of[[-6+8*w,170,0],[18,190,1],[-20+10*w,214,2],[30,226,3],[80,230,4]])c.quadraticCurveTo(x+20,y-20+4*sin(t*T*2+k),x,y)
|
||||
c.lineTo(180,200);c.bezierCurveTo(196,140,190,50,128,50);c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(60,110);c.quadraticCurveTo(30,150,10+6*w,170);c.moveTo(70,150);c.quadraticCurveTo(46,190,0+8*w,212);c.stroke();c.strokeStyle='#111'})
|
||||
// coat with flapping tails, screentone, big white cuffs and a jabot
|
||||
const ct=()=>{c.beginPath();c.moveTo(92,156);c.quadraticCurveTo(64,170,60,206);c.quadraticCurveTo(40,226+6*w,22+8*w,246+4*w);c.quadraticCurveTo(50,242,64,252);c.lineTo(168,252);c.quadraticCurveTo(164,176,128,156);c.closePath()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';ct();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(0,150,180,110);c.restore();ct();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(60,206);c.quadraticCurveTo(44,228,34+7*w,242);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(96,158);c.lineTo(110,252);c.lineTo(132,252);c.lineTo(126,158);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(112,160);for(let y=160,k=0;y<230;y+=10,k++)c.quadraticCurveTo(k%2?98:126,y+5,112,y+10);c.lineTo(112,160);c.fill();c.lineWidth=3;c.stroke()
|
||||
c.beginPath();c.moveTo(112,166);c.lineTo(112,226);c.stroke()
|
||||
c.fillStyle='#111';for(const y of[214,232])c.beginPath(),c.arc(140,y,4,0,T),c.fill()
|
||||
// head
|
||||
H(()=>{
|
||||
c.lineWidth=7;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 patch over her left eye, strap across the forehead
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(74,112);c.lineTo(96,134);c.moveTo(112,130);c.lineTo(182,104);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(104,141,12,10,-.2,0,T);c.fill()
|
||||
// her uncovered eye, big and bright; the eyepiece covers it while she looks
|
||||
eye(c,153,140,1,tn)
|
||||
// grin and blush
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(112,168);c.quadraticCurveTo(128,184,146,166);c.quadraticCurveTo(128,172,112,168);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;for(const x of[85,171])for(const k of[-5,1,7])c.beginPath(),c.moveTo(x+k-3,170),c.lineTo(x+k+3,162),c.stroke()
|
||||
// wavy ink bangs
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(70,150);c.bezierCurveTo(52,74,86,44,128,44);c.bezierCurveTo(170,44,204,74,186,150);c.quadraticCurveTo(184,118,176,104)
|
||||
c.quadraticCurveTo(168,110,160,108);c.quadraticCurveTo(158,94,146,88);c.quadraticCurveTo(138,104,128,108);c.quadraticCurveTo(118,96,108,90);c.quadraticCurveTo(102,106,92,110);c.quadraticCurveTo(84,104,80,100);c.quadraticCurveTo(72,120,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;for(const[a,e]of[[1.34,1.46],[1.54,1.64]])c.beginPath(),c.arc(128,110,52,PI*a,PI*e),c.stroke();c.strokeStyle='#111'
|
||||
// tricorn hat with skull and crossbones
|
||||
const hat=()=>{c.beginPath();c.moveTo(40,82);c.quadraticCurveTo(70,66,84,40);c.quadraticCurveTo(128,4,172,40);c.quadraticCurveTo(186,66,216,82);c.quadraticCurveTo(170,74,128,86);c.quadraticCurveTo(86,74,40,82);c.closePath()}
|
||||
c.lineWidth=7;c.fillStyle='#111';hat();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(48,80);c.quadraticCurveTo(86,70,128,80);c.quadraticCurveTo(170,70,208,80);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(114,44);c.lineTo(142,66);c.moveTo(142,44);c.lineTo(114,66);c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(128,46,9,0,T);c.fill();c.fillRect(123,50,10,7);c.fillStyle='#111';c.beginPath();c.arc(124.5,46,2.5,0,T);c.arc(131.5,46,2.5,0,T);c.fill()
|
||||
c.strokeStyle='#111'
|
||||
})
|
||||
// spyglass from her right eye, segments sliding out
|
||||
const ey=[156-38*lf,178-68*lf],L=[34,30*ex,26*ex],sa=-.95+(.95+sa0)*lf
|
||||
c.save();c.translate(...ey);c.rotate(sa)
|
||||
let x=4;const seg=[[34,13,true],[30,10.5,false],[26,8,false]]
|
||||
for(let k=2;k>=0;k--){const o=4+L.slice(0,k).reduce((a,b)=>a+b,0)-(k?6:0),[l,r,tone]=seg[k];if(k&&!ex)continue
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.rect(o,-r,l,2*r);c.fill();if(tone){c.save();c.clip();c.fillStyle=tn;c.fillRect(o,0,l,r);c.restore()}c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(o+l-3,-r-2);c.lineTo(o+l-3,r+2);c.stroke()}
|
||||
const tip=4+L[0]+L[1]+L[2]-(ex?12:0)
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.ellipse(tip+(ex?0:0),0,4,ex?9:13,0,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
// hand on the scope
|
||||
const hp=[ey[0]+30*cos(sa),ey[1]+30*sin(sa)+12]
|
||||
for(const[l,s]of[[20,'#111'],[11,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(132,172),c.quadraticCurveTo(170,186-14*lf,hp[0],hp[1]),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.ellipse(hp[0],hp[1]-2,10,9,0,0,T);c.fill();c.stroke()
|
||||
// she spotted something
|
||||
if(spot){const g=min(1,(t-.55)/.06);c.lineWidth=4;c.fillStyle='#fff';c.save();c.translate(178,40);c.scale(1.35*g,1.35*g);c.beginPath();c.moveTo(-5,-20);c.lineTo(5,-20);c.lineTo(2,4);c.lineTo(-2,4);c.closePath();c.fill();c.stroke();c.beginPath();c.arc(0,12,4.5,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;for(const a of[-2.6,-.5])c.beginPath(),c.moveTo(cos(a)*16,sin(a)*16-4),c.lineTo(cos(a)*24,sin(a)*24-4),c.stroke();c.restore()}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// manga chibi princess twirling in a ball gown: skirt folds sweep around, hem flares, curly white hair swings, sparkles circle her
|
||||
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 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 star=(c,x,y,s)=>{c.beginPath();for(let k=0;k<8;k++){const r=k%2?s*.3:s,a=k*PI/4;c.lineTo(x+r*cos(a),y+r*sin(a))}c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat'),r=t*T,sw=sin(r),fl=1+.06*sin(2*r)
|
||||
// sparkles circling, the ones behind her first
|
||||
const sp=k=>{const a=r+k*T/5;return[128+100*cos(a),168+22*sin(a),sin(a),k]}
|
||||
const drawSp=([x,y,z,k])=>{c.fillStyle='#fff';c.lineWidth=2.5;star(c,x,y,(5+3*z)*(1+.3*sin(r*3+k)))}
|
||||
for(let k=0;k<5;k++){const p=sp(k);if(p[2]<0)drawSp(p)}
|
||||
// curly white hair behind: a ringlet lock down each side, swinging out with the spin
|
||||
for(const d of[-1,1]){const X=x=>128+d*x,o=-d*10*sw,lk=()=>{c.beginPath();c.moveTo(X(26),46);c.quadraticCurveTo(X(58),46,X(60),80)
|
||||
for(let k=0;k<4;k++){const y=90+k*20,ox=o*(k+1)/4;c.quadraticCurveTo(X(70)+ox,y+4,X(60)+ox,y+12)}
|
||||
c.quadraticCurveTo(X(54)+o,182,X(42)+o,172);for(let k=3;k>=0;k--){const y=90+k*20,ox=o*k/4;c.quadraticCurveTo(X(34)+ox,y+8,X(40)+ox,y-4)}c.closePath()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';lk();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(let k=0;k<4;k++){const y=96+k*20,ox=o*(k+.5)/4;c.moveTo(X(42)+ox,y);c.quadraticCurveTo(X(54)+ox,y+4,X(60)+ox,y+12)}c.stroke()}
|
||||
// ball gown: bell skirt whose folds sweep around as she turns
|
||||
const W=82*fl,hem=x=>222+5*sin((x-128)/W*PI*3-r*2)
|
||||
const sk=()=>{c.beginPath();c.moveTo(112,136);c.bezierCurveTo(100,170,128-W*.8,190,128-W,hem(128-W));for(let x=128-W;x<=128+W;x+=4)c.lineTo(x,hem(x));c.bezierCurveTo(128+W*.8,190,156,170,144,136);c.closePath()}
|
||||
c.lineWidth=5;c.fillStyle='#fff';sk();c.fill()
|
||||
c.save();c.clip()
|
||||
for(let k=0;k<8;k++){const a=r+k*T/8,z=sin(a);if(z<0)continue;const x=128+W*cos(a)
|
||||
c.fillStyle=k%2?tn:'#fff';c.beginPath();c.moveTo(128+16*cos(a),136);c.quadraticCurveTo(128+W*.6*cos(a),190,x,232);const a2=a+T/8;c.lineTo(128+W*cos(a2),232);c.quadraticCurveTo(128+W*.6*cos(a2),190,128+16*cos(a2),136);c.closePath();if(k%2)c.fill()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128+16*cos(a),136);c.quadraticCurveTo(128+W*.6*cos(a),190,x,232);c.stroke()}
|
||||
c.restore();sk();c.stroke()
|
||||
// ruffled hem
|
||||
c.lineWidth=3;c.beginPath();for(let x=128-W+8;x<=128+W-8;x+=12)c.moveTo(x+6,hem(x)-6),c.arc(x,hem(x)-6,6,0,PI);c.stroke()
|
||||
// bodice with puffed sleeves and a bow
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(110,104);c.lineTo(146,104);c.lineTo(142,140);c.quadraticCurveTo(128,146,114,140);c.closePath();c.fill();c.save();c.clip();c.fillStyle=tn;c.fillRect(100,100,56,50);c.restore();c.stroke()
|
||||
for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*22,110,11,9,d*.4,0,T),c.fillStyle='#fff',c.fill(),c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3.5;for(const d of[-1,1])c.beginPath(),c.moveTo(128,140),c.lineTo(128+d*12,132),c.lineTo(128+d*12,148),c.closePath(),c.fill(),c.stroke()
|
||||
// arms: one raised in a flourish, one out to hold the skirt
|
||||
for(const[pts,hx,hy]of[[[146,112,170,96],174,70],[[110,112,92,128],80,146]]){for(const[l,s]of[[11,'#111'],[5,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(pts[0],pts[1]),c.quadraticCurveTo(pts[2],pts[3],hx,hy),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.arc(hx,hy,5.5,0,T);c.fill();c.stroke()}
|
||||
// head
|
||||
c.save();c.translate(128,66);c.scale(.48,.48);c.translate(-128,-120)
|
||||
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()
|
||||
// happy closed eyes with lashes
|
||||
c.lineWidth=7;for(const[x,d]of[[103,-1],[153,1]]){c.beginPath();c.moveTo(x-16,144);c.quadraticCurveTo(x,126,x+16,144);c.stroke();c.lineWidth=4;c.beginPath();c.moveTo(x+d*14,138);c.lineTo(x+d*22,132);c.moveTo(x+d*16,144);c.lineTo(x+d*24,142);c.stroke();c.lineWidth=7}
|
||||
c.lineWidth=3.5;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=5;c.fillStyle='#fff';c.beginPath();c.moveTo(114,166);c.quadraticCurveTo(128,186,142,166);c.closePath();c.fill();c.stroke()
|
||||
// wavy white bangs with strand lines
|
||||
c.lineWidth=8;c.fillStyle='#fff';c.beginPath();c.moveTo(70,146);c.bezierCurveTo(52,74,88,40,128,40);c.bezierCurveTo(168,40,204,74,186,146)
|
||||
c.quadraticCurveTo(184,114,172,102);c.quadraticCurveTo(166,112,156,114);c.quadraticCurveTo(152,96,138,90);c.quadraticCurveTo(134,106,124,110);c.quadraticCurveTo(118,96,104,92)
|
||||
c.quadraticCurveTo(100,108,90,112);c.quadraticCurveTo(84,106,80,102);c.quadraticCurveTo(70,118,70,146);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();for(const[a,b,e,f]of[[98,56,88,90],[118,50,116,84],[148,52,154,84],[166,62,176,98]])c.moveTo(a,b),c.quadraticCurveTo((a+e)/2+6,(b+f)/2,e,f);c.stroke()
|
||||
// tiara with jewels and a glint
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(88,62);c.lineTo(100,36);c.lineTo(112,52);c.lineTo(128,20);c.lineTo(144,52);c.lineTo(156,36);c.lineTo(168,62);c.quadraticCurveTo(128,50,88,62);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';for(const[x,y,s]of[[128,42,7],[102,50,4],[154,50,4]])c.beginPath(),c.arc(x,y,s,0,T),c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(126,40,2.5,0,T);c.fill()
|
||||
const g=max(0,sin(r*2));if(g>.3){c.lineWidth=4;star(c,150,14,18*g)}
|
||||
c.restore()
|
||||
for(let k=0;k<5;k++){const p=sp(k);if(p[2]>=0)drawSp(p)}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// spiky-haired girl in a headband throws a punch straight at the viewer: the fist swells into a burst with focus lines
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,min,hypot}=Math,T=2*PI
|
||||
export const frames=12,delay=80,rim=4
|
||||
const dots=(col,r)=>{const k=createCanvas(6,6),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[3,3],[0,0],[6,0],[0,6],[6,6]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const TONE=dots('#111',1.4),LITE=dots('#fff',1.3)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u)
|
||||
const burst=(c,x,y,R,n,o)=>{c.beginPath();for(let k=0;k<n*2;k++){const r=k%2?R*.6:R*(1+.12*sin(k*2.3+o)),a=k*PI/n+o*.1;c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.closePath()}
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.save();c.beginPath();c.moveTo(x-16,y-4+d*4);c.lineTo(x+16,y-4-d*4);c.lineTo(x+16,y+20);c.lineTo(x-16,y+20);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+1,y+4,10,12,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-2,y,3.5,0,T);c.fill();c.beginPath();c.arc(x+4,y+9,1.8,0,T);c.fill();c.restore()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-15,y-4+d*4);c.lineTo(x+15,y-4-d*4);c.lineTo(x+d*20,y-8-d*d*5);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-6,y+18);c.lineTo(x+6,y+18);c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-13,y-20+d*6);c.lineTo(x+13,y-20-d*6);c.stroke()
|
||||
}
|
||||
const fist=c=>{
|
||||
c.lineWidth=5;c.fillStyle='#fff'
|
||||
c.beginPath();c.roundRect(-30,24,60,30,6);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(let k=-2;k<=3;k++)c.moveTo(-30+k*12,24),c.lineTo(-18+k*12,54);c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.roundRect(-38,-30,76,62,22);c.fill();c.stroke()
|
||||
for(let k=0;k<4;k++){c.beginPath();c.roundRect(-38+k*19,-32,19,32,9);c.fill();c.stroke()}
|
||||
c.beginPath();c.moveTo(-38,4);c.quadraticCurveTo(-36,22,-10,22);c.lineTo(20,20);c.quadraticCurveTo(28,12,22,4);c.lineTo(-30,4);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(let k=0;k<4;k++)c.moveTo(-31+k*19,-26),c.lineTo(-31+k*19,-20);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TONE,'repeat')
|
||||
const k=t<.3?0:t<.4?ease((t-.3)/.1):t<.7?1:1-ease((t-.7)/.3),hit=t>=.4&&t<.7,f=(t*12|0)%2
|
||||
const fx=206-44*k,fy=186-16*k,s=.5+.85*k,tr=t<.3?1.5*sin(t*T*9):0
|
||||
if(hit){c.lineWidth=3;c.fillStyle='#111'
|
||||
for(let j=0;j<28;j++){const a=j*T/28+f*.11,dx=cos(a),dy=sin(a),e=min(dx>0?(248-fx)/dx:(fx-8)/-dx,dy>0?(248-fy)/dy:(fy-8)/-dy)-2,r=96+(j%3)*6;if(e>r+8){const w=.03;c.beginPath();c.moveTo(fx+cos(a-w)*e,fy+sin(a-w)*e);c.lineTo(fx+dx*r,fy+dy*r);c.lineTo(fx+cos(a+w)*e,fy+sin(a+w)*e);c.closePath();c.fill()}}}
|
||||
c.save();c.translate(96+tr-10*k,124-6*k);c.rotate(-.08*k);c.scale(.86,.86);c.translate(-128,-128)
|
||||
c.lineWidth=6;c.fillStyle='#fff'
|
||||
for(const d of[-1,1]){const w=hit?6*sin(t*T*6+d):3*sin(t*T*2+d);c.beginPath();c.moveTo(66,92);c.quadraticCurveTo(40,96+d*10,20,100+d*16+w);c.lineTo(26,110+d*16+w);c.quadraticCurveTo(44,106+d*8,64,100);c.closePath();c.fill();c.stroke()}
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(70,90);c.quadraticCurveTo(52,170,62,214);c.lineTo(78,204);c.lineTo(84,222);c.lineTo(172,222);c.lineTo(178,204);c.lineTo(194,214);c.quadraticCurveTo(204,170,186,90);c.closePath();c.fill();c.stroke();c.fillStyle='#fff'
|
||||
c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(58,198,48,262);c.lineTo(208,262);c.quadraticCurveTo(198,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=tn;c.fillRect(40,186,180,80);c.fillStyle='#fff';for(const x of[70,74,182,186])c.fillRect(x,186,3,80);c.restore();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(108,188);c.lineTo(116,206);c.lineTo(128,198);c.lineTo(140,206);c.lineTo(148,188);c.closePath();c.fill();c.lineWidth=4;c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(128,200);c.lineTo(128,262);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.rect(116,166,24,28);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,192);c.moveTo(140,168);c.lineTo(140,192);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,138,-1);eye(c,154,138,1)
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const j of[-5,0,5])c.beginPath(),c.moveTo(128+d*44+j-3,164),c.lineTo(128+d*44+j+3,156),c.stroke()
|
||||
const m=hit?1:.35;c.lineWidth=4;c.fillStyle='#111';c.beginPath();c.moveTo(110,160);c.lineTo(146,160);c.quadraticCurveTo(144,160+22*m,128,162+22*m);c.quadraticCurveTo(112,160+22*m,110,160);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(112,161);c.lineTo(144,161);c.lineTo(142,166);c.lineTo(114,166);c.fill()
|
||||
if(hit){c.beginPath();c.moveTo(116,160+20*m);c.lineTo(140,160+20*m);c.lineTo(138,156+20*m);c.lineTo(118,156+20*m);c.fill()}
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(127,150);c.lineTo(125,154);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(70,150)
|
||||
c.lineTo(62,120);c.lineTo(54,124);c.lineTo(60,92);c.lineTo(46,84);c.lineTo(70,66);c.lineTo(62,46);c.lineTo(92,48);c.lineTo(96,24);c.lineTo(118,40);c.lineTo(134,18);c.lineTo(146,40);c.lineTo(172,26);c.lineTo(170,52);c.lineTo(198,52);c.lineTo(186,74);c.lineTo(208,88);c.lineTo(192,98);c.lineTo(200,124);c.lineTo(188,120);c.lineTo(186,150)
|
||||
c.lineTo(180,112);c.lineTo(168,118);c.lineTo(162,102);c.lineTo(150,114);c.lineTo(142,98);c.lineTo(130,114);c.lineTo(122,98);c.lineTo(110,116);c.lineTo(104,100);c.lineTo(92,116);c.lineTo(88,102);c.lineTo(78,114);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();for(const[a,b]of[[1.2,1.32],[1.42,1.5],[1.6,1.7]])c.arc(128,104,52,PI*a,PI*b);c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(72,86);c.quadraticCurveTo(128,70,184,86);c.lineTo(184,100);c.quadraticCurveTo(128,84,72,100);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(70,94,9,8,0,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
if(hit){c.lineWidth=5;c.fillStyle='#fff';burst(c,fx+6,fy-4,72+6*f,13,f);c.fill();c.stroke();c.fillStyle=tn;burst(c,fx+6,fy-4,56,13,f+1);c.fill()}
|
||||
c.lineWidth=5;for(const[l,st]of[[44*s+10,'#111'],[44*s,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(176,250),c.quadraticCurveTo(200-30*k,214-20*k,fx+6*s,fy+30*s),c.stroke()
|
||||
c.strokeStyle='#111';c.save();c.translate(fx,fy);c.scale(s,s);fist(c);c.restore()
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// girl in a hoodie with screentone twin tails suddenly gets it: focus lines rush in, a "!" pops over her head and her eyes go wide
|
||||
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 DK=(()=>{const t=createCanvas(6,6),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(3,3,1.4,0,T);x.fill();return t})()
|
||||
const toned=(c,path)=>{path();c.fillStyle='#fff';c.fill();c.fillStyle=c.createPattern(DK,'repeat');c.fill();c.stroke()}
|
||||
export default(c,t,i)=>{
|
||||
const L=[0,0,0,0,0,1,1,1,1,1,1,1,1,.55,.2,0][i],on=i>=5&&i<=12,fl=on?max(0,1-(i-5)/4):0
|
||||
if(L){c.fillStyle='#111';for(let k=0;k<44;k++){const a=k*T/44+((k*37)%11)/40,ro=118,ri=ro-L*(30+((k*53+i*7)%7)*6),w=2.6/ro
|
||||
c.beginPath();c.moveTo(128+cos(a-w)*ro,128+sin(a-w)*ro);c.lineTo(128+cos(a)*ri,128+sin(a)*ri);c.lineTo(128+cos(a+w)*ro,128+sin(a+w)*ro);c.closePath();c.fill()}}
|
||||
c.lineWidth=5
|
||||
for(const d of[-1,1]){c.save();c.translate(128+d*58,70);c.rotate(-d*(.12+.4*fl))
|
||||
toned(c,()=>{c.beginPath();c.moveTo(0,-6);c.bezierCurveTo(d*44,0,d*50,70,d*28,128);c.quadraticCurveTo(d*22,142,d*12,148);c.quadraticCurveTo(d*20,90,-d*4,10);c.closePath()})
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(d*12,14);c.bezierCurveTo(d*30,40,d*36,80,d*26,120);c.stroke();c.lineWidth=5
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(d*2,0,8,10,d*.5,0,T);c.fill();c.stroke();c.restore()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(84,184);c.quadraticCurveTo(128,170,172,184);c.lineTo(186,204);c.quadraticCurveTo(128,186,70,204);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(100,192);c.quadraticCurveTo(62,200,56,246);c.lineTo(200,246);c.quadraticCurveTo(194,200,156,192);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.rect(116,166,24,26);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=3.5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*14,198),c.quadraticCurveTo(128+d*16,218,128+d*12,232),c.stroke(),c.beginPath(),c.arc(128+d*12,235,3.5,0,T),c.fillStyle='#fff',c.fill(),c.stroke()
|
||||
c.lineWidth=6;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(on){c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.ellipse(x,y,14,18,0,0,T);c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.arc(x,y+1,4.5,0,T);c.fill()
|
||||
c.lineWidth=4;c.beginPath();c.arc(x,y-14,16,PI*1.15,PI*1.85);c.stroke()}
|
||||
else{c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,17,0,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.ellipse(x-3,y-1,10,14,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-7,y-6,4.5,0,T);c.fill();c.beginPath();c.arc(x+1,y+7,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-15,y-7);c.quadraticCurveTo(x,y-19,x+15,y-7);c.moveTo(x+d*14,y-8);c.lineTo(x+d*20,y-13);c.stroke()}
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(x-10,y-(on?42:30));c.quadraticCurveTo(x,y-(on?48:33),x+10,y-(on?42:31));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(on){c.fillStyle='#111';c.ellipse(128,170,5,6.5,0,0,T);c.fill()}else{c.moveTo(121,169);c.lineTo(133,167);c.stroke()}
|
||||
const hair=()=>{c.beginPath();c.moveTo(70,144);c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,144)
|
||||
c.quadraticCurveTo(184,114,174,100);c.lineTo(166,112);c.lineTo(160,96);c.lineTo(148,114);c.lineTo(140,94);c.lineTo(126,116);c.lineTo(116,94);c.lineTo(106,114);c.lineTo(96,96);c.lineTo(88,112);c.lineTo(82,100);c.quadraticCurveTo(72,114,70,144);c.closePath()}
|
||||
c.lineWidth=6;hair();c.fillStyle='#fff';c.fill();c.save();c.clip();c.fillStyle=c.createPattern(DK,'repeat');c.fillRect(40,20,180,140)
|
||||
c.strokeStyle='#fff';c.lineWidth=10;for(const[a,e]of[[1.24,1.36],[1.44,1.56],[1.64,1.76]])c.beginPath(),c.arc(128,104,52,PI*a,PI*e),c.stroke();c.restore();c.strokeStyle='#111';c.lineWidth=6;hair();c.stroke()
|
||||
if(on){const p=i==5?1.35:i==6?.9:1+.06*sin(i*2.1)
|
||||
c.save();c.translate(214,58);c.scale(p,p);c.rotate(.12);c.fillStyle='#111';c.beginPath();c.moveTo(-9,-38);c.lineTo(9,-38);c.lineTo(3,6);c.lineTo(-3,6);c.closePath();c.fill();c.beginPath();c.arc(0,18,6,0,T);c.fill()
|
||||
c.lineWidth=4;c.beginPath();for(const a of[-2.5,-1.6,-.6])c.moveTo(cos(a)*24,-14+sin(a)*24),c.lineTo(cos(a)*34,-14+sin(a)*34);c.stroke();c.restore()}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// girl with long wavy white hair at the rooftop chain-link fence, wind streaming her hair and tie, clouds drifting behind the mesh
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI}=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 cloud=(c,x,y)=>{c.beginPath();for(const[dx,dy,r]of[[-26,4,14],[-6,-8,20],[16,-2,16],[30,6,10]])c.moveTo(x+dx+r,y+dy),c.arc(x+dx,y+dy,r,0,T);c.rect(x-28,y+4,58,14);c.save();c.lineWidth=8;c.stroke();c.restore();c.fill()}
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat')
|
||||
// clouds drifting between the tall posts above the rail
|
||||
c.save();c.beginPath();c.rect(20,6,216,40);c.clip();c.fillStyle='#fff';c.lineWidth=4
|
||||
for(let k=0;k<2;k++){const x=20+(k*108+t*108)%216;for(const o of[0,-216])c.save(),c.translate(x+o,28+k*4),c.scale(.7,.7),cloud(c,0,0),c.restore()}
|
||||
c.restore()
|
||||
// chain-link fence
|
||||
c.save();c.beginPath();c.rect(16,52,224,140);c.clip();c.lineWidth=2.5
|
||||
c.lineWidth=1.8;for(let x=-140;x<260;x+=30)c.beginPath(),c.moveTo(x,52),c.lineTo(x+140,192),c.moveTo(x+140,52),c.lineTo(x,192),c.stroke()
|
||||
c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=4
|
||||
for(const[x,y,w,h]of[[10,44,236,10],[10,190,236,8],[10,6,10,196],[236,6,10,196]])c.beginPath(),c.rect(x,y,w,h),c.fill(),c.stroke()
|
||||
c.save();c.translate(-28,6)
|
||||
// hair streaming right in the wind: tapered wavy locks behind the head
|
||||
c.lineWidth=5;c.fillStyle='#fff'
|
||||
for(let k=3;k>=0;k--){
|
||||
const L=124-k*4,yc=x=>62+k*30+(x-140)*.18+(x-140)*.14*sin(x*.055-t*T*2-k*.9),w=x=>21*Math.pow(1-(x-140)/L,.55)+.5
|
||||
c.beginPath();for(let x=140;x<=140+L;x+=3)c.lineTo(x,yc(x)-w(x));for(let x=140+L;x>=140;x-=3)c.lineTo(x,yc(x)+w(x));c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;c.beginPath();for(let x=150;x<=114+L;x+=3)c.lineTo(x,yc(x)-w(x)*.15);c.stroke();c.lineWidth=5
|
||||
}
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(80,150);c.bezierCurveTo(60,70,96,38,134,40);c.bezierCurveTo(176,42,196,90,180,188);c.lineTo(92,190);c.quadraticCurveTo(84,176,80,150);c.closePath();c.fill();c.stroke()
|
||||
// blazer in screentone, shirt, flapping tie
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(64,198,58,244);c.lineTo(198,244);c.quadraticCurveTo(192,198,156,190);c.closePath();c.fillStyle='#fff';c.fill();c.fillStyle=dot;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(108,191);c.lineTo(128,232);c.lineTo(148,191);c.closePath();c.fill();c.stroke()
|
||||
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()
|
||||
const f=sin(t*T*2),g=sin(t*T*2-1.2)
|
||||
c.fillStyle='#111';c.lineWidth=4;c.beginPath();c.moveTo(122,196);c.lineTo(134,196);c.quadraticCurveTo(152+6*f,206,170+8*g,206+6*g);c.lineTo(164+8*g,218+6*g);c.quadraticCurveTo(146+6*f,214,124,206);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.ellipse(128,198,7,5,0,0,T);c.fill()
|
||||
// face: calm half-lidded eyes looking into the wind
|
||||
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()
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,141,13,15,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x-3,142,10,14,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-6,136,4,0,T);c.fill();c.beginPath();c.arc(x+1,149,1.8,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-d*12,129);c.quadraticCurveTo(x,121,x+d*15,130);c.lineTo(x+d*21,126);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-6,157);c.lineTo(x+4,157);c.stroke()
|
||||
}
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(122,156);c.lineTo(120,160);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(118,169);c.quadraticCurveTo(126,174,134,168);c.stroke()
|
||||
// bangs pushed right by the wind
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(72,150)
|
||||
c.bezierCurveTo(56,74,90,40,134,40);c.bezierCurveTo(172,42,204,80,188,132)
|
||||
const bx=k=>3*sin(t*T*2-k)
|
||||
c.quadraticCurveTo(190,110,196+bx(0),104);c.quadraticCurveTo(180,100,172,110);c.quadraticCurveTo(170,96,178+bx(1),84);c.quadraticCurveTo(158,90,150,108)
|
||||
c.quadraticCurveTo(144,94,156+bx(2),80);c.quadraticCurveTo(132,86,126,108);c.quadraticCurveTo(118,92,130+bx(3),78);c.quadraticCurveTo(104,84,98,110)
|
||||
c.quadraticCurveTo(92,96,98,86);c.quadraticCurveTo(80,96,78,118);c.quadraticCurveTo(74,132,72,150);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const[a,b,e]of[[[96,58],[120,50],[146,56]],[[90,72],[112,62],[136,64]],[[150,50],[170,58],[184,74]]])c.beginPath(),c.moveTo(...a),c.quadraticCurveTo(...b,...e),c.stroke()
|
||||
c.restore()
|
||||
// wind streaks
|
||||
for(const[y,o]of[[214,0],[230,.5]]){const p=(t+o)%1,x=176+p*40,l=28*sin(PI*p);if(l>6)for(const[lw,st]of[[8,'#111'],[3,'#fff']]){c.lineWidth=lw;c.strokeStyle=st;c.beginPath();c.moveTo(x,y);c.bezierCurveTo(x+l*.4,y-10,x+l*.7,y+6,x+l,y-2);c.stroke()}}c.strokeStyle='#111'
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// scheming girl with a hatched shadow over her eyes, glasses glaring white, grins wide and rubs her hands together
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max,abs}=Math,T=2*PI
|
||||
export const frames=12,delay=90,rim=4
|
||||
const dots=(col,r,s=6)=>{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 TONE=dots('#111',1.5)
|
||||
const hand=(c,x,y,d)=>{c.save();c.translate(x,y);c.scale(d,1);c.rotate(.12)
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(0,46);c.lineTo(0,-30);c.quadraticCurveTo(-2,-40,-8,-40);c.quadraticCurveTo(-28,-36,-30,-10);c.lineTo(-30,30);c.quadraticCurveTo(-26,46,-14,50);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const fx of[-8,-16,-23])c.moveTo(fx,-34+abs(fx+12)*.8),c.lineTo(fx,-14);c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(-30,4);c.quadraticCurveTo(-38,-4,-34,-16);c.stroke()
|
||||
c.restore()}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TONE,'repeat'),r=6*sin(t*T*3),m=abs(sin(t*T*3))
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(84,196);c.quadraticCurveTo(28,206,20,252);c.lineTo(236,252);c.quadraticCurveTo(228,206,172,196);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(104,192);c.lineTo(128,232);c.lineTo(152,192);c.closePath();c.fill();c.lineWidth=5;c.stroke()
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(54,168);c.bezierCurveTo(40,110,52,40,128,38);c.bezierCurveTo(204,40,216,110,202,168);c.quadraticCurveTo(194,180,180,174);c.lineTo(76,174);c.quadraticCurveTo(62,180,54,168);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=7;c.beginPath();c.rect(114,170,28,28);c.fill();c.beginPath();c.moveTo(114,170);c.lineTo(114,194);c.moveTo(142,170);c.lineTo(142,194);c.stroke()
|
||||
const face=()=>{c.beginPath();c.moveTo(68,100);c.bezierCurveTo(64,160,100,194,128,196);c.bezierCurveTo(156,194,192,160,188,100);c.bezierCurveTo(188,46,68,46,68,100)}
|
||||
face();c.fill();c.save();face();c.clip();c.lineWidth=3;c.beginPath();for(let x=64;x<196;x+=6)c.moveTo(x,60),c.lineTo(x,138+4*sin(x*.3));c.stroke();c.restore();c.lineWidth=7;face();c.stroke()
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(84,154);c.quadraticCurveTo(128,186+6*m,172,154);c.quadraticCurveTo(160,174+8*m,128,180+8*m);c.quadraticCurveTo(96,174+8*m,84,154);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(86,156);c.quadraticCurveTo(128,170,170,156);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();for(const x of[104,116,128,140,152])c.moveTo(x,162),c.lineTo(x,170+5*m-abs(x-128)*.15);c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(80,150);c.quadraticCurveTo(82,158,88,158);c.moveTo(176,150);c.quadraticCurveTo(174,158,168,158);c.stroke()
|
||||
c.lineWidth=7;c.fillStyle='#fff';for(const x of[100,156]){c.beginPath();c.roundRect(x-25,110,50,30,8);c.fill();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(x-12,134);c.lineTo(x+4,114);c.moveTo(x-2,136);c.lineTo(x+12,118);c.stroke();c.lineWidth=7}
|
||||
c.beginPath();c.moveTo(125,122);c.quadraticCurveTo(128,116,131,122);c.moveTo(75,118);c.lineTo(64,114);c.moveTo(181,118);c.lineTo(192,114);c.stroke()
|
||||
if(t>=.33&&t<.67){const p=(t-.33)/.34,s=14*sin(PI*p);c.lineWidth=3.5;c.fillStyle='#fff';c.beginPath();c.moveTo(170,104-s);c.quadraticCurveTo(172,110,170+s,112);c.quadraticCurveTo(172,114,170,112+s);c.quadraticCurveTo(168,114,170-s,112);c.quadraticCurveTo(168,110,170,104-s+0);c.fill();c.stroke()}
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(62,128);c.bezierCurveTo(50,60,90,34,128,34);c.bezierCurveTo(166,34,206,60,194,128);c.quadraticCurveTo(186,96,166,80);c.quadraticCurveTo(150,92,128,70);c.quadraticCurveTo(106,92,90,80);c.quadraticCurveTo(70,96,62,128);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.save();c.translate(170,66);c.rotate(.5);c.beginPath();c.roundRect(-16,-5,32,10,4);c.fill();c.stroke();c.restore();c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(128,96,60,PI*1.22,PI*1.36);c.moveTo(128+60*cos(PI*1.62),96+60*sin(PI*1.62));c.arc(128,96,60,PI*1.62,PI*1.76);c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=4;for(const d of[-1,1])for(const[o,dx]of[[0,0],[.33,12],[.66,-8]]){const p=(t+o)%1,x=128+d*(96+dx),y=150-60*p,a=sin(PI*p);if(a>.15){c.beginPath();c.moveTo(x,y+14*a);c.bezierCurveTo(x+8*a,y+5*a,x-8*a,y-5*a,x,y-14*a);c.stroke()}}
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(76,112);c.lineTo(66,105);c.moveTo(180,112);c.lineTo(190,105);c.stroke()
|
||||
hand(c,128,236+r,-1);hand(c,128,236-r,1)
|
||||
c.lineWidth=3;for(const d of[-1,1])c.beginPath(),c.arc(128,226,40+4*m,d<0?PI*.85:-PI*.15,d<0?PI*1.05:PI*.05),c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// sailor-uniform girl hit by a shock: lightning cracks open behind her, her eyes go blank and gloom lines run down her face, then she blinks back
|
||||
const{min,PI}=Math,T=2*PI
|
||||
export const frames=16,delay=80,rim=4
|
||||
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)}
|
||||
const eye=(c,x,y,d,blank,shut)=>{
|
||||
if(shut){c.lineWidth=5;c.beginPath();c.moveTo(x-14,y+2);c.quadraticCurveTo(x,y+9,x+14,y+2);c.moveTo(x+d*13,y+3);c.lineTo(x+d*19,y-1);c.stroke();return}
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,17,0,0,T);c.fill()
|
||||
if(blank){c.lineWidth=3;c.stroke()}
|
||||
else{
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+2,10.5,14.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-4,4.5,0,T);c.fill();c.beginPath();c.arc(x+4,y+8,2.2,0,T);c.fill()
|
||||
}
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-15,y-8);c.quadraticCurveTo(x,y-21,x+15,y-8);c.moveTo(x+d*14,y-9);c.lineTo(x+d*21,y-14);c.stroke()
|
||||
}
|
||||
const bolt=(c,f)=>{c.beginPath();[[24,10],[78,10],[60,54],[92,52],[30,136],[48,78],[14,82]].forEach(([x,y],k)=>c[k?'lineTo':'moveTo'](f?256-x:x,y));c.closePath();c.fill();c.stroke()}
|
||||
export default(c,t,i)=>{
|
||||
const s=i>=4&&i<=12,g=s?min(1,(i-3)/4):0,j=s?(i%2?1:-1):0
|
||||
c.translate(j,s?-2:0)
|
||||
if(s){
|
||||
const p=i==4?1.12:1
|
||||
c.save();c.translate(128,70);c.scale(p,p);c.translate(-128,-70);c.fillStyle='#fff';c.lineWidth=5;bolt(c,0);bolt(c,1);c.restore()
|
||||
}
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(128,36);c.bezierCurveTo(198,36,210,100,204,170);c.lineTo(208,244);c.lineTo(48,244);c.lineTo(52,170);c.bezierCurveTo(46,100,58,36,128,36);c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(64,198,58,246);c.lineTo(198,246);c.quadraticCurveTo(192,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.rect(116,166,24,28);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=16;c.beginPath();c.moveTo(96,194);c.lineTo(128,226);c.lineTo(160,194);c.stroke();c.strokeStyle='#fff';c.lineWidth=2.5;c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(114,222);c.lineTo(142,222);c.lineTo(128,244);c.closePath();c.fill()
|
||||
c.lineWidth=6;c.fillStyle='#fff';face(c);c.fill();c.stroke()
|
||||
eye(c,103,140,-1,s,i==14);eye(c,153,140,1,s,i==14)
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128,155);c.lineTo(127,158);c.stroke()
|
||||
c.lineWidth=3.5
|
||||
if(s){c.fillStyle='#fff';c.beginPath();c.ellipse(128,171,6+i%2,8-i%2,0,0,T);c.fill();c.stroke()}
|
||||
else{c.beginPath();c.arc(128,162,8,.2*PI,.8*PI);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,164),c.lineTo(128+d*42+k+3,157),c.stroke()}
|
||||
if(s){c.save();face(c);c.clip();c.lineWidth=2.2;for(let x=80;x<180;x+=6){c.beginPath();c.moveTo(x,60);c.lineTo(x,60+96*g*(.7+.3*((x*7)%5)/5));c.stroke()}c.restore()}
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(74,150);c.bezierCurveTo(58,74,88,36,128,36);c.bezierCurveTo(168,36,198,74,182,150)
|
||||
c.lineTo(178,112);c.lineTo(160,110);c.lineTo(150,116);c.lineTo(140,109);c.lineTo(128,115);c.lineTo(114,109);c.lineTo(104,116);c.lineTo(94,110);c.lineTo(78,112);c.closePath();c.fill();c.stroke()
|
||||
for(const d of[-1,1]){c.beginPath();c.moveTo(128+d*58,100);c.lineTo(128+d*46,112);c.lineTo(128+d*46,186);c.lineTo(128+d*50,244);c.lineTo(128+d*70,244);c.lineTo(128+d*62,186);c.closePath();c.fill();c.stroke()}
|
||||
c.strokeStyle='#fff';c.lineWidth=5;for(const[a,e]of[[1.22,1.34],[1.42,1.58],[1.66,1.78]])c.beginPath(),c.arc(128,106,56,PI*a,PI*e),c.stroke()
|
||||
c.lineWidth=2.5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*55,150),c.lineTo(128+d*58,232),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
if(s&&i>5){c.fillStyle='#fff';c.lineWidth=3.5;const y=112+(i-6)*2;c.beginPath();c.moveTo(196,y-14);c.quadraticCurveTo(206,y,202,y+6);c.arc(196,y+5,6,0,PI);c.quadraticCurveTo(188,y,196,y-14);c.fill();c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// shoujo heroine with huge starry eyes, long wavy white hair drifting while bubbles rise and flowers turn around her
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,max}=Math,T=2*PI
|
||||
export const frames=12,delay=100,rim=4
|
||||
const tone=(f,r)=>{const t=createCanvas(5,5),x=t.getContext('2d');x.fillStyle=f;x.beginPath();x.arc(2.5,2.5,r,0,T);x.fill();return t}
|
||||
const DK=tone('#111',1.1)
|
||||
const star=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r);for(const[a,b]of[[1,0],[0,1],[-1,0],[0,-1]])c.quadraticCurveTo(x+(a+b)*r*.14,y+(b-a)*r*.14,x+a*r,y+b*r);c.fill()}
|
||||
const flower=(c,x,y,r,a)=>{
|
||||
c.save();c.translate(x,y);c.rotate(a);c.fillStyle='#fff';c.lineWidth=2.5
|
||||
for(let k=0;k<5;k++){c.save();c.rotate(k*T/5);c.beginPath();c.ellipse(0,-r*.56,r*.36,r*.54,0,0,T);c.fill();c.stroke();c.restore()}
|
||||
c.fillStyle='#111';c.beginPath();c.arc(0,0,r*.22,0,T);c.fill();c.restore()
|
||||
}
|
||||
const eye=(c,x,y,d,t)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,17,22,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,14,20,0,0,T);c.clip();c.fillStyle='#111';c.fillRect(x-20,y-24,40,50)
|
||||
c.restore();c.strokeStyle='#fff';c.lineWidth=1.6;c.beginPath();c.ellipse(x,y+4,10,15,0,PI*.15,PI*.85);c.stroke();c.strokeStyle='#111'
|
||||
const k=.75+.25*sin(2*T*t+d)
|
||||
c.fillStyle='#fff';star(c,x-5,y-7,9*k);c.beginPath();c.arc(x+6,y+8,3,0,T);c.fill();star(c,x+6,y-2,5*(1.75-k));c.beginPath();c.arc(x-6,y+12,1.8,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-19,y-8);c.quadraticCurveTo(x,y-29,x+19,y-8);c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();for(const[a,b,e,f]of[[13,-14,20,-23],[17,-9,26,-14],[19,-3,26,-3]])c.moveTo(x+d*a,y+b),c.lineTo(x+d*e,y+f);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-7,y+25);c.quadraticCurveTo(x,y+27,x+7,y+25);c.stroke()
|
||||
}
|
||||
const wv=(y,t,p)=>6*sin(T*t-y*.045+p)
|
||||
export default(c,t)=>{
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(128,34)
|
||||
c.bezierCurveTo(176,34,196,62,192,100)
|
||||
for(let y=100;y<=240;y+=10)c.lineTo(128+64+(y-100)*.28+wv(y,t,0),y)
|
||||
for(let y=240;y>=100;y-=10)c.lineTo(128-64-(y-100)*.28+wv(y,t,2),y)
|
||||
c.bezierCurveTo(60,62,80,34,128,34);c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const d of[-1,1])for(const o of[30]){c.beginPath();for(let y=120;y<=232;y+=8)c.lineTo(128+d*(o+20+(y-100)*.24)+wv(y,t,d<0?2:0),y);c.stroke()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(66,198,60,246);c.lineTo(196,246);c.quadraticCurveTo(190,198,156,190);c.closePath();c.fill();c.stroke()
|
||||
c.beginPath();c.rect(116,166,24,28);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=3.5;c.fillStyle='#fff';for(let k=0;k<6;k++){const a=PI*(.08+k*.168);c.beginPath();c.arc(128+cos(a)*30,192+sin(a)*12,7,0,T);c.fill();c.stroke()}
|
||||
for(const d of[-1,1]){c.fillStyle=c.createPattern(DK,'repeat');c.beginPath();c.moveTo(128,212);c.quadraticCurveTo(128+d*22,190,128+d*34,200);c.quadraticCurveTo(128+d*34,222,128,212);c.fill();c.stroke()
|
||||
c.beginPath();c.moveTo(128+d*2,214);c.lineTo(128+d*14,244);c.lineTo(128+d*22,236);c.closePath();c.fillStyle='#fff';c.fill();c.stroke()}
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(128,211,6,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=6;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,101,138,-1,t);eye(c,155,138,1,t)
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128,157);c.lineTo(127,160);c.stroke()
|
||||
c.lineWidth=3;for(const x of[101,155])c.beginPath(),c.arc(x,140,30,PI*1.4,PI*1.6),c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(121,170);c.quadraticCurveTo(128,175,135,170);c.stroke()
|
||||
c.lineWidth=2;for(const d of[-1,1])for(const k of[-6,-1,4])c.beginPath(),c.moveTo(128+d*44+k-3,170),c.lineTo(128+d*44+k+3,162),c.stroke()
|
||||
for(const d of[-1,1]){c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(128+d*52,100)
|
||||
for(let y=104;y<=214;y+=10)c.lineTo(128+d*(56+(y-100)*.1)+wv(y,t,d<0?2:0)*.7,y)
|
||||
for(let y=214;y>=104;y-=10)c.lineTo(128+d*(46+(y-100)*.1)+wv(y,t,d<0?2:0)*.5,y)
|
||||
c.closePath();c.fill();c.stroke()}
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(70,128);c.bezierCurveTo(54,70,90,36,128,36);c.bezierCurveTo(166,36,202,70,186,128)
|
||||
c.quadraticCurveTo(184,108,176,100);c.quadraticCurveTo(166,90,166,84);c.quadraticCurveTo(166,88,153,102);c.quadraticCurveTo(140,88,140,82);c.quadraticCurveTo(140,86,128,100);c.quadraticCurveTo(116,88,116,82);c.quadraticCurveTo(116,86,103,102);c.quadraticCurveTo(90,90,90,84);c.quadraticCurveTo(90,88,80,100);c.quadraticCurveTo(72,108,70,128);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;for(const[a,b,e,f]of[[100,50,86,82],[128,44,124,78],[156,50,168,80]])c.beginPath(),c.moveTo(a,b),c.quadraticCurveTo((a+e)/2+4,(b+f)/2,e,f),c.stroke()
|
||||
c.lineWidth=2.5;for(let k=0;k<4;k++){const[x,y]=[[36,70],[222,62],[34,198],[224,190]][k];flower(c,x,y,15+k%2*3,T*t/5*(k%2?1:-1))}
|
||||
c.fillStyle='#fff';for(let k=0;k<5;k++){const p=(t+k/5)%1,s=max(0,sin(PI*p)),x=[50,208,22,236,196][k]+5*sin(T*(t+k/5)*2),y=232-p*196,r=[9,7,6,8,5][k]*(.4+.6*s);c.lineWidth=2.5;c.beginPath();c.arc(x,y,r,0,T);c.stroke();c.lineWidth=2;c.beginPath();c.arc(x,y,r*.6,PI*1.1,PI*1.5);c.stroke()}
|
||||
for(const[x,y,o]of[[64,30,0],[196,26,.5],[18,128,.25],[238,124,.75]]){c.fillStyle='#111';star(c,x,y,7*(.3+.7*Math.abs(sin(T*(t+o)))))}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// manga girl with black braids at a shrine: flicks a coin into the offering box, shakes the bell rope, claps twice and prays
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max,abs}=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)),lerp=(a,b,u)=>a+(b-a)*ease(u)
|
||||
const spark=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x,y,x+r,y);c.quadraticCurveTo(x,y,x,y+r);c.quadraticCurveTo(x,y,x-r,y);c.quadraticCurveTo(x,y,x,y-r);c.fill();c.stroke()}
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,10.5,14,0,0,T);c.clip();c.fillStyle='#111';c.fill();c.fillStyle='#777';c.beginPath();c.ellipse(x,y+13,10,6,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-4,y-4,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+4,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-5);c.quadraticCurveTo(x,y-20,x+16,y-5);c.moveTo(x+d*14,y-8);c.lineTo(x+d*21,y-13);c.stroke()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
const shake=t>=.18&&t<.45,clap=t>=.45&&t<.62,pray=t>=.62&&t<.96,sw=shake?14*sin((t-.18)/.27*T*3):0
|
||||
// hands, viewer's left and right
|
||||
const gap=clap?14*abs(cos((t-.45)/.17*PI*2)):0
|
||||
const L=t<.18?[98,200]:t<.22?[lerp(98,70+sw,(t-.18)/.04),lerp(200,150,(t-.18)/.04)]:shake?[70+sw,150]:t<.49?[lerp(70,121,(t-.45)/.04),lerp(150,166,(t-.45)/.04)]:t<.96?[128-3-gap,166]:[lerp(125,98,(t-.96)/.04),lerp(166,200,(t-.96)/.04)]
|
||||
const R=t<.08?[lerp(160,190,t/.08),lerp(200,172,t/.08)]:t<.18?[190,172]:t<.22?[lerp(190,160,(t-.18)/.04),lerp(172,200,(t-.18)/.04)]:shake?[160,200]:t<.49?[lerp(160,135,(t-.45)/.04),lerp(200,166,(t-.45)/.04)]:t<.96?[128+3+gap,166]:[lerp(131,160,(t-.96)/.04),lerp(166,200,(t-.96)/.04)]
|
||||
// bell and rope
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.roundRect(24,8,84,10,3);c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
const ba=shake?.25*sin((t-.18)/.27*T*3):0
|
||||
c.save();c.translate(66,18);c.rotate(ba);c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(0,22,20,0,T);c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(-19,16,38,8);c.fill();c.lineWidth=3.5;c.stroke();c.lineWidth=4;c.beginPath();c.moveTo(-8,32);c.lineTo(8,32);c.stroke();c.fillStyle='#111';for(const x of[-8,8])c.beginPath(),c.arc(x,32,3,0,T),c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(-8,8,5,3,-.5,0,T);c.fill();c.restore()
|
||||
if(shake){c.lineWidth=4;for(const d of[-1,1])for(const k of[0,1])c.beginPath(),c.arc(66,40,28+k*10,d<0?PI*.85:-PI*.15,d<0?PI*1.15:PI*.15),c.stroke()}
|
||||
const rx=66+sw,ry=184
|
||||
for(const[l,st]of[[17,'#111'],[10,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(66+sin(ba)*-40,60),c.quadraticCurveTo(66,120,rx,ry),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=3;for(let k=1;k<16;k++){const u=k/16,x=(1-u)**2*(66-sin(ba)*40)+2*u*(1-u)*66+u*u*rx,y=(1-u)**2*60+2*u*(1-u)*120+u*u*ry;c.beginPath();c.moveTo(x-5,y-2);c.lineTo(x+5,y+3);c.stroke()}
|
||||
// black braids behind
|
||||
for(const d of[-1,1]){c.lineWidth=6;for(let k=0;k<5;k++){const x=128+d*(46+k*1.5),y=118+k*14;c.fillStyle='#111';c.beginPath();c.ellipse(x,y,10,9,0,0,T);c.fill();c.stroke();c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.arc(x-2,y-2,5,PI*1.1,PI*1.6);c.stroke();c.strokeStyle='#111';c.lineWidth=6}}
|
||||
// coat
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(110,150);c.quadraticCurveTo(70,156,62,214);c.lineTo(194,214);c.quadraticCurveTo(186,156,146,150);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(108,150);c.quadraticCurveTo(128,170,148,150);c.quadraticCurveTo(150,164,128,174);c.quadraticCurveTo(106,164,108,150);c.fill();c.stroke()
|
||||
for(const[l,st]of[[14,'#111'],[8,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(100,170),c.quadraticCurveTo(128,184,156,170),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
for(const d of[-1,1]){c.fillStyle='#111';c.beginPath();c.moveTo(128+d*24,176);c.lineTo(128+d*30,206);c.lineTo(128+d*18,206);c.closePath();c.fill()}
|
||||
// offering box
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(36,196);c.lineTo(220,196);c.lineTo(212,214);c.lineTo(44,214);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';for(let x=52;x<206;x+=14)c.beginPath(),c.moveTo(x,199),c.lineTo(x+8,199),c.lineTo(x+7,211),c.lineTo(x-1,211),c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(44,214,168,34);c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.lineWidth=3;c.strokeStyle='#fff';for(const y of[224,236])c.beginPath(),c.moveTo(52,y),c.bezierCurveTo(100,y-4,150,y+4,204,y),c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(128,231,12,0,T);c.fill();c.stroke();c.fillStyle='#111';for(let k=0;k<3;k++){const b=k*T/3;c.beginPath();c.arc(128+cos(b)*5,231+sin(b)*5,3.5,0,T);c.fill()}
|
||||
// arms and hands
|
||||
for(const[[sx,sy],[hx,hy]]of[[[104,164],L],[[152,164],R]]){for(const[l,st]of[[18,'#111'],[10,'#fff'],[10,DOT]])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(sx,sy),c.lineTo(hx,hy+10),c.stroke()
|
||||
c.strokeStyle='#111'}
|
||||
for(const[hx,hy]of[L,R]){c.lineWidth=4.5;c.fillStyle='#fff';c.beginPath();if(clap||pray)c.ellipse(hx,hy,5,12,0,0,T);else c.ellipse(hx,hy,9,9,0,0,T);c.fill();c.stroke()}
|
||||
if(clap){const u=(t-.45)/.17*2%1;if(u<.35){c.lineWidth=4;for(let k=0;k<6;k++){const b=k*PI/3+PI/6,r=18+u*30;c.beginPath();c.moveTo(128+cos(b)*r,166+sin(b)*r);c.lineTo(128+cos(b)*(r+9),166+sin(b)*(r+9));c.stroke()}}}
|
||||
// head
|
||||
c.save();c.translate(128,92);c.scale(.76,.76);c.translate(-128,-120)
|
||||
c.lineWidth=7;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(pray){c.lineWidth=6;for(const x of[103,153])c.beginPath(),c.arc(x,140,13,.15*PI,.85*PI),c.stroke();c.lineWidth=4;for(const x of[103,153])c.beginPath(),c.moveTo(x-14,144),c.lineTo(x-20,150),c.stroke()}
|
||||
else{eye(c,103,140,-1);eye(c,153,140,1)}
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*40+k-3,166),c.lineTo(128+d*40+k+3,158),c.stroke()
|
||||
c.lineWidth=5;c.beginPath();if(pray)c.arc(128,166,6,.2*PI,.8*PI);else if(clap)c.moveTo(118,168),c.lineTo(138,168);else c.arc(128,164,8,.15*PI,.85*PI);c.stroke()
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(70,150)
|
||||
c.bezierCurveTo(54,72,88,36,128,36);c.bezierCurveTo(168,36,202,72,186,150)
|
||||
c.quadraticCurveTo(184,120,180,106);c.lineTo(166,112);c.lineTo(160,96);c.lineTo(146,110);c.lineTo(136,94);c.lineTo(126,110);c.lineTo(114,96);c.lineTo(104,110);c.lineTo(94,98);c.lineTo(88,112);c.lineTo(76,106);c.quadraticCurveTo(72,124,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=6;for(const[s,e]of[[1.22,1.34],[1.42,1.56],[1.64,1.76]])c.beginPath(),c.arc(128,104,52,PI*s,PI*e),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*60,128),c.lineTo(128+d*76,120),c.lineTo(128+d*74,140),c.closePath(),c.fill(),c.stroke()
|
||||
c.restore()
|
||||
// coin into the box
|
||||
if(t<.2){const u=min(1,t/.18),x=200-u*48,y=168-sin(u*PI)*52+u*30;const cw=10*abs(cos(u*PI*4))+2;c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,cw,10,0,0,T);c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.ellipse(x,y,cw*.3,3,0,0,T);c.fill()
|
||||
c.lineWidth=3;for(const k of[-1,1])c.beginPath(),c.moveTo(x+14,y+k*6-8),c.lineTo(x+24,y+k*6-14),c.stroke()
|
||||
if(t>=.15){c.fillStyle='#fff';c.lineWidth=3;spark(c,160,188,8);spark(c,138,182,5)}}
|
||||
if(pray){c.fillStyle='#fff';c.lineWidth=3;for(let k=0;k<3;k++){const p=((t-.62)/.34*2+k/3)%1,[x,y]=[[176,64],[84,40],[192,120]][k];spark(c,x,y-p*10,3+7*sin(PI*p))}}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// awkward girl with a hatched bob: a giant sweat drop slides down beside her head while she scratches her cheek with a wobbly smile
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,PI,min,max}=Math,T=2*PI
|
||||
export const frames=16,delay=80,rim=4
|
||||
const DK=(()=>{const t=createCanvas(6,6),x=t.getContext('2d');x.fillStyle='#111';x.beginPath();x.arc(3,3,1.2,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 hair=c=>{c.beginPath();c.moveTo(62,176);c.bezierCurveTo(40,80,80,34,128,34);c.bezierCurveTo(176,34,216,80,194,176);c.quadraticCurveTo(196,186,184,186);c.quadraticCurveTo(186,130,176,104)
|
||||
c.quadraticCurveTo(160,112,150,104);c.quadraticCurveTo(140,112,128,104);c.quadraticCurveTo(116,112,104,104);c.quadraticCurveTo(94,112,80,104);c.quadraticCurveTo(70,130,72,186);c.quadraticCurveTo(60,186,62,176);c.closePath()}
|
||||
export default(c,t)=>{
|
||||
const sc=4*sin(4*T*t),u=t<.15?0:t>.85?1:(t-.15)/.7,ds=t<.15?.4+.6*t/.15:t>.85?1-(t-.85)/.15:1,dy=70+56*u
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,56,246);c.lineTo(200,246);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill()
|
||||
c.save();c.clip();c.fillStyle=c.createPattern(DK,'repeat');c.fillRect(40,180,180,70);c.fillStyle='#fff';c.beginPath();c.moveTo(104,190);c.lineTo(128,246);c.lineTo(152,190);c.fill();c.restore();c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(104,190);c.lineTo(122,246);c.moveTo(152,190);c.lineTo(134,246);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.fillStyle='#111';c.beginPath();c.moveTo(128,204);c.lineTo(118,196);c.lineTo(118,212);c.closePath();c.moveTo(128,204);c.lineTo(138,196);c.lineTo(138,212);c.closePath();c.fill()
|
||||
c.lineWidth=6;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
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+5,y+2,9,13,0,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x+2,y-3,3.8,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-15,y-6);c.quadraticCurveTo(x,y-17,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-11,y-22-d*5);c.lineTo(x+11,y-22+d*5);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(114,168);for(let k=1;k<=7;k++)c.lineTo(114+k*4,168+(k%2?-2.2:1.6)-(k==1||k==7?2:0));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,164),c.lineTo(128+d*42+k+3,157),c.stroke()
|
||||
c.lineWidth=6;hair(c);c.fillStyle='#fff';c.fill();c.save();c.clip();c.strokeStyle='#111';hatch(c,40,220,30,190);c.strokeStyle='#fff';c.lineWidth=9;c.beginPath();c.arc(128,110,58,PI*1.2,PI*1.42);c.stroke();c.beginPath();c.arc(128,110,58,PI*1.52,PI*1.8);c.stroke();c.restore()
|
||||
c.strokeStyle='#111';c.lineWidth=6;hair(c);c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=4;for(const y of[76,88])c.beginPath(),c.roundRect(170,y,22,7,3),c.fill(),c.stroke()
|
||||
c.save();c.translate(0,sc)
|
||||
c.lineWidth=24;c.beginPath();c.moveTo(58,250);c.quadraticCurveTo(52,206,76,184);c.stroke();c.lineWidth=14;c.strokeStyle='#fff';c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4.5;c.beginPath();c.ellipse(80,178,11,12,-.4,0,T);c.fill();c.stroke()
|
||||
c.beginPath();c.roundRect(80,152,9,26,4.5);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.ellipse(80,178,9,10,-.4,0,T);c.fill()
|
||||
c.restore()
|
||||
c.lineWidth=2.5;for(const k of[0,1,2]){const a=-1.9+k*.5;c.beginPath();c.moveTo(78+Math.cos(a)*16,150+Math.sin(a)*16+sc*.5),c.lineTo(78+Math.cos(a)*22,150+Math.sin(a)*22+sc*.5);c.stroke()}
|
||||
c.save();c.translate(206,dy);c.scale(ds,ds);c.fillStyle='#fff';c.lineWidth=4.5;c.beginPath();c.moveTo(0,-34);c.quadraticCurveTo(20,-6,19,10);c.arc(0,10,19,0,PI);c.quadraticCurveTo(-20,-6,0,-34);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.arc(0,10,12,PI*.75,PI*1.05);c.stroke();c.restore()
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// white-ponytailed swordswoman waits eyes shut as a petal falls, then a quick draw flashes and the petal drops in two halves
|
||||
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 dots=(col,r)=>{const k=createCanvas(6,6),x=k.getContext('2d');x.fillStyle=col;for(const[a,b]of[[3,3],[0,0],[6,0],[0,6],[6,6]])x.beginPath(),x.arc(a,b,r,0,T),x.fill();return k}
|
||||
const TONE=dots('#111',1.7),LITE=dots('#fff',1.2)
|
||||
const ease=u=>u<0?0:u>1?1:u*u*(3-2*u)
|
||||
const petal=c=>{c.beginPath();c.moveTo(0,12);c.bezierCurveTo(-12,4,-10,-10,-3,-12);c.lineTo(0,-8);c.lineTo(3,-12);c.bezierCurveTo(10,-10,12,4,0,12);c.closePath()}
|
||||
const eye=(c,x,y,d,lt,o)=>{
|
||||
if(!o){c.lineWidth=6;c.beginPath();c.moveTo(x-14,y+2);c.quadraticCurveTo(x,y+10,x+14,y+2);c.moveTo(x+d*14,y+2);c.lineTo(x+d*19,y-1);c.stroke();return}
|
||||
c.save();c.beginPath();c.moveTo(x-16,y-4+d*2);c.lineTo(x+16,y-4-d*2);c.lineTo(x+16,y+20);c.lineTo(x-16,y+20);c.clip()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+3,12,13,0,0,T);c.fill();c.clip()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+5,y+4,8,11,0,0,T);c.fill();c.fillStyle=lt;c.fillRect(x-4,y+6,20,12)
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+3,y,3.2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-14,y-4+d*2);c.lineTo(x+14,y-4-d*2);c.lineTo(x+d*19,y-8);c.stroke()
|
||||
}
|
||||
const blade=(c,b=1)=>{
|
||||
if(b){c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(0,-4);c.lineTo(78,-6);c.quadraticCurveTo(88,-6,92,-2);c.quadraticCurveTo(84,2,76,3);c.lineTo(0,4);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2;c.beginPath();c.moveTo(4,0);c.lineTo(80,-2);c.stroke()}
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(0,0,4,11,0,0,T);c.fill()
|
||||
c.beginPath();c.roundRect(-30,-5,28,10,4);c.fill();c.fillStyle='#fff';for(let j=0;j<3;j++)c.beginPath(),c.moveTo(-26+j*8,0),c.lineTo(-22+j*8,-3),c.lineTo(-18+j*8,0),c.lineTo(-22+j*8,3),c.fill()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TONE,'repeat'),lt=c.createPattern(LITE,'repeat')
|
||||
const k=t<.45?0:t<.5?1:t<.72?1:1-ease((t-.72)/.2),open=t>=.45&&t<.9,flash=t>=.45&&t<.6
|
||||
const hx=165+7*k,hy=205-55*k,ha=-.42-.33*k
|
||||
if(flash){const f=1-(t-.45)/.15*.7;c.lineWidth=4;c.fillStyle='#fff';c.beginPath()
|
||||
for(let j=0;j<=20;j++){const u=j/20,a=.32-1.05*u;c.lineTo(70+156*cos(a),166+156*sin(a))}
|
||||
for(let j=20;j>=0;j--){const u=j/20,a=.32-1.05*u,r=156-18*f*sin(PI*u);c.lineTo(70+r*cos(a),166+r*sin(a))}
|
||||
c.closePath();c.fill();c.stroke()}
|
||||
const py=t<.45?14+112*t/.45:0,px=212+8*sin(t*T*2)
|
||||
if(t<.45){c.save();c.translate(px,py);c.rotate(.6*sin(t*T*3));c.lineWidth=3.5;c.fillStyle='#fff';petal(c);c.fill();c.stroke();c.restore()}
|
||||
else{const u=(t-.45)/.55,yy=126+100*u*u+20*u,s=1-u*.6
|
||||
for(const d of[-1,1]){c.save();c.translate(px-8*u+d*(5+22*u),yy+d*4*u);c.rotate(-.4+d*(.2+2*u));c.scale(s,s);c.beginPath();c.rect(d<0?-20:0,-20,20,40);c.clip();c.lineWidth=3.5;c.fillStyle='#fff';petal(c);c.fill();c.stroke();c.lineWidth=2.5;c.beginPath();c.moveTo(0,-14);c.lineTo(0,14);c.stroke();c.restore()}}
|
||||
const sw=3*sin(t*T)+(t>=.45&&t<.72?8*sin((t-.45)/.27*PI):0)
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(88,30);c.bezierCurveTo(40,24,24-sw,90,20-sw,170);c.quadraticCurveTo(30-sw,150,36-sw,190);c.quadraticCurveTo(46-sw,120,58,92);c.quadraticCurveTo(70,60,96,46);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(70,40);c.bezierCurveTo(44,60,34-sw,110,30-sw,160);c.moveTo(80,46);c.bezierCurveTo(58,80,48-sw,120,44-sw,150);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle=tn;c.beginPath();c.moveTo(56,206);c.lineTo(40,256);c.lineTo(160,256);c.lineTo(150,206);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const x of[74,98,122,140])c.moveTo(x,210),c.lineTo(x-6,252);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(78,150);c.quadraticCurveTo(46,158,46,214);c.lineTo(160,214);c.quadraticCurveTo(156,158,118,150);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(84,150);c.lineTo(112,196);c.lineTo(118,190);c.lineTo(94,150);c.closePath();c.fill();c.beginPath();c.moveTo(114,150);c.lineTo(100,174);c.lineTo(104,180);c.lineTo(120,152);c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(84,150);c.lineTo(114,198);c.moveTo(114,150);c.lineTo(98,176);c.stroke()
|
||||
c.fillStyle='#111';c.fillRect(48,200,112,10)
|
||||
c.save();c.translate(150,212);c.rotate(-.42);c.fillStyle='#111';c.beginPath();c.roundRect(-118,-6,118,12,6);c.fill();c.fillStyle='#fff';c.fillRect(-60,-6,4,12);c.restore()
|
||||
|
||||
c.fillStyle='#fff';c.lineWidth=6;c.beginPath();c.rect(88,128,22,26);c.fill();c.beginPath();c.moveTo(88,128);c.lineTo(88,152);c.moveTo(110,128);c.lineTo(110,152);c.stroke()
|
||||
c.save();c.translate(98,92);c.scale(.66,.66);c.translate(-128,-128)
|
||||
c.lineWidth=7;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,106,138,-1,lt,open);eye(c,156,138,1,lt,open)
|
||||
c.lineWidth=4;c.beginPath();if(open)c.moveTo(130,170),c.lineTo(146,168);else c.moveTo(130,170),c.quadraticCurveTo(138,174,146,170);c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(70,150)
|
||||
c.bezierCurveTo(54,74,88,40,128,40);c.bezierCurveTo(168,40,202,74,186,150)
|
||||
c.quadraticCurveTo(186,118,180,104);c.quadraticCurveTo(176,108,170,118);c.quadraticCurveTo(164,100,150,92);c.quadraticCurveTo(148,106,140,116)
|
||||
c.quadraticCurveTo(134,98,124,90);c.quadraticCurveTo(118,104,112,112);c.quadraticCurveTo(104,96,94,94);c.quadraticCurveTo(90,108,84,118)
|
||||
c.quadraticCurveTo(80,108,78,106);c.quadraticCurveTo(68,124,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();for(const[x0,y0,x1,y1]of[[110,50,96,92],[134,46,130,88],[158,54,160,92],[90,64,80,100],[176,72,180,104]])c.moveTo(x0,y0),c.quadraticCurveTo((x0+x1)/2+4,(y0+y1)/2,x1,y1);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(86,52,12,9,-.6,0,T);c.fill()
|
||||
c.restore()
|
||||
if(k<.5){c.save();c.translate(150,212);c.rotate(-.42);c.translate(32,0);blade(c,0);c.restore()}else{c.save();c.translate(hx,hy);c.rotate(ha);c.scale(.85,.85);c.translate(16,0);blade(c);c.restore()}
|
||||
for(const[l,s]of[[24,'#111'],[14,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(70,166),c.quadraticCurveTo(100+20*k,214-50*k,hx-4,hy+4),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy,10,9,ha,0,T);c.fill();c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// manga girl with a screentone side tail ties her wish strip to a bamboo branch, then clasps her hands and wishes while the strips flutter and stars twinkle
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=16,delay=100,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)),lerp=(a,b,u)=>a+(b-a)*ease(u)
|
||||
const spark=(c,x,y,r)=>{c.beginPath();c.moveTo(x,y-r);c.quadraticCurveTo(x,y,x+r,y);c.quadraticCurveTo(x,y,x,y+r);c.quadraticCurveTo(x,y,x-r,y);c.quadraticCurveTo(x,y,x,y-r);c.fill();c.stroke()}
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.clip();c.beginPath();c.ellipse(x+3,y-3,10.5,14,0,0,T);c.fillStyle='#111';c.fill();c.fillStyle='#777';c.beginPath();c.ellipse(x+3,y+8,10,6,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-1,y-8,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+7,y+3,2,0,T);c.fill();c.restore()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-5);c.quadraticCurveTo(x,y-20,x+16,y-5);c.moveTo(x+d*14,y-8);c.lineTo(x+d*21,y-13);c.stroke()
|
||||
}
|
||||
const leaf=(c,x,y,a,l)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(l*.45,-8,l,0);c.quadraticCurveTo(l*.45,8,0,0);c.fill();c.stroke();c.lineWidth=2;c.beginPath();c.moveTo(4,0);c.lineTo(l*.7,0);c.stroke();c.restore()}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),HAIR=tone(c,5,1.1)
|
||||
const tie=t<.35,wish=t>=.42&&t<.92
|
||||
// bamboo branch with leaves
|
||||
for(const[l,st]of[[14,'#111'],[8,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(244,14),c.quadraticCurveTo(186,22,108,58),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=3;for(const[x,y]of[[214,17],[160,33]])c.beginPath(),c.moveTo(x-2,y-6),c.lineTo(x+2,y+6),c.stroke()
|
||||
const bz=.06*sin(t*T)
|
||||
for(const[x,y,a,l]of[[110,57,2.5,40],[118,52,3.5,34],[150,38,2.2,30],[188,26,-.6,32],[232,16,2.4,30],[200,22,1.9,26]])leaf(c,x,y,a+bz,l)
|
||||
// twinkling stars near the branch
|
||||
c.fillStyle='#fff';c.lineWidth=3;for(let k=0;k<4;k++){const p=(t*2+k/4)%1,[x,y]=[[112,22],[156,12],[242,60],[200,82]][k];spark(c,x,y,3+6*sin(PI*p))}
|
||||
// wish strips on strings: two already hanging, hers in the middle
|
||||
const strip=(x,y,a,f)=>{c.lineWidth=2.5;c.beginPath();c.moveTo(x,y);c.lineTo(x+sin(-a)*12,y+cos(a)*12);c.stroke();c.save();c.translate(x+sin(-a)*12,y+cos(a)*12);c.rotate(a);c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.rect(-9,0,18,48);c.fill();f();c.lineWidth=4;c.beginPath();c.rect(-9,0,18,48);c.stroke();c.restore()}
|
||||
strip(142,44,.18*sin(t*T*2),()=>{c.fillStyle=DOT;c.fillRect(-9,0,18,48);c.lineWidth=2.5;for(const y of[10,20,30])c.beginPath(),c.moveTo(-3,y),c.lineTo(3,y+4),c.stroke()})
|
||||
strip(218,22,.16*sin(t*T*2+1.5),()=>{c.lineWidth=2.5;c.beginPath();for(const y of[8,16,24,32,40])c.moveTo(0,y),c.lineTo(0,y+4);c.stroke()})
|
||||
const ha=tie?.04*sin(t*T*8):.2*sin(t*T*2+.7)
|
||||
strip(178,30,ha,()=>{c.save();c.translate(0,14);c.fillStyle='#111';c.beginPath();c.moveTo(0,6);c.bezierCurveTo(-8,0,-5,-7,0,-3);c.bezierCurveTo(5,-7,8,0,0,6);c.fill();c.restore();c.lineWidth=2.5;c.beginPath();for(const y of[28,36])c.moveTo(-4,y),c.quadraticCurveTo(0,y-3,4,y);c.stroke()})
|
||||
// screentone side tail
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(56,130);c.bezierCurveTo(26,150,22,200,34,240);c.quadraticCurveTo(46,232,52,244);c.bezierCurveTo(50,200,58,170,74,150);c.closePath();c.fill();c.fillStyle=HAIR;c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(50,150);c.bezierCurveTo(36,170,36,200,42,226);c.stroke();c.strokeStyle='#111'
|
||||
// yukata with stripes and obi
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(80,196);c.quadraticCurveTo(44,204,38,252);c.lineTo(172,252);c.quadraticCurveTo(160,204,124,196);c.closePath();c.fill()
|
||||
c.save();c.clip();c.lineWidth=3;for(let x=30;x<180;x+=12)c.beginPath(),c.moveTo(x,196),c.lineTo(x+4,252),c.stroke();c.restore();c.stroke()
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(80,196);c.lineTo(112,240);c.lineTo(124,232);c.lineTo(98,196);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(42,236);c.lineTo(168,236);c.lineTo(170,252);c.lineTo(40,252);c.closePath();c.fill()
|
||||
// her hands: tying the strip, then clasped
|
||||
const hx=tie?178+2*sin(t*T*8):t<.42?lerp(178,112,(t-.35)/.07):t<.92?112:lerp(112,178,(t-.92)/.08),hy=tie?52:t<.42?lerp(52,206,(t-.35)/.07):t<.92?206:lerp(206,52,(t-.92)/.08)
|
||||
const lerp2=(a,b,u)=>a+(b-a)*u,ex=lerp2(150,hx,.5),ey=lerp2(212,hy+12,.5)
|
||||
if(!wish){for(const[l,st]of[[16,'#111'],[8,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.beginPath(),c.moveTo(ex,ey),c.lineTo(hx,hy+10),c.stroke()
|
||||
for(const[l,st]of[[30,'#111'],[21,'#fff']])c.lineWidth=l,c.strokeStyle=st,c.lineCap='butt',c.beginPath(),c.moveTo(150,214),c.lineTo(ex,ey),c.stroke()
|
||||
c.lineCap='round';c.strokeStyle='#111';c.lineWidth=3;const g=Math.atan2(ey-214,ex-150);for(let k=1;k<5;k++){const u=k/5,x=lerp2(150,ex,u),y=lerp2(214,ey,u);c.beginPath();c.moveTo(x-cos(g+PI/2)*9,y-sin(g+PI/2)*9);c.lineTo(x+cos(g+PI/2)*9,y+sin(g+PI/2)*9);c.stroke()}
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(ex,ey,14,6,g+PI/2,0,T);c.fill();c.stroke()}
|
||||
c.lineWidth=4.5;c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy,9,10,0,0,T);c.fill();c.stroke()
|
||||
if(wish){c.beginPath();c.ellipse(hx-14,hy+2,9,10,0,0,T);c.fill();c.stroke();c.lineWidth=3;c.beginPath();c.moveTo(hx-7,hy-6);c.lineTo(hx-7,hy+8);c.stroke()}
|
||||
// head, tilted up toward the branch
|
||||
c.save();c.translate(92,146);c.rotate(-.12);c.scale(.74,.74);c.translate(-128,-128)
|
||||
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(wish){c.lineWidth=7;for(const x of[103,153])c.beginPath(),c.arc(x,140,13,.15*PI,.85*PI),c.stroke();c.lineWidth=4.5;for(const x of[103,153])c.beginPath(),c.moveTo(x-14,144),c.lineTo(x-20,150),c.stroke()}
|
||||
else{eye(c,103,140,-1);eye(c,153,140,1)}
|
||||
c.lineWidth=4;for(const d of[-1,1])for(const k of[-7,0,7])c.beginPath(),c.moveTo(128+d*40+k-3,167),c.lineTo(128+d*40+k+3,158),c.stroke()
|
||||
c.lineWidth=6;c.beginPath();c.arc(128,164,8,.15*PI,.85*PI);c.stroke()
|
||||
c.lineWidth=8;c.fillStyle='#fff';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(172,104,166,112);c.quadraticCurveTo(160,98,150,92);c.quadraticCurveTo(144,104,136,110)
|
||||
c.quadraticCurveTo(132,96,124,90);c.quadraticCurveTo(118,102,110,108);c.quadraticCurveTo(106,96,96,94);c.quadraticCurveTo(90,104,86,112)
|
||||
c.quadraticCurveTo(80,104,78,102);c.quadraticCurveTo(70,122,70,150);c.closePath();c.fill();c.fillStyle=HAIR;c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=8;for(const[s,e]of[[1.24,1.36],[1.44,1.54],[1.62,1.74]])c.beginPath(),c.arc(128,108,54,PI*s,PI*e),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=5;c.beginPath();for(let k=0;k<10;k++){const r=k%2?6:15,b=k*PI/5-PI/2;c.lineTo(160+cos(b)*r,62+sin(b)*r)}c.closePath();c.fill();c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// girl with a messy hatched pixie cut wilting over a test scored zero, paper trembling, gloom lines and a little rain cloud overhead
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI}=Math,T=2*PI
|
||||
export const frames=12,delay=100,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 fill2=(c,p)=>{c.fillStyle='#fff';c.fill();c.fillStyle=p;c.fill();c.stroke()}
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat'),hat=c.createPattern(ht,'repeat'),dr=Math.round(3+3*sin(t*T)),sh=i%2?1:-1
|
||||
c.save();c.translate(-36,14)
|
||||
// hoodie body
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(96,186);c.quadraticCurveTo(56,196,50,236);c.lineTo(206,236);c.quadraticCurveTo(200,196,160,186);c.closePath();fill2(c,dot)
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(92,188);c.quadraticCurveTo(128,222,164,188);c.quadraticCurveTo(128,204,92,188);c.fill();c.stroke();for(const d of[-1,1])for(const[l,s2]of[[7,'#111'],[3,'#fff']])c.lineWidth=l,c.strokeStyle=s2,c.beginPath(),c.moveTo(128+d*12,206),c.lineTo(128+d*13,228),c.stroke();c.strokeStyle='#111'
|
||||
// head droops forward and sighs
|
||||
c.save();c.translate(128,182);c.rotate(.07);c.translate(-128,-182+dr)
|
||||
c.lineWidth=6;c.beginPath();for(let k=0;k<=26;k++){const a=PI*(.82+k*.0525),r=k%2?66:58+3*sin(k*7);c.lineTo(128+r*cos(a),106+r*sin(a))}c.closePath();fill2(c,hat)
|
||||
c.fillStyle='#fff';c.beginPath();c.rect(116,168,24,20);c.fill();c.lineWidth=5;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()
|
||||
// gloom shading down the top half of the face
|
||||
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=2.5;for(let x=80;x<180;x+=6){c.beginPath();c.moveTo(x,96);c.lineTo(x,124+12*sin(x*.7)*.5);c.stroke()}c.restore()
|
||||
// hollow despair eyes, a tear, wobbly mouth
|
||||
for(const x of[103,153]){c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.ellipse(x,142,11,13,0,0,T);c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.arc(x+sh*.8,146,2.2,0,T);c.fill()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(x-14,132);c.quadraticCurveTo(x,124,x+14,132);c.stroke()}
|
||||
c.lineWidth=3.5;c.beginPath();c.moveTo(112,172);for(let k=1;k<=8;k++)c.lineTo(112+k*4,172+(k%2?-2.5:2.5)*sh);c.stroke()
|
||||
const tp=(t*2)%1;c.fillStyle='#fff';c.lineWidth=3;c.beginPath();const tx=166,ty=154+tp*20;c.moveTo(tx,ty-7);c.quadraticCurveTo(tx+6,ty+2,tx,ty+4);c.quadraticCurveTo(tx-6,ty+2,tx,ty-7);c.fill();c.stroke()
|
||||
// messy hatched pixie bangs
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(70,150);c.bezierCurveTo(54,80,88,40,128,40);c.bezierCurveTo(170,40,204,80,186,150)
|
||||
for(const[x,y]of[[182,112],[176,116],[170,98],[158,114],[150,96],[140,118],[128,98],[118,116],[108,96],[98,114],[88,100],[80,118],[74,112]])c.lineTo(x,y)
|
||||
c.closePath();fill2(c,hat)
|
||||
c.strokeStyle='#fff';c.lineWidth=5;for(const[a,e]of[[1.27,1.36],[1.45,1.53],[1.62,1.7]])c.beginPath(),c.arc(128,108,54,PI*a,PI*e),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.beginPath();c.moveTo(128,42);c.quadraticCurveTo(140,22,152,26);c.stroke()
|
||||
c.restore()
|
||||
// the test, trembling in both hands
|
||||
c.save();c.translate(232,118);c.rotate(.1+sh*.025);c.scale(.8,.8)
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.rect(-52,-64,104,124);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.rect(-44,-56,40,12);c.stroke()
|
||||
for(let k=0;k<5;k++){const y=-30+k*16;c.beginPath();c.moveTo(-44,y);c.lineTo(-4-k%2*10,y);c.stroke();c.lineWidth=3;c.beginPath();c.moveTo(4,y-5);c.lineTo(12,y+3);c.moveTo(12,y-5);c.lineTo(4,y+3);c.stroke();c.lineWidth=2.5}
|
||||
c.lineWidth=7;c.beginPath();c.ellipse(32,-34,11,17,0,0,T);c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(16,-10);c.lineTo(48,-12);c.moveTo(18,-5);c.lineTo(46,-7);c.stroke()
|
||||
c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=4.5;for(const[x,y]of[[194,166],[268,128]])c.beginPath(),c.ellipse(x+sh*.6,y,11,13,0,0,T),c.fill(),c.stroke()
|
||||
c.restore()
|
||||
// little rain cloud over her head
|
||||
c.save();c.translate(0,Math.round(2*sin(t*T*2)))
|
||||
c.lineWidth=4.5;c.beginPath();for(const[x,y,r]of[[72,30,15],[94,22,18],[118,28,15],[86,38,13],[108,38,13]])c.moveTo(x+r,y),c.arc(x,y,r,0,T);c.save();c.lineWidth=9;c.stroke();c.fillStyle=dot;c.restore();c.fillStyle='#fff';c.fill();c.fillStyle=dot;c.fill()
|
||||
c.restore()
|
||||
c.lineWidth=2.5;for(let k=0;k<4;k++){const p=(t*2+k/4)%1,x=96+k*14;c.beginPath();c.moveTo(x-p*2,52+p*22);c.lineTo(x-2-p*2,60+p*22);c.stroke()}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// manga girl dozing on the train, swaying from a hanging strap, nodding off then jerking awake, window streaks sliding past
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,min,max}=Math,T=2*PI
|
||||
export const frames=16,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 eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,10.5,14,0,0,T);c.clip();c.fillStyle='#111';c.fill();c.fillStyle='#777';c.beginPath();c.ellipse(x,y+13,10,6,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-4,y-4,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+4,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-5);c.quadraticCurveTo(x,y-20,x+16,y-5);c.moveTo(x+d*14,y-8);c.lineTo(x+d*21,y-13);c.stroke()
|
||||
}
|
||||
const zz=(c,x,y,s)=>{c.lineWidth=4;c.beginPath();c.moveTo(x-s,y-s);c.lineTo(x+s,y-s);c.lineTo(x-s,y+s);c.lineTo(x+s,y+s);c.stroke()}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8),a=.045*sin(t*T),P=[128,420]
|
||||
const nod=t<.7?.2*(t/.7)**1.5:t<.74?-.04:-.04*(1-(t-.74)/.26),awake=t>=.7&&t<.84,half=t>=.84
|
||||
const rot=(x,y)=>[30+.86*(P[0]+(x-P[0])*cos(a)-(y-P[1])*sin(a)),30+.86*(P[1]+(x-P[0])*sin(a)+(y-P[1])*cos(a))]
|
||||
// train window behind her, streaks sliding left
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.roundRect(8,30,94,136,12);c.fill();c.stroke()
|
||||
c.save();c.beginPath();c.roundRect(18,40,74,116,6);c.clip();c.fillStyle=FINE;c.fillRect(18,40,74,116)
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(18,120);for(let x=18;x<=92;x+=12)c.lineTo(x,106+((x*7)%17));c.lineTo(92,156);c.lineTo(18,156);c.fill()
|
||||
c.lineWidth=3;for(let k=0;k<6;k++){const x=18+((k*53-t*144*2)%144+144)%144-36,y=50+k*17;c.beginPath();c.moveTo(x,y);c.lineTo(x+22+k%3*8,y);c.stroke()}
|
||||
c.restore();c.lineWidth=5;c.beginPath();c.roundRect(18,40,74,116,6);c.stroke()
|
||||
c.save();c.translate(30,30);c.scale(.86,.86);c.translate(...P);c.rotate(a);c.translate(-P[0],-P[1])
|
||||
const head=f=>{c.save();c.translate(128,184);c.rotate(-nod);c.translate(-128,-184);f();c.restore()}
|
||||
head(()=>{c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(128,36);c.bezierCurveTo(198,36,208,100,202,160);c.lineTo(208,250);c.lineTo(48,250);c.lineTo(54,160);c.bezierCurveTo(48,100,58,36,128,36);c.fill();c.stroke()})
|
||||
// blazer with tone, shirt and tie
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(106,186);c.quadraticCurveTo(66,194,60,252);c.lineTo(196,252);c.quadraticCurveTo(190,194,150,186);c.closePath();c.fill();c.fillStyle=DOT;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(110,188);c.lineTo(128,226);c.lineTo(146,188);c.closePath();c.fill();c.lineWidth=5;c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(122,200);c.lineTo(134,200);c.lineTo(138,240);c.lineTo(128,250);c.lineTo(118,240);c.closePath();c.fill()
|
||||
c.fillStyle='#fff';c.fillRect(116,166,24,26);c.lineWidth=5;c.beginPath();c.moveTo(116,166);c.lineTo(116,190);c.moveTo(140,166);c.lineTo(140,190);c.stroke()
|
||||
// raised arm to the strap
|
||||
for(const[l,s]of[[28,'#111'],[17,'#fff'],[17,DOT]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(176,214),c.lineTo(194,86),c.stroke()
|
||||
c.strokeStyle='#111'
|
||||
head(()=>{
|
||||
c.lineWidth=6;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(awake){eye(c,103,140,-1);eye(c,153,140,1)}
|
||||
else{c.lineWidth=5;for(const x of[103,153])c.beginPath(),half?(c.moveTo(x-15,138),c.lineTo(x+15,138),c.moveTo(x-12,138),c.quadraticCurveTo(x,152,x+12,138)):c.arc(x,136,13,.15*PI,.85*PI),c.stroke()
|
||||
if(half){c.fillStyle='#111';for(const x of[103,153])c.beginPath(),c.arc(x,142,5,0,PI),c.fill()}}
|
||||
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*36+k-3,166),c.lineTo(128+d*36+k+3,158),c.stroke()
|
||||
c.lineWidth=3.5;c.beginPath();if(awake)c.ellipse(128,170,5,6,0,0,T);else c.ellipse(128,170,4,2.5,0,0,T);c.stroke()
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(70,150)
|
||||
c.bezierCurveTo(54,74,88,38,128,38);c.bezierCurveTo(168,38,202,74,186,150)
|
||||
c.quadraticCurveTo(184,118,178,104);c.quadraticCurveTo(170,100,164,110);c.quadraticCurveTo(156,96,146,92);c.quadraticCurveTo(140,104,132,110)
|
||||
c.quadraticCurveTo(126,96,116,92);c.quadraticCurveTo(110,104,102,110);c.quadraticCurveTo(96,98,88,98);c.quadraticCurveTo(84,106,80,110)
|
||||
c.quadraticCurveTo(72,124,70,150);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;for(const[s,e]of[[1.2,1.3],[1.36,1.44],[1.5,1.6],[1.66,1.74]])c.beginPath(),c.arc(128,108,52,PI*s,PI*e),c.stroke()
|
||||
c.lineWidth=2;for(const s of[1.25,1.4,1.55,1.7])c.beginPath(),c.moveTo(128+cos(PI*s)*46,108+sin(PI*s)*46),c.lineTo(128+cos(PI*s)*40,108+sin(PI*s)*40),c.stroke()
|
||||
c.strokeStyle='#111';c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(160,78,18,7,3);c.fill();c.stroke()
|
||||
if(awake){c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(62,104);c.quadraticCurveTo(72,120,62,124);c.quadraticCurveTo(52,120,62,104);c.fill();c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(68,58);c.lineTo(62,40);c.moveTo(80,52);c.lineTo(80,34);c.moveTo(56,68);c.lineTo(44,56);c.stroke()}
|
||||
})
|
||||
c.restore()
|
||||
if(t<.7)for(let k=0;k<3;k++){const p=(t/.7*1.5+k/3)%1;c.globalAlpha=min(1,(1-p)*3);zz(c,96-p*30,74-p*44,4+p*5)}
|
||||
c.globalAlpha=1
|
||||
// rail, strap and ring, gripped by her hand
|
||||
const[hx,hy]=rot(194,82),ang=Math.atan2(hx-197,hy-20)
|
||||
c.lineWidth=14;c.beginPath();c.moveTo(126,16);c.lineTo(244,16);c.stroke();c.lineWidth=6;c.strokeStyle='#fff';c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.roundRect(189,10,16,12,3);c.fill();c.stroke()
|
||||
const L=Math.hypot(hx-197,hy-20)-30,rx=197+sin(ang)*L,ry=20+cos(ang)*L
|
||||
for(const[l,s]of[[11,'#111'],[5,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(197,22),c.lineTo(rx,ry),c.stroke()
|
||||
for(const[l,s]of[[10,'#111'],[4,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.arc(rx+sin(ang)*12,ry+cos(ang)*12,12,0,T),c.stroke()
|
||||
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.ellipse(hx,hy,11,10,-a,0,T);c.fill();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(hx-6,hy-4);c.lineTo(hx+6,hy-4);c.stroke()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// shy girl with an ink-black bob holding her umbrella out to share the dry side, a little heart under it, rain bouncing off the canopy
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,atan2,abs,min}=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()
|
||||
// canopy: a dome in the umbrella's own frame, tilted to the right
|
||||
const TX=166,TY=42,A=atan2(28,134),R=84,D=38,H=46
|
||||
const g=(x,y)=>[TX+x*cos(A)-y*sin(A),TY+x*sin(A)+y*cos(A)]
|
||||
const top=[...Array(61)].map((_,k)=>g(-R*cos(k*PI/60),D-H*sin(k*PI/60)))
|
||||
const can=c=>{c.beginPath();c.moveTo(-R,D);c.bezierCurveTo(-R,D-H*1.35,R,D-H*1.35,R,D);for(let k=5;k>=0;k--){const x0=-R+k*R/3,x1=x0+R/3;c.quadraticCurveTo((x0+x1)/2,D-12,x0,D)}c.closePath()}
|
||||
const surf=x=>{let b=null;for(const[px,py]of top)if(abs(px-x)<2&&(b==null||py<b))b=py;return b}
|
||||
const drops=[[12,0],[30,.55],[46,.3],[64,.8],[84,.12],[104,.62],[124,.38],[142,.88],[160,.2],[178,.7],[196,.45],[212,.95],[228,.25],[242,.75],[22,.42],[236,.08]]
|
||||
export default(c,t,i)=>{
|
||||
const dot=c.createPattern(tile,'repeat')
|
||||
// puddle and ripples
|
||||
c.lineWidth=4;c.beginPath();c.ellipse(128,236,112,12,0,0,T);c.fillStyle='#fff';c.fill();c.fillStyle=dot;c.fill();c.stroke()
|
||||
// girl, small full body
|
||||
c.save();c.translate(84,118);c.scale(.66,.66);c.translate(-128,-120)
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(128,36);c.bezierCurveTo(198,36,212,100,204,172);c.quadraticCurveTo(202,180,192,180);c.lineTo(64,180);c.quadraticCurveTo(54,180,52,172);c.bezierCurveTo(44,100,58,36,128,36);c.fill();c.stroke()
|
||||
// legs, rain boots, dress
|
||||
c.fillStyle='#fff';for(const x of[108,148]){c.lineWidth=7;c.beginPath();c.rect(x-8,250,16,50);c.fill();c.stroke();c.fillStyle='#111';c.beginPath();c.moveTo(x-12,284);c.lineTo(x+12,284);c.lineTo(x+14,318);c.lineTo(x-16,318);c.closePath();c.fill();c.stroke();c.fillStyle='#fff'}
|
||||
c.beginPath();c.moveTo(104,184);c.quadraticCurveTo(78,196,72,262);c.lineTo(184,262);c.quadraticCurveTo(178,196,152,184);c.closePath();c.fillStyle='#fff';c.fill();c.fillStyle=dot;c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(108,186);c.quadraticCurveTo(128,206,148,186);c.quadraticCurveTo(128,196,108,186);c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const x of[96,118,140,162])c.beginPath(),c.moveTo(x+(x-128)*.05,228),c.lineTo(x+(x-128)*.15,262),c.stroke()
|
||||
c.lineWidth=7;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.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 bl=i==7
|
||||
for(const[x,d]of[[103,-1],[153,1]]){
|
||||
if(bl){c.lineWidth=6;c.beginPath();c.moveTo(x-14,142);c.quadraticCurveTo(x,150,x+14,142);c.stroke();continue}
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,140,13,16,0,0,T);c.fill()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x+4,141,10,15,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.arc(x+1,135,4.5,0,T);c.fill();c.beginPath();c.arc(x+8,149,2,0,T);c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-d*12,130);c.quadraticCurveTo(x,119,x+d*15,131);c.lineTo(x+d*21,125);c.stroke()
|
||||
}
|
||||
c.lineWidth=3.5;for(const d of[-1,1])for(let k=0;k<4;k++)c.beginPath(),c.moveTo(128+d*40+(k-1.5)*6-3,172),c.lineTo(128+d*40+(k-1.5)*6+3,161),c.stroke()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(120,170);c.quadraticCurveTo(128,176,136,170);c.stroke()
|
||||
// side-swept ink bangs with a shine band
|
||||
c.lineWidth=7;c.fillStyle='#111';c.beginPath();c.moveTo(64,176);c.bezierCurveTo(46,74,88,36,128,36);c.bezierCurveTo(168,36,210,74,192,176)
|
||||
c.quadraticCurveTo(188,128,178,108);c.quadraticCurveTo(160,104,150,116);c.quadraticCurveTo(146,100,132,94);c.quadraticCurveTo(122,108,104,114);c.quadraticCurveTo(100,100,90,96);c.quadraticCurveTo(80,118,76,128);c.quadraticCurveTo(70,150,64,176);c.closePath();c.fill();c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();for(let k=0;k<=30;k++){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?52:48)*cos(a),104+(k%2?52:48)*sin(a))}for(let k=30;k>=0;k--){const a=PI*(1.2+k*.02);c.lineTo(128+(k%2?44:40)*cos(a),104+(k%2?44:40)*sin(a))}c.fill()
|
||||
c.restore()
|
||||
// umbrella: shaft, hand, scalloped canopy with toned panels
|
||||
c.save();c.translate(TX,TY);c.rotate(A)
|
||||
c.lineWidth=9;c.beginPath();c.moveTo(0,0);c.lineTo(0,142);c.arc(-8,142,8,0,PI);c.stroke();c.strokeStyle='#fff';c.lineWidth=3;c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=5;can(c);c.fillStyle='#fff';c.fill()
|
||||
c.save();c.clip();c.fillStyle=dot;for(let k=0;k<6;k+=2){const x0=-R+k*R/3;c.beginPath();c.moveTo(0,-8);c.lineTo(x0,D);c.lineTo(x0+R/3,D);c.closePath();c.fill()}c.restore();can(c);c.stroke()
|
||||
c.lineWidth=3;for(let k=1;k<6;k++)c.beginPath(),c.moveTo(0,-8),c.quadraticCurveTo((-R+k*R/3)*.6,0,-R+k*R/3,D),c.stroke()
|
||||
c.lineWidth=5;c.beginPath();c.moveTo(0,-8);c.lineTo(0,-16);c.stroke()
|
||||
c.restore()
|
||||
c.lineWidth=13;c.beginPath();c.moveTo(102,168);c.quadraticCurveTo(118,178,134,172);c.stroke();c.strokeStyle='#fff';c.lineWidth=6;c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.ellipse(139,170,9,10,0,0,T);c.fill();c.stroke()
|
||||
// a little heart floating in the dry space
|
||||
const hp=1+.15*sin(t*T*2),hy=136+4*sin(t*T)
|
||||
c.save();c.translate(198,hy);c.scale(hp,hp);c.fillStyle='#111';c.beginPath();c.moveTo(0,10);c.bezierCurveTo(-16,-1,-11,-15,0,-6);c.bezierCurveTo(11,-15,16,-1,0,10);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(-5,-4,2.5,3.5,-.5,0,T);c.fill();c.restore()
|
||||
// rain: falls, splashes on the canopy or ripples in the puddle
|
||||
c.lineCap='round'
|
||||
for(const[x,o]of drops){
|
||||
const p=(t*2+o)%1,y=-14+p*260,s=surf(x),stop=s??232
|
||||
if(y<stop-16){c.lineWidth=2.5;c.beginPath();c.moveTo(x,y);c.lineTo(x-1.5,y+15);c.stroke()}
|
||||
else if(y<stop+30){const k=(y-stop+14)/44;c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(x+d*(3+k*12),stop-2-10*sin(PI*k),1.8,0,T),c.fill()
|
||||
if(s==null){c.lineWidth=2;c.beginPath();c.ellipse(x,236,6+k*14,1.5+k*3,0,0,T);c.stroke()}}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// manga girl with a short white bob presses a vending machine button, the can clunks out and flies to her hand, she cracks it and it fizzes all over her
|
||||
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 lerp=(a,b,u)=>a+(b-a)*max(0,min(1,u)),ease=u=>(u=max(0,min(1,u)),u*u*(3-2*u))
|
||||
const eye=(c,x,y,d)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,13,16,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+2,10.5,14,0,0,T);c.clip();c.fillStyle='#111';c.fill();c.fillStyle='#777';c.beginPath();c.ellipse(x,y+13,10,6,0,0,T);c.fill();c.restore()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-4,y-4,4,5.5,0,0,T);c.fill();c.beginPath();c.arc(x+4,y+8,2,0,T);c.fill()
|
||||
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-5);c.quadraticCurveTo(x,y-20,x+16,y-5);c.moveTo(x+d*14,y-8);c.lineTo(x+d*21,y-13);c.stroke()
|
||||
}
|
||||
const can=(c,x,y,a,DOT)=>{
|
||||
c.save();c.translate(x,y);c.rotate(a);c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.roundRect(-9,-14,18,28,4);c.fill();c.stroke()
|
||||
c.fillStyle=DOT;c.fillRect(-7,-4,14,10);c.lineWidth=2.5;c.beginPath();c.moveTo(-9,-4);c.lineTo(9,-4);c.moveTo(-9,6);c.lineTo(9,6);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.moveTo(-4,0);c.lineTo(0,-3);c.lineTo(4,0);c.lineTo(0,3);c.fill();c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const DOT=tone(c,6,1.3),FINE=tone(c,4,.8)
|
||||
// vending machine
|
||||
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.roundRect(10,14,92,232,8);c.fill();c.stroke()
|
||||
c.fillStyle=DOT;c.fillRect(88,20,10,222);c.lineWidth=4;c.beginPath();c.moveTo(88,17);c.lineTo(88,243);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.roundRect(16,20,68,16,4);c.fill();c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();for(let x=22;x<=78;x+=4)c.lineTo(x,28+3*sin(x*.5));c.stroke();c.strokeStyle='#111'
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(18,44,64,92,4);c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
for(const y of[50,92])for(const x of[24,44,64]){can(c,x+6,y+18,0,DOT)}
|
||||
const press=t<.12,lit=t>=.06&&t<.16
|
||||
for(const x of[24,44,64]){c.fillStyle=x==64&&lit?'#111':'#fff';c.lineWidth=3;c.beginPath();c.roundRect(x+1,140,12,8,3);c.fill();c.stroke()}
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(66,158,14,24,3);c.fill();c.stroke();c.lineWidth=3;c.beginPath();c.moveTo(73,163);c.lineTo(73,177);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.roundRect(20,198,62,32,5);c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.roundRect(24,202,54,12,3);c.fill();c.stroke()
|
||||
// clunk
|
||||
if(t>=.1&&t<.2){c.lineWidth=4;for(let k=0;k<7;k++){const b=-PI+k*PI/6,r=lerp(30,40,(t-.1)/.1);c.beginPath();c.moveTo(51+cos(b)*r,214+sin(b)*r*.7);c.lineTo(51+cos(b)*(r+12),214+sin(b)*(r+12)*.7);c.stroke()}}
|
||||
// girl
|
||||
const s=.62,fz=t>=.46&&t<.82,dead=t>=.82,bub=t>=.46?ease((t-.46)/.1)*(1-ease((t-.72)/.12)):0
|
||||
c.save();c.translate(-10,0);c.lineWidth=5;c.fillStyle='#fff'
|
||||
for(const x of[164,190]){c.lineWidth=13;c.beginPath();c.moveTo(x,208);c.lineTo(x+(x<170?-2:2),236);c.stroke();c.lineWidth=5;c.strokeStyle='#fff';c.stroke();c.strokeStyle='#111'
|
||||
c.lineWidth=4.5;c.beginPath();c.ellipse(x+(x<170?-6:6),240,12,7,0,0,T);c.fill();c.stroke();c.fillStyle=DOT;c.beginPath();c.ellipse(x+(x<170?-6:6),242,9,3,0,0,PI);c.fill();c.fillStyle='#fff'}
|
||||
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(146,188);c.lineTo(210,188);c.lineTo(220,216);c.lineTo(136,216);c.closePath();c.fill();c.fillStyle=FINE;c.fill();c.stroke()
|
||||
c.lineWidth=3;for(const x of[160,178,196])c.beginPath(),c.moveTo(x,192),c.lineTo(x+(x-178)*.4,214),c.stroke()
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(164,124);c.quadraticCurveTo(144,132,142,196);c.lineTo(214,196);c.quadraticCurveTo(212,132,192,124);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=DOT;c.fillRect(196,120,24,80);c.restore();c.beginPath();c.moveTo(164,124);c.quadraticCurveTo(144,132,142,196);c.lineTo(214,196);c.quadraticCurveTo(212,132,192,124);c.closePath();c.stroke()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(178,128);c.lineTo(178,194);c.stroke();c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(160,124);c.quadraticCurveTo(178,140,196,124);c.quadraticCurveTo(178,130,160,124);c.fill();c.stroke()
|
||||
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.moveTo(178,136),c.lineTo(178+d*14,128),c.lineTo(178+d*14,144),c.closePath(),c.fill()
|
||||
c.fillStyle='#fff';c.lineWidth=3;c.beginPath();c.arc(178,136,4,0,T);c.fill();c.stroke()
|
||||
// hands: one presses the button, then both hold the can at her chest
|
||||
const hold=t>=.26&&t<.94,toss=t>=.94,[lx,ly]=t<.12?[lerp(152,114,t/.05),lerp(176,144,t/.05)]:t<.36?[lerp(114,152,(t-.12)/.1),lerp(144,176,(t-.12)/.1)]:t<.48?[lerp(152,210,(t-.36)/.06),lerp(176,152,(t-.36)/.06)]:[lerp(210,152,(t-.48)/.06),lerp(152,178,(t-.48)/.06)]
|
||||
const[cx,cy]=t<.18?[61,214]:t<.26?[lerp(61,220,(t-.18)/.08),lerp(214,164,(t-.18)/.08)-sin((t-.18)/.08*PI)*70]:toss?[lerp(220,250,(t-.94)/.06),lerp(164,40,(t-.94)/.06)]:[220,164]
|
||||
const sh=fz?2*sin(t*T*20):0
|
||||
for(const[sx,sy,hx,hy]of[[150,140,lx,ly],[206,140,hold?224:204,hold?174:184]]){c.lineWidth=15;c.beginPath();c.moveTo(sx,sy);c.lineTo(hx+sh,hy);c.stroke();c.lineWidth=7;c.strokeStyle='#fff';c.stroke();c.strokeStyle='#111'}
|
||||
if(t>=.1)can(c,cx+sh,cy,t<.26?(t-.18)*60:toss?(t-.94)*60:0,DOT)
|
||||
if(t>=.42&&t<.5){c.lineWidth=3.5;for(let k=0;k<8;k++){const b=k*PI/4,r=10+(t-.42)*120;c.beginPath();c.moveTo(220+cos(b)*r,150+sin(b)*r);c.lineTo(220+cos(b)*(r+7),150+sin(b)*(r+7));c.stroke()}}
|
||||
for(const[sx,sy,hx,hy]of[[150,140,lx,ly],[206,140,hold?224:204,hold?174:184]]){c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.arc(hx+sh,hy,7,0,T);c.fill();c.stroke()}
|
||||
// head
|
||||
c.save();c.translate(176,84);c.scale(s,s);c.translate(-128,-128)
|
||||
c.lineWidth=7;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(fz){c.lineWidth=6;c.fillStyle='#fff';for(const x of[103,153])c.beginPath(),c.ellipse(x,140,14,17,0,0,T),c.stroke(),c.fillStyle='#111',c.beginPath(),c.arc(x,140,3.5,0,T),c.fill()
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath();c.moveTo(114,166);c.quadraticCurveTo(120,160,128,166);c.quadraticCurveTo(136,172,142,166);c.lineTo(138,180);c.quadraticCurveTo(128,186,118,180);c.closePath();c.fill();c.stroke()}
|
||||
else if(dead){c.lineWidth=6;for(const x of[103,153])c.beginPath(),c.moveTo(x-15,138),c.lineTo(x+15,138),c.stroke(),c.fillStyle='#111',c.beginPath(),c.arc(x,140,5,0,PI),c.fill()
|
||||
c.lineWidth=4;c.beginPath();c.moveTo(118,172);c.lineTo(138,172);c.stroke()}
|
||||
else{eye(c,103,140,-1);eye(c,153,140,1);c.lineWidth=4.5;c.beginPath();if(t<.42)c.arc(128,164,9,.15*PI,.85*PI);else c.moveTo(120,166),c.quadraticCurveTo(128,176,136,166),c.closePath();c.fillStyle='#fff';c.fill();c.stroke()}
|
||||
c.lineWidth=3;for(const d of[-1,1])for(const k of[-6,0,6])c.beginPath(),c.moveTo(128+d*38+k-3,166),c.lineTo(128+d*38+k+3,158),c.stroke()
|
||||
// white pixie hair with strand lines
|
||||
c.lineWidth=7;c.fillStyle='#fff';c.beginPath();c.moveTo(68,172)
|
||||
c.bezierCurveTo(46,70,86,30,130,32);c.bezierCurveTo(176,34,212,72,188,172)
|
||||
c.quadraticCurveTo(184,140,178,112);c.lineTo(168,120);c.lineTo(160,100);c.lineTo(146,112);c.lineTo(136,92);c.lineTo(122,110);c.lineTo(110,94);c.lineTo(98,112);c.lineTo(86,100);c.lineTo(80,114);c.quadraticCurveTo(72,140,68,172);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=4;for(const[a,b,x,y]of[[96,56,86,84],[120,44,112,74],[146,46,150,76],[170,62,174,90],[108,40,92,60]])c.beginPath(),c.moveTo(a,b),c.quadraticCurveTo((a+x)/2+6,(b+y)/2,x,y),c.stroke()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(130,32);c.quadraticCurveTo(140,14,154,18);c.stroke()
|
||||
c.lineWidth=8;c.beginPath();c.moveTo(150,66);c.lineTo(172,80);c.moveTo(152,80);c.lineTo(170,64);c.stroke()
|
||||
c.restore()
|
||||
// fizz eruption and drips
|
||||
if(bub>0){const top=148-bub*100,B=[]
|
||||
for(let k=0;k<7;k++){const u=k/6;B.push([220+u*4+(k%2?1:-1)*(3+u*6*bub)+3*sin(t*T*7+k),lerp(150,top,u),6+u*11*bub])}
|
||||
for(let k=0;k<5;k++){const b=-PI/2+(k-2)*.5,r=bub*(40+k*9)+((t*9+k*.3)%1)*16;B.push([214+cos(b)*r*.6,top+30+sin(b)*r*.4+r*.3,3+k%2*2])}
|
||||
c.lineWidth=10;for(const[x,y,r]of B)c.beginPath(),c.arc(x,y,r,0,T),c.stroke()
|
||||
c.fillStyle='#fff';for(const[x,y,r]of B)c.beginPath(),c.arc(x,y,r,0,T),c.fill()
|
||||
c.lineWidth=2.5;for(const[x,y,r]of B)if(r>9)c.beginPath(),c.arc(x-r*.3,y-r*.3,r*.35,PI,1.5*PI),c.stroke()
|
||||
c.fillStyle='#fff';c.lineWidth=3;for(let k=0;k<4;k++){const p=(t*5+k/4)%1;c.beginPath();c.arc(210+(k-1.5)*16*p,top-6-p*30+p*p*80,3,0,T);c.fill();c.stroke()}}
|
||||
if(dead||fz&&t>.6){c.lineWidth=4;c.fillStyle='#fff';for(const[x,y]of[[156,58],[200,60],[180,40]]){const d=dead?(t-.82)*30:0;c.beginPath();c.moveTo(x-8,y-8);c.quadraticCurveTo(x,y-14,x+8,y-8);c.lineTo(x+4,y+d);c.quadraticCurveTo(x,y+6+d,x-4,y+d);c.closePath();c.fill();c.stroke()}}
|
||||
if(fz){c.fillStyle='#fff';c.lineWidth=3.5;for(const[x,y]of[[132,74],[148,52]]){c.beginPath();c.moveTo(x,y);c.quadraticCurveTo(x+8,y+14,x,y+18);c.quadraticCurveTo(x-8,y+14,x,y);c.fill();c.stroke()}}
|
||||
c.restore()
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// manga yurei girl in a white kimono bobbing in the air, hands drooping, wispy tail for feet, three hitodama flames circling her
|
||||
import{createCanvas}from'@napi-rs/canvas'
|
||||
const{sin,cos,PI,round,atan2}=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 eye=(c,x,y,d,tn)=>{
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y+2,13,15,0,0,T);c.fill()
|
||||
c.save();c.beginPath();c.ellipse(x,y+4,11,13,0,0,T);c.clip();c.fillStyle=tn;c.fillRect(x-12,y-10,24,28);c.fillStyle='#111';c.fillRect(x-12,y-10,24,9);c.restore()
|
||||
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+4,11,13,0,0,T);c.stroke()
|
||||
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+5,5,6.5,0,0,T);c.fill()
|
||||
c.fillStyle='#fff';c.beginPath();c.ellipse(x-4,y,4,5,-.4,0,T);c.fill();c.beginPath();c.arc(x+4,y+10,2,0,T);c.fill()
|
||||
c.lineWidth=7;c.beginPath();c.moveTo(x-16,y-2);c.quadraticCurveTo(x,y-9,x+16,y-2);c.moveTo(x+d*15,y-2);c.lineTo(x+d*20,y+4);c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(x-6,y+20);c.lineTo(x+6,y+20);c.stroke()
|
||||
}
|
||||
// hitodama: a glowing round head with a long wavy dotted tail trailing behind it
|
||||
const flame=(c,x,y,a,s,tn,w)=>{
|
||||
c.save();c.translate(x,y);c.rotate(a);c.scale(s,s)
|
||||
c.beginPath();c.arc(0,0,10,-PI/2,PI/2);c.bezierCurveTo(-12,10,-20,8+w,-30,4+w);c.quadraticCurveTo(-38,1+w,-46,-2-w);c.quadraticCurveTo(-34,-6-w,-26,-8);c.bezierCurveTo(-16,-10,-8,-10,0,-10);c.closePath()
|
||||
c.fillStyle='#fff';c.lineWidth=3.5/s;c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=tn;c.fillRect(-48,-14,50,30);c.restore()
|
||||
c.fillStyle='#fff';c.lineWidth=2.5/s;c.beginPath();c.arc(1,0,6,0,T);c.fill();c.stroke()
|
||||
c.restore()
|
||||
}
|
||||
export default(c,t)=>{
|
||||
const tn=c.createPattern(TL,'repeat'),b=round(5*sin(t*T)),w=sin(t*T*2)
|
||||
const orb=k=>{const a=t*T+k*T/3;return[a,128+96*cos(a),150+22*sin(a)+b,sin(a)]}
|
||||
const fl=k=>{const[a,x,y,z]=orb(k);flame(c,x,y,atan2(22*cos(a),-96*sin(a)),.95+.2*z,tn,3*sin(t*T*4+k))}
|
||||
for(const k of[0,1,2])if(orb(k)[3]<0)fl(k)
|
||||
c.save();c.translate(0,b);c.translate(128,134);c.scale(1.12,1.12);c.translate(-128,-134)
|
||||
// long hair behind
|
||||
c.lineWidth=5;c.fillStyle='#111';c.beginPath();c.moveTo(128,30);c.bezierCurveTo(70,30,68,80,74,120)
|
||||
c.bezierCurveTo(78,160,66,180,62+3*w,200);c.quadraticCurveTo(80,192,92,200);c.lineTo(164,200);c.quadraticCurveTo(176,192,194-3*w,200)
|
||||
c.bezierCurveTo(190,180,178,160,182,120);c.bezierCurveTo(188,80,186,30,128,30);c.fill();c.stroke()
|
||||
// kimono body tapering into a wispy tail
|
||||
const tl=10*sin(t*T)
|
||||
c.fillStyle='#fff';c.beginPath();c.moveTo(110,120);c.quadraticCurveTo(88,128,88,160);c.quadraticCurveTo(90,192,106,208)
|
||||
c.bezierCurveTo(118,222,138+tl,226,148+tl*1.4,242);c.bezierCurveTo(156+tl,226,166,212,168,194)
|
||||
c.quadraticCurveTo(170,166,168,152);c.quadraticCurveTo(166,126,146,120);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=tn;c.beginPath();c.moveTo(150,186);c.quadraticCurveTo(170,200,156+tl,240);c.lineTo(200,240);c.lineTo(200,186);c.fill();c.restore()
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(120,210);c.quadraticCurveTo(134,218,142+tl*.6,232);c.stroke()
|
||||
// collar, crossed left over right
|
||||
c.beginPath();c.moveTo(114,121);c.lineTo(134,152);c.moveTo(142,121);c.lineTo(128,142);c.stroke()
|
||||
// obi
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(92,170);c.quadraticCurveTo(128,176,166,170);c.lineTo(166,184);c.quadraticCurveTo(128,190,94,184);c.closePath();c.fill();c.stroke()
|
||||
c.save();c.clip();c.fillStyle=tn;c.fillRect(80,160,100,30);c.restore()
|
||||
// furisode sleeves hanging from the forearms, hands drooping at the wrists in front of the chest
|
||||
for(const d of[-1,1]){
|
||||
c.lineWidth=4;c.fillStyle='#fff';c.beginPath();c.moveTo(128+d*30,124);c.quadraticCurveTo(128+d*46,132,128+d*48,150);c.lineTo(128+d*46,190+2*w*d)
|
||||
c.quadraticCurveTo(128+d*32,196+2*w*d,128+d*18,188+2*w*d);c.lineTo(128+d*12,150);c.quadraticCurveTo(128+d*18,136,128+d*30,124);c.fill();c.stroke()
|
||||
c.lineWidth=2.5;c.beginPath();c.moveTo(128+d*38,160);c.quadraticCurveTo(128+d*40,176,128+d*36,186+2*w*d);c.stroke()
|
||||
const hx=128+d*10,hy=150
|
||||
c.lineWidth=3;c.beginPath();c.moveTo(hx+d*4,hy-6);c.quadraticCurveTo(hx-d*8,hy-6,hx-d*7,hy+6);c.quadraticCurveTo(hx-d*6,hy+16,hx-d*1,hy+17);c.quadraticCurveTo(hx+d*2,hy+10,hx+d*4,hy+6);c.closePath();c.fill();c.stroke()
|
||||
c.lineWidth=1.8;c.beginPath();c.moveTo(hx-d*4,hy+6);c.lineTo(hx-d*3,hy+14);c.stroke()
|
||||
}
|
||||
// head
|
||||
c.save();c.translate(128,84);c.scale(.6,.6);c.translate(-128,-120)
|
||||
c.lineWidth=6;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,136,-1,tn);eye(c,153,136,1,tn)
|
||||
c.lineWidth=2.5;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=3.5;c.beginPath();c.ellipse(128,171,5,6+w,0,0,T);c.stroke()
|
||||
// straight hime bangs, ink black with a shine band
|
||||
c.lineWidth=6;c.fillStyle='#111';c.beginPath();c.moveTo(72,150);c.bezierCurveTo(54,70,88,38,128,38);c.bezierCurveTo(168,38,202,70,184,150)
|
||||
c.lineTo(180,112);c.lineTo(76,112);c.closePath();c.fill();c.stroke()
|
||||
c.strokeStyle='#fff';c.lineWidth=4;for(const[a,e]of[[1.3,1.44],[1.52,1.62],[1.68,1.74]])c.beginPath(),c.arc(128,112,52,PI*a,PI*e),c.stroke()
|
||||
c.lineWidth=2.5;for(const x of[100,118,138,156])c.beginPath(),c.moveTo(x,84),c.lineTo(x-1,108),c.stroke();c.strokeStyle='#111'
|
||||
// tenkan triangle on the forehead
|
||||
c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(112,100);c.lineTo(144,100);c.lineTo(128,74);c.closePath();c.fill();c.stroke()
|
||||
c.restore()
|
||||
c.restore()
|
||||
for(const k of[0,1,2])if(orb(k)[3]>=0)fl(k)
|
||||
}
|
||||
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 228 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 149 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 149 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 205 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 199 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 207 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 225 KiB |
|
After Width: | Height: | Size: 167 KiB |
|
After Width: | Height: | Size: 259 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 165 KiB |