注册
web

从零基于 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 库,直接导入这个库就可以完成文本到图片的一个转换了。


image.png


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


作者:唐某人丶
来源:juejin.cn/post/7485758756911857683

0 个评论

要回复文章请先登录注册