(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 = 3000,
|
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),
|
state = !!d["state"]?d["state"]:0,
|
g = d3.select(this) // 柱状图 分组
|
.append("g")
|
.attr("transform", "translate(75)");
|
debugger;
|
// 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(0)
|
.attr("width", w1)
|
.attr("x", reverse ? x1 : 0);
|
|
range.transition()
|
.duration(0)
|
.attr("x", reverse ? x1 : 0)
|
.attr("width", w1)
|
.attr("height", height);
|
// // 判断状态;
|
var measureNum = markerz[0];
|
// for(var index=1;index<rangez.length;index++){
|
// var rangeNum = rangez[index];
|
// if(measureNum >= rangeNum){
|
// break;
|
// }
|
// }
|
// var state = 4-index;
|
// 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+"_"+state;
|
})
|
.attr("width", w0)
|
.attr("height", height)
|
.attr("x", reverse ? x0 : 0)
|
.attr("y", 0)
|
.transition()
|
.duration(duration)
|
.attr("width", w1)
|
.attr("x", reverse ? x1 : 0);
|
|
measure.transition()
|
.duration(duration)
|
.attr("width", w1)
|
.attr("height", height)
|
.attr("x", reverse ? x1 : 0)
|
.attr("y", 0);
|
|
// 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)
|
// .attr("y1", height*0.025)
|
// .attr("y2", height*0.95)
|
// .transition()
|
// .duration(duration)
|
// .attr("x1", x1)
|
// .attr("x2", x1);
|
|
// marker.transition()
|
// .duration(duration)
|
// .attr("x1", x1)
|
// .attr("x2", x1)
|
// .attr("y1", height*0.025)
|
// .attr("y2", height*0.95);
|
// .attr("y1", height / 6)
|
// .attr("y2", height * 5 / 6);
|
// console.log("w1============");
|
// debugger;
|
// 添加状态说明
|
var meaningState = g.selectAll("meaning.state")
|
.data(measurez);
|
meaningState.enter().append("text")
|
.attr("display","none")
|
.attr("class","meaning state")
|
.attr("x", x1)
|
.attr("transform", "translate(-80)")
|
.attr("y", height*0.72)
|
.text(function () {
|
var txt;
|
switch(state){
|
case 1 : txt = "轻度";break;
|
case 2 : txt = "中度";break;
|
case 3 : txt = "重度";break;
|
default : txt = "";break;
|
}
|
return txt;
|
});
|
// 添加数字
|
var meaningText = g.selectAll("meaning.text")
|
.data(measurez);
|
meaningText.enter().append("text")
|
.attr("display","none")
|
.attr("class","meaning text")
|
.attr("x", x1)
|
.attr("transform", "translate(8)")
|
.attr("y", height*0.75)
|
.text(function () {
|
return measureNum;
|
})
|
.transition()
|
.call(function (){
|
setTimeout(function(){
|
$(".meaning.state,.meaning.text").fadeIn(duration*0.6);
|
}, duration*0.8);
|
});
|
// Compute the tick format.
|
// var format = tickFormat || x1.tickFormat(8);
|
|
// // Update the tick groups.
|
// var tick = g.selectAll("g.tick")
|
// .data(x1.ticks(3), 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*0.9; // 改变宽度
|
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) {
|
var ranges = [];
|
var start = !!d['startPoint']?d['startPoint']:0;
|
d.ranges.forEach(function (value, index) {
|
var _value = Math.abs(value-start);
|
_value = _value==0?100:_value;
|
ranges[index] = _value;
|
});
|
var endRange = ranges[ranges.length-1];
|
endRange += (!!d.state&&d.state)>0?(Math.abs(endRange-start)/ranges.length):0;
|
ranges.push(endRange);
|
return ranges;
|
}
|
|
function bulletMarkers(d) {
|
return d.markers;
|
}
|
|
function bulletMeasures(d) {
|
var measures = [];
|
d.measures.forEach(function (value, index) {
|
var _value = Math.abs(value);
|
measures[index] = _value;
|
});
|
return 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);
|
};
|
}
|
|
})();
|