一、问题描述 我们知道,在一个div里加上contenteditable="true"之后,它就变成了一个可编辑的框,而且是能满足基本需求的富文本编辑框。例如下面的文字:
文章图片
它的源HTML如下所示:
这是第一行。This is the second line.这是第四行。This is the fourth line.
如果要将上述HTML转成svg文本,而且看起来跟HTML效果一模一样,有两个难点需要解决:
- 理清HTML源代码的结构。
- 处理换行的问题。例如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有相似的地方,也有特别之处。在此列出几点特别之处:
- SVG的文字使用
包住,如果需要分行或者分成几段,在里面使用
。
- SVG的文字不会自动换行,设定的宽度也不会。
- SVG文字在垂直对齐上,有多种对齐基线,默认对应于HTML的是alphabetic基线。
- SVG文字颜色使用fill属性,而不是color。其他样式跟CSS一致。
- 一个tspan接着一个tspan,如果没有重新设定x、y,它们是横向连在一起的。
- 调节换行,需要计算tspan的y值。
- 单倍的行高接近字体大小的1.3倍。
四、转换算法 4.1 HTML文本分行
HTML文本是一个树状结构,使用深度优先算法遍历所有结点。算法思想大致如下:
- 从根DIV结点开始遍历。
- 用一个数组存储每一行的结点。
- 如果是#text结点,添加到数组中。
- 如果是DIV结点,则一行完成,再新建一行。
- 如果是BR结点,结束上一行,添加新的一行,只有BR结点这个元素,再新建一行。
4.2 处理自动换行问题
上文中已经说过,HTML里的文本是会自动换行的,但SVG里的文本不会。那么,原来在HTML里的一行,可能在SVG里需要变成多行。
算法的思想是:一个字一个字的选取,获取选择部分的位置,当选择部分的y值比第一个字的y值大(而且超过了某个阈值),则从这个字符开始换行了。
具体来说,使用document.createRange()创建一个选择区域,使用range.setStart和range.setEnd进行单字符选择。使用range.getBoundingClientRect()获取选择区域的位置。
4.3 组建SVG文本
完成了上面的工作之后,这一步相对就比较简单了。生成的步骤如下:
- 在text里面,为每一行新建一个tspan。
- 一行里面也会有很多格式,每种格式都新建一个tspan。
- 根据遍历时记录的字体样式,设置tspan里的style。
- 根据单字符选择时记录的y值,设置tspan的y值。
【前端|contenteditable格式化html文本转svg】经过上面的步骤,就可以生成跟HTML所见一模一样的SVG文本了。本文的源代码:格式化html文本转svg文本源代码-Typescript文档类资源-CSDN文库
推荐阅读
- 笔记|flex布局入门讲解
- CSS|30分钟学会html+css实现乒乓球快打特效(内附源码)
- css|记录使用@media的尴尬
- 前端|bootstrapvalidator表单验证、验证清除重置
- 前端|一道面试题牵出12个前端硬核知识点,你知道几个((瀏覽器安全.網絡安全))
- javascript|Vue3 + Vite2 项目实战复盘总结(干货!)
- vue.js|vue3+antd实现table中点击具体某一列展示弹窗
- vue|Vue+element-ui+ts封装table业务组件
- 面试|理解JS的三座大山