如何将用户输入的名称转成艺术字体-fontmin.js

写在开头

日常我们在页面中使用特殊字体,一般操作都是直接由前端来全量引入设计师提供的整个字体包即可,具体操作如下:

javascript 复制代码
<template>
  <div class="font">橙某人</div>
</template>

<style scoped>
@font-face {
  font-family: "orange";
  src: url("./orange.ttf");
}
.font {
  font-family: "orange";
}
</style>

很简单吧🤡,但有时应用场景不同,可能需要我们考虑一下性能问题。

一般来说,我们常见的字体包整个是非常大的,小的有几M到十几M,大的可能去到上百M都有,特别是中文类的字体包会相对英文类的要更大一些。

如本文案例,我们仅需在用户输入完后加载对应的字体包即可,这样能避免性能的损耗。

为此,我们需要把整个字体包拆分、细致化、子集化,让它能达到按需引入的效果。

那么这要如何来做这个事情呢?这个方案单单前端可做不了,我们需要配合后端一起,下面就来看看具体的实现过程吧。😗

前端

前端小编用 Vue 来编写,具体如下:

javascript 复制代码
<template>
  <div>
    <input v-model="name" />
    <button @click="handleClick">生成</button>
    <div v-if="showName" class="font">{{ showName }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: "",
      showName: "",
    };
  },
  methods: {
    handleClick() {
      // 创建link标签
      const linkElement = document.createElement("link");
      linkElement.setAttribute("rel", "stylesheet");
      linkElement.setAttribute("type", "text/css");
      linkElement.href = `http://localhost:3000?name=${encodeURIComponent(this.name)}`;
      document.body.appendChild(linkElement);
      // 优化显示效果
      setTimeout(() => {
        this.showName = this.name;
      }, 300);
    },
  },
};
</script>

<style>
.font {
  font-family: orange;
  font-size: 50px;
}
</style>

应该都能看懂吧,主要就是生成了一个 <link /> 标签并插入到文档中,标签的请求地址指向我们服务端,至于服务端会返回什么你可以先猜一猜。👻

服务端

服务端小编选择用 Koa2 来编写,你也可以选择 Express 或者 Egg ,甚至 Node 也是可以的,差异不大,具体逻辑如下:

javascript 复制代码
const koa = require("koa2");
const fs = require("fs");
const FontMin = require("fontmin");

const app = new koa();

/** @name 优化,缓存已经加载过的字体包进内存 **/
const fontFamilyMap = {};

/** @name 加载字体包 **/
function loadFontLibrary(fontPath, fontFamily) {
  if (fontFamilyMap[fontFamily]) return fontFamilyMap[fontFamily];
  return new Promise((resolve, reject) => {
    fs.readFile(fontPath, (error, file) => {
        if (error) {
          reject(new Error(error.message));
        } else {
          fontFamilyMap[fontFamily] = file;
          resolve(file);
        }
    });
  });
}

app.use(async (ctx) => {
  const { name } = ctx.query;
  // 设置返回文件类型
  ctx.set("Content-Type", "text/css");

  const fontPath = "./font/orange.ttf";
  const fontFamily = "orange";
  if (!fs.existsSync(fontPath)) return (ctx.body = "字体包读取失败");

  const fontMin = new FontMin();
  const fontFile = await loadFontLibrary(fontPath, fontFamily);
  fontMin.src(fontFile);

  const getFontCSS = () => {
    return new Promise((resolve) => {
      fontMin
        .use(FontMin.glyph({ text: name }))
        .use(FontMin.css({ base64: true, fontFamily }))
        .run((error, files) => {
          if (error) {
            console.log("error", error.message);
          } else {
            const fontContent = files?.[1]?.contents;
            resolve(fontContent);
          }
      });
    });
  };

  const fontCSS = await getFontCSS();

  ctx.body = fontCSS;
});

app.listen(3000);

console.log("服务器开启: http://localhost:3000/");

我们主要是采用了 Fontmin 库来完成整个字体包的按需加载功能,这个库是第一个纯 JavaScript 字体子集化方案。

可能有后端是 Java 或者其他技术栈的小伙伴,你们也不用担心,据小编和公司后端同事了解,不同技术栈也是有对应的库可以解决的,需要的可以自行查查看。


至此,本篇文章就写完啦,撒花撒花。

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。

老样子,点赞+评论=你会了,收藏=你精通了。

相关推荐
Patrick_Wilson10 分钟前
最佳实践是有保质期的:从一次 CDN external 白屏事故说起
前端·性能优化·前端工程化
YHHLAI35 分钟前
[特殊字符] Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活
前端·人工智能
nuIl42 分钟前
我把 5 个编码 Agent 塞进了一个 npm 包
前端·agent·claude
L-影1 小时前
FastAPI 静态文件:Web 页面的“固定展柜”与“加速引擎”
前端·fastapi
陪我去看海1 小时前
受够了 AI 写完代码留下一堆端口?我做了一个端口管理App!
前端·macos·vibecoding
Xuepoo1 小时前
我抛弃了 DOM,但保留了无障碍访问
前端
李明卫杭州1 小时前
Vue2 vs Vue3 的 h 函数:渲染函数完整迁移指南
前端
星栈1 小时前
LiveView 的认证系统:从登录到权限,我一开始以为有 `current_user` 就算完事了
前端·前端框架·elixir
在因斯坦1 小时前
摸摸鱼之前端自己研究 Jenkins 自动化部署
前端
snpgroupcn2 小时前
SAP实施风险管控注意事项:四大风险域与防范策略
前端·数据库·人工智能