D3.js 刷选控件:获取范围宽度和坐标

13
1个回答

59

Brush.extent()

使用刷子控件时,可以通过刷子对象上的 .extent() 方法访问有关刷子状态的信息。

.extent() 方法返回的信息取决于您连接到刷子对象的比例尺类型。

如果您连接了一个比例尺(无论是 X 轴比例尺还是 Y 轴比例尺,但不是两者都连接),则 extent 方法返回形式为 [minimum, maximum] 的两个元素的数组。

如果您将 X 和 Y 比例尺都附加到刷子对象,则 extent 方法返回嵌套形式的数组: [‍​[xMinimum, yMinimum], [xMaximum, yMaximum]​]

但是,这些最小值和最大值是什么?这也取决于比例尺。如果比例尺具有有效的 .invert(value) 方法,则最小值和最大值将转换为您的数据域值。对于离散型、阈值型和其他没有简单的 invert 方法的比例尺,刷子函数将返回在刷子元素有效的坐标系中的值。

1. 单维度刷子

要回答您所链接的特定示例的问题,需要查看刷子对象和比例尺对象。在该示例中,刷子连接到较小的“上下文”图表(x2 比例尺)的水平比例尺上:

var x = d3.time.scale().range([0, width]),
    x2 = d3.time.scale().range([0, width]),
    y = d3.scale.linear().range([height, 0]),
    y2 = d3.scale.linear().range([height2, 0]);

var brush = d3.svg.brush()
    .x(x2)
    .on("brush", brushed);

画笔初始化

上面创建的画笔对象仅存在于Javascript中,而不在文档中。 然而,该对象也是一个函数,可以调用(类似于轴函数),以创建一系列响应鼠标事件的矩形(这些矩形是不可见的),以及一个“extent”矩形(在此示例中为带白色边框的灰色)。

context.append("g")
  .attr("class", "x brush")
  .call(brush)  //call the brush function, causing it to create the rectangles
.selectAll("rect") //select all the just-created rectangles
  .attr("y", -6)
  .attr("height", height2 + 7); //set their height

不可见矩形的默认大小基于X和Y比例尺的输出范围。由于此刷子没有Y比例尺,因此必须明确设置矩形的恒定高度和垂直位置。

extent矩形的初始大小基于brush对象的范围(默认情况下宽度和高度为零)。该矩形的高度也在上述代码中设置。

刷子交互

当用户与屏幕上的刷子进行交互时,刷子对象会捕获这些事件并(1)更新“extent”矩形的宽度,(2)调用你在.on("brush", brushed)中关联的“brush”事件的函数。

brushed()函数是:

function brushed() {
  x.domain(brush.empty() ? x2.domain() : brush.extent());
  focus.select(".area").attr("d", area);
  focus.select(".x.axis").call(xAxis);
}

该刷子的目的是对主图进行缩放,这是通过设置主图X轴的域来实现的。如果刷子的宽度为零,则brush.empty()返回true,主图的X轴域设置为小图中显示的完整域。

然而,如果刷子具有有效宽度,则空测试返回false,并将域设置为brush.extent()的结果。因为刷子附加到一个线性X比例尺上,没有Y比例尺,所以范围以[xMin, xMax](在数据数字中)的形式返回,这正好是设置域所需的。

从刷子中提取值

如果您需要了解数据值中的宽度,只需简单地减去即可:

var extent = brush.extent(); //returns [xMin, xMax]
var width = extent[1] - extent[0]; //data-width = max - min

然而,如果您要在屏幕上绘制其他元素,则需要知道SVG中的实际坐标,而不仅仅是数据值。 要进行转换,您使用与将数据转换为SVG坐标所用的相同内容:比例尺函数。请牢记使用控制小图表的x2比例尺,而不是主图表缩放的比例尺,那就是:

var extent = brush.extent(); //returns [xMin, xMax]
var rangeExtent = [x2( extent[0] ), x2( extent[1] ) ]; //convert
var rangeWidth  = rangeExtent[1] - rangeExtent[0];

