80 lines
3.7 KiB
JavaScript
80 lines
3.7 KiB
JavaScript
/**
|
|
* 画个标 · 海报生成引擎:版式模板 + 配色 + 元素构造
|
|
* 决策(paletteId/layoutId/title/sub/icon)→ 元素数组,供 renderer 渲染。
|
|
*/
|
|
const HGB_PosterGenerator = (function () {
|
|
'use strict';
|
|
|
|
const PALETTES = [
|
|
{ id: 'indigo', name: '靛蓝', bg: '#1e1b4b', primary: '#ffffff', accent: '#f59e0b' },
|
|
{ id: 'crimson', name: '深红', bg: '#7f1d1d', primary: '#ffffff', accent: '#fbbf24' },
|
|
{ id: 'ocean', name: '海洋', bg: '#0c4a6e', primary: '#ffffff', accent: '#38bdf8' },
|
|
{ id: 'forest', name: '森林', bg: '#14532d', primary: '#ffffff', accent: '#86efac' },
|
|
{ id: 'minimal', name: '极简', bg: '#ffffff', primary: '#111827', accent: '#4f46e5' }
|
|
];
|
|
|
|
function uid(p) { return p + '_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6); }
|
|
|
|
function rect(id, x, y, w, h, fill) { return { id, type: 'rect', x, y, w, h, fill }; }
|
|
function circle(id, cx, cy, r, fill) { return { id, type: 'circle', cx, cy, r, fill }; }
|
|
function text(id, text, x, y, fontSize, fontWeight, fill, align) {
|
|
return { id, type: 'text', text, x, y, fontSize, fontWeight, fill, align: align || 'start' };
|
|
}
|
|
|
|
const LAYOUTS = [
|
|
{
|
|
id: 'centered', name: '居中对称', emoji: '🎯',
|
|
build(spec, o) {
|
|
const cw = spec.w, ch = spec.h, els = [];
|
|
els.push(rect(uid('bg'), 0, 0, cw, ch, o.palette.bg));
|
|
els.push(circle(uid('dot'), cw / 2, ch * 0.30, ch * 0.12, o.palette.accent));
|
|
const fs = Math.round(cw * 0.075);
|
|
els.push(text(uid('title'), o.title, cw / 2, ch * 0.50, fs, 800, o.palette.primary, 'middle'));
|
|
if (o.sub) els.push(text(uid('sub'), o.sub, cw / 2, ch * 0.56, Math.round(fs * 0.38), 400, o.palette.primary, 'middle'));
|
|
els.push(rect(uid('bar'), cw / 2 - cw * 0.12, ch * 0.62, cw * 0.24, 8, o.palette.accent));
|
|
return els;
|
|
}
|
|
},
|
|
{
|
|
id: 'left', name: '左对齐', emoji: '◀️',
|
|
build(spec, o) {
|
|
const cw = spec.w, ch = spec.h, els = [];
|
|
els.push(rect(uid('bg'), 0, 0, cw, ch, o.palette.bg));
|
|
els.push(rect(uid('side'), 0, 0, cw * 0.06, ch, o.palette.accent));
|
|
const mx = cw * 0.13;
|
|
const fs = Math.round(cw * 0.08);
|
|
els.push(text(uid('title'), o.title, mx, ch * 0.44, fs, 800, o.palette.primary, 'start'));
|
|
if (o.sub) els.push(text(uid('sub'), o.sub, mx, ch * 0.52, Math.round(fs * 0.36), 400, o.palette.primary, 'start'));
|
|
els.push(rect(uid('bar'), mx, ch * 0.58, cw * 0.18, 8, o.palette.accent));
|
|
return els;
|
|
}
|
|
},
|
|
{
|
|
id: 'banner', name: '顶部横幅', emoji: '📢',
|
|
build(spec, o) {
|
|
const cw = spec.w, ch = spec.h, els = [];
|
|
els.push(rect(uid('bg'), 0, 0, cw, ch, o.palette.bg));
|
|
els.push(rect(uid('band'), 0, ch * 0.18, cw, ch * 0.16, o.palette.accent));
|
|
const fs = Math.round(cw * 0.07);
|
|
els.push(text(uid('title'), o.title, cw / 2, ch * 0.26, fs, 800, o.palette.bg, 'middle'));
|
|
if (o.sub) els.push(text(uid('sub'), o.sub, cw / 2, ch * 0.55, Math.round(fs * 0.42), 400, o.palette.primary, 'middle'));
|
|
return els;
|
|
}
|
|
}
|
|
];
|
|
|
|
/** 决策 → 元素数组 */
|
|
function generate(spec, decision) {
|
|
const palette = PALETTES.find(p => p.id === decision.paletteId) || PALETTES[0];
|
|
const layout = LAYOUTS.find(l => l.id === decision.layoutId) || LAYOUTS[0];
|
|
const o = { palette, title: decision.title || '海报标题', sub: decision.sub || '' };
|
|
return layout.build(spec, o);
|
|
}
|
|
|
|
function defaultDecision(title, sub) {
|
|
return { paletteId: 'indigo', layoutId: 'centered', title: title || '海报标题', sub: sub || '' };
|
|
}
|
|
|
|
return { PALETTES, LAYOUTS, generate, defaultDecision };
|
|
})();
|