-
-
Save tamascsaba/10017408 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
"use strict"; | |
d3.json("bullets.json", function(error, data) { | |
var myChart = d3.select("body").chart("Bullets", { | |
seriesCount: data.length | |
}); | |
myChart.margin({ top: 5, right: 40, bottom: 20, left: 120 }) | |
.width(960) | |
.height(50) | |
.duration(1000); | |
myChart.draw(data); | |
d3.selectAll("button").on("click", function() { | |
data.forEach(randomize); | |
myChart.draw(data); | |
}); | |
}); | |
function randomize(d) { | |
if (!d.randomizer) d.randomizer = randomizer(d); | |
d.ranges = d.ranges.map(d.randomizer); | |
d.markers = d.markers.map(d.randomizer); | |
d.measures = d.measures.map(d.randomizer); | |
return d; | |
} | |
function randomizer(d) { | |
var k = d3.max(d.ranges) * .2; | |
return function(d) { | |
return Math.max(0, d + k * (Math.random() - .5)); | |
}; | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Chart design based on original implementation by Mike Bostock: | |
// http://bl.ocks.org/mbostock/4061961 | |
d3.chart("Bullet", { | |
initialize: function() { | |
this.xScale = d3.scale.linear(); | |
this.base.classed("bullet", true); | |
this.titleGroup = this.base.append("g") | |
.style("text-anchor", "end"); | |
this.title = this.titleGroup.append("text") | |
.attr("class", "title"); | |
this.subtitle = this.titleGroup.append("text") | |
.attr("class", "subtitle") | |
.attr("dy", "1em"); | |
this._margin = { top: 0, right: 0, bottom: 0, left: 0 }; | |
// Default configuration | |
this.duration(0); | |
this.markers(function(d) { | |
return d.markers; | |
}); | |
this.measures(function(d) { | |
return d.measures; | |
}); | |
this.width(380); | |
this.height(30); | |
this.tickFormat(this.xScale.tickFormat(8)); | |
this.orient("left"); // TODO top & bottom | |
this.ranges(function(d) { | |
return d.ranges; | |
}); | |
this.layer("ranges", this.base.append("g").classed("ranges", true), { | |
dataBind: function(data) { | |
// This layer operates on "ranges" data | |
data = data.ranges; | |
return this.selectAll("rect.range").data(data); | |
}, | |
insert: function() { | |
return this.append("rect"); | |
}, | |
events: { | |
enter: function() { | |
var chart = this.chart(); | |
this.attr("class", function(d, i) { return "range s" + i; }) | |
.attr("width", chart.xScale) | |
.attr("height", chart.height()) | |
.attr("x", this.chart()._reverse ? chart.xScale : 0); | |
}, | |
"merge:transition": function() { | |
var chart = this.chart(); | |
this.duration(chart.duration()) | |
.attr("width", chart.xScale) | |
.attr("x", chart._reverse ? chart.xScale : 0); | |
}, | |
exit: function() { | |
this.remove(); | |
} | |
} | |
}); | |
this.layer("measures", this.base.append("g").classed("measures", true), { | |
dataBind: function(data) { | |
// This layer operates on "measures" data | |
data = data.measures; | |
return this.selectAll("rect.measure").data(data); | |
}, | |
insert: function() { | |
return this.append("rect"); | |
}, | |
events: { | |
enter: function() { | |
var chart = this.chart(); | |
var hy = chart.height() / 3; | |
this.attr("class", function(d, i) { return "measure s" + i; }) | |
.attr("width", chart.xScale) | |
.attr("height", hy) | |
.attr("x", chart._reverse ? chart.xScale : 0) | |
.attr("y", hy); | |
}, | |
"merge:transition": function() { | |
var chart = this.chart(); | |
this.duration(chart.duration()) | |
.attr("width", chart.xScale) | |
.attr("x", chart.reverse ? chart.xScale : 0); | |
} | |
} | |
}); | |
this.layer("markers", this.base.append("g").classed("markers", true), { | |
dataBind: function(data) { | |
// This layer operates on "markers" data | |
data = data.markers; | |
return this.selectAll("line.marker").data(data); | |
}, | |
insert: function() { | |
return this.append("line"); | |
}, | |
events: { | |
enter: function() { | |
var chart = this.chart(); | |
var height = chart.height(); | |
this.attr("class", "marker") | |
.attr("x1", chart.xScale) | |
.attr("x2", chart.xScale) | |
.attr("y1", height / 6) | |
.attr("y2", height * 5 / 6); | |
}, | |
"merge:transition": function() { | |
var chart = this.chart(); | |
var height = chart.height(); | |
this.duration(chart.duration()) | |
.attr("x1", chart.xScale) | |
.attr("x2", chart.xScale) | |
.attr("y1", height / 6) | |
.attr("y2", height * 5 / 6); | |
} | |
} | |
}); | |
this.layer("ticks", this.base.append("g").classed("ticks", true), { | |
dataBind: function() { | |
var format = this.chart().tickFormat(); | |
return this.selectAll("g.tick").data(this.chart().xScale.ticks(8), function(d) { | |
return this.textContent || format(d); | |
}); | |
}, | |
insert: function() { | |
var tick = this.append("g").attr("class", "tick"); | |
var chart = this.chart(); | |
var height = chart.height(); | |
var format = chart.tickFormat(); | |
tick.append("line") | |
.attr("y1", height) | |
.attr("y2", height * 7 / 6); | |
tick.append("text") | |
.attr("text-anchor", "middle") | |
.attr("dy", "1em") | |
.attr("y", height * 7 / 6) | |
.text(format); | |
return tick; | |
}, | |
events: { | |
enter: function() { | |
var chart = this.chart(); | |
this.attr("transform", function(d) { | |
return "translate(" + chart.xScale(d) + ",0)"; | |
}) | |
.style("opacity", 1e-6); | |
}, | |
"merge:transition": function() { | |
var chart = this.chart(); | |
var height = chart.height(); | |
this.duration(chart.duration()) | |
.attr("transform", function(d) { | |
return "translate(" + chart.xScale(d) + ",0)"; | |
}) | |
.style("opacity", 1); | |
this.select("line") | |
.attr("y1", height) | |
.attr("y2", height * 7 / 6); | |
this.select("text") | |
.attr("y", height * 7 / 6); | |
}, | |
"exit:transition": function() { | |
var chart = this.chart() | |
this.duration(chart.duration()) | |
.attr("transform", function(d) { | |
return "translate(" + chart.xScale(d) + ",0)"; | |
}) | |
.style("opacity", 1e-6) | |
.remove(); | |
} | |
} | |
}); | |
d3.timer.flush(); | |
}, | |
transform: function(data) { | |
// Copy data before sorting | |
var newData = { | |
title: data.title, | |
subtitle: data.subtitle, | |
randomizer: data.randomizer, | |
ranges: data.ranges.slice().sort(d3.descending), | |
measures: data.measures.slice().sort(d3.descending), | |
markers: data.markers.slice().sort(d3.descending) | |
}; | |
this.xScale.domain([0, Math.max(newData.ranges[0], newData.measures[0], newData.markers[0])]); | |
this.title.text(data.title); | |
this.subtitle.text(data.subtitle); | |
return newData; | |
}, | |
// left, right, top, bottom | |
orient: function(x) { | |
if (!arguments.length) return this._orient; | |
this._orient = x; | |
this._reverse = this._orient == "right" || this._orient == "bottom"; | |
return this; | |
}, | |
// ranges (bad, satisfactory, good) | |
ranges: function(x) { | |
if (!arguments.length) return this._ranges; | |
this._ranges = x; | |
return this; | |
}, | |
// markers (previous, goal) | |
markers: function(x) { | |
if (!arguments.length) return this._markers; | |
this._markers = x; | |
return this; | |
}, | |
// measures (actual, forecast) | |
measures: function(x) { | |
if (!arguments.length) return this._measures; | |
this._measures = x; | |
return this; | |
}, | |
width: function(x) { | |
var margin; | |
if (!arguments.length) { | |
return this._width; | |
} | |
margin = this.margin(); | |
x -= margin.left + margin.right | |
this._width = x; | |
this.xScale.range(this._reverse ? [x, 0] : [0, x]); | |
this.base.attr("width", x); | |
return this; | |
}, | |
height: function(x) { | |
var margin; | |
if (!arguments.length) { | |
return this._height; | |
} | |
margin = this.margin(); | |
x -= margin.top + margin.bottom; | |
this._height = x; | |
this.base.attr("height", x); | |
this.titleGroup.attr("transform", "translate(-6," + x / 2 + ")"); | |
return this; | |
}, | |
margin: function(margin) { | |
if (!margin) { | |
return this._margin; | |
} | |
["top", "right", "bottom", "left"].forEach(function(dimension) { | |
if (dimension in margin) { | |
this._margin[dimension] = margin[dimension]; | |
} | |
}, this); | |
this.base.attr("transform", "translate(" + this._margin.left + "," + | |
this._margin.top + ")") | |
return this; | |
}, | |
tickFormat: function(x) { | |
if (!arguments.length) return this._tickFormat; | |
this._tickFormat = x; | |
return this; | |
}, | |
duration: function(x) { | |
if (!arguments.length) return this._duration; | |
this._duration = x; | |
return this; | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Chart design based on original implementation by Mike Bostock: | |
// http://bl.ocks.org/mbostock/4061961 | |
d3.chart("Bullets", { | |
initialize: function(options) { | |
var mixins = this.mixins = []; | |
var idx, len, mixin; | |
if (options && options.seriesCount) { | |
for (idx = 0, len = options.seriesCount; idx < len; ++idx) { | |
this._addSeries(idx); | |
} | |
} | |
}, | |
_addSeries: function(idx) { | |
var mixin = this.mixin("Bullet", this.base.append("svg").append("g")); | |
// Cache the prototype's implementation of `transform` so that it may | |
// be invoked from the overriding implementation. This is admittedly a | |
// bit of a hack, and it may point to a future improvement for d3.chart | |
var t = mixin.transform; | |
mixin.transform = function(data) { | |
return t.call(mixin, data[idx]); | |
}; | |
this.mixins.push(mixin); | |
}, | |
width: function(width) { | |
if (!arguments.length) { | |
return this._width; | |
} | |
this._width = width; | |
this.base.attr("width", width); | |
this.base.selectAll("svg").attr("width", width); | |
this.mixins.forEach(function(mixin) { | |
mixin.width(width); | |
}); | |
return this; | |
}, | |
height: function(height) { | |
if (!arguments.length) { | |
return this._height; | |
} | |
this._height = height; | |
this.base.selectAll("svg").attr("height", height); | |
this.mixins.forEach(function(mixin) { | |
mixin.height(height); | |
}); | |
return this; | |
}, | |
duration: function(duration) { | |
if (!arguments.length) { | |
return this._duration; | |
} | |
this._duration = duration; | |
this.mixins.forEach(function(mixin) { | |
mixin.duration(duration); | |
}); | |
}, | |
margin: function(margin) { | |
this.mixins.forEach(function(mixin) { | |
mixin.margin(margin); | |
}); | |
return this; | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{"title":"Revenue","subtitle":"US$, in thousands","ranges":[150,225,300],"measures":[220,270],"markers":[250]}, | |
{"title":"Profit","subtitle":"%","ranges":[20,25,30],"measures":[21,23],"markers":[26]}, | |
{"title":"Order Size","subtitle":"US$, average","ranges":[350,500,600],"measures":[100,320],"markers":[550]}, | |
{"title":"New Customers","subtitle":"count","ranges":[1400,2000,2500],"measures":[1000,1650],"markers":[2100]}, | |
{"title":"Satisfaction","subtitle":"out of 5","ranges":[3.5,4.25,5],"measures":[3.2,4.7],"markers":[4.4]} | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
margin: auto; | |
padding-top: 40px; | |
position: relative; | |
width: 960px; | |
} | |
button { | |
position: absolute; | |
right: 10px; | |
top: 10px; | |
} | |
.bullet { font: 10px sans-serif; } | |
.bullet .marker { stroke: #000; stroke-width: 2px; } | |
.bullet .tick line { stroke: #666; stroke-width: .5px; } | |
.bullet .range.s0 { fill: #eee; } | |
.bullet .range.s1 { fill: #ddd; } | |
.bullet .range.s2 { fill: #ccc; } | |
.bullet .measure.s0 { fill: lightsteelblue; } | |
.bullet .measure.s1 { fill: steelblue; } | |
.bullet .title { font-size: 14px; font-weight: bold; } | |
.bullet .subtitle { fill: #999; } | |
</style> | |
<script src="http://d3js.org/d3.v3.js"></script> | |
<script src="http://misoproject.com/js/d3.chart.js"></script> | |
</head> | |
<body> | |
<button>Update</button> | |
<script src="bullet-chart.js"></script> | |
<script src="bullets-chart.js"></script> | |
<script src="bullet-app.js"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment