如何在D3 v7.6.1+TypeScript的饼图中实现文本换行及this引用
问题解决方案
一、箭头函数中替代d3.select(this)的写法
D3 v7的.each()方法回调函数提供三个参数:(数据d, 索引i, 节点数组nodes),箭头函数里可通过nodes[i]获取当前遍历的元素,再用d3.select()包裹,替代原有的d3.select(this)。
示例:
d3.selectAll('text').each((d, i, nodes) => { const currentText = d3.select(nodes[i]); // 基于currentText执行后续操作 });
二、适配TypeScript和D3 v7.6.1的wrap函数
原函数需补充类型注解,同时调整箭头函数的元素引用逻辑,以下是修改后的完整版本:
import * as d3 from 'd3'; // 定义wrap函数类型:接收文本元素选择器和换行宽度 function wrap( text: d3.Selection<SVGTextElement, unknown, HTMLElement, any>, width: number ) { text.each((_, i, nodes) => { // 获取当前文本元素 const currentText = d3.select(nodes[i]); // 拆分文本为单词数组并反转 const words = currentText.text().split(/\s+/).reverse(); let word: string | undefined; const line: string[] = []; let lineNumber = 0; const lineHeight = 1.1; // 行高(em单位) const y = currentText.attr('y') || '0'; const dy = parseFloat(currentText.attr('dy') || '0'); // 清空原文本,添加第一个tspan容器 let tspan = currentText.text(null) .append('tspan') .attr('x', 0) .attr('y', y) .attr('dy', `${dy}em`); while (word = words.pop()) { line.push(word); tspan.text(line.join(' ')); // 检查当前行宽度是否超出限制 const tspanNode = tspan.node(); if (tspanNode && tspanNode.getComputedTextLength() > width) { line.pop(); tspan.text(line.join(' ')); line = [word]; // 添加新tspan,设置行偏移量 tspan = currentText.append('tspan') .attr('x', 0) .attr('y', y) .attr('dy', `${++lineNumber * lineHeight + dy}em`) .text(word); } } }); } // React组件中的使用示例(结合useEffect) function PieChart() { React.useEffect(() => { // 给svg添加文本并调用换行函数 d3.select('svg') .append('text') .text('This is a circle with some text in it.') .attr('x', 20) .attr('y', 20) .call(wrap, 100); }, []); return <svg width={400} height={400}></svg>; }
关键调整说明:
- 类型注解:给函数参数补充TypeScript类型,明确
text是D3文本元素选择器类型,避免类型报错。 - 元素引用:在
.each()的箭头函数中,通过nodes[i]获取当前元素,替代原有的this绑定。 - 空值处理:对
attr('y')和attr('dy')的返回值设置默认值,避免parseFloat处理空值报错。 - 节点校验:调用
getComputedTextLength()前先检查tspan.node()是否存在,符合TypeScript严格类型要求。
内容的提问来源于stack exchange,提问作者user20377051
相关产品推荐
相关产品推荐

