diff --git a/bullet/bullet.js b/bullet/bullet.js index 3c7e514..643ae4a 100644 --- a/bullet/bullet.js +++ b/bullet/bullet.js @@ -4,15 +4,16 @@ // 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 + var orient = "left", reverse = false, + vertical = false, duration = 0, ranges = bulletRanges, markers = bulletMarkers, measures = bulletMeasures, width = 380, height = 30, - tickFormat = null; + xAxis = d3.svg.axis(); // For each small multiple… function bullet(g) { @@ -20,12 +21,25 @@ d3.bullet = function() { 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); + g = d3.select(this), + extentX, + extentY; + + var wrap = g.select("g.wrap"); + if (wrap.empty()) wrap = g.append("g").attr("class", "wrap"); + + if (vertical) { + extentX = height, extentY = width; + wrap.attr("transform", "rotate(90)translate(0," + -width + ")"); + } else { + extentX = width, extentY = height; + wrap.attr("transform", "translate(0)"); + } // 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]); + .range(reverse ? [extentX, 0] : [0, extentX]); // Retrieve the old x-scale, if this is an update. var x0 = this.__chart__ || d3.scale.linear() @@ -40,13 +54,13 @@ d3.bullet = function() { w1 = bulletWidth(x1); // Update the range rects. - var range = g.selectAll("rect.range") + var range = wrap.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("height", extentY) .attr("x", reverse ? x0 : 0) .transition() .duration(duration) @@ -57,18 +71,18 @@ d3.bullet = function() { .duration(duration) .attr("x", reverse ? x1 : 0) .attr("width", w1) - .attr("height", height); + .attr("height", extentY); // Update the measure rects. - var measure = g.selectAll("rect.measure") + var measure = wrap.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("height", extentY / 3) .attr("x", reverse ? x0 : 0) - .attr("y", height / 3) + .attr("y", extentY / 3) .transition() .duration(duration) .attr("width", w1) @@ -77,20 +91,20 @@ d3.bullet = function() { measure.transition() .duration(duration) .attr("width", w1) - .attr("height", height / 3) + .attr("height", extentY / 3) .attr("x", reverse ? x1 : 0) - .attr("y", height / 3); + .attr("y", extentY / 3); // Update the marker lines. - var marker = g.selectAll("line.marker") + var marker = wrap.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) + .attr("y1", extentY / 6) + .attr("y2", extentY * 5 / 6) .transition() .duration(duration) .attr("x1", x1) @@ -100,117 +114,67 @@ d3.bullet = function() { .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); + .attr("y1", extentY / 6) + .attr("y2", extentY * 5 / 6); - 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() + var axis = g.selectAll("g.axis").data([0]); + axis.enter().append("g").attr("class", "axis"); + axis.transition() .duration(duration) - .attr("transform", bulletTranslate(x1)) - .style("opacity", 1e-6) - .remove(); + .call(xAxis.scale(x1)); }); d3.timer.flush(); } // left, right, top, bottom - bullet.orient = function(x) { + bullet.orient = function(_) { if (!arguments.length) return orient; - orient = x; + orient = _ + ""; reverse = orient == "right" || orient == "bottom"; + xAxis.orient((vertical = orient == "top" || orient == "bottom") ? "left" : "bottom"); return bullet; }; // ranges (bad, satisfactory, good) - bullet.ranges = function(x) { + bullet.ranges = function(_) { if (!arguments.length) return ranges; - ranges = x; + ranges = _; return bullet; }; // markers (previous, goal) - bullet.markers = function(x) { + bullet.markers = function(_) { if (!arguments.length) return markers; - markers = x; + markers = _; return bullet; }; // measures (actual, forecast) - bullet.measures = function(x) { + bullet.measures = function(_) { if (!arguments.length) return measures; - measures = x; + measures = _; return bullet; }; - bullet.width = function(x) { + bullet.width = function(_) { if (!arguments.length) return width; - width = x; + width = +_; return bullet; }; - bullet.height = function(x) { + bullet.height = function(_) { if (!arguments.length) return height; - height = x; - return bullet; - }; - - bullet.tickFormat = function(x) { - if (!arguments.length) return tickFormat; - tickFormat = x; + height = +_; return bullet; }; - bullet.duration = function(x) { + bullet.duration = function(_) { if (!arguments.length) return duration; - duration = x; + duration = +_; return bullet; }; - return bullet; + return d3.rebind(bullet, xAxis, "tickFormat"); }; function bulletRanges(d) {