扩展原生 d3 组件(如 d3.svg.axis())的惯用方法是什么?
What's the idiomatic way to extend a native d3 component like d3.svg.axis()?
对于 d3 中的时间序列可视化,我想在轴上突出显示年份。我通过制作我自己的 xAxis 渲染器实现了这一点,它调用本机 axis
函数,然后实现我自己的自定义逻辑来格式化它渲染的刻度。
我就是这样做的 (see working example on 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
经过大量代码检查和黑客攻击,并与经验丰富的 d3 人员交谈,我了解到 d3.svg.axis()
是一个函数(不是对象,也不是 class),所以它可以'既不延长也不包裹。因此,对于 "extend" 我们将创建一个新轴,运行 在基础 axis()
上进行选择以选择那些刻度线,然后从基础 [=13] 复制所有属性=] 一举成名, return 这个功能扩展版。
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
现在完全 "inherits" d3 轴组件的属性。您可以安全地执行以下操作:
customXAxis
.ticks(2)
.scale(xScale)
.tickPadding(50)
.tickFormat(dateFormatter);
canvas.append("g").call(customXAxis);
*借助@HerbCaudill 的样板代码,并受到@meetamit 的启发。
是的,你可以做到这一切。关注 mbostock's suggestions here in conjunction with `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;
}
要使用此 class,您只需调用
myCustomXAxis = InstantiateCustomXAxis();
您现在也可以拨打
myCustomXAxis
.scale(d3.scale.ordinal())
.ticks(5)
当然,以下内容将继续有效:
xAxisElement = canvas.append("g")
.classed("axis x", true)
.call(myCustomXAxis);
总结
这是在 d3 中实现 classes 的惯用方式。 Javascript 有其他创建 classes 的方法,比如使用 prototype
对象,但 d3 自己的可重用代码使用上述方法——而不是原型方式。并且,在其中,d3.rebind
是将方法调用从自定义 class 转发到本质上是 subclass.
的方式
对于 d3 中的时间序列可视化,我想在轴上突出显示年份。我通过制作我自己的 xAxis 渲染器实现了这一点,它调用本机 axis
函数,然后实现我自己的自定义逻辑来格式化它渲染的刻度。
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
经过大量代码检查和黑客攻击,并与经验丰富的 d3 人员交谈,我了解到 d3.svg.axis()
是一个函数(不是对象,也不是 class),所以它可以'既不延长也不包裹。因此,对于 "extend" 我们将创建一个新轴,运行 在基础 axis()
上进行选择以选择那些刻度线,然后从基础 [=13] 复制所有属性=] 一举成名, return 这个功能扩展版。
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
现在完全 "inherits" d3 轴组件的属性。您可以安全地执行以下操作:
customXAxis
.ticks(2)
.scale(xScale)
.tickPadding(50)
.tickFormat(dateFormatter);
canvas.append("g").call(customXAxis);
*借助@HerbCaudill 的样板代码,并受到@meetamit 的启发。
是的,你可以做到这一切。关注 mbostock's suggestions here in conjunction with `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;
}
要使用此 class,您只需调用
myCustomXAxis = InstantiateCustomXAxis();
您现在也可以拨打
myCustomXAxis
.scale(d3.scale.ordinal())
.ticks(5)
当然,以下内容将继续有效:
xAxisElement = canvas.append("g")
.classed("axis x", true)
.call(myCustomXAxis);
总结
这是在 d3 中实现 classes 的惯用方式。 Javascript 有其他创建 classes 的方法,比如使用 prototype
对象,但 d3 自己的可重用代码使用上述方法——而不是原型方式。并且,在其中,d3.rebind
是将方法调用从自定义 class 转发到本质上是 subclass.