javascript - d3 直链树图
问题描述
我正在创建一个具有树形和径向可视化的树形图。
我在用着
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];
});
渲染节点之间的链接,但我希望它是直的而不是圆形的。
我错过了什么吗?
这是我的代码的一个jsfiddle。
解决方案
要使用直线渲染链接,您只需设置 的d
属性<path>
以在节点之间形成直线。
d
然而,巧合的是,d3.js 约定命名回调函数的参数,这些回调函数传递给函数 likeselection.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:
节点链接()
返回此节点的链接数组,其中每个链接都是定义源和目标属性的对象。每个链接的源是父节点,目标是子节点。
在您的情况下,您只需定义一个函数来为每个链接创建正确的 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)
推荐阅读
- shell - 如何在没有pid的unix机器中无限期地关闭在后台运行的进程?
- excel - 只选择需要的内容
- python-3.x - 有没有办法处理数据集中两个变量之间的多重共线性?
- java - 来自 PEMKeyPair 的 KeyPair 与 Android 上的 BouncyCastle
- c# - 创建不检查管理员角色声明的策略
- reactjs - useState setter 不立即更新数组
- android - 使用可见按钮删除 RecyclerView 行未按预期工作
- python - 使用 django-oauth-toolkit 进行身份验证和授权
- sql - Sql 查询返回逗号分隔的列名
- assembly - GNU 汇编指令 .code16 的目的是什么?