最近做的项目涉及到 web 绘图, 采用 d3 操作 svg 实现图中通常会用到文本, 而 svg 里的 text 元素不像普通 DOM 元素那样能很好地处理文本换行, 所以要用到一些技巧
通常有两种做法
用 tspan 把 text 拆成多行, 重新计算每个 tspan 的 y 坐标
- <svg xmlns="http://www.w3.org/2000/svg">
- <text font-size="14">
- <tspan x="0" y="10"> 较长文文本一行 </tspan>
- <tspan x="0" y="28"> 放不下就换行 </tspan>
- </text>
- </svg>
用 foreignObject 包裹 DOM 元素, 利用 DOM 的文本布局能力自动处理换行
- <svg xmlns="http://www.w3.org/2000/svg">
- <foreignObject width="120" height="50">
- <body xmlns="http://www.w3.org/1999/xhtml">
- <p style="font-size:14px;margin:0;"> 较长文文本一行放不下就换行 </p>
- </body>
- </foreignObject>
- </svg>
两种方法各有千秋, 方法 1 可以精确控制换行位置, 但是需要计算具体的坐标方法 2 无需计算内部坐标, 但它是根据单词来分割的, 针对长英文单词也无能为力
笔者碰到的就是单个长英文单词也要换行显示, 所以只能用方法 1 产品需求是文本根据屏幕大小自动适配, 如果宽度不够就换行显示效果如下图所示:
雷达图
Outperformance 这个单词被截断原理就是先在 text 元素下插入一个 tspan, 将单词里的字母逐个填进去, 判断元素宽度是否达到限制如果超过限制就再插入一个 tspan, 同时计算新 tspan 的 y 坐标由于事先知道 text 的具体位置, x 保持一致就可以了代码如下:
- function wrapWord(text, width) {
- text.each(function() {
- var text = d3.select(this),
- words = text.text().split('').reverse(),
- word,
- line = [],
- lineNumber = 0,
- lineHeight = text.node().getBoundingClientRect().height,
- x = +text.attr('x'),
- y = +text.attr('y'),
- tspan = text.text(null).append('tspan').attr('x', x).attr('y', y);
- while (word = words.pop()) {
- line.push(word);
- const dash = lineNumber> 0 ? '-' : '';
- tspan.text(dash + line.join(''));
- if (tspan.node().getComputedTextLength()> width) {
- line.pop();
- tspan.text(line.join(''));
- line = [word];
- tspan = text.append('tspan').attr('x', x).attr('y', ++lineNumber * lineHeight + y).text(word);
- }
- }
- });
- }
各位看官如果有更好的办法, 欢迎评论, 不吝赐教!
来源: https://juejin.im/entry/5ab4ca3df265da23a3352b93