This example is a rewrite of the Bullet Charts example from Mike Bostock.
By using a template the amount of code needed to create the SVG elements is reduced.
The construction and layout of the SVG elements are more recognizable than from code.
The SVG elements do contain more data-
attributes defining the (transformed) data. You decide which form you prefer.
Last active
September 30, 2018 14:25
-
-
Save ErikOnBike/dc95a68532f111da923ae533fc7d54d3 to your computer and use it in GitHub Desktop.
Rewrite of Mike Rostock's Bullet Charts gist using d3-template
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
license: gpl-3.0 |
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> | |
<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> | |
<button>Update</button> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://unpkg.com/d3-template-plugin/build/d3-template.min.js"></script> | |
<div id="template" data-repeat="{{d}}"> | |
<svg class="bullet" width="960" height="50"> | |
<g transform="translate(120,5)"> | |
<g data-repeat="{{d.ranges.sort(sortDescending)}}"> | |
<rect data-attr-class="{{`range s${i}`}}" width="0" data-attr-width="{{scale(d, this)}}" height="25" x="0" y="0"></rect> | |
</g> | |
<g data-repeat="{{d.measures.sort(sortDescending)}}"> | |
<rect data-attr-class="{{`measure s${i}`}}" width="0" data-attr-width="{{scale(d, this)}}" height="8.333333" x="0" y="8.333333"></rect> | |
</g> | |
<g data-repeat="{{d.markers.sort(sortDescending)}}"> | |
<line data-attr-class="{{`marker s${i}`}}" x1="0" x2="0" data-attr-x1="{{scale(d, this)}}" data-attr-x2="{{scale(d, this)}}" y1="4" y2="21"></line> | |
</g> | |
<g data-repeat="{{ticks(d)}}"> | |
<g class="tick" transform="translate(990,0)" data-attr-transform="{{`translate(${scale(d.x, this)},0)`}}"> | |
<line y1="25" y2="29"></line> | |
<text text-anchor="middle" dy="1em" y="29">{{d.text}}</text> | |
</g> | |
</g> | |
<g transform="translate(-6,12.5)" text-anchor="end"> | |
<text class="title">{{d.title}}</text> | |
<text class="subtitle" dy="1em">{{d.subtitle}}</text> | |
</g> | |
</g> | |
</svg> | |
</div> | |
<script> | |
var margin = {top: 5, right: 40, bottom: 20, left: 120}, | |
width = 960 - margin.left - margin.right, | |
height = 50 - margin.top - margin.bottom; | |
// Create template helper functions | |
function sortDescending(a, b) { | |
return b - a; | |
} | |
function scale(d, node) { | |
return d3.select(node.parentNode).datum().scale(d); | |
} | |
function ticks(d) { | |
return d.scale.ticks(8).map(function(tick) { | |
return { x: tick, text: d.scale.tickFormat(8)(tick) }; | |
}); | |
} | |
// Create template from DOM | |
var template = d3.select("#template").template(); | |
// Read data and render it | |
d3.json("bullets.json", function(error, data) { | |
if (error) throw error; | |
// Add scales to data items | |
data.forEach(function(item) { | |
addScale(item); | |
}); | |
// Render data onto template | |
template.render(data); | |
// Add event handler | |
d3.selectAll("button").on("click", function() { | |
// Update data | |
data.forEach(function(item) { | |
randomize(item); | |
addScale(item); | |
}); | |
// Render new data with transition | |
template.transition() | |
.duration(1000) | |
.render(data) | |
; | |
}); | |
}); | |
function addScale(d) { | |
d.scale = d3.scaleLinear() | |
.domain([ 0, Math.max( | |
Math.max.apply(null, d.ranges), | |
Math.max.apply(null, d.measures), | |
Math.max.apply(null, d.markers) | |
) ]) | |
.range([0, width]) | |
; | |
} | |
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)); | |
}; | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment