d3.js-具有树形布局,如何在D3中更改X轴以使用时间刻度? [英] d3.js - Having a tree layout, how to change the X-axis to use a time scale in D3?
问题描述
我有这个树形布局,需要它在X轴上使用时间刻度来将节点固定为日期.另外,我需要将根节点(它在JSON数据中具有is_root
属性)保留在时间范围之外.
此处是具有树布局功能的Codepen,我也将代码粘贴在这里:
I have this tree layout and need it to use a time scale in the X-axis to fix the nodes to dates. Also, I would need to keep the root node (it has a is_root
property in the JSON data) outside of the time scale.
Here is the Codepen with the tree layout working and I also paste the code here:
var json = {
"name": "Meet Treat",
"is_root": true,
"children": [
{
"name": "Meeting 1",
"date": "Sun Jan 01 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 2",
"date": "Tue Jan 10 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 5",
"date": "Fri Feb 10 2012 00:00:00 GMT-0300 (ART)"
}
]
},
{
"name": "Meeting 4",
"date": "Wed Feb 01 2012 00:00:00 GMT-0300 (ART)"
}
]
},
{
"name": "Meeting 3",
"date": "Fri Jan 20 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 7",
"date": "Thu Mar 01 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 8",
"date": "Sat Mar 10 2012 00:00:00 GMT-0300 (ART)"
}
]
}
]
},
{
"name": "Meeting 6",
"date": "Mon Feb 20 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 9",
"date": "Tue Mar 20 2012 00:00:00 GMT-0300 (ART)"
},
{
"name": "Meeting 10",
"date": "Sun Apr 01 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 13",
"date": "Tue May 01 2012 00:00:00 GMT-0300 (ART)"
}
]
}
]
},
{
"name": "Meeting 11",
"date": "Tue Apr 10 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 14",
"date": "Thu May 10 2012 00:00:00 GMT-0300 (ART)"
},
{
"name": "Meeting 16",
"date": "Fri Jun 01 2012 00:00:00 GMT-0300 (ART)"
}
]
},
{
"name": "Meeting 12",
"date": "Fri Apr 20 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 15",
"date": "Sun May 20 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 17",
"date": "Sun Jun 10 2012 00:00:00 GMT-0300 (ART)"
}
]
},
{
"name": "Meeting 18",
"date": "Wed Jun 20 2012 00:00:00 GMT-0300 (ART)"
}
]
},
{
"name": "Meeting 19",
"date": "Sun Jul 01 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 21",
"date": "Fri Jul 20 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 22",
"date": "Wed Aug 01 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 23",
"date": "Fri Aug 10 2012 00:00:00 GMT-0300 (ART)"
},
{
"name": "Meeting 24",
"date": "Mon Aug 20 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 25",
"date": "Sat Sep 01 2012 00:00:00 GMT-0300 (ART)"
}
]
}
]
}
]
},
{
"name": "Meeting 27",
"date": "Thu Sep 20 2012 00:00:00 GMT-0300 (ART)"
}
]
},
{
"name": "Meeting 20",
"date": "Tue Jul 10 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 26",
"date": "Mon Sep 10 2012 00:00:00 GMT-0300 (ART)",
"children": [
{
"name": "Meeting 28",
"date": "Mon Oct 01 2012 00:00:00 GMT-0300 (ART)"
}
]
},
{
"name": "Meeting 29",
"date": "Wed Oct 10 2012 00:00:00 GMT-0300 (ART)"
}
]
}
]
};
var m = [20, 120, 20, 120],
w = 1280 - m[1] - m[3],
h = 1000 - m[0] - m[2],
i = 0,
root;
var tree = d3.layout.tree()
.size([h, w]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var vis = d3.select("#graph").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
root = json;
root.x0 = h / 2;
root.y0 = 0;
update(root);
function update(source) {
var duration = d3.event && d3.event.altKey ? 5000 : 500;
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse();
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Update the nodes…
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
;//.on("click", function(d) { toggle(d); update(d); });
nodeEnter.append('image')
.attr('xlink:href', 'http://www.uni-regensburg.de/Fakultaeten/phil_Fak_II/Psychologie/Psy_II/beautycheck/english/durchschnittsgesichter/m(01-32)_gr.jpg')
.attr('width', 40)
.attr('height', 40)
.attr('x', -40)
.attr('y', -20)
.attr('clip-path', 'url(#clip1)');
var clip1 = nodeEnter.append('clipPath')
.attr('id', 'clip1')
.attr('x', 0)
.attr('y', 0);
clip1.append('circle')
.attr('r', 20)
.attr('cx', -20);
nodeEnter.append('image')
.attr('xlink:href', 'http://0.tqn.com/d/hairremoval/1/0/e/-/-/-/eyebrow-classic.jpg')
.attr('width', 40)
.attr('height', 40)
.attr('x', 0 )
.attr('y', -20)
.attr('clip-path', 'url(#clip2)');
var clip2 = nodeEnter.append('clipPath')
.attr('id', 'clip2')
.attr('x', 0)
.attr('y', 0);
clip2.append('circle')
.attr('r', 20)
.attr('cx', 20);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
因为我正在玩一个示例,所以代码很混乱. 我试过的是添加一个像这样的时间刻度:
The code is messy because I'm playing with a example. What I've tried is adding a time scale like this:
var timeScale = d3.time.scale().domain([new Date(2012, 0, 1), new Date(2012, 10, 1)]).range([100, w]);
并替换此行(#231):
and replacing this line (#231):
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
与此:
.attr("transform", function(d) { return "translate(" + timeScale(new Date(d.date)) + "," + d.x + ")"; });
或者这个:
.attr("transform", function(d) { return "translate(" + d.y + "," + timeScale(new Date(d.date)) + ")"; });
但是没有运气,他们失去了连贯的位置.另外,我不知道为什么在我所基于的示例中,它使用Y值然后使用X值来应用CSS过渡translate
,其中translate
的CSS规范规定值应首先为X,然后为Y .tree.nodes()
我缺少什么吗?
But no luck, they loose coherent position. Also, I don't know why in the example I'm basing on, it applies the CSS transition translate
using Y and then X values, where the CSS specification for translate
states that values should be X first, then Y. Is there something that I'm missing from tree.nodes()
?
推荐答案
我想出了方法.首先,我们需要创建一个时间刻度:
I figured out how to do it. First, we need to create a time scale:
var timeScale = d3.time.scale().domain([new Date(2012, 0, 1), new Date(2012, 10, 1)]).range([100, w]);
请注意,我已经设置了固定的日期范围.您可以构建逻辑来使用数据来获取数据.
Notice that I've put a fixed date range. You can build a logic to use your data to get that.
然后,更新节点的过渡...
Then, update the transition for the nodes...
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
var y = timeScale(new Date(d.date));
if (d3.map(d).has('is_root')) {
y = d.y;
}
return "translate(" + y + "," + d.x + ")";
});
...以及链接的过渡:
...and the transition for the links:
link.transition()
.duration(duration)
.attr("d", function (d) {
if (d3.map(d.source).has('is_root') && d.source.is_root) {
return diagonal({ source: { x: d.source.x, y: d.source.y }, target: { x: d.target.x, y: timeScale(new Date(d.target.date)) } });
}
return diagonal({ source: { x: d.source.x, y: timeScale(new Date(d.source.date)) }, target: { x: d.target.x, y: timeScale(new Date(d.target.date)) } });
});
最后,如果要在图形上显示日期刻度,则可以添加以下代码,否则不需要.
Finally, if you want to show the date ticks on the graph, you can add the following code, otherwise, not needed.
var dates = [];
for (var i = 0; i < 12; i++) {
for (var j = 1; j <= 20; j += 9) {
(j == 19) && (j = 20);
dates.push(new Date(2012, i, j));
}
}
var axisGroup = vis.append('svg:g');
axisGroup.selectAll('.xTicks')
.data(dates)
.enter()
.append('svg:line')
.attr('x1', timeScale)
.attr('y1', -5)
.attr('x2', timeScale)
.attr('y2', h + 5)
.attr('stroke', 'lightgray')
.attr('stroke-width', 1)
.attr('class', 'xTicks');
axisGroup.selectAll('text.xAxisBottom')
.data(dates)
.enter()
.append('svg:text')
.text(function (datum) { return datum.getDate() + '/' + (datum.getMonth() + 1) + '/' + datum.getFullYear(); })
.attr('x', timeScale)
.attr('y', h + 20)
.attr('text-anchor', 'middle')
.attr('class', 'xAxisBottom');
它会生成将被打勾的日期,并添加相应的行和标签.
It generates the dates that will be ticked and add the corresponding lines and labels.
至于...
此外,我也不知道为什么在我所基于的示例中,它将CSS转换转换应用为先使用Y值再使用X值,其中转换的CSS规范规定值应先为X,然后为Y. tree.nodes()是否缺少我的东西?
Also, I don't know why in the example I'm basing on, it applies the CSS transition translate using Y and then X values, where the CSS specification for translate states that values should be X first, then Y. Is there something that I'm missing from tree.nodes()?
以这种方式为链接创建对角线:
Creating the diagonal for the links this way:
var diagonal = d3.svg.diagonal().projection(function(d) { return [d.y, d.x]; });
反转X& Y轴使用投影方法,并赋予其返回反转轴的功能.这就是为什么Y变成X,反之亦然.
inverts the X & Y axis using the projection method and giving it a function that returns the axis inverted. That's why Y becomes X and vice versa.
这篇关于d3.js-具有树形布局,如何在D3中更改X轴以使用时间刻度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!