food_chain/tree-sprites.js
2026-09-29 19:10:03 +09:00

12 lines
1.3 KiB
JavaScript

// Top-down canopies are functional world markers. Sprites are rasterized once per palette variant.
export function createTreeSprites(createCanvas){
return Array.from({length:6},(_,variant)=>{const c=createCanvas(192,192),g=c.getContext('2d');c.width=c.height=192;
const leaf=['#254a29','#345b2d','#416634','#507339','#5d7c40'];
g.fillStyle='#071e1740';g.beginPath();g.ellipse(99,103,82,77,0,0,Math.PI*2);g.fill();
g.strokeStyle='#65553a';g.lineWidth=4;
for(let j=0;j<9;j++){const angle=j*Math.PI*2/9+variant*.38,reach=43+(j%3)*9;g.beginPath();g.moveTo(96,96);g.lineTo(96+Math.cos(angle)*reach,96+Math.sin(angle)*reach);g.stroke()}
for(let ring=0;ring<2;ring++){const count=ring?8:13;for(let j=0;j<count;j++){const angle=j*Math.PI*2/count+variant*.38+ring*.4,dist=ring?31:58,x=96+Math.cos(angle)*dist,y=96+Math.sin(angle)*dist,r=ring?28:23+(j+variant)%6;g.fillStyle=leaf[(j+variant+ring)%leaf.length];g.beginPath();g.arc(x,y,r,0,Math.PI*2);g.fill();g.fillStyle='#a8bc7130';g.beginPath();g.ellipse(x-6,y-8,r*.55,r*.35,-.5,0,Math.PI*2);g.fill()}}
// A small central opening keeps the exact solid trunk visible at every zoom.
g.globalCompositeOperation='destination-out';g.beginPath();g.arc(96,96,10,0,Math.PI*2);g.fill();g.globalCompositeOperation='source-over';return c;
});
}