update at 2026-02-08 18:28:39
This commit is contained in:
130
miniprogram/utils/mp/font-loader.js
Normal file
130
miniprogram/utils/mp/font-loader.js
Normal file
@@ -0,0 +1,130 @@
|
||||
const { request, downloadFile, readFile } = require('./wx-promisify')
|
||||
|
||||
const localFonts = require('../../assets/fonts')
|
||||
|
||||
const fontBufferCache = new Map()
|
||||
const MAX_FONT_CACHE = 4
|
||||
|
||||
function normalizePath(path, baseUrl) {
|
||||
if (!path) {
|
||||
return ''
|
||||
}
|
||||
if (/^https?:\/\//i.test(path)) {
|
||||
return path
|
||||
}
|
||||
if (path.startsWith('//')) {
|
||||
return `https:${path}`
|
||||
}
|
||||
if (path.startsWith('/')) {
|
||||
return `${baseUrl}${path}`
|
||||
}
|
||||
return `${baseUrl}/${path}`
|
||||
}
|
||||
|
||||
function normalizeFontItem(item, baseUrl) {
|
||||
const path = item.path || item.url || ''
|
||||
const normalizedPath = normalizePath(path, baseUrl)
|
||||
const filename = item.filename || normalizedPath.split('/').pop() || `${item.name || 'font'}.ttf`
|
||||
return {
|
||||
id: item.id || `${item.category || '默认'}/${item.name || filename}`,
|
||||
name: item.name || filename.replace(/\.[^.]+$/, ''),
|
||||
category: item.category || '默认',
|
||||
filename,
|
||||
path,
|
||||
url: normalizedPath,
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeManifest(fonts, baseUrl) {
|
||||
if (!Array.isArray(fonts)) {
|
||||
return []
|
||||
}
|
||||
return fonts
|
||||
.map((item) => normalizeFontItem(item, baseUrl))
|
||||
.filter((item) => item.url)
|
||||
}
|
||||
|
||||
async function loadFontsManifest(options = {}) {
|
||||
const app = getApp()
|
||||
const manifestUrl = options.manifestUrl || app.globalData.fontsManifestUrl
|
||||
const baseUrl = options.baseUrl || app.globalData.fontsBaseUrl
|
||||
|
||||
if (Array.isArray(app.globalData.fonts) && app.globalData.fonts.length > 0) {
|
||||
return app.globalData.fonts
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await request({
|
||||
url: manifestUrl,
|
||||
method: 'GET',
|
||||
timeout: 10000,
|
||||
})
|
||||
|
||||
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||
throw new Error(`获取字体清单失败,状态码: ${response.statusCode}`)
|
||||
}
|
||||
|
||||
const fonts = normalizeManifest(response.data, baseUrl)
|
||||
if (!fonts.length) {
|
||||
throw new Error('字体清单为空')
|
||||
}
|
||||
|
||||
app.globalData.fonts = fonts
|
||||
return fonts
|
||||
} catch (error) {
|
||||
console.warn('远程字体清单加载失败,回退到本地清单:', error)
|
||||
const fallbackFonts = normalizeManifest(localFonts, baseUrl)
|
||||
app.globalData.fonts = fallbackFonts
|
||||
return fallbackFonts
|
||||
}
|
||||
}
|
||||
|
||||
function setLruCache(key, value) {
|
||||
if (fontBufferCache.has(key)) {
|
||||
fontBufferCache.delete(key)
|
||||
}
|
||||
fontBufferCache.set(key, value)
|
||||
|
||||
while (fontBufferCache.size > MAX_FONT_CACHE) {
|
||||
const firstKey = fontBufferCache.keys().next().value
|
||||
fontBufferCache.delete(firstKey)
|
||||
}
|
||||
}
|
||||
|
||||
async function loadFontBuffer(fontItem) {
|
||||
const cacheKey = fontItem.id
|
||||
if (fontBufferCache.has(cacheKey)) {
|
||||
const cached = fontBufferCache.get(cacheKey)
|
||||
setLruCache(cacheKey, cached)
|
||||
return cached
|
||||
}
|
||||
|
||||
if (!fontItem.url) {
|
||||
throw new Error('字体地址为空')
|
||||
}
|
||||
|
||||
const downloadRes = await downloadFile({ url: fontItem.url })
|
||||
if (downloadRes.statusCode < 200 || downloadRes.statusCode >= 300) {
|
||||
throw new Error(`字体下载失败,状态码: ${downloadRes.statusCode}`)
|
||||
}
|
||||
|
||||
const readRes = await readFile(downloadRes.tempFilePath)
|
||||
const result = {
|
||||
tempFilePath: downloadRes.tempFilePath,
|
||||
buffer: readRes.data,
|
||||
}
|
||||
|
||||
setLruCache(cacheKey, result)
|
||||
return result
|
||||
}
|
||||
|
||||
function listCategories(fonts) {
|
||||
const set = new Set(fonts.map((font) => font.category || '默认'))
|
||||
return ['全部', '收藏', ...Array.from(set)]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loadFontsManifest,
|
||||
loadFontBuffer,
|
||||
listCategories,
|
||||
}
|
||||
Reference in New Issue
Block a user