/**
* 画个标 · 装饰图形系统
* 落地 GitHub logo-design-skill 专业设计方法论:专业 LOGO 的"设计感"来自
* 图形层次 —— 双线环、点状环绕、星点、角括号、弧线、绶带、几何衬底(容器)。
* 所有装饰原语为 24x24 viewBox 字符串,作为 icon 元素渲染。
* fill=true 表示实心填充型(无描边,可用于容器/色块);fill=false 表示描边型。
*/
const HGB_Decorations = (function () {
'use strict';
let seq = 0;
function uid() { return 'dc' + Date.now().toString(36) + (seq++).toString(36); }
/** 颜色亮化:amt 0~1(值越大越接近白色) */
function lighten(hex, amt) {
const c = String(hex || '#888888').replace('#', '');
if (c.length !== 6) return '#cccccc';
const f = (v) => Math.round(v + (255 - v) * Math.min(1, Math.max(0, amt))).toString(16).padStart(2, '0');
return '#' + f(parseInt(c.slice(0, 2), 16)) + f(parseInt(c.slice(2, 4), 16)) + f(parseInt(c.slice(4, 6), 16));
}
/** 颜色加深:amt 0~1 */
function shade(hex, amt) {
const c = String(hex || '#888888').replace('#', '');
if (c.length !== 6) return '#444444';
const f = (v) => Math.round(v * (1 - Math.min(1, Math.max(0, amt)))).toString(16).padStart(2, '0');
return '#' + f(parseInt(c.slice(0, 2), 16)) + f(parseInt(c.slice(2, 4), 16)) + f(parseInt(c.slice(4, 6), 16));
}
/** 判断颜色明暗(返回 true 表示偏深色) */
function isDark(hex) {
const c = String(hex || '#FFFFFF').replace('#', '');
if (c.length !== 6) return false;
const r = parseInt(c.slice(0, 2), 16), g = parseInt(c.slice(2, 4), 16), b = parseInt(c.slice(4, 6), 16);
return (0.2126 * r + 0.7152 * g + 0.0722 * b) < 150;
}
/**
* 装饰原语库
*/
const DECO_KIT = {
/* ---- 环绕/边框类(描边型) ---- */
'deco-dotring': { name: '点状环绕', fill: true, sw: 0, opacity: 0.9,
svg: '' },
'deco-rayring': { name: '射线环', fill: false, sw: 2, opacity: 0.95,
svg: (function () { let p = ''; for (let i = 0; i < 12; i++) { const a = i * 30 * Math.PI / 180, c = Math.cos(a), s = Math.sin(a); p += 'M' + (12 + 8 * c).toFixed(2) + ' ' + (12 + 8 * s).toFixed(2) + 'L' + (12 + 11.5 * c).toFixed(2) + ' ' + (12 + 11.5 * s).toFixed(2); } return p; })() },
'deco-doublering': { name: '双线环', fill: false, sw: 4, opacity: 0.85,
svg: '' },
'deco-arc-top': { name: '上弧形', fill: false, sw: 5, opacity: 0.9,
svg: '' },
'deco-arc-bottom': { name: '下弧形', fill: false, sw: 5, opacity: 0.9,
svg: '' },
'deco-corner-tl': { name: '左上角括号', fill: false, sw: 5, opacity: 0.95,
svg: '' },
'deco-corner-br': { name: '右下角括号', fill: false, sw: 5, opacity: 0.95,
svg: '' },
'deco-stars': { name: '星点散落', fill: true, sw: 0, opacity: 0.9,
svg: '' },
'deco-divider': { name: '菱形分隔线', fill: true, sw: 0, opacity: 0.95,
svg: '' },
'deco-chevron': { name: '人字饰带', fill: false, sw: 5, opacity: 0.9,
svg: '' },
'deco-tri-corner': { name: '三角角标', fill: true, sw: 0, opacity: 0.9,
svg: '' },
'deco-line': { name: '细分隔线', fill: true, sw: 0, opacity: 0.9,
svg: '' },
'deco-spark': { name: '光芒四射', fill: true, sw: 0, opacity: 0.9,
svg: '' },
'deco-laurel-l': { name: '左月桂叶', fill: false, sw: 5, opacity: 0.9,
svg: '' },
'deco-laurel-r': { name: '右月桂叶', fill: false, sw: 5, opacity: 0.9,
svg: '' },
/* ---- 实心几何衬底(容器,图标徽章化) ---- */
'deco-shape-circle': { name: '圆形衬底', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-shape-squircle':{ name: '圆角方形衬底', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-shape-hexagon': { name: '六边形衬底', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-shape-star': { name: '星形衬底', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-shape-shield': { name: '盾形衬底', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-shape-diamond': { name: '菱形衬底', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-shape-triangle': { name: '三角衬底', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-shape-arch': { name: '拱形衬底', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-shape-pill': { name: '胶囊衬底', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-shape-ribbon': { name: '绶带横幅', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-shape-heart': { name: '爱心衬底', fill: true, sw: 0, opacity: 1,
svg: '' },
/* ---- 分屏/背景面板(填充型,铺满画布) ---- */
'deco-panel-left': { name: '左半色板', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-panel-diag': { name: '对角色板', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-panel-band': { name: '横向色带', fill: true, sw: 0, opacity: 1,
svg: '' },
'deco-panel-barc': { name: '底边色块', fill: true, sw: 0, opacity: 1,
svg: '' }
};
/**
* 构造装饰元素(同 icon 元素结构,渲染走 applyIconStyle)
* @param {string} id DECO_KIT 键名
* @param {number} x 左上角 x
* @param {number} y 左上角 y
* @param {number} w 宽
* @param {number} h 高
* @param {string} color 填充/描边色
* @param {object} grad {id,type,from,to} 渐变对象(有则覆盖纯色)
*/
function decoEl(id, x, y, w, h, color, grad) {
const def = DECO_KIT[id];
if (!def) return null;
const fill = grad ? 'url(#' + grad.id + ')' : (def.fill ? color : 'none');
return {
id: uid(), type: 'icon', name: def.name, iconId: id,
svg: def.svg, x, y, w, h, rotation: 0,
fill: fill,
stroke: def.fill ? 'none' : color,
strokeWidth: def.sw || 4,
grad: grad || null,
opacity: def.opacity != null ? def.opacity : 1,
visible: true, locked: false
};
}
/**
* 按风格返回装饰组合(供布局选择参考;布局也可自选具体原语)
* @param {string} styleId 风格 id
* @param {object} palette 配色 {colors:[主,辅,点,背景]}
* @returns {{dec:Array, decoColor:string, grad:object}}
*/
function buildDecoSet(styleId, palette) {
const colors = (palette && palette.colors) || ['#2563EB', '#3B82F6', '#F59E0B', '#FFFFFF'];
const decoColor = lighten(colors[1] || colors[0], 0.42); // 浅色点缀,低对比
const grad = {
id: 'grad-main' + (++seq),
type: 'linear',
from: colors[0],
to: lighten(colors[0], 0.28)
};
const map = {
minimal: ['deco-doublering'],
gradient: ['deco-dotring'],
vintage: ['deco-arc-top', 'deco-arc-bottom'],
tech: ['deco-corner-tl', 'deco-corner-br'],
playful: ['deco-stars'],
luxury: ['deco-divider'],
flat: ['deco-shape-circle']
};
return { dec: map[styleId] || [], decoColor, grad };
}
return { decoEl, buildDecoSet, lighten, shade, isDark, DECO_KIT };
})();