134 lines
6.5 KiB
JavaScript
134 lines
6.5 KiB
JavaScript
/**
|
||
* 图标生成脚本:微信小程序不支持内联 <svg>,因此把初设图里的 SVG symbol
|
||
* 编译成 WXSS 的 background-image(base64 data-uri),按「名称 + 色调」生成类名。
|
||
*
|
||
* 用法:node scripts/build-icons.mjs
|
||
* 产出:src/styles/icons.scss
|
||
*/
|
||
import fs from "node:fs";
|
||
import path from "node:path";
|
||
|
||
const ROOT = path.resolve(import.meta.dirname, "..");
|
||
const OUT = path.join(ROOT, "src/styles/icons.scss");
|
||
|
||
/** 颜色 token(与 design-token.scss 保持一致) */
|
||
const TONES = {
|
||
ink: "#14181C",
|
||
ink2: "#6B7280",
|
||
ink3: "#9CA3AF",
|
||
white: "#FFFFFF",
|
||
brand: "#00B578",
|
||
brandDeep: "#009A67",
|
||
accent: "#FF8A3D",
|
||
danger: "#E5484D",
|
||
};
|
||
|
||
/** stroke 型图标:24×24 线性图标,stroke-width 1.9 */
|
||
const STROKE_ICONS = {
|
||
pin: '<path d="M12 21s7-6.1 7-11a7 7 0 1 0-14 0c0 4.9 7 11 7 11z"/><circle cx="12" cy="10" r="2.4"/>',
|
||
arrow: '<path d="M4.5 12h15"/><path d="M13.5 6l6 6-6 6"/>',
|
||
chev: '<path d="M9.5 5.5 16 12l-6.5 6.5"/>',
|
||
back: '<path d="M14.5 5.5 8 12l6.5 6.5"/>',
|
||
phone:
|
||
'<path d="M4.2 4.4h3.9l1.9 4.8-2.4 1.5a12.2 12.2 0 0 0 5.8 5.8l1.5-2.4 4.8 1.9v3.9a1 1 0 0 1-1.1 1A16.8 16.8 0 0 1 3.2 5.5a1 1 0 0 1 1-1.1z"/>',
|
||
copy: '<rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15.5V5.5a1.5 1.5 0 0 1 1.5-1.5h10"/>',
|
||
gift: '<path d="M20 12v8.5H4V12"/><path d="M2.5 7.5h19V12h-19z"/><path d="M12 7.5v13"/><path d="M12 7.5H8.6a2.3 2.3 0 1 1 0-4.6C11.6 2.9 12 7.5 12 7.5z"/><path d="M12 7.5h3.4a2.3 2.3 0 1 0 0-4.6C12.4 2.9 12 7.5 12 7.5z"/>',
|
||
home: '<path d="M4 11.2 12 4.4l8 6.8v8.3a1 1 0 0 1-1 1h-4.2v-6H9.2v6H5a1 1 0 0 1-1-1z"/>',
|
||
ticket:
|
||
'<path d="M3.5 9.6a2 2 0 0 1 2-2h13a2 2 0 0 1 2 2v1.2a1.4 1.4 0 0 0 0 2.8v1.2a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2v-1.2a1.4 1.4 0 0 0 0-2.8z"/><path d="M13.6 7.6v2M13.6 13v2"/>',
|
||
user: '<circle cx="12" cy="8.2" r="3.9"/><path d="M4.6 20.6a7.4 7.4 0 0 1 14.8 0"/>',
|
||
check: '<path d="M5.5 12.6 10 17l8.5-9"/>',
|
||
close: '<path d="M6.5 6.5l11 11M17.5 6.5l-11 11"/>',
|
||
doc: '<path d="M6.5 3.5h7.5l4.5 4.5v12.5h-12z"/><path d="M14 3.5V8h4.5"/><path d="M9 13h6M9 16.5h4.5"/>',
|
||
shield: '<path d="M12 3.5 19.5 6v5.6c0 4.6-3.6 7.6-7.5 8.9-3.9-1.3-7.5-4.3-7.5-8.9V6z"/><path d="M9.2 12.2l2.2 2.2 3.6-3.6"/>',
|
||
info: '<circle cx="12" cy="12" r="8.6"/><path d="M12 11v5.2"/><path d="M12 7.8h.01"/>',
|
||
headset:
|
||
'<path d="M4.5 15v-3a7.5 7.5 0 0 1 15 0v3"/><path d="M4.5 13.6h2.2v5H5.6a1.1 1.1 0 0 1-1.1-1.1z"/><path d="M19.5 13.6h-2.2v5h1.1a1.1 1.1 0 0 0 1.1-1.1z"/><path d="M17.3 18.6v.6a2 2 0 0 1-2 2h-2.8"/>',
|
||
logout: '<path d="M15 12H4"/><path d="M11.4 8.4 15 12l-3.6 3.6"/><path d="M15.5 4.5h3a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-3"/>',
|
||
trash:
|
||
'<path d="M4.5 7h15"/><path d="M9.5 7V4.8h5V7"/><path d="M6.5 7l.9 13h9.2l.9-13"/><path d="M10.5 10.5v6M13.5 10.5v6"/>',
|
||
share:
|
||
'<path d="M12 16V3.8"/><path d="M8.2 7.6 12 3.8l3.8 3.8"/><path d="M5 13.6V19a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-5.4"/>',
|
||
clock: '<circle cx="12" cy="12" r="8.6"/><path d="M12 7.4v4.8l3.2 1.9"/>',
|
||
map: '<path d="M9 4.5 3.5 6.6v13L9 17.4l6 2.1 5.5-2.1v-13L15 6.6z"/><path d="M9 4.5v12.9M15 6.6v12.9"/>',
|
||
food: '<path d="M6.6 3.6v5M9.4 3.6v5"/><path d="M8 3.6v16.8"/><path d="M6.6 8.6h2.8"/><path d="M16.4 3.6c1.7 0 2.8 1.3 2.8 3.1 0 1.9-1.1 3.1-2.8 3.1s-2.8-1.2-2.8-3.1c0-1.8 1.1-3.1 2.8-3.1z"/><path d="M16.4 9.8v10.6"/>',
|
||
bed: '<path d="M3.6 18.6V6.4"/><path d="M3.6 15h16.8v3.6"/><path d="M20.4 15v-2.6a1.6 1.6 0 0 0-1.6-1.6h-7V15"/><circle cx="7.6" cy="12.1" r="1.7"/>',
|
||
peak: '<path d="M2.8 19.2 9 8.4l3.6 6.2 2.2-3.4 6.4 8z"/>',
|
||
people:
|
||
'<circle cx="9.2" cy="8.4" r="3.2"/><path d="M3.6 19.4a5.6 5.6 0 0 1 11.2 0"/><path d="M15.8 5.6a3.2 3.2 0 0 1 0 6"/><path d="M17.4 14.8a5.6 5.6 0 0 1 3 4.6"/>',
|
||
};
|
||
|
||
/** fill 型图标(实心) */
|
||
const FILL_ICONS = {
|
||
wx: '<path d="M8.9 3.4C5.2 3.4 2.2 5.9 2.2 9c0 1.8 1 3.4 2.6 4.4l-.7 2.4 2.8-1.4c.6.2 1.3.3 2 .3h.5a5.4 5.4 0 0 1-.2-1.5c0-3 3-5.3 6.8-5.3h.5C16 5.3 12.7 3.4 8.9 3.4z"/><path d="M15.9 8.8c-3.4 0-6.1 2-6.1 4.4 0 1.5.9 2.8 2.3 3.6l-.6 1.9 2.3-1.1c.5.1 1.1.2 1.6.2 3.4 0 6.1-2 6.1-4.6s-2.7-4.4-5.6-4.4z"/>',
|
||
};
|
||
|
||
/** 需要生成的「图标 × 色调」组合(只生成用得到的,控制体积) */
|
||
const MATRIX = {
|
||
pin: ["ink3", "brand", "white"],
|
||
arrow: ["white", "brandDeep", "ink"],
|
||
chev: ["ink3", "white", "brand", "ink2"],
|
||
back: ["ink", "white", "brand"],
|
||
phone: ["white", "brandDeep"],
|
||
copy: ["brandDeep", "brand"],
|
||
gift: ["white", "brandDeep", "accent"],
|
||
home: ["ink3", "brand"],
|
||
ticket: ["ink3", "brand", "accent", "white", "ink2"],
|
||
user: ["ink3", "brand", "white", "ink2"],
|
||
check: ["white", "brand"],
|
||
close: ["ink3", "white"],
|
||
doc: ["ink2", "brand"],
|
||
shield: ["ink2", "white", "brand"],
|
||
info: ["ink2", "brand"],
|
||
headset: ["ink2", "brand"],
|
||
logout: ["ink2", "white"],
|
||
trash: ["ink2", "danger"],
|
||
share: ["white"],
|
||
clock: ["brand", "ink3", "white"],
|
||
map: ["brandDeep", "white"],
|
||
food: ["brandDeep", "accent", "white", "brand"],
|
||
bed: ["accent", "brand", "white", "brandDeep"],
|
||
peak: ["accent", "brand", "white"],
|
||
people: ["ink3", "brand", "white", "brandDeep"],
|
||
wx: ["white"],
|
||
};
|
||
|
||
const buildSvg = (name, body, color, type) => {
|
||
const style =
|
||
type === "fill"
|
||
? `fill='${color}' stroke='none'`
|
||
: `fill='none' stroke='${color}' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'`;
|
||
return `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' ${style}>${body}</svg>`;
|
||
};
|
||
|
||
const toDataUri = (svg) => `data:image/svg+xml;base64,${Buffer.from(svg, "utf8").toString("base64")}`;
|
||
|
||
const lines = [
|
||
"/* 由 scripts/build-icons.mjs 生成,请勿手改 */",
|
||
"",
|
||
".ai{display:block;flex:none;background-repeat:no-repeat;background-position:center;background-size:100% 100%}",
|
||
"",
|
||
];
|
||
|
||
let count = 0;
|
||
for (const [name, tones] of Object.entries(MATRIX)) {
|
||
const type = FILL_ICONS[name] ? "fill" : "stroke";
|
||
const body = FILL_ICONS[name] || STROKE_ICONS[name];
|
||
if (!body) throw new Error(`未定义的图标:${name}`);
|
||
for (const tone of tones) {
|
||
const color = TONES[tone];
|
||
if (!color) throw new Error(`未定义的色调:${tone}`);
|
||
if (type === "stroke") {
|
||
// stroke 型图标 stroke-width 随字号缩放会变细,统一用 1.9
|
||
}
|
||
const uri = toDataUri(buildSvg(name, body, color, type));
|
||
lines.push(`.ai-${name}.t-${tone}{background-image:url("${uri}")}`);
|
||
count += 1;
|
||
}
|
||
}
|
||
|
||
lines.push("");
|
||
fs.writeFileSync(OUT, lines.join("\n"), "utf8");
|
||
const kb = (fs.statSync(OUT).size / 1024).toFixed(1);
|
||
console.log(`icons.scss 已生成:${count} 条规则 / ${kb} KB`);
|