海报挪进Worker画,canvas字体又变回默认了

生成海报这种活,我打算放到 Web Worker 里去画。Web Worker 是浏览器给的后台线程,活放在里面干,页面照样能滚能点。配套的 OffscreenCanvas 是一块不挂在页面上的画布,Worker 里可以直接拿来画。真动手之前我先写了个测试页,主线程里把字体加载好,再让 Worker 用同一个 font 字符串画同一行字。画出来一看,标题那行思源宋体变成了黑体,页面没有抛任何错误,图也照常画出来了。

这个黑体我认得,是 font 字符串里排在后面的 sans-serif,下面都叫它默认字体。主线程那边字体明明加载好了,页面上同一行字画出来是宋体。我就在测试页里把先后顺序挨个试了一遍。字体用的是思源宋体裁出来的 3755 字子集,一个 1,046,216 字节的 woff2。画上去的是「秋季新品 全场八折」这一行,字号 48px。用没用上我不靠眼睛判断。每次画完把整张画布的像素算一个校验值,再跟两张参照比:一张是确定加载好以后画的,一张是直接拿 sans-serif 画的。我用的机器是一台 Apple M4 的 Mac。浏览器我跑了四个,Chromium 149、Chrome 155、Firefox 151 和 macOS 上的 WebKit 26.5。

第一个结果四个浏览器一样。主线程里字体加载完也登记过了,Worker 里直接画,全是默认字体。这有点像两间各自开伙的厨房。这边买的酱油那边灶台上没有,那边要用得自己再买一瓶。好在四个浏览器的 Worker 里都有 FontFace 和 self.fonts 这两样东西。FontFace 是用脚本声明一个字体的对象。给它一个名字和文件地址,它负责去下载。self.fonts 是 Worker 自己那份字体清单,下载好的字体要 add 进去,画的时候才找得到。我在 Worker 里把这三步走完再画第一笔,四个浏览器画出来都是思源宋体。

到这儿本来可以收工了。可我多试了一种顺序,结果比前面那条麻烦得多。我让 Worker 先用默认字体画一次,然后才去加载字体、add 进清单,再在同一块画布上用同一个 font 字符串重画。Firefox 重画出来是宋体。Chromium、Chrome 和 WebKit 没变,还是默认字体。Chromium 和 Chrome 这边我又隔了 300 毫秒画一次,没变。我还在 Worker 里补调了一次 self.fonts.load,它的意思是「把这个 font 字符串要用的字体加载好再告诉我」,等它返回再画,还是没变。接着我在同一个 Worker 里新建一块 OffscreenCanvas 来画。WebKit 这回对了,Chromium 和 Chrome 在新画布上照样是默认字体。

这张图看第三、第四行就够了。字是同一行,font 字符串也是同一个,差别只在先画过一次。Chromium 和 Chrome 那一列后面两步全是默认字体,Firefox 那一列这两步都是自定义字体。

还有一个小发现我拿不准。我顺手把字号从 48px 改成 47px 又画了一次,四个浏览器量出来的文字宽度都是 388。这是宋体按比例缩下来的宽度,默认字体的话应该在 391.7 上下。看上去换个字号字体就用上了。不过这一步我只比了宽度,没比像素,不敢当成办法写进代码里。为什么先画一次就拧不回来,我没去查。这里只记看到的现象。

我现在的写法很死板。Worker 一启动就去加载字体,把这个等待存起来。之后每次画之前都先等它一下。字体没到位之前,这个 Worker 里一个带这种字体的字都不画,宁可让调用方多等一会儿。原来我还想过先出一张默认字体的草稿图顶着,字体到了再换正式图。测完就把这个念头掐了。在 Chromium 和 Chrome 里草稿一画,同一个 font 字符串的正式图就出不来了。

写完我好奇现成的做图工具是怎么排这个先后的。我用的是图映 ImgIng(imging.cn/)的在线做图,在电脑上... Chromium 149 里看的。它的文字图层有几款字体要现下载,下拉框下面写着第一次选中要下多大。韩文那款写的是约 4.5 MB。我把这款字体的回应故意拖慢,选中之后大约 0.2 秒就点「导出当前页」。导出文件是等字体回应到了才写出来的。这张 PNG 是 39,765 字节,和等字体到位以后再导出的那张逐字节相同。换成系统字体导出的那张是 39,870 字节。它里面用没用 Worker、代码怎么写的,我没看。字体只试了韩文这一种,流程也只走了这一条。

上面一张是系统字体导出的,下面一张是选中韩文字体后立刻点导出得到的。两张都是「가을 신상품 세일」,笔画粗细明显不是同一套字。

想在自己项目里查这件事,办法不复杂。在 Worker 里画完把像素传回主线程,跟主线程加载好字体后画的同一行字比一下,不一样就是没用上。挑字体时选一款和默认字体差得远的,肉眼也能看出来。手机浏览器、Windows、Safari 正式版和 Edge 我这次都没测,这几处是什么表现我不知道。字体用的是思源宋体(Noto Serif SC),开源,按 SIL OFL 1.1 许可。

相关推荐
看浪的路人5 小时前
第2讲:ReAct 模式从零实现——不用框架,手写一个 Agent 内核
前端·react.js·前端框架
独孤九剑打醒他5 小时前
【原创开源·航空终极篇】氢能航空动力升级:双燃料掺氢、富氧补气与露天储氢安全模型
前端·架构·开源·硬件工程·能源
律宏阔6 小时前
阿里云 ESA + Pages 域名部分电脑无法访问
前端
Lank_M6 小时前
canvas 第一次画字没用上字体,字体却是它要来的
前端
古夕6 小时前
iframe 授权失败时露出 60000 JSON 的复盘
前端·javascript
古夕6 小时前
地址变了,页面是空的:一次 Vue Router 菜单切换复盘
前端·vue.js
JavaGuide6 小时前
推荐一个好用 DeepSeek Harness 插件,直接把 Agent 上下文给可视化了!
前端·后端
数据掘金6 小时前
白屏故障定位:从资源请求到路由渲染的排查顺序
前端
程序员Sunday6 小时前
ES Module 和 CommonJS 有什么区别?循环依赖时会发生什么?
前端·javascript·面试