Skip to content

Instantly share code, notes, and snippets.

@curran
Last active September 30, 2016 14:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save curran/46fd4087d4dca0bf17cdd9a93810ae77 to your computer and use it in GitHub Desktop.
Save curran/46fd4087d4dca0bf17cdd9a93810ae77 to your computer and use it in GitHub Desktop.
Bar Chart with ReactiveModel
license: mit
letter count
A 1
B 2
C 3
D 4
E 5
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Bar Chart with ReactiveModel</title>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="//datavis-tech.github.io/reactive-model/reactive-model-v0.11.0.min.js"></script>
<style>
/* Make the chart container fill the page using CSS. */
#chart-container {
position: fixed;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
}
.axis text {
font: 10pt sans-serif;
}
.axis-label {
font: 14pt sans-serif;
}
.axis path {
display: none;
}
.tick line {
fill: none;
stroke: #DDD;
stroke-width: 1px;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<!-- The SVG graphics will be injected into this div. -->
<div id="chart-container"></div>
<script>
var transitionDuration = 800;
// Resizes the SVG container.
function SVG(my){
my("svg")
("width", 100)
("height", 100)
("svg-width", function (svg, width){
svg.attr("width", width);
}, "svg, width")
("svg-height", function (svg, height){
svg.attr("height", height);
}, "svg, height");
}
// Encapsulates the margin convention.
function Margin(my){
my("marginTop", 50)
("marginBottom", 50)
("marginLeft", 50)
("marginRight", 50)
("innerWidth", function (width, marginLeft, marginRight){
return width - marginLeft - marginRight;
}, "width, marginLeft, marginRight")
("innerHeight", function (height, marginTop, marginBottom){
return height - marginTop - marginBottom;
}, "height, marginTop, marginBottom")
("g", function (svg){
return svg.append("g");
}, "svg")
("g-transform", function (g, marginLeft, marginTop){
g.attr("transform", "translate(" + marginLeft + "," + marginTop + ")");
}, "g, marginLeft, marginTop");
}
// Adds the "data" property.
function Data(my){
my("data");
}
// Adds a column and accessor for the given column name.
function Column(my, name){
my(name + "Column")
(name + "Accessor", function (column){
return function (d){ return d[column]; };
}, name + "Column");
}
// Sets up a linear scale with the given name.
function ScaleLinear(my, name){
var scale = d3.scaleLinear();
my(name + "ScaleDomain", function (data, accessor){
return [0, d3.max(data, accessor)];
}, "data, " + name + "Accessor");
if(name === "x"){
my("xScaleRange", function (innerWidth){
return [0, innerWidth];
}, "innerWidth");
} else if(name === "y"){
my("yScaleRange", function (innerHeight){
return [innerHeight, 0];
}, "innerHeight");
}
my(name + "Scale", function(domain, range){
return scale
.domain(domain)
.range(range)
.nice();
}, name + "ScaleDomain, " + name + "ScaleRange")
(name + "Scaled", function(scale, accessor){
return function (d){
return scale(accessor(d));
};
}, name + "Scale, " + name + "Accessor");
}
// Sets up a Band ordinal scale with the given name.
function ScaleBand(my, name){
var scale = d3.scaleBand();
my(name + "ScaleDomain", function (data, accessor){
return data.map(accessor);
}, "data, " + name + "Accessor");
my(name + "ScalePadding", 0.1);
if(name === "x"){
my("xScaleRange", function (innerWidth){
return [0, innerWidth];
}, "innerWidth");
} else if(name === "y"){
my("yScaleRange", function (innerHeight){
return [innerHeight, 0];
}, "innerHeight");
}
my(name + "Scale", function(domain, range, padding){
return scale
.padding(padding)
.domain(domain)
.range(range);
}, name + "ScaleDomain, " + name + "ScaleRange, " + name + "ScalePadding")
(name + "Scaled", function(scale, accessor){
return function (d){
return scale(accessor(d));
};
}, name + "Scale, " + name + "Accessor");
}
// Sets up an axis with the given name ("x" or "y")
function Axis(my, name){
var axisLengthProperty;
var tickSizeProperty;
var axis;
// Approximate number of pixels between ticks.
my(name + "AxisTickSpacing", 70)
(name + "AxisG", function (g){
return g.append("g").attr("class", name + " axis");
}, "g");
if(name === "x"){
axisLengthProperty = "innerWidth";
tickSizeProperty = "innerHeight";
axis = d3.axisBottom();
my(function(xAxisG, innerHeight){
xAxisG.attr("transform", "translate(0," + innerHeight + ")");
}, "xAxisG, innerHeight");
} else if(name === "y"){
axisLengthProperty = "innerHeight";
tickSizeProperty = "innerWidth";
axis = d3.axisLeft();
}
my(name + "AxisTicks", function (xAxisTickSpacing, axisLength){
return axisLength / xAxisTickSpacing;
}, name + "AxisTickSpacing," + axisLengthProperty)
// If true, tick marks will span the entire innerWidth or innerHeight.
(name + "AxisTickFullSpan", true)
(name + "Axis", function(ticks, scale, tickSize, tickFullSpan){
return axis
.scale(scale)
.tickSize(tickFullSpan? -tickSize : 10)
.ticks(ticks);
}, [
name + "AxisTicks",
name + "Scale",
tickSizeProperty,
name + "AxisTickFullSpan"
])
(function(axisG, axis){
axis(axisG.transition().duration(transitionDuration));
}, name + "AxisG, " + name + "Axis");
}
function AxisLabel(my, name){
my(name + "AxisLabelOffset", 15)
(name + "AxisLabel", function (svg){
return svg.append("text")
.attr("class", name + " axis-label")
.style("text-anchor", "middle");
}, "svg")
my(function (axisLabel, column){
axisLabel.text(column);
}, name + "AxisLabel," + name + "Column");
if(name === "x"){
my(function (axisLabel, marginLeft, innerWidth){
axisLabel.attr("x", marginLeft + innerWidth / 2);
}, "xAxisLabel, marginLeft, innerWidth")
(function (axisLabel, height, offset){
axisLabel.attr("y", height - offset);
}, "xAxisLabel, height, xAxisLabelOffset");
} else if(name === "y"){
my(function (label, offset, innerHeight, marginTop){
label.attr("transform", [
"translate(",
offset,
",",
marginTop + innerHeight / 2,
") rotate(-90)"
].join(""));
}, "yAxisLabel, yAxisLabelOffset, innerHeight, marginTop");
}
}
function BarChartMarks(my){
my("circleG", function (g){
return g.append("g");
}, "g")
("barColor", "black")
(function (circleG, data, xScale, xScaled, yScaled, barColor, innerHeight){
var rect = circleG.selectAll("rect").data(data);
rect.exit().remove();
rect.enter().append("rect")
.merge(rect)
.attr("fill", barColor)
.attr("x", xScaled)
.attr("y", yScaled)
.attr("width", xScale.bandwidth)
.attr("height", function (d){ return innerHeight - yScaled(d); });
}, "circleG, data, xScale, xScaled, yScaled, barColor, innerHeight");
}
function BarChart(){
return ReactiveModel()
.call(SVG)
.call(Margin)
.call(Data)
.call(Column, "x")
.call(Column, "y")
.call(ScaleBand, "x")
.call(ScaleLinear, "y")
.call(BarChartMarks)
.call(Axis, "x")
.call(Axis, "y")
.xAxisTickFullSpan(false)
.call(AxisLabel, "x")
.call(AxisLabel, "y");
}
// Respond to resize by setting width and height from DOM element.
function Resize(my, el){
function resize(){
my.width(el.clientWidth)
.height(el.clientHeight);
}
resize();
window.addEventListener("resize", resize);
}
// The main program that uses the ScatterPlot component.
function main(){
// Set up the ScatterPlot instance.
var container = d3.select("#chart-container"),
barChart = BarChart()
.svg(container.append("svg"))
.call(Resize, container.node());
// Tweak properties
barChart
.marginTop(10)
.marginBottom(52)
.marginLeft(57)
.marginRight(40)
.xAxisLabelOffset(10)
.yAxisLabelOffset(20)
.yAxisTickSpacing(121);
// Configure the visual encoding.
barChart
.xColumn("letter")
.yColumn("count");
// Load the data.
d3.csv("data.csv", function (d){
d.count = +d.count;
return d;
}, function(err, data){
barChart.data(data);
});
}
main();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment