source: Dev/trunk/d3/src/chart/bullet.js @ 76

Last change on this file since 76 was 76, checked in by fpvanagthoven, 14 years ago

d3

File size: 6.3 KB
RevLine 
[76]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/
4d3.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
214function d3_chart_bulletRanges(d) {
215  return d.ranges;
216}
217
218function d3_chart_bulletMarkers(d) {
219  return d.markers;
220}
221
222function d3_chart_bulletMeasures(d) {
223  return d.measures;
224}
225
226function d3_chart_bulletTranslate(x) {
227  return function(d) {
228    return "translate(" + x(d) + ",0)";
229  };
230}
231
232function d3_chart_bulletWidth(x) {
233  var x0 = x(0);
234  return function(d) {
235    return Math.abs(x(d) - x0);
236  };
237}
Note: See TracBrowser for help on using the repository browser.