Merge pull request #77 from JacksonTian/butterfly

柱状图调优
This commit is contained in:
Jackson Tian 2012-11-29 10:29:40 -08:00
commit 1d2c16f94c
4 changed files with 154 additions and 147 deletions

View File

@ -1,39 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Column</title>
<style type="text/css"></style>
</head>
<head>
<meta charset="utf-8" />
<title>Column</title>
</head>
<body>
<script src="../../build/deps.js"></script>
<script src="../../deps/seajs/sea.js"></script>
<script>
var dir = location.href.replace(/\\/g, '/').replace(/\/[^\/]*$/, '');
seajs.config({
alias: {
'DataV': dir + '/../../lib/datav.js',
'Column': dir + '/../../lib/charts/column.js'
}
});
</script>
<div id="chart"></div>
<script>
seajs.use(["DataV", "Column"], function (DataV, Column) {
var column = new Column("chart", {
"width": 1024,
"height": 600,
"margin": 30,
"typeNames": ["Y", "Z"]
});
DataV.csv("DataExample.csv", function (dataSource) {
column.setSource(dataSource);
column.render();
});
});
</script>
</body>
<body>
<script src="../../build/deps.js"></script>
<script src="../../lib/datav.js"></script>
<script src="../../lib/charts/column.js"></script>
<div id="chart"></div>
<script>
var source = [
['X','2012/3/1',18],
['Y','2012/3/1',102],
['Z','2012/3/1',102],
['X','2012/3/2',60],
['Y','2012/3/2',46],
['Z','2012/3/2',102],
['X','2012/3/3',200],
['Z','2012/3/3',102],
['Y','2012/3/3',12],
['X','2012/3/4',86],
['Z','2012/3/4',102],
['Y','2012/3/4',158],
['X','2012/3/5',4],
['Y','2012/3/5',190],
['Z','2012/3/5',102],
['X','2012/3/6',314],
['Y','2012/3/6',336],
['Z','2012/3/6',336],
['X','2012/3/7',72],
['Y','2012/3/7',124],
['Z','2012/3/7',336],
['X','2012/3/8',42],
['Y','2012/3/8',74],
['Z','2012/3/8',336],
['X','2012/3/9',418],
['Y','2012/3/9',418],
['Z','2012/3/9',418],
['X','2012/3/10',68],
['Y','2012/3/10',324],
['Z','2012/3/10',336],
['X','2012/3/11',68],
['Y','2012/3/11',68],
['Z','2012/3/11',68]
];
var column = new Column("chart", {
"width": 980,
"height": 600,
"gap": 10,
"yBase": 50
});
column.setSource(source, {
column: 0,
x: 1,
value: 2
});
column.render();
</script>
</body>
</html>

View File

@ -1,65 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Column</title>
</head>
<body>
<script src="../../build/deps.js"></script>
<script src="../../lib/datav.js"></script>
<script src="../../lib/charts/column.js"></script>
<div id="chart"></div>
<script>
var source = [
['X','2012/3/1',18],
['Y','2012/3/1',102],
['Z','2012/3/1',102],
['X','2012/3/2',60],
['Y','2012/3/2',46],
['Z','2012/3/2',102],
['X','2012/3/3',200],
['Z','2012/3/3',102],
['Y','2012/3/3',12],
['X','2012/3/4',86],
['Z','2012/3/4',102],
['Y','2012/3/4',158],
['X','2012/3/5',4],
['Y','2012/3/5',190],
['Z','2012/3/5',102],
['X','2012/3/6',314],
['Y','2012/3/6',336],
['Z','2012/3/6',336],
['X','2012/3/7',72],
['Y','2012/3/7',124],
['Z','2012/3/7',336],
['X','2012/3/8',42],
['Y','2012/3/8',74],
['Z','2012/3/8',336],
['X','2012/3/9',418],
['Y','2012/3/9',418],
['Z','2012/3/9',418],
['X','2012/3/10',68],
['Y','2012/3/10',324],
['Z','2012/3/10',336],
['X','2012/3/11',68],
['Y','2012/3/11',68],
['Z','2012/3/11',68]
];
var column = new Column("chart", {
"width": 980,
"height": 600,
"margin": 50,
"gap": 20,
"yBase": 50
});
column.setSource(source, {
column: 0,
x: 1,
value: 2
});
column.render();
</script>
</body>
</html>

