Files
2026-10-24 14:37:40 +00:00

38 lines
3.3 KiB
JavaScript

// side profile of a blank-staring head shown in cross-section, and up top there's nothing but empty desert with a tumbleweed bouncing through on the wind
const{sin,cos,PI,abs}=Math,T=2*PI
export const frames=24,delay=Array(24).fill(70),rim=4
const SK='#ffd8b5',SKD='#e8a27a',SKY='#fff1d0',SAND='#f2c879',SD='#cf9440',TW='#a5692c',CA='#69db7c',FL=118
const head=c=>{c.beginPath();c.moveTo(84,256);c.lineTo(86,206);c.bezierCurveTo(36,186,22,104,52,62);c.bezierCurveTo(86,14,176,12,202,64);c.bezierCurveTo(214,90,214,112,208,128);c.lineTo(228,158);c.quadraticCurveTo(224,164,210,166);c.quadraticCurveTo(216,174,211,180);c.quadraticCurveTo(217,188,209,194);c.quadraticCurveTo(208,210,194,214);c.quadraticCurveTo(178,220,162,218);c.lineTo(160,256)}
export default(c,t)=>{
c.fillStyle=SK;head(c);c.fill()
c.save();head(c);c.clip()
// inside the skull: sky, sun, dunes, cactus
c.fillStyle=SKY;c.fillRect(0,0,256,FL)
c.fillStyle='#ffe066';c.lineWidth=4;c.beginPath();c.arc(168,58,13,0,T);c.fill()
c.fillStyle=SAND;c.beginPath();c.moveTo(0,104);c.quadraticCurveTo(70,88,120,100);c.quadraticCurveTo(170,110,256,94);c.lineTo(256,FL);c.lineTo(0,FL);c.fill();c.stroke()
c.lineWidth=3;c.strokeStyle=SD;for(const[x,y]of[[70,108],[132,110],[176,104]])c.beginPath(),c.moveTo(x,y),c.lineTo(x+10,y),c.stroke()
c.strokeStyle='#111';c.fillStyle=CA;c.lineWidth=4;c.beginPath();c.roundRect(64,64,11,34,5.5);c.fill();c.stroke()
for(const[a,b,d]of[[64,82,-1],[75,76,1]])c.beginPath(),c.moveTo(a,b),c.quadraticCurveTo(a+d*10,b,a+d*10,b-10),c.stroke()
// wind streaks
c.lineWidth=3;c.globalAlpha=.55;for(let k=0;k<4;k++){const x=((t+k*.31)%1)*220+10,y=44+k*14;c.beginPath();c.moveTo(x,y);c.lineTo(x+18+k*5,y);c.stroke()}c.globalAlpha=1
// tumbleweed, comes in from behind the skull wall and leaves through the forehead so the loop jump is hidden
const x=22+t*204,h=abs(sin(2*T*t)),r=20,y=FL-6-r-h*28,a=x/r*.9
c.fillStyle=SD;c.globalAlpha=.45;c.beginPath();c.ellipse(x,FL-5,r*(1-h*.5),3,0,0,T);c.fill();c.globalAlpha=1
c.save();c.translate(x,y);c.rotate(a);c.fillStyle='#ecd09a';c.beginPath();c.arc(0,0,r-2,0,T);c.fill();c.strokeStyle=TW;c.lineWidth=4
for(let k=0;k<6;k++){c.beginPath();c.ellipse(sin(k*2.1)*3,cos(k*1.7)*3,r-3,(r-3)*(.3+.13*(k%3)),k*PI/6,0,T);c.stroke()}
c.strokeStyle='#5c3a17';c.lineWidth=2.5;for(let k=0;k<5;k++){const b=k*1.26;c.beginPath();c.moveTo(cos(b)*r,sin(b)*r);c.lineTo(cos(b)*(r+4),sin(b)*(r+4));c.stroke()}
c.restore()
// skull wall: thick skin band hugging the outline, with the cut floor
for(const[w,s]of[[30,'#111'],[24,SKD],[14,SK]])c.lineWidth=w,c.strokeStyle=s,head(c),c.stroke()
c.fillStyle=SK;c.fillRect(0,FL,256,140);c.fillStyle=SKD;c.fillRect(0,FL,256,5);c.lineWidth=3.5;c.strokeStyle='#111';c.beginPath();c.moveTo(0,FL);c.lineTo(256,FL);c.stroke()
c.restore()
c.lineWidth=7;head(c);c.stroke()
// ear, blank stare, flat mouth
c.fillStyle=SK;c.lineWidth=6;c.beginPath();c.ellipse(106,176,13,18,.15,0,T);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.arc(108,176,6,-1.6,1.4);c.stroke()
const bl=t>.82&&t<.88
c.fillStyle='#111';if(bl){c.beginPath();c.moveTo(182,140);c.lineTo(196,140);c.stroke()}else{c.beginPath();c.arc(190,140,5.5,0,T);c.fill()}
c.beginPath();c.moveTo(178,131);c.lineTo(200,132);c.stroke()
c.beginPath();c.moveTo(198,187);c.lineTo(210,187);c.stroke()
}