-
Notifications
You must be signed in to change notification settings - Fork 84
chart attach
When creating a new d3.chart based chart constructor, you may want to use charts that already exist by composing them together. For example, if one has:
A BarChart
chart constructor
and
A PieChart
chart constructor
And you wish to combine them so that each bar in your bar chart has a pie chart at the top of it, you can do so by creating one parent chart that instantiates these appror
======= STOPPPED HERE =======
Using the mixin
api allows you to mix charts together to create new charts. Imagine that you have two charts:
- one that paints rectangles of a certain height along an x axis
- one that paints text labels along an x axis
You could combine those two to produce a single chart that renders rectangles with labels on them.
Imagine we have the following charts:
A labels chart that renders labels along the x axis:
d3.chart("LabelChart", {
initialize: function() {
// setup some size defaults
this._width = this._width || this.base.attr("width") || 200;
this._height = this._height || this.base.attr("height") || 200;
// initialize a scale along which we will render
// the labels
this.xScale = d3.scale.linear()
.range([0, this._width]);
// create a labels layer
this.layer("labels", this.base.append("g"), {
dataBind: function(data) {
var chart = this.chart();
// set the domain of the xScale so we know
// how to map it. Give it a range of 10 on both
// ends.
chart.xScale.domain([
Math.min.apply(null, data) - 10,
Math.max.apply(null, data) + 10
]);
return this.selectAll("text")
.data(data);
},
insert: function() {
return this.append("text")
.classed("label", true);
},
events: {
enter: function() {
var chart = this.chart();
// position the labels at the mid height point
return this.attr("y", chart.height()/2)
.attr("x", function(d) {
return chart.xScale(d);
})
.attr("text-anchor", "middle")
.text(function(d) { return d; });
}
}
});
},
width: function(newWidth) {
if (arguments.length === 0) {
return this._width;
}
this._width = newWidth;
this.base.attr("width", this._width);
this.xScale.range([0, this._width]);
return this;
},
height: function(newHeight) {
if (arguments.length === 0) {
return this._height;
}
this._height = newHeight;
this.base.attr("height", this._height);
return this;
}
});
var labels = d3.select("#vis")
.append("svg")
.chart("LabelChart")
.width(100)
.height(100);
labels.draw([10,24,50,60,85]);
Now we also have a circle rendering chart that renders circles along the x axis:
// define a new chart type: a circle chart
d3.chart("CircleChart", {
initialize: function() {
// setup some size defaults
this._width = this._width || this.base.attr("width") || 200;
this._height = this._height || this.base.attr("height") || 200;
// initialize a scale along which we will render
// the circles
this.xScale = d3.scale.linear()
.range([0, this._width]);
// create a layer of circles that will go into
// a new group element on the base of the chart
this.layer("circles", this.base.append("g"), {
// select the elements we wish to bind to and
// bind the data to them.
dataBind: function(data) {
var chart = this.chart();
// set the domain of the xScale so we know
// how to map it. Give it a range of 10 on both
// ends.
chart.xScale.domain([
Math.min.apply(null, data) - 10,
Math.max.apply(null, data) + 10
]);
return this.selectAll("circle")
.data(data);
},
// insert actual circles
insert: function() {
return this.append("circle");
},
// define lifecycle events
events: {
// paint new elements, but set their radius to 0
// and make them red
"enter": function() {
var chart = this.chart();
return this.attr("cx", function(d) {
return chart.xScale(d);
})
.attr("cy", 10)
.attr("r", 0)
.style("fill", "red");
},
// then transition them to a radius of 5 and change
// their fill to blue
"enter:transition": function() {
var chart = this.chart();
return this
.delay(500)
.attr("r", chart.radius())
.style("fill", "yellow");
}
}
});
},
radius: function(newradius) {
if (arguments.length === 0) {
return this._radius;
}
this._radius = newradius;
return this;
},
width: function(newWidth) {
if (arguments.length === 0) {
return this._width;
}
this._width = newWidth;
this.base.attr("width", this._width);
this.xScale.range([0, this._width]);
return this;
},
height: function(newHeight) {
if (arguments.length === 0) {
return this._height;
}
this._height = newHeight;
this.base.attr("height", this._height);
return this;
}
});
Now if we wanted to combine them like so:
We can define a new chart that mixes the above two in. See the jsBin for this code:
d3.chart("LabeledCirclesChart", {
initialize: function() {
// setup some size defaults
this._width = this._width || this.base.attr("width") || 200;
this._height = this._height || this.base.attr("height") || 100;
this.circles = this.mixin("CircleChart", this.base.append("g"));
this.labels = this.mixin("LabelChart", this.base.append("g"));
// make the radius larger than the default
this.circles.radius(10);
// overlay the labels on top of the circles
this.labels.layer("labels")
.attr("transform", "translate(0,-" + (
(this.height() / 2) - this.circles.radius()*1.3)
+ ")");
// move the circles to the mid point of the height
this.circles.layer("circles").on("enter", function() {
var chart = this.chart();
return this.attr("y", chart.height()/2);
});
},
width: function(newWidth) {
if (arguments.length === 0) {
return this._width;
}
this._width = newWidth;
this.base.attr("width", this._width);
this.circles.width(this._width);
this.labels.width(this._width);
return this;
},
height: function(newHeight) {
if (arguments.length === 0) {
return this._height;
}
this._height = newHeight;
this.base.attr("height", this._height);
this.circles.height(this._height);
this.labels.height(this._height);
return this;
}
});
// create an instance of the chart on a d3 selection
var chart = d3.select('#vis')
.append("svg")
.chart("LabeledCirclesChart")
.height(100)
.width(200);
// render it with some data
chart.draw([10,24,50,60,85]);