问题来自于尝试使用画笔设置 x 尺度域,当您的 x 尺度是ordinal规模。换句话说,x 轴的预期域是类别列表,而不是最大最小数值范围。所以问题就出在最上面的刷机函数上:
function brushed() {
main_x0.domain(brush.empty() ? mini_x0.domain() : brush.extent());
设置的域为brush.extent()
是一个由两个数字组成的数组,它完全偏离了你的序数范围。
根据维基百科,如果附加到画笔函数的比例之一是序数比例,则返回的值brush.extent()
是输出范围内的值,而不是输入域内的值。序数音阶没有invert() method将范围值转换为域值。
因此,您有几种如何继续的选择:
您可以使用主 x 轴的线性时间尺度而不是序数尺度来重新绘制整个图表。但是,您必须编写自己的函数来计算该轴上每天的宽度,而不是能够使用.rangeBand()
.
您可以创建自己的“反转”函数来找出哪些分类值(日期)mini_x0.domain
) 包含在返回的范围内brush.extent()
。那么你就必须both重置main_x0.domain
仅在轴上包含这些日期,and过滤掉您的矩形以仅绘制这些矩形。
Or你可以离开domain of main_x0.
是,并改变range反而。通过扩大图表的范围,可以使条形间隔更大。与剪切路径相结合以切断绘图区域之外的条形,这具有仅显示条形的特定子集的效果,这正是您想要的。
但新的范围应该是什么呢?返回的范围brush.extent()
是刷牙矩形的开始和结束位置。如果您使用这些值作为主图表上的范围,则整个图表将被压缩到该宽度。这与你想要的相反。你想要的是图表的面积起初填充要拉伸的宽度以填充整个绘图区域。
因此,如果您的原始 x 范围是从 [0,100] 开始,并且画笔覆盖区域 [20,60],那么您需要一个满足以下条件的新范围:
- 新范围宽度的20%标记为0;
- 新范围宽度的 60% 标记为 100。
所以,
- 新范围的总宽度为 ( (100-0) / (60-20) )*(100-0) = 250;
- 新范围的起点为 (0 - (20/100)*250) = -50;
- 新范围的终点位于 (-50) + 250 = 200。
现在您可以自己完成所有代数计算来计算出这种转换。但这实际上只是另一种类型的缩放方程,所以为什么不创建一个new缩放函数在旧范围和放大范围之间进行转换。
具体来说,我们需要一个线性标尺,其输出范围设置为绘图区域的实际范围。然后根据我们要拉伸以覆盖绘图区域的画笔区域的范围来设置域。最后我们算出范围序数尺度通过使用线性刻度来计算出范围的原始最大值和最小值离屏幕有多远。从那里,我们可以调整其他顺序比例的大小并重新定位所有矩形。
In code:
//Initialization:
var main_xZoom = d3.scale.linear()
.range([0, main_width - 275])
.domain([0, main_width - 275]);
//Brushing function:
function brushed() {
var originalRange = main_xZoom.range();
main_xZoom.domain(brush.empty() ?
originalRange:
brush.extent() );
main_x0.rangeRoundBands( [
main_xZoom(originalRange[0]),
main_xZoom(originalRange[1])
], 0.2);
main_x1.rangeRoundBands([0, main_x0.rangeBand()], 0);
bar.selectAll("rect")
.attr("transform", function (d) {
return "translate(" + main_x0(d.date) + ",0)";
})
.attr("width", function (d) {
return main_x1.rangeBand();
})
.attr("x", function (d) {
return main_x1(d.portfolio);
});
main.select("g.x.axis").call(main_xAxis);
}
基于简化代码的工作小提琴(注意:您仍然需要在主图上设置一个剪切矩形):
http://fiddle.jshell.net/CjaD3/1/