mirror of
https://github.com/2234839/web-font.git
synced 2026-06-29 03:32:31 +08:00
feat: SDK 多模式架构 + 首页输入事件驱动
- 重构 webfont-sdk.js 为核心增量引擎 + 多触发器架构 - 支持 loadFont(轮询)、observeFont(MutationObserver)、loadText(手动传文本)三种模式 - 三种模式共享 loadedChars,按 fontName|family 自动去重增量加载 - loadFont interval 可从外部配置 - 首页改用 loadText 模式,输入即时触发字体加载 - textarea 高度根据文本行数动态变化 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
909b91a8d5
commit
2f7ce0cb72
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "webfont",
|
||||
"private": true,
|
||||
"version": "1.2.1",
|
||||
"version": "1.3.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "pnpx tsx scripts/dev-all.ts",
|
||||
|
||||
@ -1,103 +1,378 @@
|
||||
/**
|
||||
* WebFont SDK — 按需增量加载字体片段,无闪烁
|
||||
* 用法:WebFont.loadFont({ fontName, selector, baseUrl, family })
|
||||
*
|
||||
* 架构:核心增量引擎 + 多种触发方式
|
||||
* - 核心:FontLoader 按 fontKey 管理已加载字符集,只生成增量 CSS
|
||||
* - 触发器:loadFont(轮询)、observeFont(DOM 事件)、loadText(手动传文本)
|
||||
* - 同一 fontKey 下所有触发器共享字符集,绝不会重复请求
|
||||
*
|
||||
* 用法:
|
||||
* // 轮询模式
|
||||
* WebFont.loadFont({ fontName, selector, family, interval });
|
||||
*
|
||||
* // 事件驱动模式
|
||||
* var obs = WebFont.observeFont({ fontName, selector, family });
|
||||
* obs.dispose();
|
||||
*
|
||||
* // 直接传文本模式
|
||||
* var loader = WebFont.loadText({ fontName, text: "你好世界", family });
|
||||
* loader.update("追加文字");
|
||||
* loader.dispose();
|
||||
*
|
||||
* // 清理全部
|
||||
* WebFont.disposeAll();
|
||||
*/
|
||||
var WebFont = (function () {
|
||||
/** 按 selector 索引的活跃任务,重复调用同一选择器时自动清理旧任务 */
|
||||
var tasks = {};
|
||||
/* ============================================================
|
||||
* 核心增量引擎 — 按 fontKey 管理已加载字符集,生成增量 CSS
|
||||
* ============================================================ */
|
||||
|
||||
/** @type {Object.<string, { loadedChars: Object.<string,boolean>, injectedStyles: Element[], applied: boolean, fontName: string, family: string, baseUrl: string }>} */
|
||||
var loaders = {};
|
||||
|
||||
/**
|
||||
* 生成 fontKey,同一字体+family 归入同一组
|
||||
*/
|
||||
function fontKey(fontName, family) {
|
||||
return fontName + "|" + family;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取或创建对应 fontKey 的加载器
|
||||
*/
|
||||
function getLoader(fontName, baseUrl, family) {
|
||||
var key = fontKey(fontName, family);
|
||||
if (!loaders[key]) {
|
||||
loaders[key] = {
|
||||
loadedChars: {},
|
||||
injectedStyles: [],
|
||||
applied: false,
|
||||
fontName: fontName,
|
||||
family: family,
|
||||
baseUrl: baseUrl
|
||||
};
|
||||
}
|
||||
return loaders[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* 差量加载新字符,生成 unicode-range CSS 并注入
|
||||
* @param {Object} loader - getLoader 返回的加载器对象
|
||||
* @param {string[]} newChars - 待加载的新字符数组
|
||||
*/
|
||||
function loadChars(loader, newChars) {
|
||||
if (newChars.length === 0) return;
|
||||
|
||||
var fontName = loader.fontName;
|
||||
var family = loader.family;
|
||||
var baseUrl = loader.baseUrl;
|
||||
var loadedChars = loader.loadedChars;
|
||||
|
||||
var text = newChars.join("");
|
||||
var url = baseUrl + "/api?font=" + encodeURIComponent(fontName) + "&text=" + encodeURIComponent(text);
|
||||
var unicodeRanges = newChars
|
||||
.map(function (c) { return "U+" + c.codePointAt(0).toString(16).padStart(4, "0"); })
|
||||
.join(", ");
|
||||
|
||||
var style = document.createElement("style");
|
||||
style.textContent =
|
||||
'@font-face {\n' +
|
||||
' font-family: "' + family + '";\n' +
|
||||
' src: url("' + url + '") format("truetype");\n' +
|
||||
' unicode-range: ' + unicodeRanges + ';\n' +
|
||||
'}\n';
|
||||
document.head.appendChild(style);
|
||||
loader.injectedStyles.push(style);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符集中过滤出未加载的新字符,标记为已加载,并生成 CSS
|
||||
* @param {Object} loader - getLoader 返回的加载器对象
|
||||
* @param {Object.<string,boolean>} charSet - 待检查的字符集
|
||||
* @returns {boolean} 是否有新字符被加载
|
||||
*/
|
||||
function processChars(loader, charSet) {
|
||||
var loadedChars = loader.loadedChars;
|
||||
var newChars = [];
|
||||
for (var c in charSet) {
|
||||
if (!loadedChars[c]) {
|
||||
loadedChars[c] = true;
|
||||
newChars.push(c);
|
||||
}
|
||||
}
|
||||
loadChars(loader, newChars);
|
||||
return newChars.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串中过滤出未加载的新字符,标记为已加载,并生成 CSS
|
||||
* @param {Object} loader - getLoader 返回的加载器对象
|
||||
* @param {string} text - 待检查的文本
|
||||
* @returns {boolean} 是否有新字符被加载
|
||||
*/
|
||||
function processText(loader, text) {
|
||||
var loadedChars = loader.loadedChars;
|
||||
var newChars = [];
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
var c = text[i];
|
||||
if (!loadedChars[c]) {
|
||||
loadedChars[c] = true;
|
||||
newChars.push(c);
|
||||
}
|
||||
}
|
||||
loadChars(loader, newChars);
|
||||
return newChars.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁加载器及其所有注入的样式
|
||||
*/
|
||||
function destroyLoader(key) {
|
||||
var loader = loaders[key];
|
||||
if (!loader) return;
|
||||
for (var i = 0; i < loader.injectedStyles.length; i++) {
|
||||
loader.injectedStyles[i].remove();
|
||||
}
|
||||
delete loaders[key];
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 辅助函数
|
||||
* ============================================================ */
|
||||
|
||||
/**
|
||||
* 获取元素的文本内容
|
||||
*/
|
||||
function getText(el) {
|
||||
var tag = el.tagName;
|
||||
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") {
|
||||
return el.value || "";
|
||||
}
|
||||
return el.textContent || "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集选择器匹配元素中的所有字符
|
||||
*/
|
||||
function collectChars(selector) {
|
||||
var charSet = {};
|
||||
var elements = document.querySelectorAll(selector);
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var text = getText(elements[i]);
|
||||
for (var j = 0; j < text.length; j++) {
|
||||
charSet[text[j]] = true;
|
||||
}
|
||||
}
|
||||
return charSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用字体到元素
|
||||
*/
|
||||
function applyFamily(selector, family) {
|
||||
var elements = document.querySelectorAll(selector);
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
elements[i].style.fontFamily = '"' + family + '", sans-serif';
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 任务管理 — 各触发器的清理
|
||||
* ============================================================ */
|
||||
|
||||
/** 按 selector 索引的 loadFont 任务 */
|
||||
var pollTasks = {};
|
||||
|
||||
/** 按选择器索引的 observeFont 任务 */
|
||||
var observeTasks = {};
|
||||
|
||||
/* ============================================================
|
||||
* 1. loadFont — 定时器轮询模式
|
||||
* ============================================================ */
|
||||
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @param {string} options.fontName - 字体文件名(如 "思源黑体.ttf")
|
||||
* @param {string} options.selector - CSS 选择器(如 ".title")
|
||||
* @param {string} [options.baseUrl] - API 基础地址,默认当前域名
|
||||
* @param {string} [options.family] - font-family 名称,默认 fontName 去掉扩展名
|
||||
* @param {string} options.fontName
|
||||
* @param {string} options.selector
|
||||
* @param {string} [options.baseUrl]
|
||||
* @param {string} [options.family]
|
||||
* @param {number} [options.interval=1000] - 轮询间隔(ms)
|
||||
*/
|
||||
function loadFont(options) {
|
||||
var selector = options.selector;
|
||||
var fontName = options.fontName;
|
||||
var baseUrl = options.baseUrl || location.origin;
|
||||
var family = options.family || fontName.replace(/\.[^.]+$/, "");
|
||||
var interval = options.interval || 1000;
|
||||
|
||||
/** 清理同一选择器的旧任务 */
|
||||
if (tasks[selector]) {
|
||||
clearInterval(tasks[selector].timer);
|
||||
/** 移除旧任务注入的 style 标签 */
|
||||
var oldStyles = tasks[selector].styles;
|
||||
for (var s = 0; s < oldStyles.length; s++) {
|
||||
oldStyles[s].remove();
|
||||
}
|
||||
/* 清理同一选择器的旧任务 */
|
||||
if (pollTasks[selector]) {
|
||||
clearInterval(pollTasks[selector].timer);
|
||||
}
|
||||
|
||||
var loadedChars = {};
|
||||
var injectedStyles = [];
|
||||
var loader = getLoader(fontName, baseUrl, family);
|
||||
var applied = false;
|
||||
|
||||
/** 获取元素的文本内容 */
|
||||
function getText(el) {
|
||||
var tag = el.tagName;
|
||||
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") {
|
||||
return el.value || "";
|
||||
}
|
||||
return el.textContent || "";
|
||||
}
|
||||
|
||||
function collectChars() {
|
||||
var charSet = {};
|
||||
var elements = document.querySelectorAll(selector);
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var text = getText(elements[i]);
|
||||
for (var j = 0; j < text.length; j++) {
|
||||
charSet[text[j]] = true;
|
||||
}
|
||||
}
|
||||
return charSet;
|
||||
}
|
||||
|
||||
function loadNewChars() {
|
||||
var current = collectChars();
|
||||
var newChars = [];
|
||||
for (var c in current) {
|
||||
if (!loadedChars[c]) {
|
||||
newChars.push(c);
|
||||
}
|
||||
}
|
||||
if (newChars.length === 0) return;
|
||||
|
||||
for (var k = 0; k < newChars.length; k++) {
|
||||
loadedChars[newChars[k]] = true;
|
||||
}
|
||||
|
||||
var text = newChars.join("");
|
||||
var url = baseUrl + "/api?font=" + encodeURIComponent(fontName) + "&text=" + encodeURIComponent(text);
|
||||
var unicodeRanges = newChars
|
||||
.map(function (c) { return "U+" + c.codePointAt(0).toString(16).padStart(4, "0"); })
|
||||
.join(", ");
|
||||
|
||||
var style = document.createElement("style");
|
||||
style.textContent =
|
||||
'@font-face {\n' +
|
||||
' font-family: "' + family + '";\n' +
|
||||
' src: url("' + url + '") format("truetype");\n' +
|
||||
' unicode-range: ' + unicodeRanges + ';\n' +
|
||||
'}\n';
|
||||
document.head.appendChild(style);
|
||||
injectedStyles.push(style);
|
||||
|
||||
if (!applied) {
|
||||
function tick() {
|
||||
var current = collectChars(selector);
|
||||
if (processChars(loader, current) && !applied) {
|
||||
applied = true;
|
||||
var elements = document.querySelectorAll(selector);
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
elements[i].style.fontFamily = '"' + family + '", sans-serif';
|
||||
}
|
||||
applyFamily(selector, family);
|
||||
}
|
||||
}
|
||||
|
||||
loadNewChars();
|
||||
var timer = setInterval(loadNewChars, 1000);
|
||||
|
||||
tasks[selector] = { timer: timer, styles: injectedStyles };
|
||||
tick();
|
||||
var timer = setInterval(tick, interval);
|
||||
pollTasks[selector] = { timer: timer };
|
||||
}
|
||||
|
||||
return { loadFont: loadFont };
|
||||
/* ============================================================
|
||||
* 2. observeFont — MutationObserver 事件驱动模式
|
||||
* ============================================================ */
|
||||
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @param {string} options.fontName
|
||||
* @param {string} options.selector
|
||||
* @param {string} [options.baseUrl]
|
||||
* @param {string} [options.family]
|
||||
* @param {number} [options.debounceMs=50] - 防抖间隔(ms)
|
||||
* @returns {{ dispose: function }}
|
||||
*/
|
||||
function observeFont(options) {
|
||||
var selector = options.selector;
|
||||
var fontName = options.fontName;
|
||||
var baseUrl = options.baseUrl || location.origin;
|
||||
var family = options.family || fontName.replace(/\.[^.]+$/, "");
|
||||
var debounceMs = options.debounceMs || 50;
|
||||
|
||||
/* 清理同一选择器的旧任务 */
|
||||
if (observeTasks[selector]) {
|
||||
observeTasks[selector].dispose();
|
||||
}
|
||||
|
||||
var loader = getLoader(fontName, baseUrl, family);
|
||||
var applied = false;
|
||||
var debounceTimer = null;
|
||||
|
||||
function doLoad() {
|
||||
var current = collectChars(selector);
|
||||
if (processChars(loader, current) && !applied) {
|
||||
applied = true;
|
||||
applyFamily(selector, family);
|
||||
}
|
||||
}
|
||||
|
||||
function debouncedLoad() {
|
||||
if (debounceTimer) clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(doLoad, debounceMs);
|
||||
}
|
||||
|
||||
var observer = new MutationObserver(function (mutations) {
|
||||
for (var i = 0; i < mutations.length; i++) {
|
||||
if (mutations[i].type === "childList" || mutations[i].type === "characterData") {
|
||||
debouncedLoad();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var inputHandler = function () { debouncedLoad(); };
|
||||
|
||||
var elements = document.querySelectorAll(selector);
|
||||
observer.observe(document.body || document.documentElement, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true,
|
||||
});
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var el = elements[i];
|
||||
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
||||
el.addEventListener("input", inputHandler);
|
||||
}
|
||||
}
|
||||
|
||||
doLoad();
|
||||
|
||||
var disposed = false;
|
||||
|
||||
var task = {
|
||||
dispose: function () {
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
observer.disconnect();
|
||||
for (var j = 0; j < elements.length; j++) {
|
||||
var el2 = elements[j];
|
||||
if (el2.tagName === "INPUT" || el2.tagName === "TEXTAREA") {
|
||||
el2.removeEventListener("input", inputHandler);
|
||||
}
|
||||
}
|
||||
if (debounceTimer) clearTimeout(debounceTimer);
|
||||
delete observeTasks[selector];
|
||||
}
|
||||
};
|
||||
|
||||
observeTasks[selector] = task;
|
||||
return task;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 3. loadText — 直接传文本模式
|
||||
* ============================================================ */
|
||||
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @param {string} options.fontName
|
||||
* @param {string} options.text
|
||||
* @param {string} [options.baseUrl]
|
||||
* @param {string} [options.family]
|
||||
* @returns {{ update: function(string): void, dispose: function(): void }}
|
||||
*/
|
||||
function loadText(options) {
|
||||
var fontName = options.fontName;
|
||||
var baseUrl = options.baseUrl || location.origin;
|
||||
var family = options.family || fontName.replace(/\.[^.]+$/, "");
|
||||
|
||||
var loader = getLoader(fontName, baseUrl, family);
|
||||
|
||||
processText(loader, options.text);
|
||||
|
||||
return {
|
||||
update: function (text) {
|
||||
processText(loader, text);
|
||||
},
|
||||
dispose: function () {
|
||||
/* loadText 不独占样式,样式由 loader 统一管理 */
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 公共 API
|
||||
* ============================================================ */
|
||||
|
||||
/**
|
||||
* 清理所有任务和加载器(页面卸载时调用)
|
||||
*/
|
||||
function disposeAll() {
|
||||
for (var sel in pollTasks) {
|
||||
clearInterval(pollTasks[sel].timer);
|
||||
}
|
||||
for (var oid in observeTasks) {
|
||||
observeTasks[oid].dispose();
|
||||
}
|
||||
pollTasks = {};
|
||||
observeTasks = {};
|
||||
|
||||
for (var key in loaders) {
|
||||
destroyLoader(key);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
loadFont: loadFont,
|
||||
observeFont: observeFont,
|
||||
loadText: loadText,
|
||||
disposeAll: disposeAll
|
||||
};
|
||||
})();
|
||||
|
||||
64
src/App.tsx
64
src/App.tsx
@ -38,15 +38,15 @@ const s = {
|
||||
} as const,
|
||||
textarea: {
|
||||
width: "100%",
|
||||
height: "72px",
|
||||
padding: "8px 12px",
|
||||
"font-size": "32px",
|
||||
border: "1px solid #d9d9d9",
|
||||
"border-radius": "6px",
|
||||
resize: "vertical",
|
||||
resize: "none",
|
||||
"box-sizing": "border-box",
|
||||
outline: "none",
|
||||
color: "#e74c3c",
|
||||
"line-height": "1.4",
|
||||
} as const,
|
||||
pre: {
|
||||
background: "#f7f7f8",
|
||||
@ -102,7 +102,7 @@ function App() {
|
||||
set_fonts(fontList);
|
||||
set_serverConfig(config);
|
||||
if (fontList.length > 0) {
|
||||
set_selectedFont(fontList[0].name);
|
||||
onFontChange(fontList[0].name);
|
||||
}
|
||||
});
|
||||
|
||||
@ -119,28 +119,41 @@ function App() {
|
||||
}`;
|
||||
});
|
||||
|
||||
/** 字体切换时使用 SDK 重新加载 */
|
||||
const prevFontRef = { value: "" };
|
||||
createMemo(() => {
|
||||
const font = selectedFont();
|
||||
if (!font) return;
|
||||
if (font !== prevFontRef.value) {
|
||||
prevFontRef.value = font;
|
||||
const el = document.getElementById("webfont-preview");
|
||||
if (el) el.style.fontFamily = 'inherit';
|
||||
(globalThis as any).WebFont?.loadFont({
|
||||
fontName: font,
|
||||
selector: "#webfont-preview",
|
||||
family: "CustomFont",
|
||||
});
|
||||
}
|
||||
/** loadText loader 引用,字体或文本变化时增量加载 */
|
||||
let textLoader: { update: (text: string) => void; dispose: () => void } | null = null;
|
||||
|
||||
/** 文本变化时增量加载字体 */
|
||||
const onTextChange = (value: string) => {
|
||||
set_text(value);
|
||||
if (!textLoader) return;
|
||||
textLoader.update(value);
|
||||
};
|
||||
|
||||
/** 根据文本行数动态计算 textarea 高度 */
|
||||
const textareaRows = createMemo(() => {
|
||||
const lines = text().split("\n").length;
|
||||
return Math.max(2, Math.min(lines, 10));
|
||||
});
|
||||
|
||||
/** 字体切换时为当前文本加载新字体 */
|
||||
const onFontChange = (font: string) => {
|
||||
set_selectedFont(font);
|
||||
if (!font) return;
|
||||
if (textLoader) textLoader.dispose();
|
||||
textLoader = (globalThis as any).WebFont?.loadText({
|
||||
fontName: font,
|
||||
text: text(),
|
||||
family: "CustomFont",
|
||||
}) ?? null;
|
||||
const el = document.getElementById("webfont-preview");
|
||||
if (el) el.style.fontFamily = '"CustomFont", sans-serif';
|
||||
};
|
||||
|
||||
async function refreshFonts() {
|
||||
const fontList = await fetchFonts();
|
||||
set_fonts(fontList);
|
||||
if (fontList.length > 0 && !selectedFont()) {
|
||||
set_selectedFont(fontList[0].name);
|
||||
onFontChange(fontList[0].name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,7 +178,7 @@ function App() {
|
||||
<select
|
||||
style={s.select}
|
||||
value={selectedFont()}
|
||||
onChange={(e) => set_selectedFont(e.target.value)}
|
||||
onChange={(e) => onFontChange(e.target.value)}
|
||||
>
|
||||
<option value="">-- 请选择 --</option>
|
||||
<For each={fonts()}>
|
||||
@ -185,8 +198,9 @@ function App() {
|
||||
style={{
|
||||
...s.textarea,
|
||||
}}
|
||||
rows={textareaRows()}
|
||||
value={text()}
|
||||
onInput={(e) => set_text(e.target.value)}
|
||||
onInput={(e) => onTextChange(e.target.value)}
|
||||
placeholder="在此输入文本..."
|
||||
/>
|
||||
</section>
|
||||
@ -242,15 +256,17 @@ function App() {
|
||||
.title { font-family: "MyFont"; }
|
||||
</style>
|
||||
<h1 class="title">你的文字</h1>`}</pre>
|
||||
<p style={{ "margin-top": "12px" }}><b>进阶用法(推荐):</b>动态内容场景下,使用 JS SDK 自动监听元素文字变化,按需增量加载字体片段,不会出现全量字体闪烁。<a href="/webfont-sdk.js" download="webfont-sdk.js">下载 SDK</a></p>
|
||||
<p style={{ "margin-top": "12px" }}><b>JS SDK(推荐):</b>增量加载字体片段,按需请求,不会出现全量字体闪烁。<a href="/webfont-sdk.js" download="webfont-sdk.js">下载 SDK</a></p>
|
||||
<pre style={{ ...s.pre, "font-size": "12px", "margin-top": "4px" }}>{`<script src="${location.origin}/webfont-sdk.js"><\/script>
|
||||
<script>
|
||||
WebFont.loadFont({
|
||||
fontName: "${selectedFont()}",
|
||||
selector: ".title",
|
||||
fontName: "字体文件名.ttf",
|
||||
selector: ".my-element",
|
||||
family: "MyFont",
|
||||
interval: 1000,
|
||||
});
|
||||
<\/script>`}</pre>
|
||||
<p style={{ "margin-top": "8px" }}>还支持 <code>WebFont.observeFont()</code>(MutationObserver 事件驱动)和 <code>WebFont.loadText()</code>(手动传文本)两种方式,多种方式可同时使用,SDK 内部自动按字体去重增量加载。</p>
|
||||
</section>
|
||||
|
||||
<footer style={{ "margin-top": "48px", "padding-top": "16px", "border-top": "1px solid #eee", "font-size": "12px", color: "#999", "text-align": "center" }}>
|
||||
|
||||
12
task.md
12
task.md
@ -3,9 +3,19 @@
|
||||
把基准测试结果文档保存在本地 benchmark_results/ ,这样我方便查看。你的文档中应该在每个重大节点更新基准测试结果(benchmark_results/OPTIMIZATION_LOG.md),这样我能方便看到你使用了哪些优化方法,得到了什么样的优化效果。
|
||||
|
||||
|
||||
|
||||
=== 字体裁剪基准测试 ===
|
||||
|
||||
8个汉字: avg=23.6ms min=18.4ms max=37.2ms 输出=16,508 bytes ssim=1.0000
|
||||
拉丁+数字: avg=16.4ms min=13.7ms max=18.2ms 输出=1,272 bytes ssim=1.0000
|
||||
千字文前段: avg=59.4ms min=47.3ms max=76.5ms 输出=161,344 bytes ssim=1.0000
|
||||
|
||||
|
||||
=== 一晚上优化后的 字体裁剪基准测试 ===
|
||||
|
||||
8个汉字: avg=7.7ms min=3.8ms max=20.7ms 输出=16,572 bytes ssim=1.0000
|
||||
拉丁+数字: avg=3.2ms min=1.6ms max=6.6ms 输出=1,272 bytes ssim=1.0000
|
||||
千字文前段: avg=11.7ms min=6.8ms max=21.7ms 输出=161,816 bytes ssim=1.0000
|
||||
|
||||
## 其他方向
|
||||
|
||||
就是有一个纯前端的优化,咱们提供的js SDK好像是通过定时器扫描的吧,这当然是一种方式,也是最省心的一种方式,但是咱们是不是还可以考虑另外一种方式,就是通过配置来启用定时扫描还是由用户自己的事件来触发,甚至由用户直接传递文本,这样的话对于首页上的demo来说,可能会有更高的及时性响应
|
||||
Loading…
x
Reference in New Issue
Block a user