目录

从零基于 Canvas 实现一个文本渲染器

一、前因后果

1.1 目的

起因是女朋友想做小红薯的账号。她每天会先把小故事写好复制到手机上,然后按照一定的图片规格一张张裁剪,最后发布到平台上。

接着我就在想,有没有什么高效的文本转成图片并且能够自动分页的方案呢?查了很久都没找到合适的方案,最后还是决定自己写一个转换工具。

1.2 需求

总结了一下我的场景,发现需求如下:

  1. 能够内容转成指定尺寸的图片
  2. 能够设置字体、背景、样式
  3. 支持自动换行、自定义换行
  4. 支持分页
  5. 支持图片下载

根据输入文本,通过 Canvas 渲染

展示全部分页图片的内容,可以批量下载

1.3 思路

基于需求,最后决定采用 Canvas 的绘制方案。思路如下:

  1. 根据输入文本,完成分行、分页的计算
  2. 将分页数据绘制到 Canvas
  3. 批量将 Canvas 的内容导出图片进行下载

1.4 难点

做的时候发现几个核心的问题难点,分别是换行、分页的问题。

I. 换行问题

在 canvas 中进行文本绘制不同于 HTML 标签,文本是不能自动换行的。所以这个需要自己去计算什么时候换行,在哪个字符段该换行。

II. 分页问题

当内容超出当前页了,我们希望能够自动进行分页,这个就需要去计算行高页面内容区高度

二、设计方案

下面介绍一下 Canvas 文本渲染器的设计方案。主要会围绕着文本计算、文本绘制、导出图片来讲解。

2.1 如何计算文本

计算文本主要做的事情就是,根据用户输入的文本和想要生成的图片、字体参数,来计算需要分多少页,每一页具体要展示多少行,每一行要展示多少内容。

I. 分词

要实现换行,就要知道一句话中从哪个分词开始是超出了当前行的最大宽度。这个分词可能是某个中文字符、某个英文单词、某串数字、某个其他字符等。

这里我们只讨论简单的中英文数字的场景

所以要做的第一步,就是将输入的文本拆解成一个个分词,然后去筛选掉空的字符。

核心代码如下:

II. 分行

现在已经将文本拆分成了足够细的分词。接下来要做的就是将每个分词不断地塞入每一行中。

你可以把每一行理解成一个固定宽度的容器,一但某个分词塞不进了,就得创建一个新的容器再把这个分词塞进去。

所这个遍历的过程,需要知道行的最大宽度以及当前分词的真实宽度。最大宽度的计算规则,根据用户设置的页面宽度减去左右边距的宽度,就是内容区的最大行宽度。

代码如下:

分词的真实宽度则是用 canvas 中的 measureText 来测量字符串的宽度。

接下来就是一个累加的过程。把分词加入当前行,判断是否超出最大宽度,如果超出就新起一行。

最后遍历完后就会得出所有的分行内容。

上面是分行大致的思路,但在实际的代码实现上,会随着分词类型、换行需求的增加变得更加复杂。例如:

  1. 英文单词前面需要追加空格
  1. 用户想自定义空行逻辑:匹配到句号自动换行并且空一行。

III. 分页

有了分行的数据,就可以进行分页了。其实逻辑差不多,只不过一个是横向,一个是纵向。把每个页当成一个固定高度的容器,不断的把行塞进去,塞不进就新起一个页容器。

这个遍历的过程需要知道页面内容区的最大高度以及每一行的行高。行高一般是用户设置的,所以只需关注页面内容区的最大高度的计算规则。

代码如下:

分页计算流程,遍历 lines 把分行不断塞入当前页。如果超出高度就放到新的页面。最后就会得出所有分页的数据。

2.2 如何绘制

有了分页数据以后,绘制主要做的事情就是根据用户设置的样式(页面边距、字体和背景)来渲染每一页具体的内容。

I. 指定图片尺寸

通过设置 Canvas 画布的大小即可。

II. 绘制背景

背景直接通过 fillRect 绘制即可

III. 绘制内容

绘制内容的时候,主要考虑两个点:

  1. 设置字体样式
  2. 根据边距、行高计算每行绘制的位置

2.3 如何导出图片

导出图片就是将 Canvas 上的内容转成 DataURL 然后下载成图片

三、最后

基于上面的思路,你不仅仅可以开发一个简单的文本渲染器,你甚至可以做一个复杂的编辑器哦~

最后我将这个工具封装成了一个 npm 库,直接导入这个库就可以完成文本到图片的一个转换了。

如果感兴趣,完整代码放置在 GitHub 了:github.com/zixingtangm...

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
thinkQuadratic3 分钟前
scss预处理器对比css的优点以及基本的使用
前端·css·scss
yuluo_YX24 分钟前
使用 Spring AI Aliabab Module RAG 构建 Web Search 应用
前端·人工智能·spring
冴羽24 分钟前
SvelteKit 最新中文文档教程(16)—— Service workers
前端·javascript·svelte
Hongwen100135 分钟前
寒冬之中的AI创意实验:48小时左右从零打造吉卜力风格AI绘图网站
前端·后端·cursor
小鸭呱呱呱1 小时前
【CSS】- 表单控件的 placeholder 如何控制换行显示?
前端·javascript·css·深度学习·面试·职场和发展·html
小浣熊喜欢揍臭臭1 小时前
vue+webpack5(高级配置)
前端·javascript·vue.js
Bigger1 小时前
Tauri(十七)—— 安装包和应用包的区别
前端·app·mac
茉莉蜜茶only1 小时前
【前端实习岗位】淘天集团2026届春季实习生招聘
前端·招聘
Book_熬夜!1 小时前
Vue2——组件的注册与通信方式、默认插槽、具名插槽、插槽的作用域
前端·javascript·vue.js·前端框架·ecmascript
夕水2 小时前
后端说,这个超大数字idxxx会变成xxx,让我知道了js的一个陷阱
前端