我一直很喜欢 Microsoft Fluent Emoji。

Fluent Emoji
Fluent Emoji

在 Microsoft 推出包含 Fluent Emoji 的 Windows 11 开发预览时,我第一时间升级了系统

我随即产生了在网页上显示 Fluent Emoji 的想法,于是 rehype-fluent-emoji 诞生了。

复制这件小事

得益于 Twemoji 作为开源 Emoji 的先行者,已经有相关生态能实现将网页上 emoji 替换为 Twemoji。

但方案大多都是直接替换为图片。

替换为图片会让复制行为变怪,比如选中文字的情况下复制不到,选中时文字与图片间样式也会需要对齐。

Emoji 被当作图片
Emoji 被当作图片

通过 JavaScript 监听复制 Event 来动态替换复制内容也能走通这条路,但这好脏。

我一直很反感复制一段文字,还在里面塞一段版权声明的行为……本质上和那些劫持剪贴板的流氓软件没什么区别。

因为 emoji 本身是文字,复制我文章里的一句话,粘贴到别处后,应该得到同一句话,而不是丢掉里面的表情。

最后比较好的实现方案是把文本层、视觉层都分开:

  • 文本层保留 Unicode,用来参与选择和复制。
  • 视觉层负责显示 Fluent Emoji。
  • 选中状态通过共享 CSS 继续让 Unicode 字形保持透明,但不抢页面自己的选区背景。

图片从哪来

解决了布局设计上,接着就是 Fluent Emoji 的图片从哪来的问题。

Microsoft 确实十分慷慨得提供了 Fluent Emoji,但它的仓库结构并不适合直接拿来用。

开源社区也有人做出了 fluent-emoji-webfont,但因为还有兼容性问题,我也没有优先考虑。

Fluent Emoji Webfont
Fluent Emoji Webfont

于是在先后考察 LobeHubemoji-regex-xsunicode-emoji-json 这些库后,我终于找到了 fluentui-emoji-unicode 这个项目。

它完美符合我的预期,把 Fluent Emoji 仓库里的图片转换为 Unicode 字符编码的文件名。

不过有一个问题,因为这个仓库只做了重命名,文件大多还是 SVG 格式,而 Safari 对于 Fluent Emoji 这种包含 filter 的 SVG 图片支持特别差,导致显示的效果不如预期。

Safari 和 Chrome 下 SVG 对比
Safari 和 Chrome 下 SVG 对比

于是我 fork 并加上了格式转换,把图片格式统一为了 WebP。

做什么,不做什么

unified 下的 remark 和 rehype 其实分工明确,所以做成 rehype 插件更符合语义。

图片下载到本地 public 下也是一个需求,因为我不想强依赖 CDN。

不过这超出了 rehype 的工作范畴,所以我把它做成了一个单独的命令。

Terminal
rehype-fluent-emoji sync content --out public/emoji --style 3d

显式执行同步命令,把文章里用到的 emoji 资产下载到指定目录。

然后渲染时只需要配置插件:

{
assetBase: '/emoji'
}

也就是说,插件只负责生成 URL。图片在哪里、什么时候同步、是否放 CDN,都交给使用者自己决定。

这也给你一些自由度,你完全可以把我的 fluentui-emoji-unicode 部署到自己的服务器,或是 fork 到 GitLab,Gitee。

当然你也可以略过拉取到本地这一步,直接用 https://raw.githubusercontent.com 或是 https://cdn.jsdelivr.net 来引用。

来试试吧

欢迎你去 GitHub 看源码,也可以去 npmx.dev 来安装这个 rehype 插件。

Terminal
pnpm add -D rehype-fluent-emoji

在 Astro 里大概是这样配置:

import rehypeFluentEmoji from 'rehype-fluent-emoji'
export default {
markdown: {
rehypePlugins: [
[
rehypeFluentEmoji,
{
assetBase: '/emoji',
style: '3d',
},
],
],
},
}

在这个站点里的用法

现在这个站点会先把文章里用到 Emoji 图片同步到本地:

{
"emoji:sync": "rehype-fluent-emoji sync content --out public/emoji --style 3d"
}

然后 MDX 里使用 rehype-fluent-emoji 渲染文章里的 emoji。

你可以查看 Fluent Emoji 渲染测试,看它在段落、列表、链接、代码块里的表现。

再见 👋

这个插件本身并不复杂,但我很喜欢一点点改进细节的过程,最后的效果我很满意。

我也很喜欢这个解法:视觉上有趣,但不破坏默认行为。

希望能做出更多有趣的东西~