如何在D3.js中调用函数为.attr()提供值以消除重复逻辑
D3.js 消除链接路径坐标计算的重复逻辑
问题说明
在D3.js中通过.attr()绘制链接路径时,原代码直接返回源节点与目标节点的x、y坐标。为实现缩短链接路径的效果,修改后的代码在四个.attr()回调中存在大量重复的坐标计算逻辑,尝试用.call()或自定义函数调用均无法正确传递返回值,需要找到消除重复的方法。
原代码
link.each(function (d){}) .attr('x1', function (d) { return d.source.x; }) .attr('y1', function (d) { return d.source.y; }) .attr('x2', function (d) { return d.target.x; }) .attr('y2', function (d) { return d.target.y; });
存在重复逻辑的修改后代码
link.attr("x1", function(d) { let diffX = d.target.x - d.source.x; let diffY = d.target.y - d.source.y; let pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY)); let offsetX = (diffX * 40) / pathLength; return d.source.x + offsetX; }).attr("y1", function(d) { let diffX = d.target.x - d.source.x; let diffY = d.target.y - d.source.y; let pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY)); let offsetY = (diffY * 40) / pathLength; return d.source.y + offsetY; }).attr("x2", function(d) { let diffX = d.target.x - d.source.x; let diffY = d.target.y - d.source.y; let pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY)); let offsetX = (diffX * 40) / pathLength; return d.target.x - offsetX; }).attr("y2", function(d) { let diffX = d.target.x - d.source.x; let diffY = d.target.y - d.source.y; let pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY)); let offsetY = (diffY * 40) / pathLength; return d.target.y - offsetY; })
尝试过的无效调用方式
.attr('x1', function (d) { return d.source.x; // 期望从.call()获取返回值 })
.attr('x1', function (d) { return this.myNewPathFunction(d); })
解决方案
方法1:提前计算偏移量复用结果
通过.each()一次性计算每个链接的偏移量并存储到数据对象中,后续.attr()直接取用,彻底消除重复计算:
link.each(function(d) { const diffX = d.target.x - d.source.x; const diffY = d.target.y - d.source.y; const pathLength = Math.sqrt(diffX * diffX + diffY * diffY); // 将偏移量存入数据对象 d.offsetX = (diffX * 40) / pathLength; d.offsetY = (diffY * 40) / pathLength; }) .attr('x1', d => d.source.x + d.offsetX) .attr('y1', d => d.source.y + d.offsetY) .attr('x2', d => d.target.x - d.offsetX) .attr('y2', d => d.target.y - d.offsetY);
方法2:封装自定义函数返回坐标对象
编写统一的计算函数,返回包含四个坐标值的对象,.attr()回调直接调用函数获取对应值,逻辑统一便于维护:
function getShortenedCoords(d) { const diffX = d.target.x - d.source.x; const diffY = d.target.y - d.source.y; const pathLength = Math.sqrt(diffX * diffX + diffY * diffY); const offsetX = (diffX * 40) / pathLength; const offsetY = (diffY * 40) / pathLength; return { x1: d.source.x + offsetX, y1: d.source.y + offsetY, x2: d.target.x - offsetX, y2: d.target.y - offsetY }; } // 调用函数获取对应坐标 link.attr('x1', d => getShortenedCoords(d).x1) .attr('y1', d => getShortenedCoords(d).y1) .attr('x2', d => getShortenedCoords(d).x2) .attr('y2', d => getShortenedCoords(d).y2);
注:此方法函数会被调用四次,性能略逊于方法1,但代码结构更清晰。
方法3:使用.attr()的对象参数形式
D3.js的.attr()支持传入包含属性键值对的对象,写法上更紧凑,但仍存在重复计算,适合快速整理代码:
link.attr({ x1: d => { const diffX = d.target.x - d.source.x; const diffY = d.target.y - d.source.y; const pathLength = Math.sqrt(diffX * diffX + diffY * diffY); return d.source.x + (diffX * 40)/pathLength; }, y1: d => { const diffX = d.target.x - d.source.x; const diffY = d.target.y - d.source.y; const pathLength = Math.sqrt(diffX * diffX + diffY * diffY); return d.source.y + (diffY * 40)/pathLength; }, x2: d => { const diffX = d.target.x - d.source.x; const diffY = d.target.y - d.source.y; const pathLength = Math.sqrt(diffX * diffX + diffY * diffY); return d.target.x - (diffX * 40)/pathLength; }, y2: d => { const diffX = d.target.x - d.source.x; const diffY = d.target.y - d.source.y; const pathLength = Math.sqrt(diffX * diffX + diffY * diffY); return d.target.y - (diffY * 40)/pathLength; } });
内容的提问来源于stack exchange,提问作者Tom Rudge
相关产品推荐
相关产品推荐

