Skip to content

Instantly share code, notes, and snippets.

@mohanmca
Forked from mbostock/.block
Created May 24, 2019 22:41
Show Gist options
  • Select an option

  • Save mohanmca/95ad11ecb64cc9b3911004b7da4e772d to your computer and use it in GitHub Desktop.

Select an option

Save mohanmca/95ad11ecb64cc9b3911004b7da4e772d to your computer and use it in GitHub Desktop.

Revisions

  1. @mbostock mbostock revised this gist Feb 9, 2016. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions .block
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    license: gpl-3.0
  2. @mbostock mbostock revised this gist Oct 31, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -29,7 +29,7 @@

    </style>
    <button>Update</button>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <script src="//d3js.org/d3.v3.min.js"></script>
    <script src="bullet.js"></script>
    <script>

  3. @mbostock mbostock revised this gist Jun 11, 2015. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -29,7 +29,7 @@

    </style>
    <button>Update</button>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <script src="bullet.js"></script>
    <script>

    @@ -42,6 +42,8 @@
    .height(height);

    d3.json("bullets.json", function(error, data) {
    if (error) throw error;

    var svg = d3.select("body").selectAll("svg")
    .data(data)
    .enter().append("svg")
  4. @mbostock mbostock revised this gist Nov 13, 2012. 1 changed file with 0 additions and 0 deletions.
    Binary file added thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  5. @mbostock mbostock revised this gist Nov 12, 2012. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -5,6 +5,7 @@
    body {
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    margin: auto;
    padding-top: 40px;
    position: relative;
    width: 960px;
    }
  6. @mbostock mbostock revised this gist Nov 12, 2012. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -52,8 +52,8 @@
    .call(chart);

    var title = svg.append("g")
    .attr("text-anchor", "end")
    .attr("transform", "translate(-6," + (height - margin.top - margin.bottom) / 2 + ")");
    .style("text-anchor", "end")
    .attr("transform", "translate(-6," + height / 2 + ")");

    title.append("text")
    .attr("class", "title")
  7. @mbostock mbostock revised this gist Nov 12, 2012. 2 changed files with 20 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions bullets.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    [
    {"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]}
    ]
    13 changes: 13 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,19 @@
    <meta charset="utf-8">
    <style>

    body {
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    margin: auto;
    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; }
  8. @mbostock mbostock created this gist Nov 12, 2012.
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    Designed by Stephen Few, a bullet chart “provides a rich display of data in a small space.” A variation on a bar chart, bullet charts compare a given quantitative measure (such as profit or revenue) against qualitative ranges (e.g., poor, satisfactory, good) and related markers (e.g., the same measure a year ago). Layout inspired by [Stephen Few](http://www.perceptualedge.com/articles/misc/Bullet_Graph_Design_Spec.pdf). Implementation based on work by [Clint Ivy](http://projects.instantcognition.com/protovis/bulletchart/), Jamie Love of [N-Squared Software](http://www.nsquaredsoftware.com/) and [Jason Davies](http://www.jasondavies.com/). The "update" button randomizes the values slightly to demonstrate transitions.
    241 changes: 241 additions & 0 deletions bullet.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,241 @@
    (function() {

    // Chart design based on the recommendations of Stephen Few. Implementation
    // based on the work of Clint Ivy, Jamie Love, and Jason Davies.
    // http://projects.instantcognition.com/protovis/bulletchart/
    d3.bullet = function() {
    var orient = "left", // TODO top & bottom
    reverse = false,
    duration = 0,
    ranges = bulletRanges,
    markers = bulletMarkers,
    measures = bulletMeasures,
    width = 380,
    height = 30,
    tickFormat = null;

    // For each small multiple…
    function bullet(g) {
    g.each(function(d, i) {
    var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
    markerz = markers.call(this, d, i).slice().sort(d3.descending),
    measurez = measures.call(this, d, i).slice().sort(d3.descending),
    g = d3.select(this);

    // Compute the new x-scale.
    var x1 = d3.scale.linear()
    .domain([0, Math.max(rangez[0], markerz[0], measurez[0])])
    .range(reverse ? [width, 0] : [0, width]);

    // Retrieve the old x-scale, if this is an update.
    var x0 = this.__chart__ || d3.scale.linear()
    .domain([0, Infinity])
    .range(x1.range());

    // Stash the new scale.
    this.__chart__ = x1;

    // Derive width-scales from the x-scales.
    var w0 = bulletWidth(x0),
    w1 = bulletWidth(x1);

    // Update the range rects.
    var range = g.selectAll("rect.range")
    .data(rangez);

    range.enter().append("rect")
    .attr("class", function(d, i) { return "range s" + i; })
    .attr("width", w0)
    .attr("height", height)
    .attr("x", reverse ? x0 : 0)
    .transition()
    .duration(duration)
    .attr("width", w1)
    .attr("x", reverse ? x1 : 0);

    range.transition()
    .duration(duration)
    .attr("x", reverse ? x1 : 0)
    .attr("width", w1)
    .attr("height", height);

    // Update the measure rects.
    var measure = g.selectAll("rect.measure")
    .data(measurez);

    measure.enter().append("rect")
    .attr("class", function(d, i) { return "measure s" + i; })
    .attr("width", w0)
    .attr("height", height / 3)
    .attr("x", reverse ? x0 : 0)
    .attr("y", height / 3)
    .transition()
    .duration(duration)
    .attr("width", w1)
    .attr("x", reverse ? x1 : 0);

    measure.transition()
    .duration(duration)
    .attr("width", w1)
    .attr("height", height / 3)
    .attr("x", reverse ? x1 : 0)
    .attr("y", height / 3);

    // Update the marker lines.
    var marker = g.selectAll("line.marker")
    .data(markerz);

    marker.enter().append("line")
    .attr("class", "marker")
    .attr("x1", x0)
    .attr("x2", x0)
    .attr("y1", height / 6)
    .attr("y2", height * 5 / 6)
    .transition()
    .duration(duration)
    .attr("x1", x1)
    .attr("x2", x1);

    marker.transition()
    .duration(duration)
    .attr("x1", x1)
    .attr("x2", x1)
    .attr("y1", height / 6)
    .attr("y2", height * 5 / 6);

    // Compute the tick format.
    var format = tickFormat || x1.tickFormat(8);

    // Update the tick groups.
    var tick = g.selectAll("g.tick")
    .data(x1.ticks(8), function(d) {
    return this.textContent || format(d);
    });

    // Initialize the ticks with the old scale, x0.
    var tickEnter = tick.enter().append("g")
    .attr("class", "tick")
    .attr("transform", bulletTranslate(x0))
    .style("opacity", 1e-6);

    tickEnter.append("line")
    .attr("y1", height)
    .attr("y2", height * 7 / 6);

    tickEnter.append("text")
    .attr("text-anchor", "middle")
    .attr("dy", "1em")
    .attr("y", height * 7 / 6)
    .text(format);

    // Transition the entering ticks to the new scale, x1.
    tickEnter.transition()
    .duration(duration)
    .attr("transform", bulletTranslate(x1))
    .style("opacity", 1);

    // Transition the updating ticks to the new scale, x1.
    var tickUpdate = tick.transition()
    .duration(duration)
    .attr("transform", bulletTranslate(x1))
    .style("opacity", 1);

    tickUpdate.select("line")
    .attr("y1", height)
    .attr("y2", height * 7 / 6);

    tickUpdate.select("text")
    .attr("y", height * 7 / 6);

    // Transition the exiting ticks to the new scale, x1.
    tick.exit().transition()
    .duration(duration)
    .attr("transform", bulletTranslate(x1))
    .style("opacity", 1e-6)
    .remove();
    });
    d3.timer.flush();
    }

    // left, right, top, bottom
    bullet.orient = function(x) {
    if (!arguments.length) return orient;
    orient = x;
    reverse = orient == "right" || orient == "bottom";
    return bullet;
    };

    // ranges (bad, satisfactory, good)
    bullet.ranges = function(x) {
    if (!arguments.length) return ranges;
    ranges = x;
    return bullet;
    };

    // markers (previous, goal)
    bullet.markers = function(x) {
    if (!arguments.length) return markers;
    markers = x;
    return bullet;
    };

    // measures (actual, forecast)
    bullet.measures = function(x) {
    if (!arguments.length) return measures;
    measures = x;
    return bullet;
    };

    bullet.width = function(x) {
    if (!arguments.length) return width;
    width = x;
    return bullet;
    };

    bullet.height = function(x) {
    if (!arguments.length) return height;
    height = x;
    return bullet;
    };

    bullet.tickFormat = function(x) {
    if (!arguments.length) return tickFormat;
    tickFormat = x;
    return bullet;
    };

    bullet.duration = function(x) {
    if (!arguments.length) return duration;
    duration = x;
    return bullet;
    };

    return bullet;
    };

    function bulletRanges(d) {
    return d.ranges;
    }

    function bulletMarkers(d) {
    return d.markers;
    }

    function bulletMeasures(d) {
    return d.measures;
    }

    function bulletTranslate(x) {
    return function(d) {
    return "translate(" + x(d) + ",0)";
    };
    }

    function bulletWidth(x) {
    var x0 = x(0);
    return function(d) {
    return Math.abs(x(d) - x0);
    };
    }

    })();
    74 changes: 74 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>

    .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="http://d3js.org/d3.v3.min.js"></script>
    <script src="bullet.js"></script>
    <script>

    var margin = {top: 5, right: 40, bottom: 20, left: 120},
    width = 960 - margin.left - margin.right,
    height = 50 - margin.top - margin.bottom;

    var chart = d3.bullet()
    .width(width)
    .height(height);

    d3.json("bullets.json", function(error, data) {
    var svg = d3.select("body").selectAll("svg")
    .data(data)
    .enter().append("svg")
    .attr("class", "bullet")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .call(chart);

    var title = svg.append("g")
    .attr("text-anchor", "end")
    .attr("transform", "translate(-6," + (height - margin.top - margin.bottom) / 2 + ")");

    title.append("text")
    .attr("class", "title")
    .text(function(d) { return d.title; });

    title.append("text")
    .attr("class", "subtitle")
    .attr("dy", "1em")
    .text(function(d) { return d.subtitle; });

    d3.selectAll("button").on("click", function() {
    svg.datum(randomize).call(chart.duration(1000)); // TODO automatic transition
    });
    });

    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>