扩展本机d3组件(如d3.svg.axis())的惯用方法是什么?

Her*_*ill 7 javascript d3.js

对于d3中的时间序列可视化,我想在轴上突出显示年份.我通过制作我自己的xAxis渲染器来实现这一点,该渲染器调用本机axis函数然后实现我自己的自定义逻辑来格式化它呈现的刻度.

截图 我就是这样做的(参见jsbin上的工作示例):

  xAxis = d3.svg.axis()
    .scale(xScale)

  customXAxis = function(){
    xAxis(this);
    d3.selectAll('.tick', this)
      .classed("year", isYear);
  };

  ...

  xAxis.ticks(10);

  xAxisElement = canvas.append("g")
    .classed("axis x", true)
    .call(customXAxis);
Run Code Online (Sandbox Code Playgroud)

这可以完成工作,但感觉不对劲; 并没有真正扩展轴,它只包裹它.理想情况下,我customXAxis会继承d3 axis组件的属性,所以我可以做到这样的事情:

customXAxis.ticks(10)
Run Code Online (Sandbox Code Playgroud)

感谢@meetamit和@drakes把它放在一起.这就是我最终得到的:http://bl.ocks.org/HerbCaudill/ece2ff83bd4be586d9af

mee*_*mit 9

是的,你可以做到这一切.按照mbostock的建议'd3.rebind'一起获得:

// This outer function is the thing that instantiates your custom axis.
// It's equivalent to the function d3.svg.axis(), which instantiates a d3 axis.
function InstantiateCustomXAxis() {
  // Create an instance of the axis, which serves as the base instance here
  // It's the same as what you named xAxis in your code, but it's hidden
  // within the custom class. So instantiating customXAxis also
  // instantiates the base d3.svg.axis() for you, and that's a good thing.
  var base = d3.svg.axis();

  // This is just like you had it, but using the parameter "selection" instead of
  // the "this" object. Still the same as what you had before, but more
  // in line with Bostock's teachings...
  // And, because it's created from within InstantiateCustomXAxis(), you
  // get a fresh new instance of your custom access every time you call
  // InstantiateCustomXAxis(). That's important if there are multiple
  // custom axes on the page.
  var customXAxis = function(selection) {
    selection.call(base);

    // note: better to use selection.selectAll instead of d3.selectAll, since there
    // may be multiple axes on the page and you only want the one in the selection
    selection.selectAll('.tick', this)
      .classed("year", isYear);
  }

  // This makes ticks() and scale() be functions (aka methods) of customXAxis().
  // Calling those functions forwards the call to the functions implemented on
  // base (i.e. functions of the d3 axis). You'll want to list every (or all)
  // d3 axis method(s) that you plan to call on your custom axis
  d3.rebind(customXAxis, base, 'ticks', 'scale');// etc...

  // return it
  return customXAxis;
}
Run Code Online (Sandbox Code Playgroud)

要使用此课程,您只需致电

myCustomXAxis = InstantiateCustomXAxis();
Run Code Online (Sandbox Code Playgroud)

你现在也可以打电话

myCustomXAxis
  .scale(d3.scale.ordinal())
  .ticks(5)
Run Code Online (Sandbox Code Playgroud)

当然,以下内容将继续有效:

xAxisElement = canvas.append("g")
  .classed("axis x", true)
  .call(myCustomXAxis);
Run Code Online (Sandbox Code Playgroud)

综上所述

这是在d3中实现类的惯用方法.Javascript有其他方法来创建类,比如使用prototype对象,但是d3自己的可重用代码使用上面的方法 - 而不是原型方法.并且,在此范围内,d3.rebind是将方法调用从自定义类转发到本质上是子类的方法.