如何在D3中对g元素进行分组? [英] How to group g elements in D3?
问题描述
例如,我有一个包含rect和text的g元素:
var cells = innercanvas
.selectAll(".newcell")
.data(treemap)
.enter()
.append("g")
.attr("class", "newcell");
cells
.append("rect")
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
})
.attr("id", "rectangle")
.attr("width", function (d) {
return d.dx;
})
.attr("height", function (d) {
return d.dy;
})
.style("fill", function (d) {
return d.children ? cfg.color(d.name) : 'none';
})
.attr("stroke", "#000000")
.attr('pointer-events', 'all')
cells
.append("text")
.attr("x", function (d) {
return d.x + d.dx / 2;
})
.attr("y", function (d) {
return d.y + d.dy / 2;
})
.attr('dy', '.95em')
.attr("text-anchor", "middle")
.text(function (d) { return d.children ? d.name : null })
我想添加一个父g元素,该元素根据其通用名称对cells
进行分组.例如,
var parent = d3.selectAll("cells").attr("groupBy",function(d){ return d.children? d.name : null;})
这是为了让我可以将父名称显示为这些常见单元格的标题.
节点不必进行物理分组即可将它们分组以应用标签或突出显示它们.目前,您使用的代码为每个父节点生成一个g
元素,其中一个rect
提供颜色填充,一个空白的text
元素.然后,将子节点覆盖在此父节点上,并在其边缘上放置黑色的stroke
并在其国家名称下放置一个text
节点.
如果您希望能够突出显示具有相同父元素的所有元素并在该元素上放置标题,则可以采用以下两种方法:
-
更改现有元素以显示标题
-
创建一个尺寸与父节点相同的叠加层以显示标题
更改现有元素
为了能够做到这一点,我们首先需要能够识别父节点及其子节点.一种实现方法是根据节点在层次结构中的位置及其数据内容提供节点类.这是执行此操作的一种可能方法:
var cells = innercanvas
.selectAll(".newcell")
.data(treemap)
.enter()
.append("g")
.attr("class", function (d,i) {
return 'newcell _' + i // i provides a unique identifier for each node
+ ' cell-level-' + d.depth // cell-level-0 for root, cell-level-1, cell-level-2, etc
+ ( d.name ? ' ' + safe_name(d.name) : '' ) // if d.name exists, use the 'safe' version
+ ( ! d.children
? ' leaf' // d has no children => it's a leaf node
: (d.depth === 0
? ' root' // d.depth = 0 => it's the root node
: ' internal ')); // has children, depth > 0 => internal node
})
// strips non-alphanumeric characters out of `name` strings, replaces with _
function safe_name (txt) {
return txt.replace(/\W/g, '_');
}
SVG g
元素现在看起来像这样:
现在,我们可以通过使用
轻松访问任何国家/地区名称c.name
的父节点
d3.select('.internal.' + safe_name(c.name))
或(例如)使用该国家/地区的叶子节点的text
元素
d3.selectAll('.leaf.' + safe_name(c.name) + ' text')
然后我们可以使用CSS来显示或隐藏文本元素,rect
笔画等;例如
var bool = false;
var toggle = d3.select('#toggle')
.on('click', toggleSvg);
function toggleSvg() {
bool = !bool;
d3.selectAll('.Cyprus')
.classed('highlightAll', bool);
}
.newcell text {
font-family: Arial, sans-serif;
font-size: 10px;
}
.newcell.leaf rect {
stroke: #000;
stroke-width: 0.5px;
}
.oversize {
display: none;
}
.internal text {
opacity: 0
}
.internal.highlightAll text {
opacity: 1
}
.highlightAll.leaf rect {
opacity: 0.1
}
.highlightAll.leaf text {
opacity: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<p><a id="toggle" href='#'>Toggle Cyprus highlighting</a></p>
<svg width="186" height="77">
<g transform="translate(-30.1234, -258.33)">
<g class="newcell _2331 cell-level-1 Cyprus internal ">
<rect x="30.123480134121516" y="258.33086334171067" width="185.81893466750355" height="76.6094615363257" style="fill: rgb(100, 200, 75);"></rect>
<title>Cyprus</title>
<text x="123.0329474678733" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
<g class="newcell _2332 cell-level-2 Cyprus leaf">
<rect x="40.12348013412152" y="268.33086334171067" width="31.51365155392795" height="23.97574841366901" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="55.88030591108549" y="280.3187375485452" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
</g>
<g class="newcell _2333 cell-level-2 Cyprus leaf">
<rect x="40.12348013412152" y="292.30661175537968" width="31.51365155392795" height="32.633713122656687" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="55.88030591108549" y="308.62346831670802" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
</g>
<g class="newcell _2334 cell-level-2 Cyprus leaf">
<rect x="71.637131688049465" y="268.33086334171067" width="55.48181226963859" height="56.60946153632569" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="99.37803782286876" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
<g class="newcell _2335 cell-level-2 Cyprus leaf">
<rect x="127.11894395768805" y="268.33086334171067" width="78.823470843937" height="56.60946153632569" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="166.53067937965655" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
</g>
</svg>
创建叠加层
这将在树图的顶部复制一个现有的父节点.
使用rect
和text
节点作为子节点,将新的g
元素添加到树图中:
var highlightG = canvas.append('g')
.attr("transform", "translate(" + cfg.margin.left + "," + cfg.margin.top + ")")
.append('g')
.classed('highlighter', true)
.attr('opacity',0);
highlightG.append('rect');
highlightG.append('text');
我们可以使用来自现有父节点的数据来设置rect
元素的适当位置和大小以及text
元素的内容.
var d = d3.select('.internal.Cyprus').datum();
highlightG.select('rect')
.attr("x", d.x)
.attr("y", d.y)
.attr("width", d.dx)
.attr("height", d.dy)
highlightG
.select("text")
.attr("x", d.x + d.dx / 2 )
.attr("y", d.y + d.dy / 2 )
.attr('dy', '.35em')
.attr("text-anchor", "middle")
.text(function () {
return d.name;
})
d3.select('.internal.Cyprus').property('__data__', {
depth: 1,
dx: 185.81893466750355,
dy: 76.6094615363257,
name: "Cyprus",
value: 446770,
x: 30.123480134121516,
y: 258.33086334171067
});
var opacity = 1;
var highlightG = d3.select('svg')
.append('g')
.attr("transform", "translate(-30.1234, -258.33)")
.append('g')
.classed('highlighter', true)
.attr('opacity', 0);
highlightG.append('rect');
highlightG.append('text');
d3.select('#toggle')
.on('click', function() {
var d = d3.select('.internal.Cyprus').datum();
highlightG.attr('opacity', opacity);
highlightG.select('rect')
.attr("x", d.x)
.attr("y", d.y)
.attr("width", d.dx)
.attr("height", d.dy)
highlightG
.select("text")
.attr("x", d.x + d.dx / 2)
.attr("y", d.y + d.dy / 2)
.attr('dy', '.35em')
.attr("text-anchor", "middle")
.text(function() {
return d.name;
})
opacity = opacity ? 0 : 1;
});
.newcell text {
font-family: Arial, sans-serif;
font-size: 10px;
}
.newcell.leaf rect {
stroke: #000;
stroke-width: 0.5px;
}
.oversize {
display: none;
}
.internal text {
opacity: 0
}
.highlighter rect {
fill: #000;
fill-opacity: 0.7;
stroke: deepskyblue;
stroke-width: 5px;
}
.highlighter text {
fill: deepskyblue;
opacity: 1
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<p><a id="toggle" href='#'>Activate Cyprus overlay</a></p>
<svg width="186" height="77">
<g transform="translate(-30.1234, -258.33)">
<g class="newcell _2331 cell-level-1 Cyprus internal ">
<rect x="30.123480134121516" y="258.33086334171067" width="185.81893466750355" height="76.6094615363257" style="fill: rgb(100, 200, 75);"></rect>
<title>Cyprus</title>
<text x="123.0329474678733" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
<g class="newcell _2332 cell-level-2 Cyprus leaf">
<rect x="40.12348013412152" y="268.33086334171067" width="31.51365155392795" height="23.97574841366901" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="55.88030591108549" y="280.3187375485452" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
</g>
<g class="newcell _2333 cell-level-2 Cyprus leaf">
<rect x="40.12348013412152" y="292.30661175537968" width="31.51365155392795" height="32.633713122656687" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="55.88030591108549" y="308.62346831670802" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
</g>
<g class="newcell _2334 cell-level-2 Cyprus leaf">
<rect x="71.637131688049465" y="268.33086334171067" width="55.48181226963859" height="56.60946153632569" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="99.37803782286876" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
<g class="newcell _2335 cell-level-2 Cyprus leaf">
<rect x="127.11894395768805" y="268.33086334171067" width="78.823470843937" height="56.60946153632569" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="166.53067937965655" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
</g>
</svg>
我已经创建了.block ,其中包含这些选项的更全面版本. >
您应该能够找到可以用于您的目的的这些组合.
Say for example I have a g element that contains a rect and text :
var cells = innercanvas
.selectAll(".newcell")
.data(treemap)
.enter()
.append("g")
.attr("class", "newcell");
cells
.append("rect")
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
})
.attr("id", "rectangle")
.attr("width", function (d) {
return d.dx;
})
.attr("height", function (d) {
return d.dy;
})
.style("fill", function (d) {
return d.children ? cfg.color(d.name) : 'none';
})
.attr("stroke", "#000000")
.attr('pointer-events', 'all')
cells
.append("text")
.attr("x", function (d) {
return d.x + d.dx / 2;
})
.attr("y", function (d) {
return d.y + d.dy / 2;
})
.attr('dy', '.95em')
.attr("text-anchor", "middle")
.text(function (d) { return d.children ? d.name : null })
I want to add a parent g element that groups cells
based on their common name. For example ,
var parent = d3.selectAll("cells").attr("groupBy",function(d){ return d.children? d.name : null;})
This is so that I can display the parent name as a header for these common cells.
Nodes don't have to be grouped together physically to be able to group them to apply a label or highlight them. At present, the code you are using produces a g
element for each parent node, with a rect
that provides the colour fill and a blank text
element. The child nodes are then overlaid on this parent node with a black stroke
along the edges and a text
node with the country name.
If you want to be able to highlight all the elements that have the same parent and put a header on the element, you can do it a couple of ways:
alter existing elements to show a header
create an overlay with the same dimensions as the parent node to display a header
Altering existing elements
To be able to do this, we first need to be able to identify parent nodes and their children. One way to do this is by giving nodes classes according to their position in the hierarchy and their data content. Here's one possible way to do this:
var cells = innercanvas
.selectAll(".newcell")
.data(treemap)
.enter()
.append("g")
.attr("class", function (d,i) {
return 'newcell _' + i // i provides a unique identifier for each node
+ ' cell-level-' + d.depth // cell-level-0 for root, cell-level-1, cell-level-2, etc
+ ( d.name ? ' ' + safe_name(d.name) : '' ) // if d.name exists, use the 'safe' version
+ ( ! d.children
? ' leaf' // d has no children => it's a leaf node
: (d.depth === 0
? ' root' // d.depth = 0 => it's the root node
: ' internal ')); // has children, depth > 0 => internal node
})
// strips non-alphanumeric characters out of `name` strings, replaces with _
function safe_name (txt) {
return txt.replace(/\W/g, '_');
}
The SVG g
elements now look like this:
Now we can easily access the parent node for any country name c.name
by using
d3.select('.internal.' + safe_name(c.name))
or (e.g.) the text
elements of the leaf nodes for the country using
d3.selectAll('.leaf.' + safe_name(c.name) + ' text')
We can then use CSS to show or hide text elements, rect
strokes, etc.; e.g.
var bool = false;
var toggle = d3.select('#toggle')
.on('click', toggleSvg);
function toggleSvg() {
bool = !bool;
d3.selectAll('.Cyprus')
.classed('highlightAll', bool);
}
.newcell text {
font-family: Arial, sans-serif;
font-size: 10px;
}
.newcell.leaf rect {
stroke: #000;
stroke-width: 0.5px;
}
.oversize {
display: none;
}
.internal text {
opacity: 0
}
.internal.highlightAll text {
opacity: 1
}
.highlightAll.leaf rect {
opacity: 0.1
}
.highlightAll.leaf text {
opacity: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<p><a id="toggle" href='#'>Toggle Cyprus highlighting</a></p>
<svg width="186" height="77">
<g transform="translate(-30.1234, -258.33)">
<g class="newcell _2331 cell-level-1 Cyprus internal ">
<rect x="30.123480134121516" y="258.33086334171067" width="185.81893466750355" height="76.6094615363257" style="fill: rgb(100, 200, 75);"></rect>
<title>Cyprus</title>
<text x="123.0329474678733" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
<g class="newcell _2332 cell-level-2 Cyprus leaf">
<rect x="40.12348013412152" y="268.33086334171067" width="31.51365155392795" height="23.97574841366901" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="55.88030591108549" y="280.3187375485452" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
</g>
<g class="newcell _2333 cell-level-2 Cyprus leaf">
<rect x="40.12348013412152" y="292.30661175537968" width="31.51365155392795" height="32.633713122656687" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="55.88030591108549" y="308.62346831670802" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
</g>
<g class="newcell _2334 cell-level-2 Cyprus leaf">
<rect x="71.637131688049465" y="268.33086334171067" width="55.48181226963859" height="56.60946153632569" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="99.37803782286876" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
<g class="newcell _2335 cell-level-2 Cyprus leaf">
<rect x="127.11894395768805" y="268.33086334171067" width="78.823470843937" height="56.60946153632569" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="166.53067937965655" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
</g>
</svg>
Creating an overlay
This will duplicate an existing parent node on top of the treemap.
Add a new g
element to the treemap with rect
and text
nodes as children:
var highlightG = canvas.append('g')
.attr("transform", "translate(" + cfg.margin.left + "," + cfg.margin.top + ")")
.append('g')
.classed('highlighter', true)
.attr('opacity',0);
highlightG.append('rect');
highlightG.append('text');
We can use the data from an existing parent node to set the appropriate position and size for the rect
element and the content for the text
element.
var d = d3.select('.internal.Cyprus').datum();
highlightG.select('rect')
.attr("x", d.x)
.attr("y", d.y)
.attr("width", d.dx)
.attr("height", d.dy)
highlightG
.select("text")
.attr("x", d.x + d.dx / 2 )
.attr("y", d.y + d.dy / 2 )
.attr('dy', '.35em')
.attr("text-anchor", "middle")
.text(function () {
return d.name;
})
d3.select('.internal.Cyprus').property('__data__', {
depth: 1,
dx: 185.81893466750355,
dy: 76.6094615363257,
name: "Cyprus",
value: 446770,
x: 30.123480134121516,
y: 258.33086334171067
});
var opacity = 1;
var highlightG = d3.select('svg')
.append('g')
.attr("transform", "translate(-30.1234, -258.33)")
.append('g')
.classed('highlighter', true)
.attr('opacity', 0);
highlightG.append('rect');
highlightG.append('text');
d3.select('#toggle')
.on('click', function() {
var d = d3.select('.internal.Cyprus').datum();
highlightG.attr('opacity', opacity);
highlightG.select('rect')
.attr("x", d.x)
.attr("y", d.y)
.attr("width", d.dx)
.attr("height", d.dy)
highlightG
.select("text")
.attr("x", d.x + d.dx / 2)
.attr("y", d.y + d.dy / 2)
.attr('dy', '.35em')
.attr("text-anchor", "middle")
.text(function() {
return d.name;
})
opacity = opacity ? 0 : 1;
});
.newcell text {
font-family: Arial, sans-serif;
font-size: 10px;
}
.newcell.leaf rect {
stroke: #000;
stroke-width: 0.5px;
}
.oversize {
display: none;
}
.internal text {
opacity: 0
}
.highlighter rect {
fill: #000;
fill-opacity: 0.7;
stroke: deepskyblue;
stroke-width: 5px;
}
.highlighter text {
fill: deepskyblue;
opacity: 1
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<p><a id="toggle" href='#'>Activate Cyprus overlay</a></p>
<svg width="186" height="77">
<g transform="translate(-30.1234, -258.33)">
<g class="newcell _2331 cell-level-1 Cyprus internal ">
<rect x="30.123480134121516" y="258.33086334171067" width="185.81893466750355" height="76.6094615363257" style="fill: rgb(100, 200, 75);"></rect>
<title>Cyprus</title>
<text x="123.0329474678733" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
<g class="newcell _2332 cell-level-2 Cyprus leaf">
<rect x="40.12348013412152" y="268.33086334171067" width="31.51365155392795" height="23.97574841366901" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="55.88030591108549" y="280.3187375485452" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
</g>
<g class="newcell _2333 cell-level-2 Cyprus leaf">
<rect x="40.12348013412152" y="292.30661175537968" width="31.51365155392795" height="32.633713122656687" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="55.88030591108549" y="308.62346831670802" dy=".35em" text-anchor="middle" class="oversize">Cyprus</text>
</g>
<g class="newcell _2334 cell-level-2 Cyprus leaf">
<rect x="71.637131688049465" y="268.33086334171067" width="55.48181226963859" height="56.60946153632569" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="99.37803782286876" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
<g class="newcell _2335 cell-level-2 Cyprus leaf">
<rect x="127.11894395768805" y="268.33086334171067" width="78.823470843937" height="56.60946153632569" style="fill: none;"></rect>
<title>Cyprus</title>
<text x="166.53067937965655" y="296.63559410987354" dy=".35em" text-anchor="middle">Cyprus</text>
</g>
</g>
</svg>
I've created a .block with more comprehensive versions of these options.
You should be able to find some combination of these that you can use for your purposes.
这篇关于如何在D3中对g元素进行分组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!