d3.js保持可缩放的y轴不会低于零

4
我有一个带缩放功能的图表。我的主要观察是x轴根据当前的缩放级别更新了其比例尺。我也希望y轴这样做,因此启用了zoom.y(y),但不良的副作用是现在用户可以向所有方向缩小,甚至到负值“下面”的图表。

http://jsfiddle.net/ericps/xJ3Ke/5/

var zoom = d3.behavior.zoom().scaleExtent([0.2, 5]) .on("zoom", draw);似乎并没有真正考虑y轴。而且用户仍然可以将图表拖动到任何方向的无限远处。

我想到的一个想法与启用zoom.y(y)无关,只需要根据当前可见范围重新绘制y轴。像某种基于X轴位置的重新绘制一样。现在我不想上下滚动,只想左右移动。

除了注释掉//zoom.y(y)之外,如何实现?感谢您的建议。

1个回答

12
你只需要在绘图方法中更新y轴比例尺的范围即可。缩放函数将修改关联的比例尺并设置它们的域以模拟缩放。因此,例如,您可以通过执行x.invert(0)和x.invert(width)来获取x可见数据范围。如果您将数据转换为使用日期而不是字符串,则我建议您使用此方法进行过滤,这可能更有效率。虽然目前,您仍然可以使用x比例尺来筛选出可见数据,找到这些值的y轴极限,并相应地设置y比例尺的域。实际上,您只需在缩放更新回调中添加几行代码即可完成这些操作。
var yExtent = d3.extent(data.filter(function(d) { 
    var dt = x(d.date);
    return dt > 0 && dt < width;
}), function(d) { return d.value; });
y.domain(yExtent).nice(); 

您可以在这里尝试它。

为了更好地解释发生了什么:

缩放行为侦听鼠标事件并修改相关比例尺的范围。

比例尺由轴使用,它们将它们作为带有刻度线的线绘制,并且您在回调中设置的路径和区域的数据也使用比例尺。

因此,当缩放更改时,它会触发回调,基本方法就是您所拥有的:

svg.select("g.x.axis").call(xAxis);
svg.select("g.y.axis").call(yAxis);
svg.select("path.area").attr("d", area);
svg.select("path.line").attr("d", line);

我们重新绘制了x轴和y轴,并使用新更新的域重新绘制(重新计算)区域和线条 - 同时使用新的x和y比例尺。因此,为了获得所需的行为,我们取消了y轴上的默认缩放行为,而是在缩放或平移时自己修改y轴的域:方便的是,由于缩放行为,我们已经有了回调函数。计算y比例尺域的第一步是找出哪些数据值可见。x轴已配置为输出0到宽度的范围,缩放行为已更新x比例尺的域,以使原始域的仅子集输出到此范围。因此,我们使用JavaScript数组的filter方法仅提取那些映射将它们放在我们可见范围内的数据对象:
data.filter(function(d) { 
    var dt = x(d.date);
    return dt > 0 && dt < width;
}

然后我们使用方便的d3 extent方法返回一个包含最小值和最大值的数组。但由于我们的数组都是对象,所以我们需要一个访问器函数,以便extent方法有一些数字可以进行比较(这是D3中常见的模式)。
d3.extents(filteredData, function(d) { return d.value; });

所以,现在我们知道了当前x轴比例尺下绘制的所有数据点的最小值和最大值。最后一步就是设置y轴比例尺的域并像往常一样继续操作!
y.domain(yExtent).nice(); 

我在api中找到了一个不错的方法,因为它是你想要一个比例尺做的事情,而d3通常会帮你完成一些你想要做的事情。
一个很好的教程来解决一些东西是:http://alignedleft.com/tutorials/ 即使是你认为已经知道的部分,也值得逐步学习。

1
这对我来说“只是工作”,但我仍然不完全理解它。我真正不理解的是你如何如此熟悉d3库,因为教程并不那么直观。 - CQM
1
有一个明显的转变,就是我们思考D3工作方式的方式。我认为这归结于核心是关于数据映射,而不是组合构件。因此,进行映射会将所有内容放在一起作为副作用...无论如何,我将编辑答案,试图更好地解释正在发生的事情。 - Superboggly
我刚刚意识到,相比之下,这个函数使得图形根据视口中当前数据集的最低值创建y轴和路径。我希望它在y轴上始终为零,但不会低于零。我该如何修改你的函数呢? - CQM
1
只需将yExtent [0] = 0设置为覆盖计算出的值即可。 - Superboggly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接