Files

255 lines
14 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 画个标 · 技能库:导出类(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 };
}
}
]);
})();