toolkit/exemples/couple/javascript/d3/src/chart/bullet.js
changeset 47 c0b4a8b5a012
equal deleted inserted replaced
46:efd9c589177a 47:c0b4a8b5a012
       
     1 // Chart design based on the recommendations of Stephen Few. Implementation
       
     2 // based on the work of Clint Ivy, Jamie Love, and Jason Davies.
       
     3 // http://projects.instantcognition.com/protovis/bulletchart/
       
     4 d3.chart.bullet = function() {
       
     5   var orient = "left", // TODO top & bottom
       
     6       reverse = false,
       
     7       duration = 0,
       
     8       ranges = d3_chart_bulletRanges,
       
     9       markers = d3_chart_bulletMarkers,
       
    10       measures = d3_chart_bulletMeasures,
       
    11       width = 380,
       
    12       height = 30,
       
    13       tickFormat = null;
       
    14 
       
    15   // For each small multiple…
       
    16   function bullet(g) {
       
    17     g.each(function(d, i) {
       
    18       var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
       
    19           markerz = markers.call(this, d, i).slice().sort(d3.descending),
       
    20           measurez = measures.call(this, d, i).slice().sort(d3.descending),
       
    21           g = d3.select(this);
       
    22 
       
    23       // Compute the new x-scale.
       
    24       var x1 = d3.scale.linear()
       
    25           .domain([0, Math.max(rangez[0], markerz[0], measurez[0])])
       
    26           .range(reverse ? [width, 0] : [0, width]);
       
    27 
       
    28       // Retrieve the old x-scale, if this is an update.
       
    29       var x0 = this.__chart__ || d3.scale.linear()
       
    30           .domain([0, Infinity])
       
    31           .range(x1.range());
       
    32 
       
    33       // Stash the new scale.
       
    34       this.__chart__ = x1;
       
    35 
       
    36       // Derive width-scales from the x-scales.
       
    37       var w0 = d3_chart_bulletWidth(x0),
       
    38           w1 = d3_chart_bulletWidth(x1);
       
    39 
       
    40       // Update the range rects.
       
    41       var range = g.selectAll("rect.range")
       
    42           .data(rangez);
       
    43 
       
    44       range.enter().append("svg:rect")
       
    45           .attr("class", function(d, i) { return "range s" + i; })
       
    46           .attr("width", w0)
       
    47           .attr("height", height)
       
    48           .attr("x", reverse ? x0 : 0)
       
    49         .transition()
       
    50           .duration(duration)
       
    51           .attr("width", w1)
       
    52           .attr("x", reverse ? x1 : 0);
       
    53 
       
    54       range.transition()
       
    55           .duration(duration)
       
    56           .attr("x", reverse ? x1 : 0)
       
    57           .attr("width", w1)
       
    58           .attr("height", height);
       
    59 
       
    60       // Update the measure rects.
       
    61       var measure = g.selectAll("rect.measure")
       
    62           .data(measurez);
       
    63 
       
    64       measure.enter().append("svg:rect")
       
    65           .attr("class", function(d, i) { return "measure s" + i; })
       
    66           .attr("width", w0)
       
    67           .attr("height", height / 3)
       
    68           .attr("x", reverse ? x0 : 0)
       
    69           .attr("y", height / 3)
       
    70         .transition()
       
    71           .duration(duration)
       
    72           .attr("width", w1)
       
    73           .attr("x", reverse ? x1 : 0);
       
    74 
       
    75       measure.transition()
       
    76           .duration(duration)
       
    77           .attr("width", w1)
       
    78           .attr("height", height / 3)
       
    79           .attr("x", reverse ? x1 : 0)
       
    80           .attr("y", height / 3);
       
    81 
       
    82       // Update the marker lines.
       
    83       var marker = g.selectAll("line.marker")
       
    84           .data(markerz);
       
    85 
       
    86       marker.enter().append("svg:line")
       
    87           .attr("class", "marker")
       
    88           .attr("x1", x0)
       
    89           .attr("x2", x0)
       
    90           .attr("y1", height / 6)
       
    91           .attr("y2", height * 5 / 6)
       
    92         .transition()
       
    93           .duration(duration)
       
    94           .attr("x1", x1)
       
    95           .attr("x2", x1);
       
    96 
       
    97       marker.transition()
       
    98           .duration(duration)
       
    99           .attr("x1", x1)
       
   100           .attr("x2", x1)
       
   101           .attr("y1", height / 6)
       
   102           .attr("y2", height * 5 / 6);
       
   103 
       
   104       // Compute the tick format.
       
   105       var format = tickFormat || x1.tickFormat(8);
       
   106 
       
   107       // Update the tick groups.
       
   108       var tick = g.selectAll("g.tick")
       
   109           .data(x1.ticks(8), function(d) {
       
   110             return this.textContent || format(d);
       
   111           });
       
   112 
       
   113       // Initialize the ticks with the old scale, x0.
       
   114       var tickEnter = tick.enter().append("svg:g")
       
   115           .attr("class", "tick")
       
   116           .attr("transform", d3_chart_bulletTranslate(x0))
       
   117           .style("opacity", 1e-6);
       
   118 
       
   119       tickEnter.append("svg:line")
       
   120           .attr("y1", height)
       
   121           .attr("y2", height * 7 / 6);
       
   122 
       
   123       tickEnter.append("svg:text")
       
   124           .attr("text-anchor", "middle")
       
   125           .attr("dy", "1em")
       
   126           .attr("y", height * 7 / 6)
       
   127           .text(format);
       
   128 
       
   129       // Transition the entering ticks to the new scale, x1.
       
   130       tickEnter.transition()
       
   131           .duration(duration)
       
   132           .attr("transform", d3_chart_bulletTranslate(x1))
       
   133           .style("opacity", 1);
       
   134 
       
   135       // Transition the updating ticks to the new scale, x1.
       
   136       var tickUpdate = tick.transition()
       
   137           .duration(duration)
       
   138           .attr("transform", d3_chart_bulletTranslate(x1))
       
   139           .style("opacity", 1);
       
   140 
       
   141       tickUpdate.select("line")
       
   142           .attr("y1", height)
       
   143           .attr("y2", height * 7 / 6);
       
   144 
       
   145       tickUpdate.select("text")
       
   146           .attr("y", height * 7 / 6);
       
   147 
       
   148       // Transition the exiting ticks to the new scale, x1.
       
   149       tick.exit().transition()
       
   150           .duration(duration)
       
   151           .attr("transform", d3_chart_bulletTranslate(x1))
       
   152           .style("opacity", 1e-6)
       
   153           .remove();
       
   154     });
       
   155     d3.timer.flush();
       
   156   }
       
   157 
       
   158   // left, right, top, bottom
       
   159   bullet.orient = function(x) {
       
   160     if (!arguments.length) return orient;
       
   161     orient = x;
       
   162     reverse = orient == "right" || orient == "bottom";
       
   163     return bullet;
       
   164   };
       
   165 
       
   166   // ranges (bad, satisfactory, good)
       
   167   bullet.ranges = function(x) {
       
   168     if (!arguments.length) return ranges;
       
   169     ranges = x;
       
   170     return bullet;
       
   171   };
       
   172 
       
   173   // markers (previous, goal)
       
   174   bullet.markers = function(x) {
       
   175     if (!arguments.length) return markers;
       
   176     markers = x;
       
   177     return bullet;
       
   178   };
       
   179 
       
   180   // measures (actual, forecast)
       
   181   bullet.measures = function(x) {
       
   182     if (!arguments.length) return measures;
       
   183     measures = x;
       
   184     return bullet;
       
   185   };
       
   186 
       
   187   bullet.width = function(x) {
       
   188     if (!arguments.length) return width;
       
   189     width = x;
       
   190     return bullet;
       
   191   };
       
   192 
       
   193   bullet.height = function(x) {
       
   194     if (!arguments.length) return height;
       
   195     height = x;
       
   196     return bullet;
       
   197   };
       
   198 
       
   199   bullet.tickFormat = function(x) {
       
   200     if (!arguments.length) return tickFormat;
       
   201     tickFormat = x;
       
   202     return bullet;
       
   203   };
       
   204 
       
   205   bullet.duration = function(x) {
       
   206     if (!arguments.length) return duration;
       
   207     duration = x;
       
   208     return bullet;
       
   209   };
       
   210 
       
   211   return bullet;
       
   212 };
       
   213 
       
   214 function d3_chart_bulletRanges(d) {
       
   215   return d.ranges;
       
   216 }
       
   217 
       
   218 function d3_chart_bulletMarkers(d) {
       
   219   return d.markers;
       
   220 }
       
   221 
       
   222 function d3_chart_bulletMeasures(d) {
       
   223   return d.measures;
       
   224 }
       
   225 
       
   226 function d3_chart_bulletTranslate(x) {
       
   227   return function(d) {
       
   228     return "translate(" + x(d) + ",0)";
       
   229   };
       
   230 }
       
   231 
       
   232 function d3_chart_bulletWidth(x) {
       
   233   var x0 = x(0);
       
   234   return function(d) {
       
   235     return Math.abs(x(d) - x0);
       
   236   };
       
   237 }