cn-font-replacer
Version:
font-replacer 是中文网字计划用于动态加载、动态更换字体所使用的库。你也可以自定义字体源来加载自己的字体,默认使用了字图 CDN的庞大字体库来源!
146 lines (141 loc) • 4.51 kB
text/typescript
/** 一个可以动态更新 html 中的字体样式的工具 */
/**
* 异步获取中文网字计划的字体映射表。
*
* @param basePath 字体资源的基础路径,默认为'https://chinese-fonts-cdn.netlify.app'。
* @example
* const CNFontMap = await getChineseFontsMap()
* const fontReplacer = new FontReplacer(CNFontMap)
*/
export const getChineseFontsMap = async (
basePath = 'https://chinese-fonts-cdn.netlify.app',
) => {
const data = await fetch(basePath + '/index.json').then((res) =>
res.json(),
);
const CNFontMap = Object.fromEntries(
Object.values(data).flatMap((i: any) => {
return i.remotePath.map((item: any) => {
return [
i.name +
'.' +
item.path.replace('/result.css', '').split('/dist/')[1],
{
cssUrl: basePath + '/' + item.path,
fontFamily: item.css.family,
fontWeight: item.css.weight,
},
];
});
}),
);
return CNFontMap;
};
/**
* 字体替换器类,用于动态替换HTML中的字体样式。
*/
export class FontReplacer<
FontMap extends Record<
string,
{ cssUrl: string; fontFamily: string; fontWeight: number }
>,
> {
fontMap: Map<string, StyleEl>;
constructor(public source: FontMap) {
this.fontMap = new Map();
const style = document.createElement('style');
style.innerText = this.style;
document.body.appendChild(style);
}
/**
* 应用字体到指定的元素。
*
* @param el 要应用字体的元素,可以是元素选择器字符串或HTMLElement对象。
* @param name fontMap 的 key 值
*/
applyFont(el: string | HTMLElement, name: string) {
this.loadFont(name);
const ele =
typeof el === 'string'
? (document.querySelector(el) as HTMLElement)
: el;
ele.style.setProperty(
'--font-replacer-weight',
this.getFontMeta(name).fontWeight.toString(),
);
ele.style.setProperty(
'--font-replacer-family',
`"${this.getFontMeta(name).fontFamily}"`,
);
ele.classList.add('font-replacer-control');
}
/** 清除对 el 元素的字体挂载 */
clearFont(el: string | HTMLElement) {
const ele =
typeof el === 'string'
? (document.querySelector(el) as HTMLElement)
: el;
ele.style.removeProperty('--font-replacer-weight');
ele.style.removeProperty('--font-replacer-family');
ele.classList.remove('font-replacer-control');
}
style =
'.font-replacer-control{ font-family:var(--font-replacer-family);font-weight:var(--font-replacer-weight); }';
/**
* 获取指定字体的元数据。
*/
private getFontMeta(name: string) {
if (!this.source[name])
throw new Error(`FontReplace.getFontMeta: ${name} not found`);
return this.source[name];
}
/**
* 加载指定字体。
*
* @param name 字体名称。
* @returns 返回StyleEl对象,表示已加载的字体样式元素。
*/
private loadFont(name: string) {
if (this.fontMap.has(name)) return this.fontMap.get(name);
const style = new StyleEl(name);
style.loadFontCSS(this.getFontMeta(name).cssUrl).mount();
this.fontMap.set(name, style);
return style;
}
}
/**
* 样式元素类,用于管理CSS样式和链接元素。
*/
class StyleEl {
el: HTMLStyleElement;
link: HTMLLinkElement;
constructor(public name: string) {
this.el = document.createElement('style');
this.link = document.createElement('link');
this.link.rel = 'stylesheet';
}
/**
* 加载字体CSS资源。
*
* @param url 字体CSS的URL。
* @returns 返回当前StyleEl实例,支持链式调用。
*/
loadFontCSS(url: string) {
this.link.href = url;
return this;
}
/**
* 将样式和链接元素挂载到文档头部。
*/
mount() {
document.head.appendChild(this.el);
document.head.appendChild(this.link);
}
/**
* 从文档中卸载样式和链接元素。
*/
unmount() {
this.el.remove();
this.link.remove();
}
}