/** * 画个标 · 核心:头像生成(Avatar) * 头像 = 复用 Mark 纯图形引擎 + 头像专属约束 + 平台规格导出 * - 仅纯图形:过滤带文字布局(monogram 首字 / ribbon 绶带区),保证小尺寸清晰 * - 圆形构图优先:头像布局以圆形/圆角/对称构图为倾向,缩小不变形 * - 圆形裁切预览与导出:社交平台头像均以圆形展示 * - 平台规格一键导出:微信 / QQ / 抖音 / YouTube / App 图标 / Favicon + 自定义 */ const HGB_AvatarGen = (function () { 'use strict'; /** 适合头像的纯图形布局(Mark 层,全部无文字) */ const AVATAR_LAYOUTS = [ 'orb', // 圆标容器(径向渐变圆) 'badge', // 双环徽章 'seal', // 双环印章 'burst', // 光芒徽章(射线环) 'duo', // 双图标对称 'emblem', // 渐变盾徽 'hexagon', // 六边形徽章 'diamond', // 菱形徽章 'arch', // 拱门徽章 'geo', // 几何构形 'techcorner', // 科技角标 'fullbleed', // 头像专属:全出血大图标(无容器) 'concentric', // 头像专属:同心圆环 'centroid' // 头像专属:中心单环 ]; /** 平台头像导出规格预设 */ const AVATAR_SIZES = [ { id: 'wechat', name: '微信头像', size: 300, note: '微信/微博 300' }, { id: 'qq', name: 'QQ 头像', size: 640, note: 'QQ 640' }, { id: 'douyin', name: '抖音头像', size: 400, note: '抖音/快手 400' }, { id: 'youtube', name: 'YouTube 头像', size: 800, note: '频道头像 800' }, { id: 'app', name: 'App 图标', size: 1024, note: 'iOS/安卓 1024' }, { id: 'favicon', name: '网站 Favicon', size: 64, note: '浏览器标签 64' } ]; /** 头像词根扩充的主力单色源(懒加载优先预载;多色 emoji/品牌源不参与词根搜索) */ const AVATAR_CORE_SOURCES = [ 'lucide', 'tabler', 'feather', 'mdi', 'fluent', 'solar', 'msymbols', 'iconpark', 'hugeicons', 'mingcute', 'carbon', 'majesticons', 'pixelarticons', 'akar-icons' ]; /** 布局 id 是否适合头像(在头像白名单内) */ function isAvatarLayout(layoutId) { return AVATAR_LAYOUTS.includes(layoutId); } /** 洗牌(保证多套头像图标各异) */ function shuffle(arr) { const a = arr.slice(); for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; } /** 内置库关键词搜索:图标名包含任一英文词根即命中(返回带库前缀 id 的图标数组;懒加载适配:先预载核心源再搜已加载源) */ async function searchBuiltin(roots, limit) { // 词根搜索依赖已加载源 → 预载头像核心源(容忍单个失败) await HGB_IconLib.preload(AVATAR_CORE_SOURCES); const out = []; const seen = new Set(); const kws = roots.filter(k => k.length >= 3); if (!kws.length) return out; for (const kw of kws) { for (const ic of HGB_IconLib.searchLoaded(kw, limit)) { const fullId = ic.id; if (seen.has(fullId)) continue; seen.add(fullId); out.push({ id: fullId, name: ic.name, svg: ic.svg, source: ic.source, type: ic.type }); if (out.length >= limit) return out; } } return out; } /** 头像素材池:用户选中 + 行业图标优先,内置库按行业图标词根扩充(解决"素材不够/重复") */ async function avatarIconPool(ind, userIcons) { const picked = (userIcons || []).filter(Boolean).slice(0, 5); const indIcons = (ind.icons || []); const poolMap = new Map(); // 本地段:用户选中 + 行业图标(保持插入顺序) for (const ic of picked) poolMap.set(ic.id, ic); for (const ic of indIcons) if (!poolMap.has(ic.id)) poolMap.set(ic.id, ic); const localCount = poolMap.size; // 词根:行业图标 id 按 '-' 拆分(英文语义词根,如 chef-hat → chef/hat) const roots = []; for (const ic of indIcons) { for (const part of String(ic.id || '').split('-')) { const k = part.toLowerCase(); if (k.length >= 3 && !roots.includes(k)) roots.push(k); } } if (roots.length) { for (const ic of await searchBuiltin(roots, 60)) { if (!poolMap.has(ic.id)) poolMap.set(ic.id, ic); } } const all = Array.from(poolMap.values()); // 分段洗牌:行业/用户图标在前(高度相关),内置库扩充在后;各自洗牌保证多套各异 return shuffle(all.slice(0, localCount)).concat(shuffle(all.slice(localCount))); } /** 批量生成头像(复用 Mark 生成引擎 + 头像布局白名单 + 内置库素材扩充) */ async function generateAvatars(opt) { const ind = opt.industry || {}; const count = Math.min(Math.max(opt.count || 5, 1), 20); // 1) 基础批量生成(风格全覆盖 + 头像布局白名单) const base = HGB_Generator.generatePlans({ ...opt, count, layoutWhitelist: AVATAR_LAYOUTS }); // 2) 头像素材池扩充(行业/用户图标在前 + 内置库词根扩充;懒加载预载核心源) const pool = await avatarIconPool(ind, opt.icons); if (pool.length < 2) return base; // 3) 逐套替换为池中不同图标(保持风格/布局/配色不变,图标素材多样) return base.map((p, i) => { const ic1 = pool[i % pool.length]; const ic2 = pool[(i + Math.ceil(pool.length / 2)) % pool.length]; if (!ic1) return p; const v = HGB_Generator.generateVariant({ ...opt, layoutWhitelist: AVATAR_LAYOUTS, layoutId: p.layoutId, styleId: p.styleId, icons: ic2 && ic2.id !== ic1.id ? [ic1, ic2] : [ic1], lockup: p.lockup }); return Object.assign({}, v, { id: p.id, name: p.name, concept: p.concept, aiIcon: ic1.id }); }); } /** 生成单个头像变体(预览弹窗微调) */ function generateVariant(opt) { return HGB_Generator.generateVariant({ ...opt, layoutWhitelist: AVATAR_LAYOUTS }); } return { AVATAR_LAYOUTS, AVATAR_SIZES, isAvatarLayout, generateAvatars, generateVariant }; })();