Files
hera-web/scripts/render-zodiac-icons.cjs

34 lines
6.1 KiB
JavaScript

// Vector source for the twelve original zodiac mascots. PNG output works in both H5 and WeChat.
// Run with a locally installed sharp, or pass its module path as the first argument.
const path = require('node:path')
const fs = require('node:fs')
const sharp = require(process.argv[2] || 'sharp')
const output = path.resolve(__dirname, '../src/assets/zodiac')
const face = (fill, extra = '') => `<path d="M29 61C29 37 43 29 64 29s35 8 35 32v20c0 22-15 31-35 31S29 103 29 81Z" fill="${fill}"/>${extra}`
const eyes = `<ellipse cx="49" cy="74" rx="3" ry="4" fill="#62463f" stroke="none"/><ellipse cx="79" cy="74" rx="3" ry="4" fill="#62463f" stroke="none"/>`
const smile = `<path d="M58 88q6 7 12 0" fill="none" stroke="#62463f" stroke-width="2.8"/><ellipse cx="40" cy="85" rx="7" ry="4" fill="#ee9f9d" opacity=".45" stroke="none"/><ellipse cx="88" cy="85" rx="7" ry="4" fill="#ee9f9d" opacity=".45" stroke="none"/>`
const art = {
rat: `<circle cx="31" cy="38" r="18" fill="#d7d0ef"/><circle cx="97" cy="38" r="18" fill="#d7d0ef"/><circle cx="31" cy="38" r="10" fill="#f4c5d5" stroke="none"/><circle cx="97" cy="38" r="10" fill="#f4c5d5" stroke="none"/>${face('#e8e1f4')}${eyes}<ellipse cx="64" cy="85" rx="6" ry="4" fill="#d98ca6"/>${smile}<path d="m29 82-12-3m13 10-13 3m82-10 12-3m-13 10 13 3" fill="none" stroke-width="2"/>`,
ox: `<path d="M39 39C18 39 17 22 24 15c0 12 8 10 19 10m42 14c21 0 22-17 15-24 0 12-8 10-19 10" fill="#f7ddb1"/><ellipse cx="26" cy="58" rx="13" ry="9" fill="#c49469"/><ellipse cx="102" cy="58" rx="13" ry="9" fill="#c49469"/>${face('#efd6b0')}${eyes}<rect x="39" y="82" width="50" height="24" rx="12" fill="#f5b6af"/><circle cx="53" cy="93" r="2.5" fill="#946457"/><circle cx="75" cy="93" r="2.5" fill="#946457"/>`,
tiger: `<circle cx="34" cy="37" r="14" fill="#f4bc62"/><circle cx="94" cy="37" r="14" fill="#f4bc62"/>${face('#ffd17d')}<path d="M40 80c0-11 15-15 24-8 9-7 24-3 24 8 0 17-10 25-24 25S40 97 40 80Z" fill="#fff3d6" stroke="none"/><path d="M64 31v19m-9-13h18m-16 9h14M30 59l13 5-13 4m68-9-13 5 13 4" fill="none" stroke-width="4"/>${eyes}<path d="m59 85 5 4 5-4Z" fill="#835946" stroke="none"/>${smile}`,
rabbit: `<path d="M38 52C25 5 47 1 51 16l6 38m14 0 6-38c4-15 26-11 13 36" fill="#fff4d8"/><path d="m40 42-3-22q4-12 8 0l4 23m30 0 4-23q4-12 8 0l-3 22" fill="#f5bac8" stroke="none"/>${face('#fff4d8')}${eyes}<path d="m60 83 4 4 4-4" fill="#e693aa" stroke="#e693aa"/>${smile}`,
dragon: `<path d="m40 39-9-21 12 6 4-14 8 27m18 0 8-27 4 14 12-6-9 21" fill="#f4d58f"/><path d="M28 54 13 45l7 22m80-13 15-9-7 22" fill="#8ad6b4"/>${face('#a1e2c3')}<path d="m53 36 11-13 11 13" fill="#6abea5" stroke="none"/>${eyes}<rect x="38" y="82" width="52" height="24" rx="12" fill="#d2f1d4"/><circle cx="51" cy="91" r="2" fill="#648b6e"/><circle cx="77" cy="91" r="2" fill="#648b6e"/><path d="M40 94q-27 11-22-8m70 8q27 11 22-8" fill="none" stroke="#7ca986" stroke-width="2.5"/>`,
snake: `<path d="M93 85c24 31-63 38-63 15 0-16 34-12 34-30" fill="none" stroke="#729f83" stroke-width="19"/><path d="M92 94c12 11-44 18-50 6" fill="none" stroke="#b1deab" stroke-width="10"/><path d="M35 46c0-21 58-26 58 2 0 25-12 39-29 39S35 68 35 46Z" fill="#b8e2a7"/><ellipse cx="49" cy="55" rx="3" ry="4" fill="#62463f" stroke="none"/><ellipse cx="79" cy="55" rx="3" ry="4" fill="#62463f" stroke="none"/><path d="M56 69q8 8 16 0" fill="none"/><path d="M64 75v13m0-2-5 4m5-4 5 4" fill="none" stroke="#e18f9e" stroke-width="2.5"/>`,
horse: `<path d="M42 48C21 25 27 4 40 17l15 26m18 0 15-26c13-13 19 8-2 31" fill="#cc9c7b"/><path d="M46 35q18-31 35-5L68 54Z" fill="#795546"/>${face('#dfb18b')}<path d="M61 37h8v33h-8Z" fill="#fff1db" stroke="none"/>${eyes}<ellipse cx="64" cy="94" rx="26" ry="17" fill="#f1d4af"/><circle cx="54" cy="93" r="2" fill="#946457"/><circle cx="74" cy="93" r="2" fill="#946457"/>`,
goat: `<path d="M39 43C9 38 14 10 32 19q17 10-2 17m59 7c30-5 25-33 7-24Q79 29 98 36" fill="#e7c6a1"/><ellipse cx="26" cy="63" rx="14" ry="8" fill="#edcfb9"/><ellipse cx="102" cy="63" rx="14" ry="8" fill="#edcfb9"/>${face('#f7e6d0')}<path d="M30 56q-12-20 8-25-1-16 18-13 8-16 21 0 20-3 18 13 20 5 6 25-9-13-18-7-10-13-20-3-12-7-20 10Z" fill="#fffdf1"/>${eyes}<path d="m60 87 4 4 4-4m-7 19 3 10 3-10" fill="none"/>${smile}`,
monkey: `<circle cx="25" cy="68" r="16" fill="#ba8d6d"/><circle cx="103" cy="68" r="16" fill="#ba8d6d"/><circle cx="25" cy="68" r="8" fill="#f4d5b0" stroke="none"/><circle cx="103" cy="68" r="8" fill="#f4d5b0" stroke="none"/>${face('#bd9373')}<path d="M34 70c0-23 23-29 30-14 7-15 30-9 30 14v15c0 18-16 24-30 24S34 103 34 85Z" fill="#ffe6ba" stroke="none"/><path d="M56 30q-2-15 14-14-8 5-4 14" fill="#bd9373"/>${eyes}<ellipse cx="64" cy="85" rx="4" ry="3" fill="#c38c78" stroke="none"/>${smile}`,
rooster: `<path d="M49 43c-24-19-4-33 4-17 0-25 25-25 22 1 15-16 30 2 8 17" fill="#ed8690"/><path d="M54 101q-2 23 10 15 12 8 10-15" fill="#ed8690"/>${face('#fff1ce')}${eyes}<path d="m64 81-11 8 11 12 11-12Z" fill="#f7bc63"/>`,
dog: `<path d="M42 34C11 27 11 57 15 81q11 12 23-10m48-37c31-7 31 23 27 47-11 12-23-10" fill="#a87d64"/>${face('#eed1a9')}<ellipse cx="81" cy="68" rx="14" ry="18" fill="#bd987d" stroke="none"/>${eyes}<ellipse cx="64" cy="88" rx="21" ry="15" fill="#fff0d4" stroke="none"/><path d="m58 84 6 6 6-6q-6-5-12 0" fill="#795b51" stroke="none"/>${smile}`,
pig: `<path d="M35 51Q17 22 33 20l23 18m16 0 23-18q16 2-2 31" fill="#f5bbc5"/>${face('#ffd3dc')}${eyes}<rect x="41" y="80" width="46" height="28" rx="14" fill="#eea2b4"/><ellipse cx="55" cy="94" rx="3" ry="5" fill="#b56f86" stroke="none"/><ellipse cx="73" cy="94" rx="3" ry="5" fill="#b56f86" stroke="none"/>`
}
async function main() {
fs.mkdirSync(output, { recursive: true })
for (const [name, content] of Object.entries(art)) {
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 128 128"><g stroke="#78594f" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round">${content}</g></svg>`
await sharp(Buffer.from(svg)).png().toFile(path.join(output, `${name}.png`))
}
console.log(`Rendered ${Object.keys(art).length} zodiac icons`)
}
main().catch((error) => { console.error(error); process.exitCode = 1 })