Files
drawlogo/js/data/decorations.js
T

160 lines
9.0 KiB
JavaScript
Raw 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.
/**
* 画个标 · 装饰图形系统
* 落地 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: '<circle cx="12" cy="2" r="1.15"/><circle cx="17.07" cy="4.93" r="1.15"/><circle cx="19.92" cy="10" r="1.15"/><circle cx="19.92" cy="14" r="1.15"/><circle cx="17.07" cy="19.07" r="1.15"/><circle cx="12" cy="22" r="1.15"/><circle cx="6.93" cy="19.07" r="1.15"/><circle cx="4.08" cy="14" r="1.15"/><circle cx="4.08" cy="10" r="1.15"/><circle cx="6.93" cy="4.93" r="1.15"/>' },
'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: '<circle cx="12" cy="12" r="10.5"/><circle cx="12" cy="12" r="7"/>' },
'deco-arc-top': { name: '上弧形', fill: false, sw: 5, opacity: 0.9,
svg: '<path d="M2 9 A 14 14 0 0 1 22 9"/>' },
'deco-arc-bottom': { name: '下弧形', fill: false, sw: 5, opacity: 0.9,
svg: '<path d="M2 15 A 14 14 0 0 0 22 15"/>' },
'deco-corner-tl': { name: '左上角括号', fill: false, sw: 5, opacity: 0.95,
svg: '<path d="M4 11 V4 H11"/>' },
'deco-corner-br': { name: '右下角括号', fill: false, sw: 5, opacity: 0.95,
svg: '<path d="M20 13 V20 H13"/>' },
'deco-stars': { name: '星点散落', fill: true, sw: 0, opacity: 0.9,
svg: '<path d="M6 4l.9 2.2L9 7l-2.1.9L6 10l-.9-2.1L3 7l2.1-.9z"/><path d="M18 14l.8 1.9 1.9.8-1.9.8-.8 1.9-.8-1.9-1.9-.8 1.9-.8z"/><path d="M16 3.5l.6 1.4 1.4.6-1.4.6-.6 1.4-.6-1.4-1.4-.6 1.4-.6z"/>' },
'deco-divider': { name: '菱形分隔线', fill: true, sw: 0, opacity: 0.95,
svg: '<rect x="2" y="11" width="7" height="2" rx="1"/><path d="M12 8.5l2.6 3.5-2.6 3.5-2.6-3.5z"/><rect x="15" y="11" width="7" height="2" rx="1"/>' },
'deco-chevron': { name: '人字饰带', fill: false, sw: 5, opacity: 0.9,
svg: '<path d="M5 6l6 6-6 6"/><path d="M13 6l6 6-6 6"/>' },
'deco-tri-corner': { name: '三角角标', fill: true, sw: 0, opacity: 0.9,
svg: '<path d="M12 3L21 21H3z"/>' },
'deco-line': { name: '细分隔线', fill: true, sw: 0, opacity: 0.9,
svg: '<rect x="0" y="11" width="24" height="2" rx="1"/>' },
'deco-spark': { name: '光芒四射', fill: true, sw: 0, opacity: 0.9,
svg: '<path d="M12 2l1.8 6.2L20 10l-6.2 1.8L12 18l-1.8-6.2L4 10l6.2-1.8z"/>' },
'deco-laurel-l': { name: '左月桂叶', fill: false, sw: 5, opacity: 0.9,
svg: '<path d="M14 21a8 8 0 0 1-8-8M14 21c-1 0-2-3-2-8s1-8 2-8M6 13a6 6 0 0 1 3-6c2-1 3 1 3 4"/>' },
'deco-laurel-r': { name: '右月桂叶', fill: false, sw: 5, opacity: 0.9,
svg: '<path d="M10 21a8 8 0 0 0 8-8M10 21c1 0 2-3 2-8s-1-8-2-8M18 13a6 6 0 0 0-3-6c-2-1-3 1-3 4"/>' },
/* ---- 实心几何衬底(容器,图标徽章化) ---- */
'deco-shape-circle': { name: '圆形衬底', fill: true, sw: 0, opacity: 1,
svg: '<circle cx="12" cy="12" r="11"/>' },
'deco-shape-squircle':{ name: '圆角方形衬底', fill: true, sw: 0, opacity: 1,
svg: '<rect x="1.6" y="1.6" width="20.8" height="20.8" rx="5"/>' },
'deco-shape-hexagon': { name: '六边形衬底', fill: true, sw: 0, opacity: 1,
svg: '<path d="M12 1.5L21.3 6.75v10.5L12 22.5 2.7 17.25V6.75z"/>' },
'deco-shape-star': { name: '星形衬底', fill: true, sw: 0, opacity: 1,
svg: '<path d="M12 1.6l3.2 6.5 7.2 1-5.2 5 1.2 7.1L12 17.9l-6.4 3.3 1.2-7.1-5.2-5 7.2-1z"/>' },
'deco-shape-shield': { name: '盾形衬底', fill: true, sw: 0, opacity: 1,
svg: '<path d="M12 1.8L20.5 5v7c0 4.6-3.5 8.2-8.5 10.2C7 20.2 3.5 16.6 3.5 12V5z"/>' },
'deco-shape-diamond': { name: '菱形衬底', fill: true, sw: 0, opacity: 1,
svg: '<path d="M12 1.5L22.5 12 12 22.5 1.5 12z"/>' },
'deco-shape-triangle': { name: '三角衬底', fill: true, sw: 0, opacity: 1,
svg: '<path d="M12 1.6 L22.4 21.5 H1.6 z"/>' },
'deco-shape-arch': { name: '拱形衬底', fill: true, sw: 0, opacity: 1,
svg: '<path d="M2 24V11a10 10 0 0 1 20 0v13z"/>' },
'deco-shape-pill': { name: '胶囊衬底', fill: true, sw: 0, opacity: 1,
svg: '<rect x="1.6" y="7" width="20.8" height="10" rx="5"/>' },
'deco-shape-ribbon': { name: '绶带横幅', fill: true, sw: 0, opacity: 1,
svg: '<path d="M2 5h20v11h-8.4l-1.6 3.4L10.4 16H2z"/>' },
'deco-shape-heart': { name: '爱心衬底', fill: true, sw: 0, opacity: 1,
svg: '<path d="M12 21s-8.5-5-10.5-10C-.5 6.5 3 3 6.5 3c2 0 3.6 1 5.5 3 1.9-2 3.5-3 5.5-3C21 3 24.5 6.5 22.5 11 20.5 16 12 21 12 21z"/>' },
/* ---- 分屏/背景面板(填充型,铺满画布) ---- */
'deco-panel-left': { name: '左半色板', fill: true, sw: 0, opacity: 1,
svg: '<rect x="0" y="0" width="12" height="24"/>' },
'deco-panel-diag': { name: '对角色板', fill: true, sw: 0, opacity: 1,
svg: '<path d="M0 0h16L6 24H0z"/>' },
'deco-panel-band': { name: '横向色带', fill: true, sw: 0, opacity: 1,
svg: '<rect x="0" y="10" width="24" height="4.4"/>' },
'deco-panel-barc': { name: '底边色块', fill: true, sw: 0, opacity: 1,
svg: '<path d="M0 16h24v8H0z"/>' }
};
/**
* 构造装饰元素(同 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 };
})();