簡體   English   中英

擴展本機d3組件(如d3.svg.axis())的慣用方法是什么?

[英]What's the idiomatic way to extend a native d3 component like d3.svg.axis()?

對於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);

這可以完成工作,但感覺不對勁; 並沒有真正擴展軸,它只包裹它。 理想情況下,我的customXAxis將繼承d3的axis組件的屬性,所以我可以做這樣的事情:

customXAxis.ticks(10)

感謝@meetamit和@drakes把它放在一起。 這就是我最終得到的: http//bl.ocks.org/HerbCaudill/ece2ff83bd4be586d9af

是的,你可以做到這一切。 按照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;
}

要使用此課程,您只需致電

myCustomXAxis = InstantiateCustomXAxis();

你現在也可以打電話

myCustomXAxis
  .scale(d3.scale.ordinal())
  .ticks(5)

當然,以下內容將繼續有效:

xAxisElement = canvas.append("g")
  .classed("axis x", true)
  .call(myCustomXAxis);

綜上所述

這是在d3中實現類的慣用方法。 Javascript有其他方法來創建類,比如使用prototype對象,但是d3自己的可重用代碼使用上面的方法 - 而不是原型方法。 而且,在其中, d3.rebind是將方法調用從自定義類轉發到本質上是子類的方法。

經過大量的代碼檢查和黑客攻擊,並與經驗豐富的d3人交談后,我了解到d3.svg.axis()是一個函數(不是對象也不是類),所以它不能擴展也不能包裝。 因此,為了“擴展”它,我們將創建一個新軸,在基axis()上運行選擇以選擇那些刻度線,然后一次性從基axis()復制所有屬性,並返回此擴展功能版本。

var customXAxis = (function() {
  var base = d3.svg.axis();

  // Select and apply a style to your tick marks
  var newAxis = function(selection) {
    selection.call(base);
    selection.selectAll('.tick', this)
      .classed("year", isYear);
  };

  // Copy all the base axis methods like 'ticks', 'scale', etc.
  for(var key in base) {
    if (base.hasOwnProperty(key)) {
       d3.rebind(newAxis, base, key);
    }
  }

  return newAxis;
})();

customXAxis現在完全“繼承”d3軸組件的屬性。 您可以安全地執行以下操作:

customXAxis
.ticks(2)
.scale(xScale)
.tickPadding(50)
.tickFormat(dateFormatter);

canvas.append("g").call(customXAxis);

*在@HerbCaudill的樣板代碼的幫助下,受@ meetamit的想法啟發。

演示: http//jsbin.com/kabonokeki/5/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM