120 lines
4.3 KiB
JavaScript
120 lines
4.3 KiB
JavaScript
/**
|
||
* 从 Lucide SVG 图标生成高质量 PNG
|
||
* 用法: node scripts/generate-icons.js
|
||
*/
|
||
const sharp = require('sharp')
|
||
const fs = require('fs')
|
||
const path = require('path')
|
||
|
||
const ICONS_DIR = path.join(__dirname, '../client/src/static/icons')
|
||
const LUCIDE_DIR = path.join(__dirname, '../node_modules/lucide-static/icons')
|
||
|
||
// 颜色映射:hex -> RGB
|
||
const COLORS = {
|
||
'#2D1B1B': { r: 45, g: 27, b: 27 }, // 暖深棕
|
||
'#8B7E7E': { r: 139, g: 126, b: 126 }, // 灰色
|
||
'#BFB3B3': { r: 191, g: 179, b: 179 }, // 浅灰
|
||
'#E0D6D0': { r: 224, g: 214, b: 208 }, // 极浅灰
|
||
'#FF8C69': { r: 255, g: 140, b: 105 }, // 珊瑚橙
|
||
'#FF6B6B': { r: 255, g: 107, b: 107 }, // 红色
|
||
'#7BC67E': { r: 123, g: 198, b: 126 }, // 绿色
|
||
'#FFFFFF': { r: 255, g: 255, b: 255 }, // 白色
|
||
}
|
||
|
||
// 图标配置:{ lucideName, variants: { colorHex: outputName } }
|
||
const ICONS = [
|
||
// 导航
|
||
{ lucide: 'arrow-left', variants: { '#2D1B1B': 'arrow-left-dark', '#8B7E7E': 'arrow-left-gray' } },
|
||
{ lucide: 'arrow-right', variants: { '#8B7E7E': 'arrow-right-gray', '#E0D6D0': 'arrow-right-pale' } },
|
||
{ lucide: 'chevron-right', variants: { '#BFB3B3': 'chevron-right-light' } },
|
||
|
||
// 操作
|
||
{ lucide: 'plus', variants: { '#FFFFFF': 'plus-white' } },
|
||
{ lucide: 'check', variants: { '#FFFFFF': 'check-white' } },
|
||
{ lucide: 'edit', variants: { '#8B7E7E': 'edit-gray', '#BFB3B3': 'edit-light' } },
|
||
{ lucide: 'trash-2', variants: { '#FF8C69': 'trash-orange', '#FF6B6B': 'trash-red', '#FFFFFF': 'trash-white' } },
|
||
{ lucide: 'refresh-cw', variants: { '#8B7E7E': 'refresh-gray', '#FF8C69': 'refresh-orange' } },
|
||
|
||
// 功能
|
||
{ lucide: 'calendar', variants: { '#8B7E7E': 'calendar-gray' } },
|
||
{ lucide: 'bell', variants: { '#8B7E7E': 'bell-gray' } },
|
||
{ lucide: 'user', variants: { '#8B7E7E': 'user-gray', '#FFFFFF': 'user-white' } },
|
||
{ lucide: 'alert-circle', variants: { '#BFB3B3': 'alert-circle-light' } },
|
||
|
||
// 趋势
|
||
{ lucide: 'trending-down', variants: { '#FF6B6B': 'trend-down-red' } },
|
||
{ lucide: 'trending-up', variants: { '#7BC67E': 'trend-up-green' } },
|
||
]
|
||
|
||
// TabBar 图标(需要 normal + active 两套)
|
||
const TABBAR_ICONS = [
|
||
{ lucide: 'home', normal: 'home', active: 'home-active', normalColor: '#BFB3B3', activeColor: '#FF8C69' },
|
||
{ lucide: 'bar-chart-2', normal: 'chart', active: 'chart-active', normalColor: '#BFB3B3', activeColor: '#FF8C69' },
|
||
{ lucide: 'receipt', normal: 'bill', active: 'bill-active', normalColor: '#BFB3B3', activeColor: '#FF8C69' },
|
||
{ lucide: 'user', normal: 'user', active: 'user-active', normalColor: '#BFB3B3', activeColor: '#FF8C69' },
|
||
]
|
||
|
||
const SIZE = 96 // 输出 PNG 尺寸(px)
|
||
|
||
async function generateIcon(svgName, outputPath, color) {
|
||
const svgPath = path.join(LUCIDE_DIR, `${svgName}.svg`)
|
||
if (!fs.existsSync(svgPath)) {
|
||
console.warn(`⚠ SVG not found: ${svgName}.svg`)
|
||
return false
|
||
}
|
||
|
||
let svgContent = fs.readFileSync(svgPath, 'utf-8')
|
||
|
||
// 替换 SVG 中的 stroke 颜色
|
||
const { r, g, b } = COLORS[color]
|
||
svgContent = svgContent.replace(/stroke="[^"]*"/g, `stroke="rgb(${r},${g},${b})"`)
|
||
// 也处理可能的 fill
|
||
svgContent = svgContent.replace(/fill="none"/g, 'fill="none"')
|
||
|
||
// 设置输出尺寸
|
||
svgContent = svgContent.replace(/width="[^"]*"/, `width="${SIZE}"`)
|
||
svgContent = svgContent.replace(/height="[^"]*"/, `height="${SIZE}"`)
|
||
|
||
await sharp(Buffer.from(svgContent))
|
||
.resize(SIZE, SIZE)
|
||
.png()
|
||
.toFile(outputPath)
|
||
|
||
return true
|
||
}
|
||
|
||
async function main() {
|
||
console.log('🎨 开始生成图标...\n')
|
||
|
||
let total = 0
|
||
let success = 0
|
||
|
||
// 生成普通图标
|
||
for (const icon of ICONS) {
|
||
for (const [color, outputName] of Object.entries(icon.variants)) {
|
||
const outputPath = path.join(ICONS_DIR, `${outputName}.png`)
|
||
total++
|
||
if (await generateIcon(icon.lucide, outputPath, color)) {
|
||
console.log(` ✓ ${outputName}.png`)
|
||
success++
|
||
}
|
||
}
|
||
}
|
||
|
||
// 生成 TabBar 图标
|
||
for (const icon of TABBAR_ICONS) {
|
||
for (const [outputName, color] of [[icon.normal, icon.normalColor], [icon.active, icon.activeColor]]) {
|
||
const outputPath = path.join(ICONS_DIR, `${outputName}.png`)
|
||
total++
|
||
if (await generateIcon(icon.lucide, outputPath, color)) {
|
||
console.log(` ✓ ${outputName}.png (tabbar)`)
|
||
success++
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log(`\n✅ 完成: ${success}/${total} 个图标已生成`)
|
||
}
|
||
|
||
main().catch(console.error)
|