mirror of
https://github.com/TBEDP/datavjs.git
synced 2025-12-08 19:45:52 +00:00
Update
This commit is contained in:
parent
4f061b9ee6
commit
54b07f816e
@ -4,6 +4,10 @@ DataV组件库的设计
|
||||
|
||||
## DataV结构
|
||||
|
||||
- Chart
|
||||
- Widget
|
||||
- Component
|
||||
|
||||
## API设计
|
||||
|
||||
## 数据映射
|
||||
|
||||
@ -6,15 +6,25 @@
|
||||
<script src="../../build/deps.js"></script>
|
||||
<script src="../../deps/seajs/sea.js"></script>
|
||||
<style type="text/css">
|
||||
.clearfix:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: " ";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
#chart {
|
||||
border-top: 1px dashed #F00;
|
||||
border-bottom: 1px dashed #F00;
|
||||
padding-left: 20px;
|
||||
border-top: 1px dashed #F00;
|
||||
border-bottom: 1px dashed #F00;
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart">
|
||||
<div id="chart" class="clearfix">
|
||||
</div>
|
||||
<script type="text/template">
|
||||
<div class="legend">
|
||||
</div>
|
||||
<div>
|
||||
@ -27,7 +37,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
//http://planetozh.com/blog/2008/04/javascript-basename-and-dirname/
|
||||
var dir = location.href.replace(/\\/g,'/').replace(/\/[^\/]*$/, '');
|
||||
@ -35,20 +45,34 @@
|
||||
alias: {
|
||||
'DataV': dir + '/../../lib/datav.js',
|
||||
'Axis': dir + '/../../lib/charts/axis.js',
|
||||
'StreamAxis': dir + '/../../lib/charts/stream_axis.js',
|
||||
'Legend': dir + '/../../lib/charts/legend.js',
|
||||
'Navi': dir + '/../../lib/charts/navi.js',
|
||||
'Tip': dir + '/../../lib/charts/tip.js',
|
||||
'Percentage': dir + '/../../lib/charts/percentage.js',
|
||||
'Stream': dir + '/../../lib/charts/stream.refine.js'
|
||||
'HoverLine': dir + '/../../lib/charts/hover_line.js',
|
||||
'PathLabel': dir + '/../../lib/charts/path_label.js',
|
||||
'Cover': dir + '/../../lib/charts/cover.js',
|
||||
'Stream': dir + '/../../lib/charts/stream_chart.js',
|
||||
'StreamComponent': dir + '/../../lib/charts/stream.refine.js'
|
||||
}
|
||||
});
|
||||
seajs.use(["Stream", "DataV"], function (Stream, DataV) {
|
||||
seajs.use(["StreamComponent", "DataV"], function (Stream, DataV) {
|
||||
DataV.changeTheme("theme0");
|
||||
var stream = new Stream("chart", {"width": 800});
|
||||
stream.setOptions({"more": false, "legendPosition": "left"});
|
||||
DataV.csv("streamDataExample.csv", function (source) {
|
||||
stream.setSource(source);
|
||||
stream.render();
|
||||
var component = new StreamComponent("chart", {"width": 800});
|
||||
component.setOptions({"legendPosition": "left"});
|
||||
var source = [
|
||||
['2012-10-21','book', 100],
|
||||
['2012-10-21','food', 110],
|
||||
['2012-10-22','book', 30],
|
||||
['2012-10-22','food', 140]
|
||||
];
|
||||
component.setSource(source, {
|
||||
x: 0,
|
||||
type: 1,
|
||||
value: 2
|
||||
});
|
||||
component.render();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
36
lib/charts/cover.js
Normal file
36
lib/charts/cover.js
Normal file
@ -0,0 +1,36 @@
|
||||
/*global Raphael, d3, $, define, _ */
|
||||
/*!
|
||||
* StreamLegend的兼容定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Cover', function (require) {
|
||||
var DataV = require('DataV');
|
||||
//cover
|
||||
var Cover = DataV.extend(DataV.Chart, {
|
||||
initialize: function (container) {
|
||||
var conf = this.defaults;
|
||||
this.node = $(container);
|
||||
this.node.css({
|
||||
"position": "absolute",
|
||||
"left": 0,
|
||||
"top": 0,
|
||||
"width": conf.chartWidth,
|
||||
"height": conf.chartHeight,
|
||||
"zIndex": 100,
|
||||
"visibility": "hidden"
|
||||
}).bind("mousemove", $.proxy(function (e) {
|
||||
this.mouse = {x: e.pageX, y: e.pageY};
|
||||
e.stopPropagation();
|
||||
}, this)).bind("mouseleave", $.proxy(function () {
|
||||
this.mouse = undefined;
|
||||
}, this));
|
||||
}
|
||||
});
|
||||
|
||||
return Cover;
|
||||
});
|
||||
69
lib/charts/hover_line.js
Normal file
69
lib/charts/hover_line.js
Normal file
@ -0,0 +1,69 @@
|
||||
/*global Raphael, d3, $, define, _ */
|
||||
/*!
|
||||
* StreamLegend的兼容定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('HoverLine', function (require) {
|
||||
var DataV = require('DataV');
|
||||
var HoverLine = DataV.extend(DataV.Widget, {
|
||||
initialize: function () {
|
||||
}
|
||||
});
|
||||
|
||||
HoverLine.prototype.render = function () {
|
||||
this.clear();
|
||||
var paper = this.owner.paper;
|
||||
var conf = this.owner.defaults;
|
||||
this.indicatorLine = paper.path("M0 0V" + conf.chartHeight).attr({
|
||||
stroke: "none",
|
||||
"stroke-width": 1,
|
||||
"stroke-dasharray": "- "
|
||||
});
|
||||
this.highlightLine = paper.path("M0 0V" + conf.chartHeight).attr({
|
||||
stroke: "none",
|
||||
"stroke-width": 2
|
||||
});
|
||||
};
|
||||
HoverLine.prototype.hidden = function () {
|
||||
this.indicatorLine.attr({"stroke": "none"});
|
||||
this.highlightLine.attr({"stroke": "none"});
|
||||
};
|
||||
HoverLine.prototype.show = function () {
|
||||
this.indicatorLine.attr({"stroke": "#000"});
|
||||
this.highlightLine.attr({"stroke": "white"});
|
||||
};
|
||||
|
||||
HoverLine.prototype.refresh = function (xIdx, pathIndex) {
|
||||
//refresh lines' position
|
||||
var owner = this.owner;
|
||||
var pathSource = owner.pathSource;
|
||||
var lineX = owner.defaults.chartWidth * xIdx / (owner.source[0].length - 1);
|
||||
var pathSourceCell = pathSource[pathSource.length - 1][xIdx];
|
||||
this.indicatorLine.attr({
|
||||
path: "M" + lineX + " " + (pathSourceCell.y0 - pathSourceCell.y) + "V" + pathSource[0][xIdx].y0
|
||||
});
|
||||
|
||||
pathSourceCell = pathSource[pathIndex][xIdx];
|
||||
this.highlightLine.attr({
|
||||
path: "M" + lineX + " " + (pathSourceCell.y0 - pathSourceCell.y) + "V" + pathSourceCell.y0
|
||||
});
|
||||
|
||||
if (pathIndex === 0 && owner.getDisplayRowInfo(pathIndex).rowIndex === -1) {
|
||||
this.highlightLine.attr({"cursor": "pointer"});
|
||||
} else {
|
||||
this.highlightLine.attr({"cursor": "auto"});
|
||||
}
|
||||
};
|
||||
|
||||
HoverLine.prototype.clear = function () {
|
||||
this.indicatorLine && this.indicatorLine.remove();
|
||||
this.highlightLine && this.highlightLine.remove();
|
||||
};
|
||||
|
||||
return HoverLine;
|
||||
});
|
||||
@ -11,12 +11,37 @@
|
||||
})('Legend', function (require) {
|
||||
var DataV = require('DataV');
|
||||
|
||||
var Legend = DataV.extend(DataV.Widget, {
|
||||
var Legend = DataV.extend(DataV.Chart, {
|
||||
initialize: function (container) {
|
||||
this.legendIndent = 21;
|
||||
this.legendIndent = 20;
|
||||
this.node = $(container);
|
||||
/**
|
||||
* 类型纬度
|
||||
*/
|
||||
this.dimension.type = {
|
||||
type: "string",
|
||||
required: true,
|
||||
index: 1
|
||||
};
|
||||
/**
|
||||
* 时间纬度
|
||||
*/
|
||||
this.dimension.x = {
|
||||
type: "string",
|
||||
required: true,
|
||||
index: 0
|
||||
};
|
||||
/**
|
||||
* 值纬度
|
||||
*/
|
||||
this.dimension.value = {
|
||||
type: "number",
|
||||
required: true,
|
||||
index: 2
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Legend.prototype.init = function () {
|
||||
var conf = this.owner.defaults;
|
||||
this.legend = $("<div></div>");
|
||||
@ -28,14 +53,18 @@
|
||||
this.node.append(this.legend);
|
||||
};
|
||||
|
||||
Legend.prototype.setSource = function (source, map) {
|
||||
map = this.map(map);
|
||||
this.list = _.keys(_.groupBy(source, map.type));
|
||||
};
|
||||
|
||||
Legend.prototype.render = function () {
|
||||
var that = this;
|
||||
this.init();
|
||||
this.clear();
|
||||
this.legends = [];
|
||||
var owner = this.owner;
|
||||
var colorFunc = owner.getColor();
|
||||
var colorArray = [],
|
||||
var colorFunc = this.defaults.colorFunc,
|
||||
hoverIn = function (e) {
|
||||
var index = e.data.index;
|
||||
owner.fire('hoverIn', index);
|
||||
@ -54,19 +83,14 @@
|
||||
});
|
||||
|
||||
var ul = $("<ul></ul>").css({
|
||||
"margin": "0px 0 0px 10px",
|
||||
"padding-left": "0"
|
||||
"margin": "0 0 0 10px",
|
||||
"paddingLeft": 0
|
||||
});
|
||||
var i, l;
|
||||
for (i = 0, l = owner.displayData.allInfos.length; i < l; i++) {
|
||||
colorArray.push(colorFunc(i));
|
||||
}
|
||||
for (i = 0, l = owner.displayData.allInfos.length; i < l; i++) {
|
||||
var color = colorArray[owner.displayData.rowIndex[i].slicedData];
|
||||
var li = $('<li style="color: ' + color + '"><span style="color: black">' + owner.getDisplayRowInfo(i).rowName + '</span></li>');
|
||||
for (var i = 0, l = this.list.length; i < l && i < this.legendIndent; i++) {
|
||||
var color = colorFunc(i);
|
||||
var li = $('<li style="color: ' + color + '"><span style="color: black">' + this.list[i] + '</span></li>');
|
||||
li.mouseenter({"index": i}, $.proxy(hoverIn, this)).mouseleave({"index": i}, $.proxy(hoverOut, this));
|
||||
ul.append(li);
|
||||
li.mouseenter({"index": i}, $.proxy(hoverIn, this));
|
||||
li.mouseleave({"index": i}, $.proxy(hoverOut, this));
|
||||
this.legends.push(li);
|
||||
}
|
||||
|
||||
|
||||
@ -9,11 +9,17 @@
|
||||
this[name] = definition(function (id) { return this[id];});
|
||||
}
|
||||
})('Navi', function (require) {
|
||||
var Navi = function (owner, container) {
|
||||
this.node = $(container);
|
||||
this.owner = owner;
|
||||
var DataV = require('DataV');
|
||||
|
||||
var Navi = DataV.extend(DataV.Chart, {
|
||||
initialize: function (container) {
|
||||
this.node = $(container);
|
||||
}
|
||||
});
|
||||
|
||||
Navi.prototype.init = function () {
|
||||
this.naviBackWidth = 80;
|
||||
var conf = this.owner.defaults;
|
||||
var conf = this.defaults;
|
||||
this.node.css({
|
||||
"border-top": "1px solid #ddd",
|
||||
"border-bottom": "1px solid #ddd",
|
||||
@ -52,9 +58,10 @@
|
||||
that.owner.fire('changeLevel');
|
||||
});
|
||||
};
|
||||
|
||||
Navi.prototype.render = function () {
|
||||
var owner = this.owner;
|
||||
var level = owner.defaults.moreConfig.level;
|
||||
this.init();
|
||||
var level = this.defaults.moreConfig.level;
|
||||
this.clear();
|
||||
for (var i = 0; i <= level; i++) {
|
||||
this.naviTrace.append($("<span> > </span>"));
|
||||
|
||||
141
lib/charts/path_label.js
Normal file
141
lib/charts/path_label.js
Normal file
@ -0,0 +1,141 @@
|
||||
/*global Raphael, d3, $, define, _ */
|
||||
/*!
|
||||
* PathLabel的兼容定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('PathLabel', function (require) {
|
||||
var DataV = require('DataV');
|
||||
//pathLabel
|
||||
var PathLabel = DataV.extend(DataV.Chart, {
|
||||
initialize: function (stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
});
|
||||
|
||||
PathLabel.prototype.render = function () {
|
||||
this.clear();
|
||||
var stream = this.stream;
|
||||
var paths = stream.chart.paths;
|
||||
var conf = stream.defaults;
|
||||
var pathSource = stream.chart.pathSource;
|
||||
var labels = [];
|
||||
var getLabelLocation = function (locArray, el) {
|
||||
var x = 0,
|
||||
y = 0,
|
||||
i;
|
||||
var ratioMargin = 0.15;
|
||||
var index = 0;
|
||||
var max = 0;
|
||||
var box = el.getBBox();
|
||||
var xInterval;
|
||||
var minTop, maxBottom;
|
||||
var showLabel = true;
|
||||
var loc;
|
||||
var height;
|
||||
|
||||
xInterval = Math.ceil(box.width / (locArray[1].x - locArray[0].x) / 2);
|
||||
if (xInterval === 0) {
|
||||
xInterval = 1;
|
||||
}
|
||||
|
||||
locArray.forEach(function (d, i, array) {
|
||||
var m = Math.max(ratioMargin * array.length, xInterval);
|
||||
if (i >= m && i <= array.length - m) {
|
||||
if (d.y > max) {
|
||||
minTop = d.y0 - d.y;
|
||||
maxBottom = d.y0;
|
||||
max = d.y;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
});
|
||||
for (i = index - xInterval; i <= index + xInterval; i++) {
|
||||
if (i < 0 || i >= locArray.length) {
|
||||
height = 0;
|
||||
showLabel = false;
|
||||
break;
|
||||
}
|
||||
loc = locArray[i];
|
||||
//top's y is small
|
||||
if (loc.y0 - loc.y > minTop) {
|
||||
minTop = loc.y0 - loc.y;
|
||||
}
|
||||
if (loc.y0 < maxBottom) {
|
||||
maxBottom = loc.y0;
|
||||
}
|
||||
}
|
||||
|
||||
if (showLabel && maxBottom - minTop >= box.height * 0.8) {
|
||||
x = locArray[index].x;
|
||||
y = (minTop + maxBottom) / 2;
|
||||
} else {
|
||||
showLabel = false;
|
||||
}
|
||||
|
||||
return {
|
||||
x: x,
|
||||
y: y,
|
||||
showLabel: showLabel
|
||||
};
|
||||
};
|
||||
|
||||
stream.labels = labels;
|
||||
var i, l, label, path;
|
||||
for (i = 0, l = paths.length; i < l; i++) {
|
||||
path = paths[i];
|
||||
label = stream.chart.paper.text(0, 0,
|
||||
conf.pathLabel ?
|
||||
stream.getDisplayRowInfo(i).rowName + " " + (Math.round(stream.getDisplayRowInfo(i).rowSum * 10000) / 100) + "%" : "")
|
||||
.attr({
|
||||
"text-anchor": "middle",
|
||||
"fill": "white",
|
||||
"font-size": conf.fontSize,
|
||||
"font-family": "微软雅黑"
|
||||
});
|
||||
label.labelLoc = getLabelLocation(pathSource[i], label);
|
||||
|
||||
if (label.labelLoc.showLabel) {
|
||||
label.attr({
|
||||
"x": label.labelLoc.x,
|
||||
"y": label.labelLoc.y
|
||||
});
|
||||
} else {
|
||||
label.attr({"opacity": 0});
|
||||
}
|
||||
if (i === 0 && stream.getDisplayRowInfo(i).rowIndex === -1) {
|
||||
path.attr({"cursor": "pointer"});
|
||||
label.attr({"cursor": "pointer"});
|
||||
} else {
|
||||
path.attr({"cursor": "auto"});
|
||||
label.attr({"cursor": "auto"});
|
||||
}
|
||||
labels.push(label);
|
||||
}
|
||||
};
|
||||
PathLabel.prototype.hidden = function () {
|
||||
this.stream.labels.forEach(function (d) {
|
||||
d.hide();
|
||||
});
|
||||
};
|
||||
PathLabel.prototype.show = function () {
|
||||
this.stream.labels.forEach(function (d) {
|
||||
if (d.labelLoc.showLabel) {
|
||||
d.show();
|
||||
}
|
||||
});
|
||||
};
|
||||
PathLabel.prototype.clear = function () {
|
||||
var stream = this.stream;
|
||||
if (stream.labels) {
|
||||
stream.labels.forEach(function (d) {
|
||||
d.remove();
|
||||
});
|
||||
}
|
||||
};
|
||||
return PathLabel;
|
||||
});
|
||||
@ -11,52 +11,71 @@
|
||||
})('Percentage', function (require) {
|
||||
var DataV = require('DataV');
|
||||
|
||||
var Percentage = DataV.extend(DataV.Widget, {
|
||||
var Percentage = DataV.extend(DataV.Chart, {
|
||||
initialize: function (container) {
|
||||
this.node = $(container);
|
||||
this.limit = 20;
|
||||
this.from = 0;
|
||||
this.to = 0;
|
||||
/**
|
||||
* 类型纬度
|
||||
*/
|
||||
this.dimension.type = {
|
||||
type: "string",
|
||||
required: true,
|
||||
index: 1
|
||||
};
|
||||
/**
|
||||
* 值纬度
|
||||
*/
|
||||
this.dimension.value = {
|
||||
type: "number",
|
||||
required: true,
|
||||
index: 2
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Percentage.prototype.init = function () {
|
||||
var owner = this.owner;
|
||||
var conf = owner.defaults;
|
||||
var conf = this.defaults;
|
||||
this.paper = new Raphael(this.node[0], conf.percentageWidth, conf.chartHeight);
|
||||
this.node.css({
|
||||
"width": conf.percentageWidth + "px",
|
||||
"height": conf.chartHeight + "px",
|
||||
"width": conf.percentageWidth,
|
||||
"height": conf.chartHeight,
|
||||
"float": "left",
|
||||
"margin-bottom": "0px",
|
||||
"border-bottom": "0px",
|
||||
"padding-bottom": "0px"
|
||||
});
|
||||
this.statDataMaxY = d3.max(owner.statisticData.columnSum);
|
||||
};
|
||||
|
||||
Percentage.prototype.setSource = function (source, map) {
|
||||
map = this.map(map);
|
||||
this.grouped = _.groupBy(source, map.type);
|
||||
this.types = _.keys(this.grouped);
|
||||
if (this.types.length > this.limit) {
|
||||
this.to = this.limit;
|
||||
}
|
||||
};
|
||||
|
||||
Percentage.prototype.render = function () {
|
||||
this.init();
|
||||
var owner = this.owner;
|
||||
if (!owner.defaults.moreConfig.more) {
|
||||
return;
|
||||
}
|
||||
var conf = owner.defaults;
|
||||
var maxY = owner.chart.getMaxY() / this.statDataMaxY;
|
||||
var y = maxY > 0.1 ? (1 - maxY) * conf.chartHeight + conf.fontSize * 2 / 3
|
||||
: (1 - maxY) * conf.chartHeight - conf.fontSize * 2 / 3;
|
||||
|
||||
var conf = this.defaults;
|
||||
var y = conf.fontSize * 2 / 3;
|
||||
if (!this.rect) {//init
|
||||
this.rect = this.paper.rect(0, (1 - maxY) * conf.chartHeight, conf.percentageWidth, maxY * conf.chartHeight)
|
||||
this.rect = this.paper.rect(0, 0, conf.percentageWidth, conf.chartHeight)
|
||||
.attr({
|
||||
"fill": "#f4f4f4",
|
||||
"stroke": "#aaa",
|
||||
"stroke-width": 0.5
|
||||
});
|
||||
this.text = this.paper.text(conf.percentageWidth / 2, y, Math.round(maxY * 100) + "%")
|
||||
this.text = this.paper.text(conf.percentageWidth / 2, y, Math.round(100) + "%")
|
||||
.attr({"text-anchor": "middle"});
|
||||
}
|
||||
this.rect.animate({"y": (1 - maxY) * conf.chartHeight, "height": maxY * conf.chartHeight}, 750);
|
||||
this.text.attr({
|
||||
"text": Math.round(maxY * 100) + "%"
|
||||
}).animate({"y": y}, 750);
|
||||
// this.rect.animate({"y": (1 - maxY) * conf.chartHeight, "height": maxY * conf.chartHeight}, 750);
|
||||
// this.text.attr({
|
||||
// "text": Math.round(maxY * 100) + "%"
|
||||
// }).animate({"y": y}, 300);
|
||||
};
|
||||
|
||||
return Percentage;
|
||||
|
||||
2113
lib/charts/stream.js.bak
Executable file
2113
lib/charts/stream.js.bak
Executable file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
126
lib/charts/stream_axis.js
Normal file
126
lib/charts/stream_axis.js
Normal file
@ -0,0 +1,126 @@
|
||||
/*global Raphael, d3, $, define, _ */
|
||||
/*!
|
||||
* Stream的兼容定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('StreamAxis', function (require) {
|
||||
var DataV = require('DataV');
|
||||
DataV.Axis = require('Axis');
|
||||
var Axis = DataV.extend(DataV.Chart, {
|
||||
initialize: function (container) {
|
||||
this.node = $(container);
|
||||
/**
|
||||
* 时间纬度
|
||||
*/
|
||||
this.dimension.x = {
|
||||
type: "string",
|
||||
required: true,
|
||||
index: 0
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Axis.prototype.setSource = function (source, map) {
|
||||
map = this.map(map);
|
||||
this.grouped = _.groupBy(source, map.x);
|
||||
this.axis = _.keys(this.grouped);
|
||||
};
|
||||
|
||||
Axis.prototype.init = function () {
|
||||
var conf = this.defaults;
|
||||
this.paper = new Raphael(this.node[0], conf.legendBesidesWidth, conf.axisHeight);
|
||||
this.node.css({
|
||||
"margin-top": "0px",
|
||||
"border-top": "1px solid #ddd",
|
||||
"height": conf.axisHeight + "px"
|
||||
});
|
||||
};
|
||||
|
||||
Axis.prototype.render = function () {
|
||||
this.init();
|
||||
this.clear();
|
||||
//all date strings' format are same, string length are same
|
||||
var conf = this.defaults,
|
||||
that = this;
|
||||
getPopPath = function (El) {
|
||||
//down pop
|
||||
var x = 0,
|
||||
y = 0,
|
||||
size = 4,
|
||||
cw = 23,
|
||||
bb = {height: 8};
|
||||
if (El) {
|
||||
bb = El.getBBox();
|
||||
bb.height *= 0.6;
|
||||
cw = bb.width / 2 - size;
|
||||
}
|
||||
return [
|
||||
'M', x, y,
|
||||
'l', size, size, cw, 0,
|
||||
'a', size, size, 0, 0, 1, size, size,
|
||||
'l', 0, bb.height,
|
||||
'a', size, size, 0, 0, 1, -size, size,
|
||||
'l', -(size * 2 + cw * 2), 0,
|
||||
'a', size, size, 0, 0, 1, -size, -size,
|
||||
'l', 0, -bb.height,
|
||||
'a', size, size, 0, 0, 1, size, -size,
|
||||
'l', cw, 0,
|
||||
'z'
|
||||
].join(',');
|
||||
};
|
||||
var left = conf.percentageWidth,
|
||||
right = conf.legendBesidesWidth - conf.percentageWidth;
|
||||
var tempWord = this.paper.text(0, 0, this.axis[0]);
|
||||
var tickNumber = Math.floor((right - left) / tempWord.getBBox().width / 2) + 1;
|
||||
tempWord.remove();
|
||||
|
||||
this.dateScale = d3.scale.linear()
|
||||
.domain([0, this.axis.length - 1])
|
||||
.range([left, right]);
|
||||
DataV.Axis().scale(this.dateScale)
|
||||
.ticks(tickNumber)
|
||||
.tickSize(6, 3, 3)
|
||||
.tickAttr({"stroke": "none"})
|
||||
.minorTickAttr({"stroke": "none"})
|
||||
.domainAttr({"stroke": "none"})
|
||||
.tickFormat(function (d) {
|
||||
return that.axis[d] || "";
|
||||
})(this.paper);
|
||||
|
||||
// this.axisPopText = this.paper.text(0, 11, '')
|
||||
// .attr({
|
||||
// "text-anchor": "middle",
|
||||
// "fill": "#fff",
|
||||
// "transform": "t" + left + ",0"
|
||||
// }).hide();
|
||||
// this.axisPopBubble = this.paper.path(getPopPath(this.axisPopText))
|
||||
// .attr({
|
||||
// "fill": "#000",
|
||||
// "transform": "t" + (-10000) + ",0"
|
||||
// }).toBack()
|
||||
// .hide();
|
||||
};
|
||||
Axis.prototype.hideTab = function () {
|
||||
this.axisPopText.hide();
|
||||
this.axisPopBubble.hide();
|
||||
};
|
||||
Axis.prototype.showTab = function () {
|
||||
this.axisPopText.show();
|
||||
this.axisPopBubble.show();
|
||||
};
|
||||
Axis.prototype.refreshTab = function (timeText, transX) {
|
||||
this.axisPopText.attr({
|
||||
"text": timeText
|
||||
}).transform("t" + transX + ",0");
|
||||
this.axisPopBubble.transform("t" + transX + ",0");
|
||||
};
|
||||
Axis.prototype.clear = function () {
|
||||
this.paper.clear();
|
||||
};
|
||||
return Axis;
|
||||
});
|
||||
459
lib/charts/stream_chart.js
Normal file
459
lib/charts/stream_chart.js
Normal file
@ -0,0 +1,459 @@
|
||||
/*global Raphael, d3, $, define, _ */
|
||||
/*!
|
||||
* Stream的兼容定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Stream', function (require) {
|
||||
var DataV = require('DataV');
|
||||
var HoverLine = require('HoverLine');
|
||||
//streamChart
|
||||
var Stream = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.node = this.checkContainer(node);
|
||||
|
||||
/**
|
||||
* 类型纬度
|
||||
*/
|
||||
this.dimension.type = {
|
||||
type: "string",
|
||||
required: true,
|
||||
index: 1
|
||||
};
|
||||
/**
|
||||
* 时间纬度
|
||||
*/
|
||||
this.dimension.x = {
|
||||
type: "string",
|
||||
required: true,
|
||||
index: 0
|
||||
};
|
||||
/**
|
||||
* 值纬度
|
||||
*/
|
||||
this.dimension.value = {
|
||||
type: "number",
|
||||
required: true,
|
||||
index: 2
|
||||
};
|
||||
|
||||
this.defaults.width = 500;
|
||||
this.defaults.height = 300;
|
||||
this.defaults.offset = "expand";//zero, expand, silhou-ette, wiggle;
|
||||
this.defaults.order = "default";//default, reverse, inside-out //in this Stream application, it will always be default, the real order is adjusted in Stream's data-process.
|
||||
this.defaults.animateDuration = 750;
|
||||
this.defaults.animateOrder = undefined;
|
||||
this.paths = undefined;
|
||||
this.source = undefined;
|
||||
this.layoutData = undefined;
|
||||
this.pathSource = undefined;
|
||||
this.setOptions(options);
|
||||
this.createPaper();
|
||||
}
|
||||
});
|
||||
|
||||
Stream.prototype.createPaper = function () {
|
||||
var conf = this.defaults;
|
||||
this.paper = new Raphael(this.node, conf.width, conf.height);
|
||||
};
|
||||
|
||||
Stream.prototype.setSource = function (source) {
|
||||
this.source = source;
|
||||
this.layoutData = this.remapSource(source.slice());
|
||||
};
|
||||
|
||||
Stream.prototype.remapSource = function (data) {
|
||||
var row = data.length;
|
||||
var column = data[0].length;
|
||||
var remap = [];
|
||||
for (var i = 0; i < row; i++) {
|
||||
remap[i] = [];
|
||||
for (var j = 0; j < column; j++) {
|
||||
remap[i][j] = {};
|
||||
remap[i][j].x = j;
|
||||
remap[i][j].y = data[i][j];
|
||||
}
|
||||
}
|
||||
return remap;
|
||||
};
|
||||
|
||||
Stream.prototype.layout = function () {
|
||||
var conf = this.defaults;
|
||||
d3.layout.stack().offset(conf.offset).order(conf.order)(this.layoutData);
|
||||
};
|
||||
|
||||
Stream.prototype.generateChartElements = function () {
|
||||
var conf = this.defaults;
|
||||
var paper = this.paper,
|
||||
paths = [],
|
||||
area = this.generateArea(),
|
||||
areaString,
|
||||
colorFunc = this.getColor(),
|
||||
color,
|
||||
path;
|
||||
|
||||
// set div's background instread;
|
||||
paper.rect(0, 0, conf.width, conf.height).attr({
|
||||
"stroke": "none",
|
||||
"fill": "#e0e0e0"
|
||||
});
|
||||
|
||||
for (var i = 0, l = this.layoutData.length; i < l; i++) {
|
||||
areaString = area(this.pathSource[i]);
|
||||
color = colorFunc(i);
|
||||
path = paper.path(areaString).attr({
|
||||
fill: color,
|
||||
stroke: color,
|
||||
"stroke-width": 1
|
||||
});
|
||||
paths[i] = path;
|
||||
}
|
||||
this.paths = paths;
|
||||
};
|
||||
|
||||
Stream.prototype.render = function (animate) {
|
||||
if (animate !== "animate") {
|
||||
this.clear();
|
||||
this.layout();
|
||||
this.generateChartElements();
|
||||
} else {
|
||||
this.layout();
|
||||
this.animate();
|
||||
}
|
||||
//hoverLine
|
||||
this.hoverLine = this.own(new HoverLine());
|
||||
this.hoverLine.render();//lines should be to front, so at last
|
||||
};
|
||||
|
||||
Stream.prototype.animate = function () {
|
||||
var time = 0,
|
||||
area,
|
||||
colorFunc,
|
||||
color,
|
||||
i, l,
|
||||
_area,
|
||||
paths = [],
|
||||
order,
|
||||
anim,
|
||||
count = this.paths.length;
|
||||
var that = this;
|
||||
var animateCallback = function () {
|
||||
count -= 1;
|
||||
if (count > 0) {
|
||||
return;
|
||||
}
|
||||
that.animateCallback();
|
||||
};
|
||||
if (typeof this.defaults.animateDuration !== 'undefined') {
|
||||
time = this.defaults.animateDuration;
|
||||
}
|
||||
|
||||
// if paths have not been created
|
||||
if (typeof this.paths === 'undefined') {
|
||||
this.generateChartElements();
|
||||
}
|
||||
|
||||
area = this.generateArea();
|
||||
colorFunc = this.getColor();
|
||||
if (typeof this.defaults.animateOrder !== 'undefined') {
|
||||
order = this.defaults.animateOrder;
|
||||
} else {
|
||||
order = d3.range(this.pathSource.length);
|
||||
}
|
||||
for (i = 0, l = this.pathSource.length; i < l; i++) {
|
||||
_area = area(this.pathSource[i]);
|
||||
paths.push(_area);
|
||||
}
|
||||
for (i = 0, l = this.pathSource.length; i < l; i++) {
|
||||
color = colorFunc(i);
|
||||
anim = Raphael.animation({"path": paths[i]}, time, animateCallback);
|
||||
this.paths[order[i]].animate(anim);
|
||||
}
|
||||
};
|
||||
|
||||
Stream.prototype.animateCallback = function () {
|
||||
var newOrderPaths = [];
|
||||
var that = this;
|
||||
if (typeof this.defaults.animateOrder !== 'undefined') {
|
||||
this.defaults.animateOrder.forEach(function (d, i) {
|
||||
newOrderPaths[i] = that.paths[d];
|
||||
});
|
||||
this.paths = newOrderPaths;
|
||||
}
|
||||
};
|
||||
|
||||
Stream.prototype.clear = function () {
|
||||
this.paper.clear();
|
||||
};
|
||||
|
||||
Stream.prototype.getColor = function (colorJson) {
|
||||
var colorMatrix = DataV.getColor();
|
||||
var color;
|
||||
var colorStyle = colorJson || {};
|
||||
var colorMode = colorStyle.mode || 'default';
|
||||
var i, l;
|
||||
|
||||
switch (colorMode) {
|
||||
case "gradient":
|
||||
l = this.source.length;
|
||||
// 最大为 colorMatrix.length - 1
|
||||
var colorL = Math.min(Math.round(l / 5), colorMatrix.length - 1);
|
||||
var testColor = [colorMatrix[0][0], colorMatrix[colorL][0]];
|
||||
var test1 = DataV.gradientColor(testColor, "special");
|
||||
var testColorMatrix = [];
|
||||
var testColorMatrix1 = [];
|
||||
for (i = 0; i < l; i++) {
|
||||
testColorMatrix.push([test1(i / (l - 1)), test1(i / (l - 1))]);
|
||||
}
|
||||
|
||||
for (i = l - 1; i >= 0; i--) {
|
||||
testColorMatrix1.push(testColorMatrix[i]);
|
||||
}
|
||||
colorMatrix = testColorMatrix;
|
||||
break;
|
||||
case "random":
|
||||
case "default":
|
||||
break;
|
||||
}
|
||||
|
||||
var ratio = colorStyle.ratio || 0;
|
||||
ratio = Math.max(ratio, 0);
|
||||
ratio = Math.min(ratio, 1);
|
||||
|
||||
var colorArray = colorMatrix.map(function () {
|
||||
return d3.interpolateRgb.apply(null, [colorMatrix[i][0], colorMatrix[i][1]])(ratio);
|
||||
});
|
||||
color = d3.scale.ordinal().range(colorArray);
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
/*
|
||||
*/
|
||||
Stream.prototype.getColor = function () {
|
||||
var count = this.defaults.colorCount;
|
||||
var color = this.defaults.gradientColor || ["#8be62f", "#1F4FD8"];
|
||||
var gradientColor = DataV.gradientColor(color, "special");
|
||||
var percent = 1 / count;
|
||||
var gotColors = [];
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
gotColors.push(gradientColor(i * percent));
|
||||
}
|
||||
|
||||
var midderNum = Math.round(count / 2);
|
||||
return function (num) {
|
||||
return num % 2 === 0 ? gotColors[midderNum + num / 2] : gotColors[midderNum - (num + 1) / 2];
|
||||
};
|
||||
};
|
||||
|
||||
Stream.prototype.getMaxY = function () {
|
||||
return d3.max(this.layoutData, function (d) {
|
||||
return d3.max(d, function (d) {
|
||||
return d.y0 + d.y;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Stream.prototype.mapPathSource = function () {
|
||||
var conf = this.defaults,
|
||||
maxX = this.layoutData[0].length - 1,//this.digitData[0].length - 1,
|
||||
maxY = this.getMaxY(),
|
||||
width = conf.width,
|
||||
height = conf.height;
|
||||
var i, j, l, l2, s, ps;
|
||||
this.pathSource = [];
|
||||
for (i = 0, l = this.layoutData.length; i < l; i++) {
|
||||
this.pathSource[i] = [];
|
||||
for (j = 0, l2 = this.layoutData[0].length; j < l2; j++) {
|
||||
s = this.layoutData[i][j];
|
||||
ps = this.pathSource[i][j] = {};
|
||||
ps.x = s.x * width / maxX;
|
||||
ps.y0 = height - s.y0 * height / maxY;
|
||||
ps.y = s.y * height / maxY;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Stream.prototype.generateArea = function () {
|
||||
this.mapPathSource();
|
||||
var area = d3.svg.area().x(function (d) {
|
||||
return d.x;
|
||||
}).y0(function (d) {
|
||||
return d.y0;
|
||||
}).y1(function (d) {
|
||||
return d.y0 - d.y;
|
||||
});
|
||||
return area;
|
||||
};
|
||||
|
||||
Stream.prototype.createInteractive = function () {
|
||||
$(this.paper.canvas).unbind();//prevent event rebind.
|
||||
|
||||
//refactor stream chart's animate function, especially change the callback
|
||||
var stream = this;
|
||||
this.animateCallback = function () {
|
||||
var newOrderPaths = [];
|
||||
var that = this;
|
||||
if (typeof this.defaults.animateOrder !== 'undefined') {
|
||||
this.defaults.animateOrder.forEach(function (d, i) {
|
||||
newOrderPaths[i] = that.paths[d];
|
||||
});
|
||||
this.paths = newOrderPaths;
|
||||
}
|
||||
|
||||
stream.cover.hidden();
|
||||
if (typeof stream.cover.mouse !== 'undefined') {
|
||||
stream.hoverLine.show();
|
||||
stream.floatTag.show();
|
||||
var mouse = stream.cover.mouse;
|
||||
$(stream.paper.canvas).trigger("mousemove", [mouse.x, mouse.y]);
|
||||
$(stream.floatTag).trigger("mousemove", [mouse.x, mouse.y]);
|
||||
stream.cover.mouse = undefined;
|
||||
}
|
||||
|
||||
stream.pathLabel.show();
|
||||
};
|
||||
|
||||
//chart mouseenter
|
||||
var mouseenter = function () {
|
||||
stream.hoverLine.show();
|
||||
stream.fire('enter');
|
||||
};
|
||||
|
||||
//chart mouseleave
|
||||
var mouseleave = function () {
|
||||
stream.hoverLine.hidden();
|
||||
var index;
|
||||
//recover prepath;
|
||||
if (typeof stream.prePath !== 'undefined') {
|
||||
stream.prePath.attr({"opacity": 1, "stroke-width": 1});
|
||||
// set legend
|
||||
index = stream.prePath.index;
|
||||
stream.prePath = undefined;
|
||||
}
|
||||
stream.fire('leave', index);
|
||||
};
|
||||
|
||||
//chart click
|
||||
var click = function (e) {
|
||||
var stream = e.data.stream;
|
||||
var count = stream.paths.length;
|
||||
var animateCallback = function () {
|
||||
count -= 1;
|
||||
if (count > 0) {
|
||||
return;
|
||||
}
|
||||
stream.cover.hidden();
|
||||
if (typeof stream.cover.mouse !== 'undefined') {
|
||||
stream.hoverLine.show();
|
||||
stream.floatTag.show();
|
||||
var canvas = $(stream.paper.canvas);
|
||||
var mouse = stream.cover.mouse;
|
||||
canvas.trigger("mousemove", [mouse.x, mouse.y]);
|
||||
canvas.trigger("mousemove", [mouse.x, mouse.y]);
|
||||
stream.cover.mouse = undefined;
|
||||
}
|
||||
stream.pathLabel.show();
|
||||
};
|
||||
|
||||
//more expand
|
||||
var path = stream.prePath;
|
||||
if (typeof path !== 'undefined' && path.index === 0 && stream.getDisplayRowInfo(path.index).rowIndex === -1) {
|
||||
stream.defaults.moreConfig.level += 1;
|
||||
stream.cover.show();
|
||||
stream.cover.mouse = {x: e.pageX, y: e.pageY};
|
||||
//redraw
|
||||
stream.processData("slicedData");
|
||||
stream.render("renderComponents");
|
||||
|
||||
//hidden
|
||||
stream.hoverLine.hidden();
|
||||
stream.floatTag.hidden();
|
||||
|
||||
stream.pathLabel.hidden();
|
||||
stream.paths.forEach(function (d) {
|
||||
d.attr({transform: "s1,0.001,0," + stream.defaults.chartHeight});
|
||||
d.animate({transform: "t0,0"}, 750, "linear", animateCallback);
|
||||
});
|
||||
}
|
||||
|
||||
//drop
|
||||
if (typeof stream.prePath !== 'undefined' && stream.prePath.index > 0) {
|
||||
(function (index) {
|
||||
var order = d3.range(stream.displayData.digitData.length);
|
||||
order.forEach(function (d, i, array) {
|
||||
if (i === 0) {
|
||||
array[i] = index;
|
||||
} else if (i <= index) {
|
||||
array[i] = i - 1;
|
||||
}
|
||||
});
|
||||
|
||||
stream.cover.show();
|
||||
stream.cover.mouse = {x: e.pageX, y: e.pageY};
|
||||
|
||||
//stream.displayDataDropReorder(stream.prePath.index);
|
||||
stream.getDisplayData({"type": "changeOrder", "order": order});
|
||||
stream.chart.setOptions({"animateOrder": order});
|
||||
stream.render("renderComponents", "animate");
|
||||
|
||||
stream.pathLabel.hidden();
|
||||
|
||||
}(stream.prePath.index));
|
||||
}
|
||||
};
|
||||
|
||||
//chart mousemove
|
||||
var mousemove = function (e, pageX, pageY) {
|
||||
var offset = $(this).parent().offset();
|
||||
var x = (e.pageX || pageX) - offset.left,
|
||||
y = (e.pageY || pageY) - offset.top;
|
||||
var path,
|
||||
pathSource = stream.pathSource,
|
||||
pathIndex;
|
||||
var xIdx = Math.floor((x / (stream.defaults.chartWidth / (stream.source[0].length - 1) / 2) + 1) / 2);
|
||||
//get path and pathIndex
|
||||
for (var i = 0, l = pathSource.length; i < l; i++) {
|
||||
if (y >= pathSource[i][xIdx].y0 - pathSource[i][xIdx].y && y <= pathSource[i][xIdx].y0) {
|
||||
path = stream.paths[i];
|
||||
pathIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (typeof path === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
var pre;
|
||||
//recover prepath;
|
||||
if (typeof stream.prePath !== 'undefined') {
|
||||
stream.prePath.attr({"opacity": 1, "stroke-width": 1});
|
||||
pre = stream.prePath.index;
|
||||
}
|
||||
//change new path;
|
||||
stream.prePath = path;
|
||||
stream.prePath.index = pathIndex;
|
||||
path.attr({"opacity": 0.5, "stroke-width": 0});
|
||||
|
||||
stream.fire('move', pre, pathIndex, xIdx);
|
||||
//set indicator and highlight line new position
|
||||
stream.hoverLine.refresh(xIdx, pathIndex);
|
||||
//customevent;
|
||||
if (stream.defaults.customEventHandle.mousemove) {
|
||||
stream.defaults.customEventHandle.mousemove.call(stream,
|
||||
{"timeIndex": xIdx, "pathIndex": pathIndex});
|
||||
}
|
||||
};
|
||||
$(this.paper.canvas).bind("mouseenter", mouseenter)
|
||||
.bind("mouseleave", mouseleave)
|
||||
.bind("click", click)
|
||||
.bind("mousemove", mousemove);
|
||||
};
|
||||
|
||||
return Stream;
|
||||
});
|
||||
34
lib/charts/tip.js
Normal file
34
lib/charts/tip.js
Normal file
@ -0,0 +1,34 @@
|
||||
/*global Raphael, d3, $, define, _ */
|
||||
/*!
|
||||
* StreamLegend的兼容定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Tip', function (require) {
|
||||
var DataV = require('DataV');
|
||||
//floatTag
|
||||
var Tip = DataV.extend(DataV.Chart, {
|
||||
initialize: function (container) {
|
||||
this.container = container;
|
||||
this.node = DataV.FloatTag()(this.container);
|
||||
this.hidden();
|
||||
},
|
||||
getContent: function (index) {
|
||||
return "呵呵";
|
||||
}
|
||||
});
|
||||
// stream.floatTag.setContent(stream.getFloatTagContent(stream.displayData.allInfos[i][0]));
|
||||
Tip.prototype.setContent = function (index) {
|
||||
var html = this.getContent(index);
|
||||
this.node.html(html);
|
||||
};
|
||||
Tip.prototype.setCss = function (cssJson) {
|
||||
this.node.css(cssJson);
|
||||
};
|
||||
|
||||
return Tip;
|
||||
});
|
||||
27
lib/datav.js
27
lib/datav.js
@ -532,28 +532,25 @@
|
||||
* 拥有一个组件
|
||||
*/
|
||||
Chart.prototype.own = function (widget) {
|
||||
// 传递defaults给子组件
|
||||
widget.setOptions(this.defaults);
|
||||
widget.owner = this;
|
||||
this.widgets.push(widget);
|
||||
return widget;
|
||||
};
|
||||
|
||||
Chart.prototype.show = function () {
|
||||
$(this.node).css('visibility', 'visible');
|
||||
return this;
|
||||
};
|
||||
|
||||
Chart.prototype.hidden = function () {
|
||||
$(this.node).css('visibility', 'hidden');
|
||||
return this;
|
||||
};
|
||||
|
||||
DataV.Chart = Chart;
|
||||
|
||||
var Widget = DataV.extend(EventProxy, {
|
||||
defaults: {}
|
||||
});
|
||||
Widget.prototype.show = function () {
|
||||
this.node.css('visibility', 'visible');
|
||||
return this;
|
||||
};
|
||||
|
||||
Widget.prototype.hidden = function () {
|
||||
this.node.css('visibility', 'hidden');
|
||||
return this;
|
||||
};
|
||||
|
||||
DataV.Widget = Widget;
|
||||
|
||||
/**
|
||||
* 浮动标签
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user