contenteditable格式化html文本转svg

一、问题描述

我们知道,在一个div里加上contenteditable="true"之后,它就变成了一个可编辑的框,而且是能满足基本需求的富文本编辑框。例如下面的文字:

 

它的源HTML如下所示:

<div contenteditable="true">这是<font size="1">第一</font>行。<div>This is the <u>second</u> line.</div><div>这是<font color="#ff0000">第四</font><b>行。</b></div><div><b>This</b> is the <font face="微软雅黑">fourth</font> line.</div></div>
如果要将上述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里使用<font></font>把文字包住,字体对应face属性,字体颜色对应color属性,字体大小对应size属性。需要注意的是,size属性只有7个值,也就是1-7,其跟实际的像素对应关系是:

size值 对应像素值
1 10px
2 12px
3 16px
4 18px
5 24px
6 32px
7 48px

文字粗体使用<b></b>包住文字,文字下划线使用<u></u>包住文字。

三、SVG文字特性

在SVG里面,文字跟HTML有相似的地方,也有特别之处。在此列出几点特别之处:

  1. SVG的文字使用<text></text>包住,如果需要分行或者分成几段,在里面使用<tspan></tspan>

  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值。

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

猜你喜欢

转载自blog.csdn.net/lweiyue/article/details/126040007