255 lines
14 KiB
JavaScript
255 lines
14 KiB
JavaScript
/**
|
||
* 画个标 · 技能库:导出类(export)
|
||
* 注册:HGB_Skills.register([...])
|
||
*/
|
||
(function () {
|
||
'use strict';
|
||
|
||
HGB_Skills.register([
|
||
{
|
||
id: 'export-multi',
|
||
name: '多格式导出',
|
||
category: 'export',
|
||
emoji: '📦',
|
||
scene: '方案导出 PNG / JPG / SVG / PDF 与多尺寸规格',
|
||
summary: '封装导出引擎:任意方案一键导出 SVG(矢量)与 PNG/PDF(JPEG 压缩位图),支持 512/1024/2048 等尺寸。',
|
||
params: [
|
||
{ name: 'plan', type: 'object', required: true, desc: '方案对象' },
|
||
{ name: 'format', type: 'string', required: false, desc: 'png/svg/pdf(默认 png)' },
|
||
{ name: 'size', type: 'number', required: false, desc: '导出边长(默认 1024)' }
|
||
],
|
||
source: '自研导出引擎(SVG 渲染 + Canvas 位图 + 手写 PDF)',
|
||
run: function (args) {
|
||
const plan = args.plan || {};
|
||
const format = (args.format || 'png').toLowerCase();
|
||
const size = args.size || 1024;
|
||
const svg = HGB_Exporter.buildSvgFrom(plan.elements || [], plan.bg || '#FFFFFF', size);
|
||
return {
|
||
plan: plan.name || '未命名', format, size,
|
||
svgLength: svg.length,
|
||
supported: ['png', 'jpg', 'svg', 'pdf'],
|
||
canExport: HGB_Exporter.exportPng && HGB_Exporter.exportSvg ? true : false,
|
||
note: format === 'pdf' ? 'PDF 内嵌 JPEG 位图,适合打印' : (format === 'svg' ? 'SVG 为矢量,可无限缩放' : 'PNG 为位图,建议 1024+')
|
||
};
|
||
},
|
||
test: function () {
|
||
const plan = HGB_Generator.generateVariant({ industry: HGB_Industries[0], brand: '导出测试' });
|
||
const r = this.run({ plan, format: 'svg', size: 1024 });
|
||
return { format: r.format, svgLength: r.svgLength, canExport: r.canExport };
|
||
}
|
||
},
|
||
{
|
||
id: 'export-avatar',
|
||
name: '头像平台规格',
|
||
category: 'export',
|
||
emoji: '🪪',
|
||
scene: '头像导出的主流平台尺寸规格',
|
||
summary: '返回微信/QQ/抖音/YouTube/App Store/Favicon 等平台头像规格清单,指导按平台要求批量导出头像。',
|
||
params: [
|
||
{ name: 'platform', type: 'string', required: false, desc: '指定平台(wechat/qq/douyin/youtube/app/favicon),留空返回全部' }
|
||
],
|
||
source: '自研(平台头像规格表)',
|
||
run: function (args) {
|
||
const specs = [
|
||
{ platform: '微信', size: 300, note: '微信个人/公众号头像推荐' },
|
||
{ platform: 'QQ', size: 640, note: 'QQ 头像常用尺寸' },
|
||
{ platform: '抖音', size: 400, note: '抖音头像展示尺寸' },
|
||
{ platform: 'YouTube', size: 800, note: 'YouTube 频道头像' },
|
||
{ platform: 'App Store', size: 1024, note: 'App 图标规范' },
|
||
{ platform: 'Favicon', size: 64, note: '浏览器标签页图标' }
|
||
];
|
||
const p = String(args.platform || '').toLowerCase();
|
||
const map = { wechat: 0, qq: 1, douyin: 2, youtube: 3, app: 4, favicon: 5 };
|
||
const list = p && map[p] !== undefined ? [specs[map[p]]] : specs;
|
||
return { list, total: specs.length, exportNote: '头像导出走 exporter.exportAvatarPngs(圆形裁切)' };
|
||
},
|
||
test: function () {
|
||
return { wechat: this.run({ platform: 'wechat' }).list[0], all: this.run({}).list.length };
|
||
}
|
||
},
|
||
{
|
||
id: 'export-sizing',
|
||
name: '导出尺寸规范',
|
||
category: 'export',
|
||
emoji: '📐',
|
||
scene: 'LOGO 各用途推荐导出尺寸',
|
||
summary: '按用途(网页/印刷/社交/App 图标)推荐 LOGO 导出尺寸与格式,网页用 PNG/SVG、印刷用矢量或高分辨率位图。',
|
||
params: [
|
||
{ name: 'use', type: 'string', required: false, desc: '用途(web/print/social/app)' }
|
||
],
|
||
source: '自研(LOGO 交付尺寸规范)',
|
||
run: function (args) {
|
||
const use = String(args.use || 'web').toLowerCase();
|
||
const specMap = {
|
||
web: { use: '网页', size: '512×512 PNG', format: 'PNG/SVG', dpi: '96dpi', note: 'Web 加载快,建议 SVG 为主 PNG 兜底' },
|
||
print: { use: '印刷', size: '1024×1024 起', format: 'PDF(矢量)/ 高分辨率 PNG', dpi: '300dpi', note: '印刷务必矢量或 300dpi 位图,避免模糊' },
|
||
social: { use: '社交媒体', size: '400~800×400~800', format: 'PNG', dpi: '144dpi', note: '头像/封面按平台规格导出' },
|
||
app: { use: 'App 图标', size: '1024×1024', format: 'PNG(含透明)', dpi: '96dpi', note: 'App Store 需 1024px 无圆角原图' }
|
||
};
|
||
return specMap[use] || specMap.web;
|
||
},
|
||
test: function () {
|
||
return { web: this.run({ use: 'web' }).size, print: this.run({ use: 'print' }).dpi };
|
||
}
|
||
},
|
||
{
|
||
id: 'favicon-kit',
|
||
name: 'Favicon 全套尺寸',
|
||
category: 'export',
|
||
emoji: '🌐',
|
||
scene: '浏览器/移动端 Favicon 全尺寸导出清单',
|
||
summary: '列出 Favicon 标准尺寸集(16/32/48/64/180/192/512),并给出各文件用途与浏览器兼容说明。',
|
||
params: [
|
||
{ name: 'plan', type: 'object', required: false, desc: '方案对象(可选)' }
|
||
],
|
||
source: '自研(Favicon 规范:HTML5 link 尺寸)',
|
||
run: function (args) {
|
||
const specs = [
|
||
{ file: 'favicon-16x16.png', size: 16, use: '浏览器标签页' },
|
||
{ file: 'favicon-32x32.png', size: 32, use: '任务栏/快捷键' },
|
||
{ file: 'favicon-48x48.png', size: 48, use: 'Windows 站点图标' },
|
||
{ file: 'favicon-64x64.png', size: 64, use: '传统标签页' },
|
||
{ file: 'apple-touch-icon.png', size: 180, use: 'iOS 主屏书签' },
|
||
{ file: 'android-chrome-192x192.png', size: 192, use: 'Android 添加到主屏' },
|
||
{ file: 'android-chrome-512x512.png', size: 512, use: 'PWA 图标' }
|
||
];
|
||
return { specs, tip: '小尺寸(16~32)务必先过「小尺寸可读性」检查;导出使用 exportSvg → PNG 缩放', canExport: !!(HGB_Exporter && HGB_Exporter.exportPng) };
|
||
},
|
||
test: function () {
|
||
const r = this.run({});
|
||
return { sizes: r.specs.map(s => s.size).join(','), canExport: r.canExport };
|
||
}
|
||
},
|
||
{
|
||
id: 'transparent-check',
|
||
name: '透明背景检查',
|
||
category: 'export',
|
||
emoji: '👁️',
|
||
scene: '导出格式的透明背景支持检查',
|
||
summary: '检查方案背景是否为透明:PNG/SVG 支持透明,JPG 不支持(自动垫白);透明底适合头像/贴纸,深色展示图则建议保留底色。',
|
||
params: [
|
||
{ name: 'plan', type: 'object', required: false, desc: '方案对象 {bg}' }
|
||
],
|
||
source: '自研(导出格式透明规则)',
|
||
run: function (args) {
|
||
const plan = args.plan || {};
|
||
const bg = plan.bg;
|
||
const transparent = !bg || String(bg).toLowerCase() === 'transparent' || plan.transparent === true;
|
||
return {
|
||
background: transparent ? '透明' : (bg || '#FFFFFF'),
|
||
transparent,
|
||
png: '支持透明(PNG-24 含 Alpha)', svg: '支持透明(矢量透明)', jpg: '不支持透明(自动填充白色)',
|
||
verdict: transparent ? '当前为透明背景:适合头像/贴纸/叠加场景,导出 PNG 或 SVG' : '当前为纯色背景:如需透明底,请切换背景为「透明」后导出 PNG',
|
||
suggest: transparent ? '导出推荐 PNG(保留 Alpha)或 SVG' : '背景为 ' + (bg || '#FFFFFF') + ',导出 JPG 无透明需求时可用'
|
||
};
|
||
},
|
||
test: function () {
|
||
return { transparent: this.run({ plan: { bg: 'transparent' } }).verdict, solid: this.run({ plan: { bg: '#FFFFFF' } }).transparent };
|
||
}
|
||
},
|
||
{
|
||
id: 'print-safety',
|
||
name: '印刷安全区检查',
|
||
category: 'export',
|
||
emoji: '🖨️',
|
||
scene: 'LOGO 印刷/裁切的安全边距检查',
|
||
summary: '检查元素是否过于贴近画布边缘:印刷裁切、印章、贴纸需要预留安全边距(出血),贴边元素易被裁掉。',
|
||
params: [
|
||
{ name: 'plan', type: 'object', required: true, desc: '方案对象 {elements}' }
|
||
],
|
||
source: '自研(印刷出血/安全边距规范)',
|
||
run: function (args) {
|
||
const els = (args.plan || {}).elements || [];
|
||
const CW = 600, CH = 600, MARGIN = 30;
|
||
const vis = els.filter(el => el.visible !== false);
|
||
if (!vis.length) return { pass: true, note: '无可见元素' };
|
||
const tight = [];
|
||
for (const el of vis) {
|
||
const b = (el.type === 'text')
|
||
? { x: el.x - (el.w || 60) / 2, y: el.y - (el.h || 40) / 2, w: el.w || 60, h: el.h || 40 }
|
||
: { x: el.x, y: el.y, w: el.w || 0, h: el.h || 0 };
|
||
if (b.x < MARGIN || b.y < MARGIN || (CW - (b.x + b.w)) < MARGIN || (CH - (b.y + b.h)) < MARGIN) {
|
||
tight.push(el.text || el.iconId || '元素');
|
||
}
|
||
}
|
||
const pass = tight.length === 0;
|
||
return {
|
||
pass,
|
||
margin: MARGIN,
|
||
tightElements: tight,
|
||
verdict: pass ? '所有元素均在安全区内,印刷裁切安全' : tight.length + ' 个元素贴近边缘(' + tight.join('、') + '),有被裁切风险',
|
||
suggest: pass ? '无需调整' : '建议把主体元素内缩至安全区(距边缘 ≥ ' + MARGIN + 'px),预留出血'
|
||
};
|
||
},
|
||
test: function () {
|
||
const plan = HGB_Generator.generateVariant({ industry: HGB_Industries[0], brand: '印刷测试' });
|
||
return this.run({ plan });
|
||
}
|
||
},
|
||
{
|
||
id: 'format-choose',
|
||
name: '导出格式推荐',
|
||
category: 'export',
|
||
emoji: '🧭',
|
||
scene: '按使用场景推荐 LOGO 导出格式与尺寸',
|
||
summary: '按用途(网页/印刷/应用图标/社交头像/视频)推荐最优导出格式(SVG/PNG/JPG)、尺寸、分辨率与背景策略,避免「一张 PNG 走天下」的质量损耗。',
|
||
params: [
|
||
{ name: 'usage', type: 'string', required: false, desc: '使用场景:web 网页 / print 印刷 / app 应用图标 / social 社交头像 / video 视频' }
|
||
],
|
||
source: '自研(各平台规格映射)',
|
||
run: function (args) {
|
||
const usage = String(args.usage || 'web');
|
||
const MAP = {
|
||
web: { format: 'SVG(主)+ PNG 512/1024', size: '512–1024px', dpi: '72', bg: '透明背景', note: '网页用 SVG 矢量最佳,PNG 用于兼容旧环境' },
|
||
print: { format: 'SVG / PDF(矢量)', size: 'A4 及以上', dpi: '300', bg: '含出血版 + 透明版各一份', note: '印刷必须矢量或 300dpi 位图,避免小图放大模糊' },
|
||
app: { format: 'PNG', size: '1024×1024', dpi: '72', bg: '不留透明(应用商店要求不透明底)', note: '系统自动圆角,主体需居中并预留约 8% 安全边距' },
|
||
social: { format: 'PNG', size: '400–1024px', dpi: '72', bg: '透明背景', note: '头像按圆形裁切展示,图形主体需居中避免切边' },
|
||
video: { format: 'PNG(带透明)', size: '1920×1080 或更大', dpi: '72', bg: '透明背景', note: '视频叠加层需透明 PNG,避免 JPG 黑/白底' }
|
||
};
|
||
const hit = MAP[usage] || MAP.web;
|
||
return { usage, usageCn: ({ web: '网页', print: '印刷', app: '应用图标', social: '社交头像', video: '视频' })[usage] || '网页', format: hit.format, size: hit.size, dpi: hit.dpi, bg: hit.bg, note: hit.note };
|
||
},
|
||
test: function () {
|
||
return { web: this.run({ usage: 'web' }).format, print: this.run({ usage: 'print' }).dpi, app: this.run({ usage: 'app' }).bg };
|
||
}
|
||
},
|
||
{
|
||
id: 'file-naming',
|
||
name: '交付文件命名',
|
||
category: 'export',
|
||
emoji: '🏷️',
|
||
scene: '交付/导出文件的统一命名规范,避免重名与版本混乱',
|
||
summary: '按「品牌_用途_规格_版本」生成统一文件命名规范(小写 + 连字符),输出各用途(logo/avatar/favicon)示例文件名与命名规则清单。',
|
||
params: [
|
||
{ name: 'brand', type: 'string', required: true, desc: '品牌名(自动转小写连字符)' },
|
||
{ name: 'purpose', type: 'string', required: false, desc: '用途:logo/avatar/favicon/vi(默认 logo)' },
|
||
{ name: 'version', type: 'string', required: false, desc: '版本号(默认 v1)' }
|
||
],
|
||
source: '自研(交付文件命名规范)',
|
||
run: function (args) {
|
||
const raw = String(args.brand || 'brand').toLowerCase().replace(/[^\w-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
||
const brand = raw || 'brand';
|
||
const purpose = String(args.purpose || 'logo').toLowerCase();
|
||
const ver = String(args.version || 'v1');
|
||
const p = ['logo', 'avatar', 'favicon', 'vi'].includes(purpose) ? purpose : 'logo';
|
||
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
||
const convention = brand + '-' + p + '-{规格}-' + ver;
|
||
const examples = p === 'favicon'
|
||
? [brand + '-favicon-32-' + ver + '.png', brand + '-favicon-180-' + ver + '.png', brand + '-favicon-512-' + ver + '.png']
|
||
: (p === 'avatar'
|
||
? [brand + '-avatar-300-' + ver + '.png', brand + '-avatar-640-' + ver + '.png', brand + '-avatar-1024-' + ver + '.png']
|
||
: [brand + '-logo-main-' + ver + '.svg', brand + '-logo-reverse-' + ver + '.png', brand + '-logo-mono-' + ver + '.png']);
|
||
return {
|
||
brand, purpose: p, version: ver, date, convention, examples,
|
||
rules: ['小写字母 + 连字符,禁止中文与空格', '「用途」段区分 logo / avatar / favicon / vi 交付物', '「规格」段标注尺寸(512 / 300)或版别(main / reverse / mono)', '「版本」段 v1 / v2…,迭代不覆盖旧版'],
|
||
tip: '批量交付按「品牌_用途_规格_版本」归档目录,迭代导出时递增版本号,配合版本号联动规则避免同名覆盖'
|
||
};
|
||
},
|
||
test: function () {
|
||
const r = this.run({ brand: '远渡教育', purpose: 'logo', version: 'v2' });
|
||
return { convention: r.convention, examples: r.examples.length, rules: r.rules.length };
|
||
}
|
||
}
|
||
]);
|
||
})();
|