我正在用树和径向可视化创建树图。
我正在使用
var radialDiagonal = d3.svg.diagonal.radial()
.projection(function (d) {
return [d.y, d.x / 180 * Math.PI];
});
和
var diagonal = d3.svg.diagonal()
.projection(function (d) {
return [d.x, d.y];
});
渲染节点之间的链接,但我希望它是直的而不是四舍五入的。
我想念什么吗?
这里是我的代码的一个小提琴手。
d
的<path>
属性以在节点之间形成直线。但是d
恰好也是d3.js约定,用于命名回调函数的参数,这些回调函数传递给selection.attr()
或selection.style()
函数之类的函数。后面的d
自变量引用附加到选择的数据。对于树形布局,可以分别使用d.source.x
和d.source.y
访问一个节点的位置,并使用d.target.x and d.target.y
访问另一节点的位置。参见https://github.com/d3/d3-hierarchy/blob/master/README.md#node_links:node.links()返回此节点的链接数组,其中每个链接都是一个对象定义源和目标属性。每个链接的来源是父节点,目标是子节点。
在您的情况下,您只需定义一个函数即可为每个链接创建正确的d属性:
var straight = d => "M" + d.source.x + "," + d.source.y
+ "H" + d.target.x + "V" + d.target.y;
然后在您为链接绘制路径时调用该函数:
var link = svg.selectAll('.link') .data(links) .enter() .append('path') .attr('class', 'link') .style('stroke', '#8da0cb') .attr('d', straight);
这里是带有直线树的示例:function findColor(info) { if (info.data.color) return info.data.color; if (info.parent) return findColor(info.parent) return '#ccc'; } function transitionToRadialTree() { var nodes = radialTree.nodes(root), // recalculate layout links = radialTree.links(nodes); svg.transition().duration(duration) .attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')'); // set appropriate translation (origin in middle of svg) link.data(links) .transition() .duration(duration) .style('stroke', '#fc8d62') .attr('d', radialDiagonal); //get the new radial path node.data(nodes) .transition() .duration(duration) .attr('transform', function (d) { return 'rotate(' + (d.x - 90) + ')translate(' + d.y + ')'; }); node.select('circle') .transition() .duration(duration) .style('stroke', '#984ea3'); }; function transitionToTree(isStraight) { var nodes = tree.nodes(root), //recalculate layout links = tree.links(nodes); svg.transition().duration(duration) .attr("transform", 'translate(' + margin.left + ',' + margin.top + ')'); link.data(links) .transition() .duration(duration) .style('stroke', '#e78ac3') .attr('d', isStraight ? straight : diagonal); // get the new tree path node.data(nodes) .transition() .duration(duration) .attr('transform', function (d) { return 'translate(' + d.x + ',' + d.y + ')'; }); node.select('circle') .transition() .duration(duration) .style('stroke', '#377eb8'); }; const root = { name: 'Main', color: '#00FF00', children: [ { name: 'First level A', color: '#F5FF8A', children: [ { name: 'Second level A', children: [ { name: 'Third level A', children: [ { name: 'Fourth level A', }, { name: 'Fourth level B', } ] }, ] } ] }, { color: '#DA59FF', name: 'First level B', children: [ { name: 'Second level B', children: [ { name: 'Third level B', children: [ { name: 'Fourth level A', } ] }, ] } ] }, { name: 'First level C', color: '#FA935A', children: [ { name: 'Second level C', children: [ { name: 'Third level C', children: [ { name: 'Fourth level A', }, { name: 'Fourth level B', } ] }, ] } ] }, ] }; // set the dimensions and margins of the diagram var margin = { top: 40, right: 0, bottom: 50, left: 0 }; var width = 500; var height = 500; var diameter = 500 - margin.top - margin.bottom; var duration = 300; var tree = d3.layout.tree() .size([height, width - 160]); var cluster = d3.layout.cluster() .size([height, width - 160]); var diagonal = d3.svg.diagonal() .projection(function (d) { return [d.x, d.y]; }); // var straight = d => "M" + d.target.x + "," + d.target.y // + "V" + d.source.y + "H" + d.source.x; var straight = d => "M" + d.source.x + "," + d.source.y + "H" + d.target.x + "V" + d.target.y; var radialTree = d3.layout.tree() .size([360, diameter / 2]) .separation(function (a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; }); var radialCluster = d3.layout.cluster() .size([360, diameter / 2]) .separation(function (a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; }); var radialDiagonal = d3.svg.diagonal.radial() .projection(function (d) { return [d.y, d.x / 180 * Math.PI]; }); var svg = d3.select('body').append('svg') .attr('width', width) .attr('height', height) .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); // var root = getData(), var nodes = cluster.nodes(root); var links = cluster.links(nodes); var link = svg.selectAll('.link') .data(links) .enter() .append('path') .attr('class', 'link') .style('stroke', '#8da0cb') .attr('d', diagonal); var node = svg.selectAll('.node') .data(nodes) .enter() .append('g') .attr('class', 'node') .attr('transform', function (d) { return 'translate(' + d.x + ',' + d.y + ')'; }) node.append('circle') .attr('r', 4.5) .style('stroke', '#e41a1c'); /* node.append('text') .attr('dx', function (d) { return d.children ? -8 : 8; }) .attr('dy', 3) .style('text-anchor', function (d) { return d.children ? 'end' : 'start'; }) .text(function (d) { return d.name; }); */ // renderTree();
/* set the CSS */ body { background-color: #181833; } .node circle { fill: #fff; /* stroke: steelblue; */ stroke-width: 3px; } .node text { font: 12px sans-serif; fill: #ccc; } /* .node--internal text { text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff; }*/ .link { fill: none; stroke: #ccc; stroke-width: 2px; opacity: 0.75; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script> <meta charset='utf-8'> <button onclick='transitionToRadialTree()'>Radial</button> <button onclick='transitionToTree(false)'>Tree</button> <button onclick='transitionToTree(true)'>straight Tree</button> <svg id='hostElement'></svg>
不过,效果不好的是另一个问题,即如何在不同的表示形式之间平稳过渡。原因是,如果标准转换可以“轻松地”弄清楚如何将初始路径的“结构”与目标路径匹配,则两条任意路径之间的转换就很平滑。您的两个示例都满足该条件,因为它们都基于三次贝塞尔曲线使用了相似的路径。为此,您可能正在研究类似https://github.com/pbeshai/d3-interpolate-path的东西(但我认为该库可与d3.v4一起使用)