D3.js追加元素时如何获取DOM节点尺寸实现文本居中
问题描述
在使用d3.js进行开发时,需要创建一组节点,为每个节点追加SVG circle与text元素,之后调整text元素的位置(设置其dx和dy属性),最终实现text元素在SVG内部居中显示。
当前采用的实现方案是先创建并追加所有SVG元素,之后二次遍历所有节点,测量每个text元素的尺寸后完成对应位置变换,实现代码如下:
/////////////////////////////////////// // Create node elements from data... // /////////////////////////////////////// const canvas = document.getElementById("canvas"); const data_nodes = state.data.nodes; const elements = canvas.svg .append("g") .attr("class", "nodes") ////// .selectAll(".node") // 创建空选择集 .data(data_nodes) .enter() ////// .append("svg") // 添加嵌套SVG .attr("class", "node"); //////////////////////////////// // ...追加circle与text元素... // //////////////////////////////// elements.append("circle").attr("r", (data) => data.size / 2); elements.append("text").text((data) => data.text.content) //////////////////// // ...文字居中处理 // //////////////////// for (let node of elements.nodes()) { const textEl = node.querySelector("text"); const width = textEl.getBoundingClientRect().width; const height = textEl.getBoundingClientRect().height; ////// textEl.setAttribute("dx", (width / 2) * -1); textEl.setAttribute("dy", height / 2); }
上述方案可以正常运行,但希望找到更优实现方式:在追加text元素的过程中或追加完成后,直接引用每个已追加text元素对应的DOM节点,当场动态设置dx/dy属性,无需单独进行二次遍历。
实现方案
不需要单独编写for循环做二次遍历,D3的选择集API原生支持在设置属性时直接获取当前操作的DOM节点,可以直接在追加text的链式调用中完成居中偏移设置。
方法1:保留原有测量逻辑,消除二次遍历
D3中.attr()方法传入函数作为参数时,只要不使用箭头函数,函数内的this就指向当前正在处理的DOM节点,和你在for循环里拿到的text元素完全一致。直接在设置dx、dy的回调中测量尺寸即可,代码如下:
// circle元素追加逻辑保持不变 elements.append("circle").attr("r", (data) => data.size / 2); // 追加text后直接链式设置偏移属性,无需额外遍历 elements.append("text") .text((data) => data.text.content) .attr("dx", function() { return -this.getBoundingClientRect().width / 2; }) .attr("dy", function() { return this.getBoundingClientRect().height / 2; });
这种写法和原有二次遍历的执行时机、渲染效果、性能表现完全一致,只是把遍历逻辑交给D3内部处理,代码更连贯简洁。注意不要在属性回调里用箭头函数,否则this会指向外层作用域,拿不到当前DOM节点。
方法2:使用SVG原生对齐属性,无需尺寸测量
更推荐的方案是直接用SVG原生的文字对齐属性实现居中,完全不需要手动测量宽高计算偏移,哪怕后续文字内容动态修改,也会自动保持居中:
elements.append("text") .text((data) => data.text.content) .attr("x", 0) .attr("y", 0) .attr("text-anchor", "middle") // 实现水平居中 .attr("dominant-baseline", "middle"); // 实现垂直居中
这种写法没有DOM尺寸测量带来的重排开销,代码更简洁,维护成本更低。
内容的提问来源于stack exchange,提问作者JoSch
相关产品推荐
相关产品推荐

