datavjs/lib/charts/bullet.js
2012-10-20 18:21:39 +08:00

337 lines
12 KiB
JavaScript

/*global Raphael, d3 */
/*!
* Bullet的兼容性定义
*/
;(function (name, definition) {
if (typeof define === 'function') { // Module
define(definition);
} else { // Assign to common namespaces or simply the global object (window)
this[name] = definition(function (id) { return this[id];});
}
})('Bullet', function (require) {
var DataV = require('DataV');
var Axis = require('Axis');
/*
* Bullet构造函数
* Options:
*
* - `width` 宽度,默认为节点宽度
*
* Examples:
* ```
* //Create bullet in a dom node with id "chart", width is 500; height is 600px;
* var bullet = new Bullet("chart", {
* "width": 500,
* "height": 600,
* "margin": [10, 10, 20, 70]
* });
* //Create bullet with log base;
* var log = new Bullet("chart2", {
* width: 300,
* height: 60,
* margin: [10, 10, 20, 70],
* backgroundColor: ["#66f", "#ddf"],
* measureColor: ["#000", "#000"],
* markerColor: "#44f",
* axisStyle: "log",
* logBase: 10
* });
* ```
* @param {Mix} node The dom node or dom node Id
* @param {Object} options options json object for determin stream style.
*/
var Bullet = DataV.extend(DataV.Chart, {
initialize: function (node, options) {
this.type = "Bullet";
this.node = this.checkContainer(node);
this.defaults = {};
// Properties
this.defaults.orient = "horizonal"; // "horizonal", "vertical"
this.defaults.axisStyle = "linear"; // "linear", "log"
this.defaults.logBase = Math.E;
this.defaults.margin = [10, 10, 20, 80];//top, right, bottom, left
this.defaults.centerBarRatio = 0.3;
this.defaults.markerWidth = 4;
this.defaults.markerRatio = 0.7;
this.defaults.titleRatio = 0.6; //title's text height : subtitle's text height = 6:4
this.defaults.backgroundColor = ["#666", "#ddd"]; //dark, light
this.defaults.measureColor = ["steelblue", "#B0C4DE"]; //dark, light
this.defaults.markerColor = "#000";
this.defaults.tickDivide = 5;
// canvas
this.defaults.width = 200;
this.defaults.height = 80;
this.setOptions(options);
this.createCanvas();
}
});
/*!
* 创建画布
*/
Bullet.prototype.createCanvas = function () {
var conf = this.defaults;
this.canvas = new Raphael(this.node, conf.width, conf.height);
};
/**
* 设置数据源
* Examples:
* ```
* bullet.setSource({
* title: "Sample",
* subtitle: "ratio",
* ranges: [0, 0.5, 0.8, 1],
* measures: [0.7, 0.9],
* markers: [0.6],
* rangeTitles: ["below 50%", "top 20% - 50%", "top 20%"],
* measureTitles: ["value is 0.7", "value is 0.9"],
* markerTitles: ["mean is 0.6"]
* })
* ```
* @param {Object} source 数据源
*/
Bullet.prototype.setSource = function (source) {
var conf = this.defaults,
range,
axisOrient;
this.data = source;
if (conf.orient === "horizonal") {
axisOrient = "bottom";
range = [conf.margin[3], conf.width - conf.margin[1]];
} else if (conf.orient === "vertical") {
axisOrient = "left";
range = [conf.height - conf.margin[2], conf.margin[0]];
}
if (conf.axisStyle === "linear") {
this.scale = d3.scale.linear();
} else if (conf.axisStyle === "log") {
this.scale = d3.scale.log();
}
this.data.min = this.data.ranges[0];
this.data.max = this.data.ranges[this.data.ranges.length - 1];
this.scale.domain([this.data.min, this.data.max])
.range(range);
if (conf.axisStyle === "linear") {
this.axis = Axis().scale(this.scale).orient(axisOrient).ticks(conf.tickDivide).domainAttr({"stroke": "none"});
} else if (conf.axisStyle === "log") {
this.logScale = d3.scale.linear()
.domain([Math.log(this.data.min)/Math.log(conf.logBase), Math.log(this.data.max)/Math.log(conf.logBase)])
.range(range);
this.axis = Axis()
.orient(axisOrient)
.scale(this.logScale)
.ticks(conf.tickDivide)
.tickFormat(function (d) {return Math.round(Math.pow(conf.logBase, d));})
.domainAttr({"stroke": "none"});
}
};
/*!
* generate bullet dom path
*/
Bullet.prototype.generatePaths = function () {
var conf = this.defaults;
//get color function
if (conf.backgroundColor) {
this.color = d3.interpolateRgb.apply(null, [conf.backgroundColor[0], conf.backgroundColor[1]]);
}
if (conf.measureColor) {
this.measureColor = d3.interpolateRgb.apply(null, [conf.measureColor[0], conf.measureColor[1]]);
}
if (conf.orient === "horizonal") {
this.paintHorizonal();
} else if (conf.orient === "vertical") {
this.paintVertical();
}
};
/*!
* paint orient horizonal bullet
*/
Bullet.prototype.paintHorizonal = function () {
var conf = this.defaults;
var paper = this.canvas,
data = this.data,
m = conf.margin,
ranges = [],
measures = [],
markers = [],
rangeTitles = [],
i,
l,
rect,
titleRatio,
w,
h = conf.height - m[0] - m[2],
left;
//axis
this.axis(paper).attr({transform: "t" + 0 + ',' + (conf.height - m[2])});
//color rect
ranges = data.ranges;
if (data.rangeTitles) {
rangeTitles = data.rangeTitles;
}
left = m[3];
for (i = 0, l = ranges.length - 1; i < l; i++) {
w = this.scale(ranges[i + 1]) - this.scale(ranges[i]);
rect = paper.rect(left, m[0], w, h)
.attr({"stroke": "none",
"fill": this.color(l === 1 ? 1 : i / (l - 1)),
"title": rangeTitles[i] ? rangeTitles[i] : ""});
left += w;
}
//measure bar
data.measures.forEach(function (d, i) {
var mTitles = data.measureTitles;
var mTitle = mTitles && mTitles[i] ? mTitles[i] : "";
measures.push({measure: d, measureTitle: mTitle});
});
measures.sort(function (a, b) { return d3.ascending(a.measure, b.measure); });
left = this.scale(data.min);
for (i = 0, l = measures.length; i < l; i++) {
value = Math.max(data.min, Math.min(data.max, measures[i].measure));
w = this.scale(value) - left;
paper.rect(left,
m[0] + h * (1 - conf.centerBarRatio) / 2,
w,
h * conf.centerBarRatio)
.attr({"stroke": "none", "fill": this.measureColor(l === 1 ? 1 : i / (l - 1)), "title": measures[i].measureTitle});
left += w;
}
//marker bar
markers = data.markers;
for (i = 0, l = markers.length; i < l; i++) {
paper.rect(this.scale(markers[i]) - conf.markerWidth / 2,
m[0] + h * (1 - conf.markerRatio) / 2,
conf.markerWidth,
h * conf.markerRatio)
.attr({"stroke": "none", "fill": conf.markerColor,
"title": data.markerTitles && data.markerTitles[i] ? data.markerTitles[i] : ""});
}
//title
if (data.title) {
titleRatio = data.subtitle ? conf.titleRatio : 1;
this.title = paper.text(m[3] - 5, m[0] + h / 2, data.title)
.attr({"text-anchor": "end", "font-weight": "bold", "font-size": h * titleRatio * 0.9});
}
//subtitle
if (data.subtitle) {
this.subtitle = paper.text(m[3] - 5, conf.height - m[2], data.subtitle)
.attr({"text-anchor": "end", "font-size": h * (1 - conf.titleRatio) * 0.9});
}
};
/*!
* paint orient vertical bullet
*/
Bullet.prototype.paintVertical = function () {
var conf = this.defaults;
var paper = this.canvas,
data = this.data,
m = conf.margin,
ranges = [],
measures = [],
markers = [],
rangeTitles = [],
i,
l,
rect,
titleRatio,
w = conf.width - m[1] - m[3],
h,
bottom;
//axis
this.axis(paper).attr({transform: "t" + m[3] + ',' + 0});
//color rect
ranges = data.ranges;
if (data.rangeTitles) {
rangeTitles = data.rangeTitles;
}
bottom = conf.height - m[2];
for (i = 0, l = ranges.length - 1; i < l; i++) {
h = -this.scale(ranges[i + 1]) + this.scale(ranges[i]);
rect = paper.rect(m[3], bottom - h, w, h)
.attr({"stroke": "none",
"fill": this.color(l === 1 ? 1 : i / (l - 1)),
"title": rangeTitles[i] ? rangeTitles[i] : ""});
bottom -= h;
}
//measure bar
data.measures.forEach(function (d, i) {
var mTitles = data.measureTitles;
var mTitle = mTitles && mTitles[i] ? mTitles[i] : "";
measures.push({measure: d, measureTitle: mTitle});
});
measures.sort(function (a, b) { return d3.ascending(a.measure, b.measure); });
bottom = this.scale(data.min);
for (i = 0, l = measures.length; i < l; i++) {
value = Math.max(data.min, Math.min(data.max, measures[i].measure));
h = -this.scale(value) + bottom;
paper.rect(m[3] + w * (1 - conf.centerBarRatio) / 2,
bottom - h,
w * conf.centerBarRatio,
h)
.attr({"stroke": "none", "fill": this.measureColor(l === 1 ? 1 : i / (l - 1)), "title": measures[i].measureTitle});
bottom -= h;
}
//marker bar
markers = data.markers;
for (i = 0, l = markers.length; i < l; i++) {
paper.rect(m[3] + w * (1 - conf.markerRatio) / 2,
this.scale(markers[i]) - conf.markerWidth / 2,
w * conf.markerRatio,
conf.markerWidth)
.attr({"stroke": "none", "fill": conf.markerColor,
"title": data.markerTitles && data.markerTitles[i] ? data.markerTitles[i] : ""});
}
//title
if (data.title) {
titleRatio = data.subtitle ? conf.titleRatio : 1;
m[0] *= 0.9; //some ratio adjust;
this.title = paper.text((conf.width + m[3] - m[1])/ 2, m[0] * titleRatio / 2, data.title)
.attr({"text-anchor": "middle", "font-weight": "bold", "font-size": m[0] * titleRatio * 0.8});
}
//subtitle
if (data.subtitle) {
this.subtitle = paper.text((conf.width + m[3] - m[1])/ 2, m[0] * (1 - (1 - titleRatio) / 2), data.subtitle)
.attr({"text-anchor": "middle", "font-size": m[0] * (1 - titleRatio) * 0.8});
}
};
/*!
* clean canvas
*/
Bullet.prototype.clearCanvas = function () {
this.canvas.clear();
};
/**
* render bullet
*/
Bullet.prototype.render = function (options) {
this.setOptions(options);
this.generatePaths();
};
return Bullet;
});