- Published on
结构图插件系列:SVG文字排版
- Authors
- Name
- 梅亮
需求
在SVG中,在指定的矩形容器内(给定宽高范围),绘制一段文本,如果文字超长则末尾显示省略号。
现状
SVG中的<text>
标签主要用于定义矢量图中的文本内容。不同于HTML的是,SVG渲染文字的本质还是绘图。 通俗的说,SVG的<text>
标签只能绘制一行文字,他不会换行,更不能利用css
自动显示省略号。
思路
回归本质,假如这段文字让你手写,你会知道什么时候换行,什么时候写到纸张最后一行,写不完了写上省略号。 那么,我们要进行朴质的运算了。
- 计算文本(字符串)的总长度
length
。 - 计算矩形容器能够绘制多少行文字
rows
。 - 计算每一行放置的文本,组成数组
strList
。 - 如何
strList
的长度大于rows
,则strList[rows-1]
的文字末尾替换为...
。 例如
const fontSize=12
const lineHeight=14
const fontWeight=100
const str="回归本质,假如这段文字让你手写,你会知道什么时候换行。"
const box={width:60,height:42}
// 经过一番计算(示例,不精准)
// 一个字宽12,一行5个字。
// 字行高14,可绘制3行
// 得到的结果是
const strList=[ "回归本质,","假如这段文","字让你..."]
顺利的不大正常,就这!
难点
果不其然,墨菲定律。需求刚提测,bug就来了。测试同事用一段包含了大量字母、符号等的字符串进行测试。 如:SVG绘制文字?用text、tspan标签,指定x y fill stroke font-size text-anchor等属性
。 仔细看才恍然大悟,中文字符是方方正正的,但是英文字母、数字、各种标点符号,他们的宽度各不相等,不完全等于fontSize
。 瞬间我就悟了:
- 除中文字符外的字符(只考虑中英文、数字和标点),都需要计算。
- 非中字符串,在
fontSize
为12px、14px、16px
等值情况下,他们的宽度并不成比例。 - 省略号
...
替换的字符串,不一定是2个。 - 其中粗体、字体、字间距都会影响每个非中字符串的宽度。
实现
如果实时计算每个字符串的宽度(在页面绘制后,可得到宽度),特别影响页面性能。于是,我把常用的字符
"0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-=[ ];',.·/!@#$%^&*()_+{}:\"<>?~`"
在12px 14px 16px 18px
的fontWeight100 600
情况下,非中字符宽度计算出来,存储为map
,供上述计算直接使用。 计算方式稍微修改:
- 每次截取文本第一个字符,累加宽度。累加至宽度超出
width
,则将前面累加的字符串加入数组strList
为一行。 - 最后一行文字提取后,还有剩余文本,说明超长。提取倒数字符,累加宽度大于等于省略号
...
的宽度,则将这几个末尾字符替换为...
总结
实现方式不重要,但是方案的性能很重要。