View File

@ -13,12 +13,20 @@
/**
* Column构造函数
* Creates Column in a DOM node with id "chart", default width is 522; height is 522px;
* Creates Column in a DOM node with id "chart"
* Options:
*
* - `width` 宽度默认为节点宽度
* - `yBase` 纵坐标的基线值有的以0为起始值有的则以数据中的最小值为起始值
* - `width` 宽度默认为522单位像素
* - `height` 高度默认为522单位像素
* - `yBase` 纵坐标的基线值默认为0可以设置为任意数值为undefined时以数据中的最小值为起始值
* - `gap` 组与组之间的缝隙宽度
* - `showLegend` 是否显示图例
* - `legendWidth` 图例的宽度
* - `margin` 图表的间距依次为上右下左
* - `xTickNumber` 横轴刻度数
* - `yTickNumber` 纵轴刻度数
* - `formatLabel` 横轴提示格式化函数传入横轴值默认函数传出原始值
* - `formatYScale` 纵轴刻度格式化函数传入纵轴刻度值
*
* Examples:
* ```
@ -38,7 +46,7 @@
this.dimension.column = {
type: "string",
required: true,
index: 0
index: undefined
};
/**
* 横向纬度
@ -61,20 +69,33 @@
// canvas parameters
this.defaults.width = 522;
this.defaults.height = 522;
this.defaults.margin = 50;
this.defaults.margin = [50, 50, 50, 50];
this.defaults.gap = 15;
this.defaults.circleR = 3;
this.defaults.barColor = ["#308BE6","#8EEC00","#DDDF0D"];
this.defaults.xTickNumber = 5;
this.defaults.yTickNumber = 5;
this.defaults.yBase = undefined;
this.defaults.yBase = 0;
this.defaults.showLegend = true;
this.defaults.legendWidth = 100;
//图例区域的左上顶点坐标xy
this.defaults.legendArea = [422, 50, 472, 220];
//散点矩阵区域的左上顶点坐标xy
this.defaults.diagramArea = [50, 50, 422, 472];
this.columnSet = [];
this.formatLabel = function (text) {
return text;
};
this.formatYScale = function (text) {
return text;
};
this.formatValue = function (value) {
return value;
};
this.setOptions(options);
this.createCanvas();
@ -145,20 +166,26 @@
} else {
dataTable = source;
}
this.columns = _.groupBy(dataTable, map.column);
// 不指定列,将当前数据作为一列
this.columns = (typeof map.column === 'undefined') ? {column: dataTable} : _.groupBy(dataTable, map.column);
var that = this;
_.each(this.columns, function (group, key) {
that.columns[key] = _.sortBy(group, map.x);
});
this.columnCount = _.keys(this.columns).length;
conf.xAxisData = _.pluck(_.first(_.values(this.columns)), map.x);
conf.xTickNumber = Math.min(conf.xAxisData.length, conf.xTickNumber);
// 纵坐标的范围
conf.yExtent = d3.extent(dataTable, function (item) {
var yExtent = d3.extent(dataTable, function (item) {
return item[map.value];
});
// 纵坐标基线值
if (conf.yBase !== undefined) {
conf.yExtent.push(conf.yBase);
conf.yExtent = d3.extent(conf.yExtent);
if (typeof conf.yBase !== 'undefined') {
yExtent.push(conf.yBase);
}
yExtent = d3.extent(yExtent);
// 最大值放大1/10
conf.yExtent = [yExtent[0], yExtent[1] * 1.1];
};
/**
@ -166,16 +193,20 @@
*/
Column.prototype.setAxis = function () {
var conf = this.defaults;
var tagWidth = conf.width / 5 > 50 ? 50 : conf.width / 5;
conf.legendArea = [conf.width - tagWidth - conf.margin, 0, conf.width, conf.height];
conf.diagramArea = [0, 0, conf.width - tagWidth - conf.margin, conf.height];
var w = conf.diagramArea[2] - 2 * conf.margin;
var h = conf.diagramArea[3] - conf.margin;
if (conf.showLegend) {
conf.legendArea = [conf.width - conf.legendWidth, 0, conf.width, conf.height];
} else {
conf.legendWidth = 0;
conf.legendArea = [0, 0, 0, 0];
}
var margin = conf.margin;
conf.diagramArea = [margin[3], margin[0], conf.width - conf.legendWidth - margin[1], conf.height - margin[2]];
//设置x轴
this.x = d3.scale.linear().domain([0, conf.xAxisData.length]).range([conf.margin, w]);
this.x = d3.scale.linear().domain([0, conf.xAxisData.length]).range([conf.diagramArea[0], conf.diagramArea[2]]);
//设置y轴
this.value = d3.scale.linear().domain(conf.yExtent).range([h, conf.margin]);
this.value = d3.scale.linear().domain(conf.yExtent).range([conf.diagramArea[3], conf.diagramArea[1]]);
var xRange = this.x.range();
var valueRange = this.value.range();
var axis = this.axisPosition = {
@ -187,7 +218,9 @@
var columnsMaxLen = _.max(this.columns, function (column) {
return column.length;
}).length;
this.barWidth = (axis.right - axis.left - columnsMaxLen * conf.gap) / columnsMaxLen / _.keys(this.columns).length;
var width = conf.diagramArea[2] - conf.diagramArea[0];
var height = conf.diagramArea[3] - conf.diagramArea[1];
this.barWidth = width / columnsMaxLen / this.columnCount - conf.gap;
};
/**
@ -202,18 +235,18 @@
var axisLines = paper.set();
var tickText = paper.set();
var axis = this.axisPosition;
var ticks;
// X轴
ticks = this.x.ticks(conf.xTickNumber);
console.log(ticks);
var range = this.x.range();
var ticks = this.x.ticks(conf.xTickNumber);
var formatLabel = conf.formatLabel || this.formatLabel;
// 修复显示不从第一个x轴单位显示的bug
for (j = 0; j < ticks.length; j++) {
// 修改x轴单位显示在所有Column组的中间位置
// 修复x轴单位对于柱位置的偏移
var x = this.x(ticks[j]) + conf.gap / 2 + this.columnCount * Math.floor(this.barWidth) / 2;
tickText.push(paper.text(x, axis.down + 14, conf.xAxisData[ticks[j]]).rotate(0, x, axis.up));
var x = this.x(ticks[j]) + conf.gap / 2 + this.columnCount * this.barWidth / 2;
var text = conf.xAxisData[ticks[j]];
tickText.push(paper.text(x, axis.down + 14, formatLabel(text)).rotate(0, x, axis.up));
// 画x轴刻度线
axisLines.push(paper.path("M" + x + "," + axis.down + "L" + x + "," + (axis.down + 5)));
}
@ -224,6 +257,7 @@
"text-anchor": "middle"
});
// 绘制Y轴
axisLines.push(paper.path("M" + axis.left + "," + axis.up + "L" + axis.left + "," + axis.down));
axisLines.attr({
"stroke": "#D7D7D7",
@ -231,8 +265,9 @@
});
//Y轴
ticks = this.value.ticks(conf.yTickNumber);
var formatYScale = conf.formatYScale || this.formatYScale;
for (j = 0; j < ticks.length; j++) {
tickText.push(paper.text(axis.left - 8, this.value(ticks[j]), ticks[j]).attr({
tickText.push(paper.text(axis.left - 8, this.value(ticks[j]), formatYScale(ticks[j])).attr({
"fill": "#878791",
"fill-opacity": 0.7,
"font-size": 12,
@ -254,7 +289,7 @@
}
hLines.attr({
"stroke": "#ECECEC",
"stroke-width": 1
"stroke-width": 0.1
});
};
@ -286,12 +321,17 @@
return that.clicked ? that.clickedColumnIndex === columnIndex : true;
});
currentSet.forEach(function (set, columnIndex) {
set.animate({
"fill-opacity": 0.3
}, 10);
set[xIndex].animate({
"fill-opacity":1
}, 10);
set.forEach(function (bar, index) {
if (index === xIndex) {
bar.stop().attr({
"fill-opacity": 1
});
} else {
bar.stop().animate({
"fill-opacity": 0.3
}, 100);
}
});
});
var hovered = currentSet.map(function (set) {
@ -320,9 +360,10 @@
}
}
}
var formatValue = conf.formatValue || that.formatValue;
hovered.forEach(function (item, columnIndex) {
var yPos = y[columnIndex];
var valueLabel = '' + values[columnIndex][xIndex][dim.value.index];
var valueLabel = '' + formatValue(values[columnIndex][xIndex][dim.value.index]);
var textWidth = 5 * valueLabel.length + 20;
var rect = paper.rect(xPos, yPos - 10, textWidth, 20, 2).attr({
@ -334,7 +375,7 @@
"fill" : conf.barColor[columnIndex],
"stroke" : conf.barColor[columnIndex]
});
var text = paper.text(xPos + 16, yPos, valueLabel).attr({
var text = paper.text(xPos + textWidth / 2, yPos, valueLabel).attr({
"fill": "#ffffff",
"fill-opacity": 1,
"font-weight": "bold",
@ -347,7 +388,8 @@
xPos = hovered.reduce(function (pre, cur) {
return pre + cur.attrs.x;
}, 0) / hovered.length + barWidth / 2;
var xLabel = '' + values[columnIndex][xIndex][dim.x.index];
var formatLabel = conf.formatLabel || this.formatLabel;
var xLabel = formatLabel(values[columnIndex][xIndex][dim.x.index]);
var textWidth = 6 * xLabel.length + 20;
//axis x rect
var rect = paper.rect(xPos - textWidth / 2, axis.down + 8, textWidth, 20, 2).attr({
@ -364,9 +406,9 @@
"text-anchor": "middle"
});
var arrow = paper.path("M" + (xPos - 4) + "," + (axis.down + 8) + "L" + xPos + "," + axis.down +
"L" + (xPos + 4) + "," + (axis.down + 8) + "H" + xPos + "Z").attr({
"fill": "#5F5F5F",
"stroke": "#5F5F5F"
"L" + (xPos + 4) + "," + (axis.down + 8) + "H" + xPos + "Z").attr({
"fill": "#5F5F5F",
"stroke": "#5F5F5F"
});
tagSet.push(rect, text, arrow);
};
@ -377,11 +419,11 @@
var currentSet = columnSet.filter(function (set, columnIndex) {
return that.clicked ? that.clickedColumnIndex === columnIndex : true;
});
tagSet.animate({"opacity": 0}, 1000, function () {
tagSet.stop().animate({"opacity": 0}, 300, function () {
tagSet.remove();
});
currentSet.forEach(function (set, columnIndex) {
set.attr({"fill-opacity": 1});
set.stop().animate({"fill-opacity": 1}, 100);
});
};
@ -397,8 +439,8 @@
"stroke": "none"
});
rect.data('column', index).data('index', i);
rect.mouseover(mouseOverBar);
rect.mouseout(mouseOutBar);
rect.mouseover(_.debounce(mouseOverBar, 300));
rect.mouseout(_.debounce(mouseOutBar, 300));
columnSet[index].push(rect);
});
});
@ -496,12 +538,15 @@
* @param {Object} options options json object for determin column style.
*/
Column.prototype.render = function (options) {
var conf = this.defaults;
this.setOptions(options);
this.canvas.clear();
this.setAxis();
this.drawAxis();
this.drawDiagram();
this.drawLegend();
if (conf.showLegend) {
this.drawLegend();
}
};
/*!
* 导出

View File

@ -1,6 +1,6 @@
{
"name": "datavjs",
"version": "0.0.1",
"version": "0.1.1",
"repository": {
"type": "git",
"url": "git://github.com/TBEDP/datavjs.git"
@ -11,10 +11,12 @@
"author": "DataV",
"license": "MIT",
"description": "DataV.js is a JavaScript library for data visualization",
"dependencies": {
"uglify-js": "*"
"devDependencies": {
"uglify-js": "*",
"anywhere": "*"
},
"scripts": {
"start": "npm run build & anywhere 8888",
"build": "node bin/build.js"
},
"tracker": "UA-17170593-4"