From 71801c8fc725ac5e50a1bf45ca5c13398db61799 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Thu, 29 Nov 2012 17:24:24 +0800 Subject: [PATCH 1/3] Add start command --- package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5c5de12..864cae6 100644 --- a/package.json +++ b/package.json @@ -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" From 8afebacbb9a3e8564069b9ae2272f7708194989a Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Thu, 29 Nov 2012 23:22:55 +0800 Subject: [PATCH 2/3] Refine column chart --- example/column/column.html | 93 +++++++++++++-------- example/column/column_source.html | 65 --------------- lib/charts/column.js | 133 ++++++++++++++++++++---------- 3 files changed, 148 insertions(+), 143 deletions(-) delete mode 100644 example/column/column_source.html diff --git a/example/column/column.html b/example/column/column.html index 3831584..e6fde2b 100644 --- a/example/column/column.html +++ b/example/column/column.html @@ -1,39 +1,64 @@ - - - - Column - - + + + Column + - - - - -
- - + + + + +
+ + \ No newline at end of file diff --git a/example/column/column_source.html b/example/column/column_source.html deleted file mode 100644 index 03611d6..0000000 --- a/example/column/column_source.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - Column - - - - - - -
- - - \ No newline at end of file diff --git a/lib/charts/column.js b/lib/charts/column.js index 6c0de8c..a23db71 100644 --- a/lib/charts/column.js +++ b/lib/charts/column.js @@ -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 = 50; //图例区域的左上顶点坐标x,y,宽,高 this.defaults.legendArea = [422, 50, 472, 220]; //散点矩阵区域的左上顶点坐标x,y,宽,高 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, @@ -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(); + } }; /*! * 导出 From 2fd19c8c89c45d69a959a4ca1f5d0152bd8799f1 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Fri, 30 Nov 2012 02:27:34 +0800 Subject: [PATCH 3/3] Update column chart --- lib/charts/column.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/charts/column.js b/lib/charts/column.js index a23db71..b226c1d 100644 --- a/lib/charts/column.js +++ b/lib/charts/column.js @@ -81,7 +81,7 @@ this.defaults.yBase = 0; this.defaults.showLegend = true; - this.defaults.legendWidth = 50; + this.defaults.legendWidth = 100; //图例区域的左上顶点坐标x,y,宽,高 this.defaults.legendArea = [422, 50, 472, 220]; //散点矩阵区域的左上顶点坐标x,y,宽,高 @@ -289,7 +289,7 @@ } hLines.attr({ "stroke": "#ECECEC", - "stroke-width": 1 + "stroke-width": 0.1 }); };