LOGO
Published on

结构图插件系列:SVG文字排版

Authors
  • avatar
    Name
    梅亮
    Twitter

需求

在SVG中,在指定的矩形容器内(给定宽高范围),绘制一段文本,如果文字超长则末尾显示省略号。

现状

SVG中的<text>标签主要用于定义矢量图中的文本内容。不同于HTML的是,SVG渲染文字的本质还是绘图。 通俗的说,SVG的<text>标签只能绘制一行文字,他不会换行,更不能利用css自动显示省略号。

思路

回归本质,假如这段文字让你手写,你会知道什么时候换行,什么时候写到纸张最后一行,写不完了写上省略号。 那么,我们要进行朴质的运算了。

  1. 计算文本(字符串)的总长度length
  2. 计算矩形容器能够绘制多少行文字rows
  3. 计算每一行放置的文本,组成数组strList
  4. 如何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。 瞬间我就悟了:

  1. 除中文字符外的字符(只考虑中英文、数字和标点),都需要计算。
  2. 非中字符串,在fontSize12px、14px、16px等值情况下,他们的宽度并不成比例。
  3. 省略号...替换的字符串,不一定是2个。
  4. 其中粗体、字体、字间距都会影响每个非中字符串的宽度。

实现

如果实时计算每个字符串的宽度(在页面绘制后,可得到宽度),特别影响页面性能。于是,我把常用的字符

"0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-=[ ];',.·/!@#$%^&*()_+{}:\"<>?~`"

12px 14px 16px 18pxfontWeight100 600情况下,非中字符宽度计算出来,存储为map,供上述计算直接使用。 计算方式稍微修改:

  1. 每次截取文本第一个字符,累加宽度。累加至宽度超出width,则将前面累加的字符串加入数组strList为一行。
  2. 最后一行文字提取后,还有剩余文本,说明超长。提取倒数字符,累加宽度大于等于省略号...的宽度,则将这几个末尾字符替换为...

总结

实现方式不重要,但是方案的性能很重要。