Files
drawlogo/js/core/iconlib.js
T

138 lines
4.5 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.
/**
* 画个标 · 内置素材库加载器(懒加载)
* 素材库由 scripts/build-icons.js 构建拆分为:
* - js/data/builtin-icons.js 轻量索引(sources 元数据 + AI iconIndex,无全量 svg)
* - js/data/sources/{id}.js 每源一个数据文件(HGB_Source_{id} = {icons:[...]})
* 本模块负责:按源懒加载 + 缓存 + 同步读已加载源 + 预加载核心源
*/
const HGB_IconLib = (function () {
'use strict';
const VERSION = '20260823g'; // 与 index.html 中 builtin-icons.js?v= 版本联动
const cache = {}; // sid -> {icons:[...]}
const pending = {}; // sid -> Promise
function fileURL(sid) {
return 'js/data/sources/' + sid + '.js?v=' + VERSION;
}
function loadScript(url) {
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = url;
s.onload = resolve;
s.onerror = function () { reject(new Error('素材源加载失败: ' + url)); };
document.head.appendChild(s);
});
}
/** 加载单个源(缓存 + 并发去重) */
function loadSource(sid) {
if (cache[sid]) return Promise.resolve(cache[sid]);
if (pending[sid]) return pending[sid];
pending[sid] = loadScript(fileURL(sid)).then(() => {
const data = window['HGB_Source_' + sid];
if (!data || !data.icons) throw new Error('素材源不存在: ' + sid);
cache[sid] = data;
delete pending[sid];
return data;
}).catch((e) => {
delete pending[sid];
throw e;
});
return pending[sid];
}
/** 并行加载多个源(全部成功才 resolve) */
function loadSources(ids) {
return Promise.all((ids || []).map(loadSource));
}
/** 并行加载多个源(容忍单个失败,返回已成功加载的) */
function preload(ids) {
return Promise.allSettled((ids || []).map(loadSource)).then((rs) =>
rs.filter(r => r.status === 'fulfilled').map(r => r.value));
}
/** 加载全部源(搜索全部/导出等场景) */
function loadAll() {
const ids = Object.keys((typeof HGB_BuiltinIcons !== 'undefined' ? HGB_BuiltinIcons.sources : {}) || {});
return preload(ids);
}
/** 同步读取已加载源(未加载返回 null) */
function getSource(sid) {
return cache[sid] || null;
}
/** 是否已加载 */
function loaded(sid) {
return !!cache[sid];
}
/** 已加载的全部源 id */
function loadedIds() {
return Object.keys(cache);
}
/** 从已加载源按 名称/id 找图标(返回 {id,name,svg,...} 或 null) */
function findIcon(ref) {
if (!ref || typeof ref !== 'string') return null;
const name = ref.trim();
const parts = name.split(':');
if (parts.length === 2) {
const src = cache[parts[0]];
const hit = src && (src.icons || []).find(i => i.name === parts[1] || i.id === name);
if (hit) return hit;
}
const lower = name.toLowerCase();
for (const sid of Object.keys(cache)) {
const hit = (cache[sid].icons || []).find(i =>
i.id === name || i.name === name ||
String(i.name).toLowerCase() === lower ||
String(i.name).toLowerCase().includes(lower));
if (hit) return hit;
}
return null;
}
/** 搜索已加载源(词根包含匹配),返回完整图标对象 */
function searchLoaded(keyword, limit) {
const kw = String(keyword || '').toLowerCase();
const out = [];
for (const sid of Object.keys(cache)) {
for (const ic of cache[sid].icons || []) {
if (ic.name && ic.name.toLowerCase().includes(kw)) {
out.push({ id: sid + ':' + ic.name, name: ic.name, svg: ic.svg, source: sid, type: ic.type });
if (out.length >= (limit || 60)) return out;
}
}
}
return out;
}
/** 搜索索引(全量 id/name,无 svg,用于技能/白名单展示) */
function searchIndex(keyword, limit) {
const kw = String(keyword || '').toLowerCase();
const out = [];
const idx = (typeof HGB_BuiltinIcons !== 'undefined' && HGB_BuiltinIcons.iconIndex) || [];
for (const ic of idx) {
if (ic.name && ic.name.toLowerCase().includes(kw)) {
out.push({ id: ic.id, name: ic.name, source: String(ic.id).split(':')[0], category: ic.category });
if (out.length >= (limit || 20)) return out;
}
}
return out;
}
/** 按 id 反查来源 id */
function sourceOf(fullId) {
return String(fullId || '').split(':')[0];
}
return {
VERSION, loadSource, loadSources, preload, loadAll,
getSource, loaded, loadedIds, findIcon, searchLoaded, searchIndex, sourceOf
};
})();