Extract title & subtitle from bullet chart.
It's nice, but I think it's a bit more flexible to not have it as part of the chart specification. This way, people can define titles however they like. It might be nice to take a similar approach with reference ticks in the future.
This commit is contained in:
Родитель
e1399b1182
Коммит
33260423a3
125
d3.chart.js
125
d3.chart.js
|
@ -12,19 +12,17 @@
|
|||
d3.chart.bullet = function() {
|
||||
var orient = "left",
|
||||
duration = 0,
|
||||
title = d3_chart_bulletTitle,
|
||||
subtitle = d3_chart_bulletSubtitle,
|
||||
ranges = d3_chart_bulletRanges,
|
||||
markers = d3_chart_bulletMarkers,
|
||||
measures = d3_chart_bulletMeasures,
|
||||
width = 380,
|
||||
height = 30,
|
||||
x0 = null,
|
||||
x1 = d3.scale.linear(),
|
||||
x0,
|
||||
tickFormat = d3.format(",.0f");
|
||||
|
||||
function bullet(g) {
|
||||
var max = 0;
|
||||
var max = 0,
|
||||
reverse = orient == "right" || orient == "bottom";
|
||||
|
||||
// Convert the data to a standardized representation, and also compute the
|
||||
// maximum value, needed to standardize the x-scale across multiples.
|
||||
|
@ -34,8 +32,6 @@ d3.chart.bullet = function() {
|
|||
z = measures.call(this, d, i).slice().sort(d3.descending);
|
||||
max = Math.max(max, r[0], m[0], z[0]);
|
||||
return {
|
||||
title: [title.call(this, d, i)],
|
||||
subtitle: [subtitle.call(this, d, i)],
|
||||
ranges: r,
|
||||
markers: m,
|
||||
measures: z,
|
||||
|
@ -43,87 +39,64 @@ d3.chart.bullet = function() {
|
|||
};
|
||||
});
|
||||
|
||||
var reversed = orient === "right" || orient === "bottom";
|
||||
|
||||
// Update the x-scales.
|
||||
var xRange = reversed ? [width, 0] : [0, width];
|
||||
x1.domain([0, max]).range(xRange);
|
||||
if (x0 === null)
|
||||
x0 = d3.scale.linear().domain([0, Infinity]).range(xRange);
|
||||
var x1 = d3.scale.linear()
|
||||
.domain([0, max])
|
||||
.range(reverse ? [width, 0] : [0, width]);
|
||||
|
||||
// Update the title.
|
||||
var titleText = g.select("text.title");
|
||||
// Initialize the old scale, if this is the first render.
|
||||
if (!x0) x0 = d3.scale.linear()
|
||||
.domain([0, Infinity])
|
||||
.range(x1.range());
|
||||
|
||||
if (titleText.empty())
|
||||
titleText = g.append("svg:text")
|
||||
.attr("class", "title")
|
||||
.attr("dy", "1em")
|
||||
.style("font-weight", "bold");
|
||||
titleText
|
||||
.text(d3_chart_bulletTitle);
|
||||
|
||||
// Update the subtitle.
|
||||
var subtitleText = g.select("text.subtitle");
|
||||
if (subtitleText.empty())
|
||||
subtitleText = g.append("svg:text")
|
||||
.attr("class", "subtitle")
|
||||
.attr("dy", "2.5em")
|
||||
.style("font-size", ".7em")
|
||||
.style("fill", "#999");
|
||||
subtitleText
|
||||
.text(d3_chart_bulletSubtitle);
|
||||
|
||||
// Update the chart.
|
||||
var chart = g.select("g.chart");
|
||||
if (chart.empty())
|
||||
chart = g.append("svg:g")
|
||||
.attr("class", "chart")
|
||||
.attr("transform", "translate(120)");
|
||||
// Derive width-scales from the x-scales.
|
||||
var w0 = d3_chart_bulletWidth(x0),
|
||||
w1 = d3_chart_bulletWidth(x1);
|
||||
|
||||
// Update the range rects.
|
||||
var range = chart.selectAll("rect.range")
|
||||
var range = g.selectAll("rect.range")
|
||||
.data(d3_chart_bulletRanges);
|
||||
|
||||
range.enter().append("svg:rect")
|
||||
.attr("class", function(d, i) { return "range s" + i; })
|
||||
.attr("width", function(d) { return Math.abs(x0(d) - x0(0)); })
|
||||
.attr("width", w0)
|
||||
.attr("height", height)
|
||||
.attr("x", reversed ? x0 : 0)
|
||||
.attr("x", reverse ? x0 : 0)
|
||||
.transition()
|
||||
.duration(duration)
|
||||
.attr("width", function(d) { return Math.abs(x1(d) - x1(0)); })
|
||||
.attr("x", reversed ? x1 : 0);
|
||||
.attr("width", w1)
|
||||
.attr("x", reverse ? x1 : 0);
|
||||
|
||||
range.transition()
|
||||
.duration(duration)
|
||||
.attr("x", reversed ? x1 : 0)
|
||||
.attr("width", function(d) { return Math.abs(x1(d) - x1(0)); })
|
||||
.attr("x", reverse ? x1 : 0)
|
||||
.attr("width", w1)
|
||||
.attr("height", height);
|
||||
|
||||
// Update the measure rects.
|
||||
var measure = chart.selectAll("rect.measure")
|
||||
var measure = g.selectAll("rect.measure")
|
||||
.data(d3_chart_bulletMeasures);
|
||||
|
||||
measure.enter().append("svg:rect")
|
||||
.attr("class", function(d, i) { return "measure s" + i; })
|
||||
.attr("width", function(d) { return Math.abs(x0(d) - x0(0)); })
|
||||
.attr("width", w0)
|
||||
.attr("height", height / 3)
|
||||
.attr("x", reversed ? x0 : 0)
|
||||
.attr("x", reverse ? x0 : 0)
|
||||
.attr("y", height / 3)
|
||||
.transition()
|
||||
.duration(duration)
|
||||
.attr("width", function(d) { return Math.abs(x1(d) - x1(0)); })
|
||||
.attr("x", reversed ? x1 : 0);
|
||||
.attr("width", w1)
|
||||
.attr("x", reverse ? x1 : 0);
|
||||
|
||||
measure.transition()
|
||||
.duration(duration)
|
||||
.attr("width", function(d) { return Math.abs(x1(d) - x1(0)); })
|
||||
.attr("width", w1)
|
||||
.attr("height", height / 3)
|
||||
.attr("x", reversed ? x1 : 0)
|
||||
.attr("x", reverse ? x1 : 0)
|
||||
.attr("y", height / 3);
|
||||
|
||||
// Update the marker lines.
|
||||
var marker = chart.selectAll("line.marker")
|
||||
var marker = g.selectAll("line.marker")
|
||||
.data(d3_chart_bulletMarkers);
|
||||
|
||||
marker.enter().append("svg:line")
|
||||
|
@ -145,19 +118,13 @@ d3.chart.bullet = function() {
|
|||
.attr("y2", height * 5 / 6);
|
||||
|
||||
// Update the tick groups.
|
||||
var tick = chart.selectAll("g.tick")
|
||||
var tick = g.selectAll("g.tick")
|
||||
.data(x1.ticks(8), tickFormat);
|
||||
|
||||
var translate = function(f) {
|
||||
return function(d) {
|
||||
return "translate(" + f(d) + ")";
|
||||
};
|
||||
};
|
||||
|
||||
// Initialize the ticks with the old scale, x0.
|
||||
var tickEnter = tick.enter().append("svg:g")
|
||||
.attr("class", "tick")
|
||||
.attr("transform", translate(x0))
|
||||
.attr("transform", d3_chart_bulletTranslate(x0))
|
||||
.attr("opacity", 1e-6);
|
||||
|
||||
tickEnter.append("svg:line")
|
||||
|
@ -173,13 +140,13 @@ d3.chart.bullet = function() {
|
|||
// Transition the entering ticks to the new scale, x1.
|
||||
tickEnter.transition()
|
||||
.duration(duration)
|
||||
.attr("transform", translate(x1))
|
||||
.attr("transform", d3_chart_bulletTranslate(x1))
|
||||
.attr("opacity", 1);
|
||||
|
||||
// Transition the updating ticks to the new scale, x1.
|
||||
var tickUpdate = tick.transition()
|
||||
.duration(duration)
|
||||
.attr("transform", translate(x1))
|
||||
.attr("transform", d3_chart_bulletTranslate(x1))
|
||||
.attr("opacity", 1);
|
||||
|
||||
tickUpdate.select("line")
|
||||
|
@ -192,13 +159,13 @@ d3.chart.bullet = function() {
|
|||
// Transition the exiting ticks to the new scale, x1.
|
||||
tick.exit().transition()
|
||||
.duration(duration)
|
||||
.attr("transform", translate(x1))
|
||||
.attr("transform", d3_chart_bulletTranslate(x1))
|
||||
.attr("opacity", 1e-6)
|
||||
.remove();
|
||||
|
||||
// Lastly, restore the original data and update the previous scale!
|
||||
g.map(d3_chart_bulletData);
|
||||
x0.domain([0, max]).range(xRange);
|
||||
x0 = x1;
|
||||
}
|
||||
|
||||
// left, right, top, bottom
|
||||
|
@ -228,8 +195,7 @@ d3.chart.bullet = function() {
|
|||
|
||||
bullet.width = function(x) {
|
||||
if (!arguments.length) return width;
|
||||
x0.range([0, width = x]);
|
||||
x1.range([0, width]);
|
||||
width = x;
|
||||
return bullet;
|
||||
};
|
||||
|
||||
|
@ -254,14 +220,6 @@ d3.chart.bullet = function() {
|
|||
return bullet;
|
||||
};
|
||||
|
||||
function d3_chart_bulletTitle(d) {
|
||||
return d.title;
|
||||
}
|
||||
|
||||
function d3_chart_bulletSubtitle(d) {
|
||||
return d.subtitle;
|
||||
}
|
||||
|
||||
function d3_chart_bulletRanges(d) {
|
||||
return d.ranges;
|
||||
}
|
||||
|
@ -277,4 +235,17 @@ function d3_chart_bulletMeasures(d) {
|
|||
function d3_chart_bulletData(d) {
|
||||
return d.data;
|
||||
}
|
||||
|
||||
function d3_chart_bulletTranslate(x) {
|
||||
return function(d) {
|
||||
return "translate(" + x(d) + ",0)";
|
||||
};
|
||||
}
|
||||
|
||||
function d3_chart_bulletWidth(x) {
|
||||
var x0 = x(0);
|
||||
return function(d) {
|
||||
return Math.abs(x(d) - x0);
|
||||
};
|
||||
}
|
||||
})()
|
|
@ -1 +1 @@
|
|||
(function(){function f(a){return a.data}function e(a){return a.measures}function d(a){return a.markers}function c(a){return a.ranges}function b(a){return a.subtitle}function a(a){return a.title}d3.chart={},d3.chart.bullet=function(){function s(s){var t=0;s.map(function(a,b){var c=k.call(this,a,b).slice().sort(d3.descending),d=l.call(this,a,b).slice().sort(d3.descending),e=m.call(this,a,b).slice().sort(d3.descending);t=Math.max(t,c[0],d[0],e[0]);return{title:[i.call(this,a,b)],subtitle:[j.call(this,a,b)],ranges:c,markers:d,measures:e,data:a}});var u=g==="right"||g==="bottom",v=u?[n,0]:[0,n];q.domain([0,t]).range(v),p===null&&(p=d3.scale.linear().domain([0,Infinity]).range(v));var w=s.select("text.title");w.empty()&&(w=s.append("svg:text").attr("class","title").attr("dy","1em").style("font-weight","bold")),w.text(a);var x=s.select("text.subtitle");x.empty()&&(x=s.append("svg:text").attr("class","subtitle").attr("dy","2.5em").style("font-size",".7em").style("fill","#999")),x.text(b);var y=s.select("g.chart");y.empty()&&(y=s.append("svg:g").attr("class","chart").attr("transform","translate(120)"));var z=y.selectAll("rect.range").data(c);z.enter().append("svg:rect").attr("class",function(a,b){return"range s"+b}).attr("width",function(a){return Math.abs(p(a)-p(0))}).attr("height",o).attr("x",u?p:0).transition().duration(h).attr("width",function(a){return Math.abs(q(a)-q(0))}).attr("x",u?q:0),z.transition().duration(h).attr("x",u?q:0).attr("width",function(a){return Math.abs(q(a)-q(0))}).attr("height",o);var A=y.selectAll("rect.measure").data(e);A.enter().append("svg:rect").attr("class",function(a,b){return"measure s"+b}).attr("width",function(a){return Math.abs(p(a)-p(0))}).attr("height",o/3).attr("x",u?p:0).attr("y",o/3).transition().duration(h).attr("width",function(a){return Math.abs(q(a)-q(0))}).attr("x",u?q:0),A.transition().duration(h).attr("width",function(a){return Math.abs(q(a)-q(0))}).attr("height",o/3).attr("x",u?q:0).attr("y",o/3);var B=y.selectAll("line.marker").data(d);B.enter().append("svg:line").attr("class","marker").attr("x1",p).attr("x2",p).attr("y1",o/6).attr("y2",o*5/6).transition().duration(h).attr("x1",q).attr("x2",q),B.transition().duration(h).attr("x1",q).attr("x2",q).attr("y1",o/6).attr("y2",o*5/6);var C=y.selectAll("g.tick").data(q.ticks(8),r),D=function(a){return function(b){return"translate("+a(b)+")"}},E=C.enter().append("svg:g").attr("class","tick").attr("transform",D(p)).attr("opacity",1e-6);E.append("svg:line").attr("y1",o).attr("y2",o*7/6),E.append("svg:text").attr("text-anchor","middle").attr("dy","1em").attr("y",o*7/6).text(r),E.transition().duration(h).attr("transform",D(q)).attr("opacity",1);var F=C.transition().duration(h).attr("transform",D(q)).attr("opacity",1);F.select("line").attr("y1",o).attr("y2",o*7/6),F.select("text").attr("y",o*7/6),C.exit().transition().duration(h).attr("transform",D(q)).attr("opacity",1e-6).remove(),s.map(f),p.domain([0,t]).range(v)}var g="left",h=0,i=a,j=b,k=c,l=d,m=e,n=380,o=30,p=null,q=d3.scale.linear(),r=d3.format(",.0f");s.orient=function(a){if(!arguments.length)return g;g=a;return s},s.ranges=function(a){if(!arguments.length)return k;k=a;return s},s.markers=function(a){if(!arguments.length)return l;l=a;return s},s.measures=function(a){if(!arguments.length)return m;m=a;return s},s.width=function(a){if(!arguments.length)return n;p.range([0,n=a]),q.range([0,n]);return s},s.height=function(a){if(!arguments.length)return o;o=a;return s},s.tickFormat=function(a){if(!arguments.length)return r;r=a;return s},s.duration=function(a){if(!arguments.length)return h;h=a;return s};return s}})()
|
||||
(function(){function f(a){var b=a(0);return function(c){return Math.abs(a(c)-b)}}function e(a){return function(b){return"translate("+a(b)+",0)"}}function d(a){return a.data}function c(a){return a.measures}function b(a){return a.markers}function a(a){return a.ranges}d3.chart={},d3.chart.bullet=function(){function p(p){var q=0,r=g=="right"||g=="bottom";p.map(function(a,b){var c=i.call(this,a,b).slice().sort(d3.descending),d=j.call(this,a,b).slice().sort(d3.descending),e=k.call(this,a,b).slice().sort(d3.descending);q=Math.max(q,c[0],d[0],e[0]);return{ranges:c,markers:d,measures:e,data:a}});var s=d3.scale.linear().domain([0,q]).range(r?[l,0]:[0,l]);n||(n=d3.scale.linear().domain([0,Infinity]).range(s.range()));var t=f(n),u=f(s),v=p.selectAll("rect.range").data(a);v.enter().append("svg:rect").attr("class",function(a,b){return"range s"+b}).attr("width",t).attr("height",m).attr("x",r?n:0).transition().duration(h).attr("width",u).attr("x",r?s:0),v.transition().duration(h).attr("x",r?s:0).attr("width",u).attr("height",m);var w=p.selectAll("rect.measure").data(c);w.enter().append("svg:rect").attr("class",function(a,b){return"measure s"+b}).attr("width",t).attr("height",m/3).attr("x",r?n:0).attr("y",m/3).transition().duration(h).attr("width",u).attr("x",r?s:0),w.transition().duration(h).attr("width",u).attr("height",m/3).attr("x",r?s:0).attr("y",m/3);var x=p.selectAll("line.marker").data(b);x.enter().append("svg:line").attr("class","marker").attr("x1",n).attr("x2",n).attr("y1",m/6).attr("y2",m*5/6).transition().duration(h).attr("x1",s).attr("x2",s),x.transition().duration(h).attr("x1",s).attr("x2",s).attr("y1",m/6).attr("y2",m*5/6);var y=p.selectAll("g.tick").data(s.ticks(8),o),z=y.enter().append("svg:g").attr("class","tick").attr("transform",e(n)).attr("opacity",1e-6);z.append("svg:line").attr("y1",m).attr("y2",m*7/6),z.append("svg:text").attr("text-anchor","middle").attr("dy","1em").attr("y",m*7/6).text(o),z.transition().duration(h).attr("transform",e(s)).attr("opacity",1);var A=y.transition().duration(h).attr("transform",e(s)).attr("opacity",1);A.select("line").attr("y1",m).attr("y2",m*7/6),A.select("text").attr("y",m*7/6),y.exit().transition().duration(h).attr("transform",e(s)).attr("opacity",1e-6).remove(),p.map(d),n=s}var g="left",h=0,i=a,j=b,k=c,l=380,m=30,n,o=d3.format(",.0f");p.orient=function(a){if(!arguments.length)return g;g=a;return p},p.ranges=function(a){if(!arguments.length)return i;i=a;return p},p.markers=function(a){if(!arguments.length)return j;j=a;return p},p.measures=function(a){if(!arguments.length)return k;k=a;return p},p.width=function(a){if(!arguments.length)return l;l=a;return p},p.height=function(a){if(!arguments.length)return m;m=a;return p},p.tickFormat=function(a){if(!arguments.length)return o;o=a;return p},p.duration=function(a){if(!arguments.length)return h;h=a;return p};return p}})()
|
|
@ -8,42 +8,65 @@
|
|||
<link type="text/css" rel="stylesheet" href="bullet.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<input type="button" value="Randomize!" onclick="randomize()"><br>
|
||||
<script type="text/javascript">
|
||||
|
||||
var bullet = function(orient) {
|
||||
d3.select(this).selectAll("g.bullet")
|
||||
.call(d3.chart.bullet()
|
||||
.orient(orient)
|
||||
.duration(1000));
|
||||
};
|
||||
var w = 500,
|
||||
h = 50,
|
||||
m = [5, 40, 20, 120]; // top right bottom left
|
||||
|
||||
var vis = d3.select("body").selectAll("svg")
|
||||
.data(["left", "right", "top", "bottom"])
|
||||
.enter().append("svg:svg")
|
||||
.attr("width", 520)
|
||||
.attr("height", 300)
|
||||
.append("svg:g")
|
||||
.attr("transform", "translate(10,0)");
|
||||
var bullet = d3.chart.bullet()
|
||||
.width(w - m[1] - m[3])
|
||||
.height(h - m[0] - m[2])
|
||||
.duration(1000);
|
||||
|
||||
vis.selectAll("g.bullet")
|
||||
var vis = d3.select("body").append("svg:svg")
|
||||
.attr("width", w)
|
||||
.attr("height", h * bullets.length);
|
||||
|
||||
var g = vis.selectAll("g.bullet")
|
||||
.data(bullets)
|
||||
.enter().append("svg:g")
|
||||
.attr("class", "bullet")
|
||||
.attr("transform", function(d, i) { return "translate(0," + (i * 60) + ")"; })
|
||||
.attr("transform", function(d, i) {
|
||||
return "translate(" + m[3] + "," + (i * h + m[0]) + ")";
|
||||
});
|
||||
|
||||
vis.each(bullet);
|
||||
g.append("svg:text")
|
||||
.attr("class", "title")
|
||||
.attr("text-anchor", "end")
|
||||
.attr("dx", "-6")
|
||||
.attr("y", bullet.height() / 2)
|
||||
.text(function(d) { return d.title; });
|
||||
|
||||
g.append("svg:text")
|
||||
.attr("class", "subtitle")
|
||||
.attr("text-anchor", "end")
|
||||
.attr("dx", "-6")
|
||||
.attr("dy", "1em")
|
||||
.attr("y", bullet.height() / 2)
|
||||
.text(function(d) { return d.subtitle; });
|
||||
|
||||
vis.each(refresh);
|
||||
|
||||
function refresh() {
|
||||
d3.select(this).selectAll("g.bullet").call(bullet);
|
||||
}
|
||||
|
||||
function randomize() {
|
||||
for (var i = 0, ii = bullets.length; i < ii; i++) {
|
||||
var b = bullets[i];
|
||||
b.ranges = b.ranges.map(function(d) { return Math.random() * 1000; });
|
||||
b.markers = b.markers.map(function(d) { return Math.random() * 1000; });
|
||||
b.measures = b.measures.map(function(d) { return Math.random() * 1000; });
|
||||
for (var i = -1, n = bullets.length, b; ++i < n;) {
|
||||
b = bullets[i];
|
||||
b.ranges = b.ranges.map(random);
|
||||
b.markers = b.markers.map(random);
|
||||
b.measures = b.measures.map(random);
|
||||
}
|
||||
vis.each(bullet);
|
||||
vis.each(refresh);
|
||||
}
|
||||
|
||||
function random() {
|
||||
return Math.random() * 1000;
|
||||
}
|
||||
|
||||
</script>
|
||||
<input type="button" value="Randomize!" onclick="randomize()">
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
.bullet { font: 10px sans-serif; }
|
||||
.bullet .marker { stroke: #000; stroke-width: 2px; }
|
||||
.bullet .tick text { font: 10px sans-serif; }
|
||||
.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; }
|
||||
|
|
|
@ -9,15 +9,20 @@
|
|||
<body>
|
||||
<script>
|
||||
|
||||
var w = 500,
|
||||
h = 50,
|
||||
m = [5, 40, 20, 120]; // top right bottom left
|
||||
|
||||
var bullet = d3.chart.bullet()
|
||||
.orient("left")
|
||||
.tickFormat(function(d) { return d / 1e6; });
|
||||
.width(w - m[1] - m[3])
|
||||
.height(h - m[0] - m[2])
|
||||
.tickFormat(function(d) { return (d / 1e6).toFixed(1); });
|
||||
|
||||
var vis = d3.select("body").append("svg:svg")
|
||||
.attr("width", 800)
|
||||
.attr("height", 300);
|
||||
.attr("width", w)
|
||||
.attr("height", h);
|
||||
|
||||
vis.append("svg:g")
|
||||
var g = vis.append("svg:g")
|
||||
.data([{
|
||||
title: "Revenue",
|
||||
subtitle: "US$, in millions",
|
||||
|
@ -26,8 +31,24 @@ vis.append("svg:g")
|
|||
markers: [1.15e6, 3.0e6]
|
||||
}])
|
||||
.attr("class", "bullet")
|
||||
.attr("transform", "translate(" + m[3] + "," + m[0] + ")")
|
||||
.call(bullet);
|
||||
|
||||
g.append("svg:text")
|
||||
.attr("class", "title")
|
||||
.attr("text-anchor", "end")
|
||||
.attr("dx", "-6")
|
||||
.attr("y", bullet.height() / 2)
|
||||
.text(function(d) { return d.title; });
|
||||
|
||||
g.append("svg:text")
|
||||
.attr("class", "subtitle")
|
||||
.attr("text-anchor", "end")
|
||||
.attr("dx", "-6")
|
||||
.attr("dy", "1em")
|
||||
.attr("y", bullet.height() / 2)
|
||||
.text(function(d) { return d.subtitle; });
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -11,19 +11,17 @@
|
|||
d3.chart.bullet = function() {
|
||||
var orient = "left",
|
||||
duration = 0,
|
||||
title = d3_chart_bulletTitle,
|
||||
subtitle = d3_chart_bulletSubtitle,
|
||||
ranges = d3_chart_bulletRanges,
|
||||
markers = d3_chart_bulletMarkers,
|
||||
measures = d3_chart_bulletMeasures,
|
||||
width = 380,
|
||||
height = 30,
|
||||
x0 = null,
|
||||
x1 = d3.scale.linear(),
|
||||
x0,
|
||||
tickFormat = d3.format(",.0f");
|
||||
|
||||
function bullet(g) {
|
||||
var max = 0;
|
||||
var max = 0,
|
||||
reverse = orient == "right" || orient == "bottom";
|
||||
|
||||
// Convert the data to a standardized representation, and also compute the
|
||||
// maximum value, needed to standardize the x-scale across multiples.
|
||||
|
@ -33,8 +31,6 @@ d3.chart.bullet = function() {
|
|||
z = measures.call(this, d, i).slice().sort(d3.descending);
|
||||
max = Math.max(max, r[0], m[0], z[0]);
|
||||
return {
|
||||
title: [title.call(this, d, i)],
|
||||
subtitle: [subtitle.call(this, d, i)],
|
||||
ranges: r,
|
||||
markers: m,
|
||||
measures: z,
|
||||
|
@ -42,87 +38,64 @@ d3.chart.bullet = function() {
|
|||
};
|
||||
});
|
||||
|
||||
var reversed = orient === "right" || orient === "bottom";
|
||||
|
||||
// Update the x-scales.
|
||||
var xRange = reversed ? [width, 0] : [0, width];
|
||||
x1.domain([0, max]).range(xRange);
|
||||
if (x0 === null)
|
||||
x0 = d3.scale.linear().domain([0, Infinity]).range(xRange);
|
||||
var x1 = d3.scale.linear()
|
||||
.domain([0, max])
|
||||
.range(reverse ? [width, 0] : [0, width]);
|
||||
|
||||
// Update the title.
|
||||
var titleText = g.select("text.title");
|
||||
// Initialize the old scale, if this is the first render.
|
||||
if (!x0) x0 = d3.scale.linear()
|
||||
.domain([0, Infinity])
|
||||
.range(x1.range());
|
||||
|
||||
if (titleText.empty())
|
||||
titleText = g.append("svg:text")
|
||||
.attr("class", "title")
|
||||
.attr("dy", "1em")
|
||||
.style("font-weight", "bold");
|
||||
titleText
|
||||
.text(d3_chart_bulletTitle);
|
||||
|
||||
// Update the subtitle.
|
||||
var subtitleText = g.select("text.subtitle");
|
||||
if (subtitleText.empty())
|
||||
subtitleText = g.append("svg:text")
|
||||
.attr("class", "subtitle")
|
||||
.attr("dy", "2.5em")
|
||||
.style("font-size", ".7em")
|
||||
.style("fill", "#999");
|
||||
subtitleText
|
||||
.text(d3_chart_bulletSubtitle);
|
||||
|
||||
// Update the chart.
|
||||
var chart = g.select("g.chart");
|
||||
if (chart.empty())
|
||||
chart = g.append("svg:g")
|
||||
.attr("class", "chart")
|
||||
.attr("transform", "translate(120)");
|
||||
// Derive width-scales from the x-scales.
|
||||
var w0 = d3_chart_bulletWidth(x0),
|
||||
w1 = d3_chart_bulletWidth(x1);
|
||||
|
||||
// Update the range rects.
|
||||
var range = chart.selectAll("rect.range")
|
||||
var range = g.selectAll("rect.range")
|
||||
.data(d3_chart_bulletRanges);
|
||||
|
||||
range.enter().append("svg:rect")
|
||||
.attr("class", function(d, i) { return "range s" + i; })
|
||||
.attr("width", function(d) { return Math.abs(x0(d) - x0(0)); })
|
||||
.attr("width", w0)
|
||||
.attr("height", height)
|
||||
.attr("x", reversed ? x0 : 0)
|
||||
.attr("x", reverse ? x0 : 0)
|
||||
.transition()
|
||||
.duration(duration)
|
||||
.attr("width", function(d) { return Math.abs(x1(d) - x1(0)); })
|
||||
.attr("x", reversed ? x1 : 0);
|
||||
.attr("width", w1)
|
||||
.attr("x", reverse ? x1 : 0);
|
||||
|
||||
range.transition()
|
||||
.duration(duration)
|
||||
.attr("x", reversed ? x1 : 0)
|
||||
.attr("width", function(d) { return Math.abs(x1(d) - x1(0)); })
|
||||
.attr("x", reverse ? x1 : 0)
|
||||
.attr("width", w1)
|
||||
.attr("height", height);
|
||||
|
||||
// Update the measure rects.
|
||||
var measure = chart.selectAll("rect.measure")
|
||||
var measure = g.selectAll("rect.measure")
|
||||
.data(d3_chart_bulletMeasures);
|
||||
|
||||
measure.enter().append("svg:rect")
|
||||
.attr("class", function(d, i) { return "measure s" + i; })
|
||||
.attr("width", function(d) { return Math.abs(x0(d) - x0(0)); })
|
||||
.attr("width", w0)
|
||||
.attr("height", height / 3)
|
||||
.attr("x", reversed ? x0 : 0)
|
||||
.attr("x", reverse ? x0 : 0)
|
||||
.attr("y", height / 3)
|
||||
.transition()
|
||||
.duration(duration)
|
||||
.attr("width", function(d) { return Math.abs(x1(d) - x1(0)); })
|
||||
.attr("x", reversed ? x1 : 0);
|
||||
.attr("width", w1)
|
||||
.attr("x", reverse ? x1 : 0);
|
||||
|
||||
measure.transition()
|
||||
.duration(duration)
|
||||
.attr("width", function(d) { return Math.abs(x1(d) - x1(0)); })
|
||||
.attr("width", w1)
|
||||
.attr("height", height / 3)
|
||||
.attr("x", reversed ? x1 : 0)
|
||||
.attr("x", reverse ? x1 : 0)
|
||||
.attr("y", height / 3);
|
||||
|
||||
// Update the marker lines.
|
||||
var marker = chart.selectAll("line.marker")
|
||||
var marker = g.selectAll("line.marker")
|
||||
.data(d3_chart_bulletMarkers);
|
||||
|
||||
marker.enter().append("svg:line")
|
||||
|
@ -144,19 +117,13 @@ d3.chart.bullet = function() {
|
|||
.attr("y2", height * 5 / 6);
|
||||
|
||||
// Update the tick groups.
|
||||
var tick = chart.selectAll("g.tick")
|
||||
var tick = g.selectAll("g.tick")
|
||||
.data(x1.ticks(8), tickFormat);
|
||||
|
||||
var translate = function(f) {
|
||||
return function(d) {
|
||||
return "translate(" + f(d) + ")";
|
||||
};
|
||||
};
|
||||
|
||||
// Initialize the ticks with the old scale, x0.
|
||||
var tickEnter = tick.enter().append("svg:g")
|
||||
.attr("class", "tick")
|
||||
.attr("transform", translate(x0))
|
||||
.attr("transform", d3_chart_bulletTranslate(x0))
|
||||
.attr("opacity", 1e-6);
|
||||
|
||||
tickEnter.append("svg:line")
|
||||
|
@ -172,13 +139,13 @@ d3.chart.bullet = function() {
|
|||
// Transition the entering ticks to the new scale, x1.
|
||||
tickEnter.transition()
|
||||
.duration(duration)
|
||||
.attr("transform", translate(x1))
|
||||
.attr("transform", d3_chart_bulletTranslate(x1))
|
||||
.attr("opacity", 1);
|
||||
|
||||
// Transition the updating ticks to the new scale, x1.
|
||||
var tickUpdate = tick.transition()
|
||||
.duration(duration)
|
||||
.attr("transform", translate(x1))
|
||||
.attr("transform", d3_chart_bulletTranslate(x1))
|
||||
.attr("opacity", 1);
|
||||
|
||||
tickUpdate.select("line")
|
||||
|
@ -191,13 +158,13 @@ d3.chart.bullet = function() {
|
|||
// Transition the exiting ticks to the new scale, x1.
|
||||
tick.exit().transition()
|
||||
.duration(duration)
|
||||
.attr("transform", translate(x1))
|
||||
.attr("transform", d3_chart_bulletTranslate(x1))
|
||||
.attr("opacity", 1e-6)
|
||||
.remove();
|
||||
|
||||
// Lastly, restore the original data and update the previous scale!
|
||||
g.map(d3_chart_bulletData);
|
||||
x0.domain([0, max]).range(xRange);
|
||||
x0 = x1;
|
||||
}
|
||||
|
||||
// left, right, top, bottom
|
||||
|
@ -227,8 +194,7 @@ d3.chart.bullet = function() {
|
|||
|
||||
bullet.width = function(x) {
|
||||
if (!arguments.length) return width;
|
||||
x0.range([0, width = x]);
|
||||
x1.range([0, width]);
|
||||
width = x;
|
||||
return bullet;
|
||||
};
|
||||
|
||||
|
@ -253,14 +219,6 @@ d3.chart.bullet = function() {
|
|||
return bullet;
|
||||
};
|
||||
|
||||
function d3_chart_bulletTitle(d) {
|
||||
return d.title;
|
||||
}
|
||||
|
||||
function d3_chart_bulletSubtitle(d) {
|
||||
return d.subtitle;
|
||||
}
|
||||
|
||||
function d3_chart_bulletRanges(d) {
|
||||
return d.ranges;
|
||||
}
|
||||
|
@ -276,3 +234,16 @@ function d3_chart_bulletMeasures(d) {
|
|||
function d3_chart_bulletData(d) {
|
||||
return d.data;
|
||||
}
|
||||
|
||||
function d3_chart_bulletTranslate(x) {
|
||||
return function(d) {
|
||||
return "translate(" + x(d) + ",0)";
|
||||
};
|
||||
}
|
||||
|
||||
function d3_chart_bulletWidth(x) {
|
||||
var x0 = x(0);
|
||||
return function(d) {
|
||||
return Math.abs(x(d) - x0);
|
||||
};
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче