458 lines
24 KiB
HTML
458 lines
24 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<link rel="icon" type="image/svg+xml" href="logo/favicon.svg">
|
||
<title>素材库 · 画个标</title>
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body { font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif; font-size: 14px; color: #1f2937; background: #f3f4f6; min-height: 100vh; }
|
||
a { color: #4f46e5; text-decoration: none; }
|
||
a:hover { text-decoration: underline; }
|
||
.hidden { display: none !important; }
|
||
|
||
/* ===== 顶栏 ===== */
|
||
.topbar { display: flex; align-items: center; justify-content: space-between; padding: 0 20px; height: 54px; background: linear-gradient(90deg, #1e1b4b, #312e81); color: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.15); position: sticky; top: 0; z-index: 20; }
|
||
.topbar .brand { display: flex; align-items: center; gap: 10px; }
|
||
.topbar .brand .icon { font-size: 20px; }
|
||
.topbar .brand .name { font-size: 17px; font-weight: 700; letter-spacing: 1px; }
|
||
.topbar .brand .tag { font-size: 11px; color: #c7d2fe; background: rgba(255,255,255,.12); padding: 2px 8px; border-radius: 20px; }
|
||
.topbar .nav { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
||
.topbar .nav a { color: #e0e7ff; font-size: 13px; padding: 6px 12px; border: 1px solid rgba(255,255,255,.35); border-radius: 6px; background: rgba(255,255,255,.1); transition: background .15s; }
|
||
.topbar .nav a:hover { background: rgba(255,255,255,.22); text-decoration: none; }
|
||
.topbar .nav a.active { background: #fff; color: #312e81; font-weight: 700; }
|
||
|
||
.wrap { max-width: 1180px; margin: 22px auto 60px; padding: 0 16px; }
|
||
|
||
/* ===== 统计 ===== */
|
||
.stats { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 16px; }
|
||
.stat-card { flex: 1; min-width: 140px; background: #fff; border: 1px solid #e5e7eb; border-radius: 10px; padding: 12px 16px; box-shadow: 0 1px 3px rgba(0,0,0,.06); }
|
||
.stat-card .num { font-size: 24px; font-weight: 800; color: #4f46e5; }
|
||
.stat-card .lab { font-size: 12px; color: #6b7280; margin-top: 2px; }
|
||
|
||
/* ===== 工具栏(Tab + 搜索) ===== */
|
||
.toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
|
||
.tabs { display: flex; gap: 6px; flex-wrap: wrap; }
|
||
.tchip { border: 1px solid #e5e7eb; background: #fff; color: #4b5563; padding: 7px 16px; border-radius: 20px; font-size: 13px; cursor: pointer; transition: all .15s; }
|
||
.tchip:hover { border-color: #c7d2fe; color: #4338ca; }
|
||
.tchip.active { background: #4f46e5; border-color: #4f46e5; color: #fff; }
|
||
.search { flex: 1; min-width: 220px; border: 1px solid #e5e7eb; border-radius: 8px; padding: 9px 12px; font-size: 13px; color: #1f2937; outline: none; }
|
||
.search:focus { border-color: #4f46e5; }
|
||
|
||
/* ===== 素材行 ===== */
|
||
.row { background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 14px 16px; margin-bottom: 12px; box-shadow: 0 1px 3px rgba(0,0,0,.06); transition: box-shadow .15s; }
|
||
.row:hover { box-shadow: 0 4px 14px rgba(79,70,229,.1); }
|
||
.row-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
||
.row-name { font-size: 15px; font-weight: 700; }
|
||
.row-en { font-size: 11px; color: #9ca3af; }
|
||
.row-cat { font-size: 11px; padding: 2px 10px; border-radius: 20px; white-space: nowrap; }
|
||
.row-cat.icon { background: #dbeafe; color: #1d4ed8; }
|
||
.row-cat.emoji { background: #fef3c7; color: #b45309; }
|
||
.row-cat.brand { background: #e0e7ff; color: #4338ca; }
|
||
.row-cat.avatar{ background: #fce7f3; color: #be185d; }
|
||
.row-cat.palette { background: #dbeafe; color: #1d4ed8; }
|
||
.row-cat.font { background: #dcfce7; color: #15803d; }
|
||
.row-cat.bg { background: #e0e7ff; color: #4338ca; }
|
||
.row-cat.decoration { background: #fef3c7; color: #b45309; }
|
||
.row-count { font-size: 11px; color: #6b7280; margin-left: auto; }
|
||
.row-desc { font-size: 12px; color: #4b5563; line-height: 1.7; margin-top: 5px; }
|
||
.row-meta { font-size: 11px; color: #9ca3af; margin-top: 3px; }
|
||
|
||
/* ===== 缩略预览 ===== */
|
||
.thumbs { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; align-items: center; }
|
||
.thumb { width: 44px; height: 44px; border: 1px solid #e5e7eb; border-radius: 8px; background: #f9fafb; display: flex; align-items: center; justify-content: center; overflow: hidden; flex-shrink: 0; }
|
||
.thumb svg { width: 26px; height: 26px; }
|
||
.thumb.avatar { width: 52px; height: 52px; border-radius: 50%; padding: 4px; }
|
||
.thumb.avatar svg { width: 100%; height: 100%; }
|
||
.thumb.svg-item { width: 56px; height: 56px; }
|
||
.thumb.svg-item svg { width: 100%; height: 100%; }
|
||
.swatch { width: 24px; height: 42px; border: 1px solid rgba(0,0,0,.08); border-radius: 5px; flex-shrink: 0; }
|
||
.font-sample { font-size: 15px; padding: 4px 10px; border: 1px solid #e5e7eb; border-radius: 8px; background: #f9fafb; color: #1f2937; min-width: 130px; }
|
||
.thumb.placeholder { color: #cbd5e1; font-size: 11px; }
|
||
|
||
/* ===== 更多 ===== */
|
||
.more-wrap { margin-top: 10px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
||
.more-btn { border: 1px solid #c7d2fe; background: #eef2ff; color: #4338ca; padding: 5px 14px; border-radius: 16px; font-size: 12px; cursor: pointer; transition: all .15s; }
|
||
.more-btn:hover { background: #e0e7ff; }
|
||
.more-btn:disabled { opacity: .5; cursor: wait; }
|
||
.more-info { font-size: 11px; color: #9ca3af; }
|
||
|
||
.empty { text-align: center; color: #6b7280; padding: 60px 0; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- ===== 顶部工具栏 ===== -->
|
||
<header class="topbar">
|
||
<div class="brand">
|
||
<img class="icon" src="logo/logo-mark.svg" alt="画个标" width="20" height="20">
|
||
<span class="name">画个标</span>
|
||
<span class="tag">素材库</span>
|
||
</div>
|
||
<nav class="nav">
|
||
<a href="index.html" title="LOGO 设计工作台">🎨 LOGO</a>
|
||
<a href="avatar.html" title="头像设计工作台">🖼 头像</a>
|
||
<a href="poster.html" title="海报设计工作台">📰 海报</a>
|
||
<a href="skills.html" title="共享技能中心">🧩 技能中心</a>
|
||
<a href="poster-skills.html" title="海报技能中心">🧩 海报技能</a>
|
||
<a class="active" href="materials.html" title="素材库:按行预览 LOGO/头像/海报素材">🗃 素材库</a>
|
||
<a href="sources.html" title="图标素材中心(来源信息)">📚 素材中心</a>
|
||
<a href="poster-sources.html" title="海报素材中心(来源信息)">📚 海报素材</a>
|
||
</nav>
|
||
</header>
|
||
|
||
<div class="wrap">
|
||
<div class="stats">
|
||
<div class="stat-card"><div class="num" id="st-total">-</div><div class="lab">当前分组素材(类)</div></div>
|
||
<div class="stat-card"><div class="num" id="st-items">-</div><div class="lab">素材总数(个)</div></div>
|
||
</div>
|
||
|
||
<div class="toolbar">
|
||
<div class="tabs" id="tabs"></div>
|
||
<input class="search" id="search" placeholder="🔍 搜索素材(名称 / 类别 / 特性描述)">
|
||
</div>
|
||
|
||
<div id="list"></div>
|
||
<div class="empty hidden" id="empty">未找到匹配素材,请调整分类或搜索词。</div>
|
||
</div>
|
||
|
||
<script src="js/core/dicebear.js?v=20260822e"></script>
|
||
<script src="js/data/builtin-icons.js?v=20260823g"></script>
|
||
<script src="js/data/poster-builtin.js?v=20260823e"></script>
|
||
<script>
|
||
(function () {
|
||
'use strict';
|
||
/* ============ 工具 ============ */
|
||
function esc(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); }
|
||
function loadScript(src) {
|
||
return new Promise(function (res, rej) {
|
||
var s = document.createElement('script');
|
||
s.src = src; s.onload = res; s.onerror = function () { rej(new Error('加载失败 ' + src)); };
|
||
document.head.appendChild(s);
|
||
});
|
||
}
|
||
/* 描边型判断 + 渲染属性(与 sources.html 一致) */
|
||
var STROKE_SOURCES = new Set(['feather', 'lucide', 'tabler', 'iconoir']);
|
||
function isStroke(ic, sid) {
|
||
return STROKE_SOURCES.has(sid) || /stroke\s*=|\bstroke-linecap\s*=|\bstroke-linejoin\s*=/.test(ic && ic.svg || '');
|
||
}
|
||
function iconAttrs(ic, sid) {
|
||
if (ic.type && ic.type !== 'icon') return 'fill="inherit" stroke="inherit"';
|
||
return isStroke(ic, sid)
|
||
? 'fill="none" stroke="#4f46e5" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"'
|
||
: 'fill="#4f46e5" stroke="none"';
|
||
}
|
||
function bodyOf(ic) {
|
||
return ic.svg.replace(/fill="currentColor"/g, 'fill="inherit"').replace(/stroke="currentColor"/g, 'stroke="inherit"');
|
||
}
|
||
/* 从 palette data 递归提取全部 hex */
|
||
function collectHex(obj, out) {
|
||
if (!obj) return;
|
||
if (typeof obj === 'string') { if (/^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/.test(obj)) out.push(obj); return; }
|
||
if (Array.isArray(obj)) { for (var i = 0; i < obj.length; i++) collectHex(obj[i], out); return; }
|
||
for (var k in obj) collectHex(obj[k], out);
|
||
}
|
||
|
||
/* ============ 状态 ============ */
|
||
var TAB = 'logo'; // logo / avatar / poster
|
||
var kw = '';
|
||
var ICON_SRC = []; // LOGO 源(manifest)
|
||
var POSTER_SRC = []; // 海报源(manifest)
|
||
var BUILTIN = (typeof HGB_BuiltinIcons !== 'undefined') ? HGB_BuiltinIcons : null;
|
||
var PBUILTIN = window.HGB_PosterBuiltin || { sources: {} };
|
||
var DICE = (typeof HGB_Dicebear !== 'undefined') ? HGB_Dicebear : null;
|
||
var loadedCache = {}; // 已加载的数据文件
|
||
var fontSeq = 0;
|
||
|
||
var TYPE_CN = { icon: '单色图标', emoji: '多色表情', brand: '品牌标识' };
|
||
var POSTER_TYPE_CN = { palette: '配色', font: '字体', bg: '背景', decoration: '装饰' };
|
||
var CAT_CLS = { icon: 'icon', emoji: 'emoji', brand: 'brand', palette: 'palette', font: 'font', bg: 'bg', decoration: 'decoration', avatar: 'avatar' };
|
||
|
||
/* ============ Manifest 加载 ============ */
|
||
function loadIconManifest() {
|
||
return fetch('js/data/manifest/icons.json?v=20260823g')
|
||
.then(function (r) { return r.json(); })
|
||
.then(function (m) { ICON_SRC = m.sources || []; })
|
||
.catch(function () { ICON_SRC = window.HGB_IconSources || []; });
|
||
}
|
||
function loadPosterManifest() {
|
||
return fetch('js/data/manifest/posters.json?v=20260823e')
|
||
.then(function (r) { return r.json(); })
|
||
.then(function (m) { POSTER_SRC = m.sources || []; })
|
||
.catch(function () { POSTER_SRC = window.HGB_PosterSources || []; });
|
||
}
|
||
|
||
/* ============ 数据文件懒加载 ============ */
|
||
function loadIconData(id) {
|
||
if (loadedCache['i_' + id]) return loadedCache['i_' + id];
|
||
loadedCache['i_' + id] = loadScript('js/data/sources/' + id + '.js?v=20260823g')
|
||
.then(function () { return window['HGB_Source_' + id] || null; });
|
||
return loadedCache['i_' + id];
|
||
}
|
||
function loadPosterData(id) {
|
||
if (loadedCache['p_' + id]) return loadedCache['p_' + id];
|
||
loadedCache['p_' + id] = loadScript('js/data/posters/' + id + '.js?v=20260823e')
|
||
.then(function () { return window['HGB_PosterSource_' + id] || null; });
|
||
return loadedCache['p_' + id];
|
||
}
|
||
|
||
/* ============ 渲染 ============ */
|
||
var tabs = [
|
||
{ id: 'logo', name: '🖼️ LOGO 素材' },
|
||
{ id: 'avatar', name: '🧑🎨 头像素材' },
|
||
{ id: 'poster', name: '🎨 海报素材' }
|
||
];
|
||
|
||
function renderTabs() {
|
||
var counts = { logo: ICON_SRC.length, avatar: (DICE ? DICE.STYLES.length : 0), poster: POSTER_SRC.length };
|
||
document.getElementById('tabs').innerHTML = tabs.map(function (t) {
|
||
return '<button class="tchip ' + (TAB === t.id ? 'active' : '') + '" data-t="' + t.id + '">' + t.name + ' <span style="opacity:.7">' + counts[t.id] + '</span></button>';
|
||
}).join('');
|
||
document.querySelectorAll('#tabs [data-t]').forEach(function (b) {
|
||
b.addEventListener('click', function () { TAB = b.dataset.t; renderTabs(); render(); });
|
||
});
|
||
}
|
||
|
||
function renderStats() {
|
||
var total = 0, items = 0;
|
||
if (TAB === 'logo') {
|
||
total = ICON_SRC.length;
|
||
items = ICON_SRC.reduce(function (s, x) { return s + ((typeof x.count === 'number') ? x.count : ((BUILTIN && BUILTIN.sources[x.id]) ? BUILTIN.sources[x.id].count : 0)); }, 0);
|
||
} else if (TAB === 'avatar') {
|
||
total = DICE ? DICE.STYLES.length : 0;
|
||
items = total; // 每风格可确定性生成无限头像,按风格数计
|
||
} else {
|
||
total = POSTER_SRC.length;
|
||
items = POSTER_SRC.reduce(function (s, x) { return s + ((typeof x.count === 'number') ? x.count : ((PBUILTIN.sources[x.id] || {}).count || 0)); }, 0);
|
||
}
|
||
document.getElementById('st-total').textContent = total;
|
||
document.getElementById('st-items').textContent = (TAB === 'avatar') ? (total + ' 风格 · 无限生成') : items.toLocaleString();
|
||
}
|
||
|
||
function rowHead(name, en, cat, catCn, count, countText) {
|
||
return '<div class="row-top">' +
|
||
'<span class="row-name">' + esc(name) + '</span>' +
|
||
(en ? '<span class="row-en">' + esc(en) + '</span>' : '') +
|
||
'<span class="row-cat ' + (CAT_CLS[cat] || '') + '">' + esc(catCn || cat) + '</span>' +
|
||
'<span class="row-count">' + (countText || ('共 ' + count + ' 个')) + '</span>' +
|
||
'</div>';
|
||
}
|
||
|
||
/* ---- LOGO 行 ---- */
|
||
function iconRow(s) {
|
||
var srcData = BUILTIN && BUILTIN.sources[s.id];
|
||
var count = (typeof s.count === 'number') ? s.count : (srcData ? srcData.count : '—');
|
||
var samples = (srcData && srcData.samples ? srcData.samples.map(function (b) { return { svg: b, type: srcData.type }; }) : []);
|
||
var type = s.type || 'icon';
|
||
var html = '<div class="row" id="row-i-' + esc(s.id) + '">' +
|
||
rowHead(s.nameCn || s.name, s.name, type, TYPE_CN[type] || s.style, count) +
|
||
'<div class="row-desc">' + esc(s.desc || '') + '</div>' +
|
||
'<div class="row-meta">' + esc(s.style || '') + ' · ' + esc(s.coordText || '') + ' · ' + esc(s.variants || '') + '</div>' +
|
||
'<div class="thumbs">' + samples.map(function (ic) {
|
||
return '<div class="thumb" title="' + esc((s.nameCn || s.name)) + '"><svg viewBox="0 0 24 24" ' + iconAttrs(ic, s.id) + '>' + bodyOf(ic) + '</svg></div>';
|
||
}).join('') + '</div>' +
|
||
'<div class="more-wrap"><button class="more-btn" data-act="more" data-id="' + esc(s.id) + '">查看更多(共 ' + count + ' 个)</button><span class="more-info" id="mi-i-' + esc(s.id) + '"></span></div>' +
|
||
'</div>';
|
||
return html;
|
||
}
|
||
function iconMore(btn, id, count) {
|
||
btn.disabled = true;
|
||
loadIconData(id).then(function (def) {
|
||
btn.disabled = false;
|
||
if (!def || !def.icons || !def.icons.length) { btn.textContent = '暂无更多数据'; return; }
|
||
var icons = def.icons;
|
||
var shown = parseInt(btn.dataset.shown || '0', 10) || 0;
|
||
var LIMIT = 100;
|
||
var slice = icons.slice(shown, shown + LIMIT);
|
||
var box = btn.closest('.row').querySelector('.thumbs');
|
||
box.insertAdjacentHTML('beforeend', slice.map(function (ic) {
|
||
return '<div class="thumb" title="' + esc(ic.name || ic.id || '') + '"><svg viewBox="0 0 24 24" ' + iconAttrs(ic, id) + '>' + bodyOf(ic) + '</svg></div>';
|
||
}).join(''));
|
||
shown += slice.length;
|
||
var info = document.getElementById('mi-i-' + id);
|
||
if (info) info.textContent = '已显示 ' + shown + ' / 共 ' + icons.length + ' 个';
|
||
if (shown < icons.length) { btn.dataset.shown = shown; btn.textContent = '再加载 ' + Math.min(LIMIT, icons.length - shown) + ' 个'; }
|
||
else { btn.textContent = '已全部加载'; btn.disabled = true; }
|
||
}).catch(function () { btn.disabled = false; btn.textContent = '加载失败,点击重试'; });
|
||
}
|
||
|
||
/* ---- 头像行 ---- */
|
||
var AV_SEEDS = ['画个标', 'brand', 'sun', 'star', 'cat', 'wow', 'happy', 'cool', 'blue', 'red', 'green', 'code'];
|
||
function avatarRow(styleId) {
|
||
var cn = DICE.CN[styleId] || styleId;
|
||
var html = '<div class="row" id="row-a-' + esc(styleId) + '">' +
|
||
rowHead(cn, styleId, 'avatar', '头像风格', 0, '无限生成') +
|
||
'<div class="row-desc">DiceBear 确定性头像生成器:同一 seed 永远生成同一头像,风格可无限产出(' + esc(styleId) + ')。</div>' +
|
||
'<div class="row-meta">seed 驱动 · 浏览器本地渲染 · 中性与彩色变体</div>' +
|
||
'<div class="thumbs" id="th-a-' + esc(styleId) + '"><div class="thumb placeholder">加载中…</div></div>' +
|
||
'<div class="more-wrap"><button class="more-btn" data-act="amore" data-id="' + esc(styleId) + '">查看更多头像</button></div>' +
|
||
'</div>';
|
||
return html;
|
||
}
|
||
function avatarPreview(styleId, n) {
|
||
var box = document.getElementById('th-a-' + styleId);
|
||
if (!box) return;
|
||
if (!DICE) { box.innerHTML = '<div class="thumb placeholder">引擎缺失</div>'; return; }
|
||
var inner = AV_SEEDS.slice(0, n).map(function (seed) {
|
||
var r = DICE.renderAvatar(styleId, seed);
|
||
if (!r.svg) return '<div class="thumb placeholder">—</div>';
|
||
return '<div class="thumb avatar" title="seed: ' + esc(seed) + '">' + r.svg + '</div>';
|
||
}).join('');
|
||
box.innerHTML = inner;
|
||
}
|
||
|
||
/* ---- 海报行 ---- */
|
||
function posterRow(s) {
|
||
var meta = PBUILTIN.sources[s.id] || {};
|
||
var count = (typeof s.count === 'number') ? s.count : (meta.count || 0);
|
||
var html = '<div class="row" id="row-p-' + esc(s.id) + '">' +
|
||
rowHead(s.nameCn || s.name, s.name, s.type, POSTER_TYPE_CN[s.type] || s.type, count) +
|
||
'<div class="row-desc">' + esc(s.desc || '') + '</div>' +
|
||
'<div class="row-meta">' + esc(s.coordText || '') + ' · ' + esc(s.variants || '') + '</div>' +
|
||
'<div class="thumbs" id="th-p-' + esc(s.id) + '"><div class="thumb placeholder">加载中…</div></div>' +
|
||
'<div class="more-wrap"><button class="more-btn" data-act="pmore" data-id="' + esc(s.id) + '">查看更多(共 ' + count + ' 个)</button><span class="more-info" id="mi-p-' + esc(s.id) + '"></span></div>' +
|
||
'</div>';
|
||
return html;
|
||
}
|
||
function posterPreview(s, limit) {
|
||
var box = document.getElementById('th-p-' + s.id);
|
||
if (!box) return;
|
||
loadPosterData(s.id).then(function (def) {
|
||
if (!def || !def.items) { box.innerHTML = '<div class="thumb placeholder">无数据</div>'; return; }
|
||
var items = def.items;
|
||
if (s.type === 'palette') {
|
||
var hexes = [];
|
||
items.forEach(function (it) { collectHex(it.data, hexes); });
|
||
var shown = hexes.slice(0, limit);
|
||
box.innerHTML = shown.map(function (h) { return '<div class="swatch" title="' + esc(h) + '" style="background:' + h + '"></div>'; }).join('');
|
||
var info = document.getElementById('mi-p-' + s.id);
|
||
if (info) info.textContent = '共 ' + hexes.length + ' 色';
|
||
} else if (s.type === 'font') {
|
||
box.innerHTML = '';
|
||
items.slice(0, limit).forEach(function (it) {
|
||
renderFontSample(box, s.id, it);
|
||
});
|
||
} else { // bg / decoration
|
||
var slice = items.slice(0, limit);
|
||
box.innerHTML = slice.map(function (it) {
|
||
var vb = it.viewBox ? ' viewBox="' + it.viewBox + '"' : ' viewBox="0 0 ' + (it.width || 100) + ' ' + (it.height || 100) + '"';
|
||
return '<div class="thumb svg-item" title="' + esc(it.name || it.id || '') + '"><svg' + vb + ' xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet">' + (it.svg || '') + '</svg></div>';
|
||
}).join('');
|
||
}
|
||
}).catch(function () { box.innerHTML = '<div class="thumb placeholder">加载失败</div>'; });
|
||
}
|
||
function renderFontSample(box, sid, it) {
|
||
var name = String(it.name || '字重').replace(/[_-]/g, ' ');
|
||
var holder = document.createElement('div');
|
||
holder.className = 'font-sample';
|
||
holder.textContent = name + ' — 画个标 123 ABC';
|
||
box.appendChild(holder);
|
||
var fam = 'mlfont' + (fontSeq++);
|
||
try {
|
||
var ff = new FontFace(fam, 'url(js/data/' + it.file + ')');
|
||
ff.load().then(function (f) {
|
||
document.fonts.add(f);
|
||
holder.style.fontFamily = '"' + fam + '", sans-serif';
|
||
}).catch(function () { holder.style.color = '#9ca3af'; });
|
||
} catch (e) { holder.style.color = '#9ca3af'; }
|
||
}
|
||
function posterMore(btn, s) {
|
||
btn.disabled = true;
|
||
loadPosterData(s.id).then(function (def) {
|
||
btn.disabled = false;
|
||
if (!def || !def.items) { btn.textContent = '暂无更多数据'; return; }
|
||
var items = def.items;
|
||
var n;
|
||
if (s.type === 'palette') { n = 9999; } // 色块轻量,全显
|
||
else if (s.type === 'font') { n = items.length; } // 字重少,全显
|
||
else { n = 60; } // SVG 上限 60,避免卡顿
|
||
posterPreview(s, n);
|
||
var info = document.getElementById('mi-p-' + s.id);
|
||
if (info) info.textContent = (s.type === 'palette') ? '全部色板已加载' : ('已显示 ' + Math.min(n, items.length) + ' / 共 ' + items.length + ' 个');
|
||
btn.textContent = '已全部加载';
|
||
btn.disabled = true;
|
||
}).catch(function () { btn.disabled = false; btn.textContent = '加载失败,点击重试'; });
|
||
}
|
||
|
||
/* ============ 主渲染 ============ */
|
||
function currentList() {
|
||
var kwl = kw.trim().toLowerCase();
|
||
if (TAB === 'logo') {
|
||
return ICON_SRC.filter(function (s) {
|
||
if (!kwl) return true;
|
||
return (s.nameCn || '').toLowerCase().includes(kwl) || (s.name || '').toLowerCase().includes(kwl) ||
|
||
(s.desc || '').toLowerCase().includes(kwl) || (s.id || '').toLowerCase().includes(kwl);
|
||
});
|
||
}
|
||
if (TAB === 'avatar') {
|
||
if (!DICE) return [];
|
||
return DICE.STYLES.filter(function (id) {
|
||
if (!kwl) return true;
|
||
var cn = DICE.CN[id] || '';
|
||
return cn.toLowerCase().includes(kwl) || id.toLowerCase().includes(kwl);
|
||
});
|
||
}
|
||
return POSTER_SRC.filter(function (s) {
|
||
if (!kwl) return true;
|
||
return (s.nameCn || '').toLowerCase().includes(kwl) || (s.name || '').toLowerCase().includes(kwl) ||
|
||
(s.desc || '').toLowerCase().includes(kwl) || (s.id || '').toLowerCase().includes(kwl) ||
|
||
(POSTER_TYPE_CN[s.type] || '').includes(kwl);
|
||
});
|
||
}
|
||
|
||
function render() {
|
||
var listEl = document.getElementById('list');
|
||
var empty = document.getElementById('empty');
|
||
renderStats();
|
||
var list = currentList();
|
||
if (!list.length) { listEl.innerHTML = ''; empty.classList.remove('hidden'); empty.textContent = '未找到匹配素材,请调整分类或搜索词。'; return; }
|
||
empty.classList.add('hidden');
|
||
|
||
if (TAB === 'logo') {
|
||
listEl.innerHTML = list.map(iconRow).join('');
|
||
// LOGO 行默认预览来自内置索引,无需异步
|
||
} else if (TAB === 'avatar') {
|
||
listEl.innerHTML = list.map(avatarRow).join('');
|
||
// 并发懒加载全部风格数据后批量渲染前 4 个预览
|
||
Promise.all(list.map(function (id) {
|
||
return DICE.loadStyle(id).catch(function () { return null; });
|
||
})).then(function () {
|
||
list.forEach(function (id) { avatarPreview(id, 4); });
|
||
});
|
||
} else {
|
||
listEl.innerHTML = list.map(posterRow).join('');
|
||
list.forEach(function (s) { posterPreview(s, 8); });
|
||
}
|
||
}
|
||
|
||
/* ============ 事件 ============ */
|
||
function onBtn(e) {
|
||
var btn = e.target.closest('[data-act]');
|
||
if (!btn) return;
|
||
var act = btn.dataset.act, id = btn.dataset.id;
|
||
if (act === 'more') {
|
||
var src = ICON_SRC.find(function (x) { return x.id === id; });
|
||
if (src) iconMore(btn, id, src.count);
|
||
} else if (act === 'amore') {
|
||
avatarPreview(id, 12);
|
||
btn.textContent = '已显示 12 个';
|
||
btn.disabled = true;
|
||
} else if (act === 'pmore') {
|
||
var ps = POSTER_SRC.find(function (x) { return x.id === id; });
|
||
if (ps) posterMore(btn, ps);
|
||
}
|
||
}
|
||
|
||
function init() {
|
||
document.getElementById('list').addEventListener('click', onBtn);
|
||
document.getElementById('search').addEventListener('input', function (e) { kw = e.target.value; render(); });
|
||
renderTabs();
|
||
render();
|
||
}
|
||
|
||
Promise.all([loadIconManifest(), loadPosterManifest()]).then(init).catch(init);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|