前端|contenteditable格式化html文本转svg

一、问题描述 我们知道,在一个div里加上contenteditable="true"之后,它就变成了一个可编辑的框,而且是能满足基本需求的富文本编辑框。例如下面的文字:
前端|contenteditable格式化html文本转svg
文章图片


它的源HTML如下所示:

这是第一行。This is the second line.这是第四行。This is the fourth line.

如果要将上述HTML转成svg文本,而且看起来跟HTML效果一模一样,有两个难点需要解决:

  1. 理清HTML源代码的结构。
  2. 处理换行的问题。例如This is the second line.这行文字,在HTML里是自动换行了,但SVG没有自动换行的功能,必须计算出原文本在哪个地方换行。

二、样式解析 本文的讲述中,文本所使用的样式包括:水平对齐、垂直对齐、字体、字体大小、字体颜色、加粗和下划线。
其中,水平对齐和垂直对齐只能对整个文本进行设置,而其他样式可以对部分文本进行设置。
2.1 水平对齐
水平对齐设置的属性是比较简单的,就是text-align,可以赋值left、center和right。
2.2 垂直对齐
垂直对齐不能通过简单的CSS属性解决,因为文本高度是未知的,随着用户输入而变化,需要通过JS控制。通过getBoundingClientRect方法获取文本实际高度。以绝对定位为例,有以下伪代码:
顶部对齐时,top=文本框.y 居中对齐时,top=(文本框.height-文本.height)/2 底部对齐时,top=文本框.height-文本.height

2.3 字体
字体、字体颜色、字体大小,在HTML里使用把文字包住,字体对应face属性,字体颜色对应color属性,字体大小对应size属性。需要注意的是,size属性只有7个值,也就是1-7,其跟实际的像素对应关系是:
size值 对应像素值
1 10px
2 12px
3 16px
4 18px
5 24px
6 32px
7 48px
文字粗体使用包住文字,文字下划线使用包住文字。

三、SVG文字特性 在SVG里面,文字跟HTML有相似的地方,也有特别之处。在此列出几点特别之处:
  1. SVG的文字使用包住,如果需要分行或者分成几段,在里面使用
  2. SVG的文字不会自动换行,设定的宽度也不会。
  3. SVG文字在垂直对齐上,有多种对齐基线,默认对应于HTML的是alphabetic基线。
  4. SVG文字颜色使用fill属性,而不是color。其他样式跟CSS一致。
  5. 一个tspan接着一个tspan,如果没有重新设定x、y,它们是横向连在一起的。
  6. 调节换行,需要计算tspan的y值。
  7. 单倍的行高接近字体大小的1.3倍。

四、转换算法 4.1 HTML文本分行
HTML文本是一个树状结构,使用深度优先算法遍历所有结点。算法思想大致如下:
  1. 从根DIV结点开始遍历。
  2. 用一个数组存储每一行的结点。
  3. 如果是#text结点,添加到数组中。
  4. 如果是DIV结点,则一行完成,再新建一行。
  5. 如果是BR结点,结束上一行,添加新的一行,只有BR结点这个元素,再新建一行。
为了后续构建文本字体样式,可以在结点中同时保存文本的格式。例如默认fontColor是#000000,fontWeight是normal,当遇到font color=#ff0000结点时,fontColor修改为#ff0000。当再遇到b结点时,fontWeight修改为bold。

4.2 处理自动换行问题
上文中已经说过,HTML里的文本是会自动换行的,但SVG里的文本不会。那么,原来在HTML里的一行,可能在SVG里需要变成多行。
算法的思想是:一个字一个字的选取,获取选择部分的位置,当选择部分的y值比第一个字的y值大(而且超过了某个阈值),则从这个字符开始换行了。
具体来说,使用document.createRange()创建一个选择区域,使用range.setStart和range.setEnd进行单字符选择。使用range.getBoundingClientRect()获取选择区域的位置。

4.3 组建SVG文本
完成了上面的工作之后,这一步相对就比较简单了。生成的步骤如下:
  1. 在text里面,为每一行新建一个tspan。
  2. 一行里面也会有很多格式,每种格式都新建一个tspan。
  3. 根据遍历时记录的字体样式,设置tspan里的style。
  4. 根据单字符选择时记录的y值,设置tspan的y值。

【前端|contenteditable格式化html文本转svg】经过上面的步骤,就可以生成跟HTML所见一模一样的SVG文本了。本文的源代码:格式化html文本转svg文本源代码-Typescript文档类资源-CSDN文库

    推荐阅读