使用 renderlet 在 dc.js 图表的背景中呈现
Rendering in the background of a dc.js chart with renderlet
我使用dc.js来显示多种分类算法的结果。更具体地说,我想展示一个精确召回图表(每个点对应一个分类系统的结果)。
我已经为此使用了 dc.js
散点图,效果很好。
另外我想要一个d3 contour in the background of the chart which shows the F-measure。
这已经实施。唯一的问题是等高线部分在前景而不是图表的背景中。
请查看 jsfiddle for a full example.
有两个问题 仍然对我开放,因为我不是 dc.js
或 d3
专家:
- 有没有办法把等高线放在背景或散点图的符号(周期)放在前景(我已经在这个Whosebug question的帮助下尝试过但没有成功)
- 我使用
'g.brush'
选择器来获取内部图表的面积。只要刷亮打开,这就可以正常工作。选择器是一个好方法还是有更好的选择(如果关闭刷牙也可能有效)。
在我的示例中,我将轮廓部分放在左上角以查看它是否有效,但我还提供了代码(当前未注释)以将轮廓的宽度和高度增加到正确的大小。
chart
.on('renderlet', function (chart) {
var innerChart = chart.select('g.brush');
var width = 300, height=300;
//getting the correct width, height
//var innerChartBoundingRect = innerChart.node().getBoundingClientRect();
//var width = innerChartBoundingRect.width, height=innerChartBoundingRect.height;
[contours, color] = generateFmeasureContours(width,height, 1);
innerChart
.selectAll("path")
.data(contours)
.enter()
.append("path")
.attr("d", d3.geoPath())
.attr("fill", d => color(d.value));
var symbols = chart.chartBodyG().selectAll('path.symbol');
symbols.moveToFront();
});
将某些东西放在背景中是一种通用的 SVG 技能。
SVG 按照声明的顺序从后到前呈现所有内容,因此关键是在语法上将您的内容放在图表中其他所有内容之前。
我建议将它封装在一个 svg <g>
元素中,为了获得正确的顺序,您可以使用 d3-selection 的 insert 方法和 :first-child
CSS 选择器而不是 append
:
.on('pretransition', function (chart) {
// add contour layer to back (beginning of svg) only if it doesn't exist
var contourLayer = chart.g().selectAll('g.contour-layer').data([0]);
contourLayer = contourLayer
.enter().insert('g', ':first-child')
.attr('class', 'contour-layer')
.attr('transform', 'translate(' + [chart.margins().left,chart.margins().top].join(',') + ')')
.merge(contourLayer);
关于此实现的更多要点:
- 使用 dc 的
pretransition
事件,因为它在渲染和重绘后立即发生(而 renderlet
等待转换完成)
- 模式
.data([0]).enter()
仅在元素不存在时才添加该元素。 (它绑定一个 1 元素数组;该元素是什么并不重要。)这很重要,因为事件处理程序将在每次重绘时被调用,我们不想继续添加层。
- 我们为我们的图层指定了不同的 class 名称
contour-layer
以便我们可以识别它,因此一次添加模式有效
contourLayer = contourLayer.enter().insert(...)...merge(contourLayer)
是另一种常见的 D3 模式,用于插入内容并将其合并回选择中,以便我们稍后将插入和修改视为相同。使用较新的 selection.join 方法可能会更简单,但我还没有尝试过。
- (我认为在排序方面可能也有一些改进,可能比
insert
更容易,但同样,我将使用我知道有效的方法。)
- 最后,我们从 margin mixin
中获取左上偏移量
接下来,我们可以使用检索实际图表主体的宽度和高度
(叹息,undocumented)来自 dc.marginMixin
的方法:
var width = chart.effectiveWidth(), height = chart.effectiveHeight();
而且我们不需要将点移到前面或任何其他地方;你的代码的其余部分和以前一样,除了我们使用这个新层而不是绘制到刷层:
contourLayer
.selectAll("path")
.data(contours)
.enter()
.append("path")
.attr("d", d3.geoPath())
.attr("fill", d => color(d.value));
同样,如果您想合作将轮廓示例放入 dc.js,那就太棒了!
我使用dc.js来显示多种分类算法的结果。更具体地说,我想展示一个精确召回图表(每个点对应一个分类系统的结果)。
我已经为此使用了 dc.js
散点图,效果很好。
另外我想要一个d3 contour in the background of the chart which shows the F-measure。
这已经实施。唯一的问题是等高线部分在前景而不是图表的背景中。
请查看 jsfiddle for a full example.
有两个问题 仍然对我开放,因为我不是 dc.js
或 d3
专家:
- 有没有办法把等高线放在背景或散点图的符号(周期)放在前景(我已经在这个Whosebug question的帮助下尝试过但没有成功)
- 我使用
'g.brush'
选择器来获取内部图表的面积。只要刷亮打开,这就可以正常工作。选择器是一个好方法还是有更好的选择(如果关闭刷牙也可能有效)。
在我的示例中,我将轮廓部分放在左上角以查看它是否有效,但我还提供了代码(当前未注释)以将轮廓的宽度和高度增加到正确的大小。
chart
.on('renderlet', function (chart) {
var innerChart = chart.select('g.brush');
var width = 300, height=300;
//getting the correct width, height
//var innerChartBoundingRect = innerChart.node().getBoundingClientRect();
//var width = innerChartBoundingRect.width, height=innerChartBoundingRect.height;
[contours, color] = generateFmeasureContours(width,height, 1);
innerChart
.selectAll("path")
.data(contours)
.enter()
.append("path")
.attr("d", d3.geoPath())
.attr("fill", d => color(d.value));
var symbols = chart.chartBodyG().selectAll('path.symbol');
symbols.moveToFront();
});
将某些东西放在背景中是一种通用的 SVG 技能。
SVG 按照声明的顺序从后到前呈现所有内容,因此关键是在语法上将您的内容放在图表中其他所有内容之前。
我建议将它封装在一个 svg <g>
元素中,为了获得正确的顺序,您可以使用 d3-selection 的 insert 方法和 :first-child
CSS 选择器而不是 append
:
.on('pretransition', function (chart) {
// add contour layer to back (beginning of svg) only if it doesn't exist
var contourLayer = chart.g().selectAll('g.contour-layer').data([0]);
contourLayer = contourLayer
.enter().insert('g', ':first-child')
.attr('class', 'contour-layer')
.attr('transform', 'translate(' + [chart.margins().left,chart.margins().top].join(',') + ')')
.merge(contourLayer);
关于此实现的更多要点:
- 使用 dc 的
pretransition
事件,因为它在渲染和重绘后立即发生(而renderlet
等待转换完成) - 模式
.data([0]).enter()
仅在元素不存在时才添加该元素。 (它绑定一个 1 元素数组;该元素是什么并不重要。)这很重要,因为事件处理程序将在每次重绘时被调用,我们不想继续添加层。 - 我们为我们的图层指定了不同的 class 名称
contour-layer
以便我们可以识别它,因此一次添加模式有效 contourLayer = contourLayer.enter().insert(...)...merge(contourLayer)
是另一种常见的 D3 模式,用于插入内容并将其合并回选择中,以便我们稍后将插入和修改视为相同。使用较新的 selection.join 方法可能会更简单,但我还没有尝试过。- (我认为在排序方面可能也有一些改进,可能比
insert
更容易,但同样,我将使用我知道有效的方法。) - 最后,我们从 margin mixin 中获取左上偏移量
接下来,我们可以使用检索实际图表主体的宽度和高度
(叹息,undocumented)来自 dc.marginMixin
的方法:
var width = chart.effectiveWidth(), height = chart.effectiveHeight();
而且我们不需要将点移到前面或任何其他地方;你的代码的其余部分和以前一样,除了我们使用这个新层而不是绘制到刷层:
contourLayer
.selectAll("path")
.data(contours)
.enter()
.append("path")
.attr("d", d3.geoPath())
.attr("fill", d => color(d.value));
同样,如果您想合作将轮廓示例放入 dc.js,那就太棒了!