2. X 和 Y 刷子

再次强调,此示例是针对具有 一个(水平 / X)线性尺度的刷子。如果您同时使用 X 和 Y 线性尺度,则需要使用以下代码分离出 X 和 Y 范围值:

function brushed() {

  if (brush.empty()) {
      //either the height OR the width is empty
      x.domain( x2.domain() ); //reset X scale
      y.domain( y2.domain() ); //reset Y scale
  }

  var extent = brush.extent();
  x.domain( [extent[0][0] , extent[1][0] ] ); //min and max data X values
  y.domain( [extent[0][1] , extent[1][1] ] ); //min and max data Y values

  var rangeExtent = [
          [x2(extent[0][0]), y2(extent[0][1])],
          [x2(extent[1][0]), y2(extent[1][1])]
       ];
  var rangeWidth  = rangeExtent[1][0] - rangeExtent[0][0];
  var rangeHeight = rangeExtent[1][1] - rangeExtent[0][1];


  focus.select(".area").attr("d", area);
  focus.select(".x.axis").call(xAxis);
  focus.select(".y.axis").call(yAxis);
}

从刷子中提取值

如果你想知道矩形左上角的坐标,你还需要知道你的Y轴比例尺是否将最小值从顶部向下切换。

或者,你可以从屏幕上的“范围”矩形获取宽度、高度和左上角坐标,刷子对象会为你修改它:

function brushed() {

  //use the brush object's values to set the data domains:
  if (brush.empty()) {
      //either the height OR the width is empty
      x.domain( x2.domain() ); //reset X scale
      y.domain( y2.domain() ); //reset Y scale
  }

  var extent = brush.extent();
  x.domain( [extent[0][0] , extent[1][0] ] ); //min and max data X values
  y.domain( [extent[0][1] , extent[1][1] ] ); //min and max data Y values

  //use the brush extent rectangle to get the SVG coordinates:
  var extentRect = d3.select("g.x.brush rect.extent");

  var rangeWidth  = extentRect.attr("width");
  var rangeHeight = extentRect.attr("height");
  var rangeLeft = extentRect.attr("x");
  var rangeTop = extentRect.attr("y");


  focus.select(".area").attr("d", area);
  focus.select(".x.axis").call(xAxis);
  focus.select(".y.axis").call(yAxis);
}

如果你正在使用序数刻度,缩放会更加复杂,但查找屏幕坐标则较为简单。 这个答案描述了如何使用一个带有序数刻度的brush来缩放。然而,由于.extent()返回的值已经在SVG坐标中,如果需要坐标和宽度,则无需使用比例尺自身进行转换。

参考资料

Brush控件的API页面在页面顶部有一组缩略图像;单击其中任意一个即可打开一个工作示例。对于更多讨论,您可能会对这篇带有另一个很好的brush实例的教程感兴趣。


10
我已经彻底学习了这个课题!+1 - FernOfTheAndes
@AmeliaBR: 非常感谢你的详细回答。我已经开始进行分段和添加章节标题的工作了。我猜我的标题和剪辑可能不够优化,如果有擅长这方面的人能够对我进行审阅,那将非常受益。 - Hugolpz
1
谢谢@Hugolpz,看起来很好,更容易理解了。我得记住在写这种长篇教程式答案时使用标题! - AmeliaBR
@AmeliaBR 你如何在d3 v4中创建一个一维刷子?我找不到任何例子。 - yeouuu
1
@AmeliaBR,您能否提供一些有关v4(或更高版本)的类似信息?在d3v4中,所有API都发生了变化,extent也不再具有相同的功能。 - seanmk
@seanmk 这是官方的变更列表:https://github.com/d3/d3/blob/master/CHANGES.md#brushes-d3-brush。我自己还没有在v4中尝试过这个功能。 - AmeliaBR

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