From 822a455b912f4f79a15d7afdf827cbabc88d5fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=81=E6=9C=97?= Date: Sat, 20 Sep 2014 22:13:56 +0800 Subject: [PATCH 1/2] add basic line --- demos/charts/line/line_basic.js | 35 ++++++ src/charts/line/line_basic.js | 182 ++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 demos/charts/line/line_basic.js create mode 100644 src/charts/line/line_basic.js diff --git a/demos/charts/line/line_basic.js b/demos/charts/line/line_basic.js new file mode 100644 index 0000000..2b88c08 --- /dev/null +++ b/demos/charts/line/line_basic.js @@ -0,0 +1,35 @@ +var Line = require('datav/charts/line/line_basic'); +var $ = require('jquery'); +var d3 = require('d3'); + +var data = [ + {date: "2014-08-08", value: 1}, + {date: "2014-08-09", value: 2}, + {date: "2014-08-10", value: 3}, + {date: "2014-08-11", value: 4}, + {date: "2014-08-12", value: 5}, + {date: "2014-08-13", value: 3}, + {date: "2014-08-14", value: 2}, + {date: "2014-08-15", value: 5}, + {date: "2014-08-16", value: 1}, + {date: "2014-08-17", value: 1} +]; + +var valueExtent = d3.extent(data, function(d) { return d.value; }); + +var click = function (e, data) { + console.log(data); +}; + +var mouseover = function (e, data) { + console.log(data); +}; + +var line = new Line(('#preview_chart')[0], { + 'min': valueExtent[0], + 'max': valueExtent[1], + 'gridNum': 3 +}); +line.render(data); + +module.exports = line; \ No newline at end of file diff --git a/src/charts/line/line_basic.js b/src/charts/line/line_basic.js new file mode 100644 index 0000000..35df8d7 --- /dev/null +++ b/src/charts/line/line_basic.js @@ -0,0 +1,182 @@ +/** + * Simple line chart + * + * @example + * + * line.render([ + * { name: 'Taobao', + * data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + * }, + * { name: 'Tmall', + * data: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + * }, + * { name: 'Alipay', + * data: [20, 18, 2, 3, 7, 19, 29, 30, 10, 8] + * } + * ]); + */ +var $ = require('jquery'); +var d3 = require('d3'); +var Event = require('../../core/event'); +var util = require('../../core/util'); +/** + * + * Class SimpleLine + * @param {Object} config + * { + * container: {String|DomNode} + * color: {Color Object} + * className: {String} css class name + * width: {Number} + * height: {Number} + * margin: {Object} {left, right, top, bottom} + * xTicks: {Array} + * emptyMessage: {String} + * } + * + */ +function Line (container, config) { + if (typeof container === 'string') { + container = $(container)[0]; + } + this.container = container; + + var defaults = {}; + defaults.width = config.width || dom.width() || 400; + defaults.height = config.height || dom.height() || 200; + defaults.margin = config.margin || {top: 20, right: 20, bottom: 30, left: 50}; + defaults.max = config.max || 100; + defaults.min = config.min || 0; + var max = defaults.max; + var max = defaults.max; + var valueStep = (defaults.max - defaults.min) / 3; + defaults.yTickValues = config.yTickValues || [min, min + valueStep, max - valueStep, max]; + defaults.yValueFormat = config.yValueFormat || 'f'; + defaults.xAxisStyle = config.xAxisStyle || {'fill': 'none', 'stroke': '#000', 'shape-rendering': 'crispEdges'}; + defaults.yAxisStyle = config.yAxisStyle || {'fill': 'none', 'stroke': '#000', 'stroke-width': '1px'}; + defaults.lineStyle = config.lineStyle || {'fill': 'none', 'stroke-width': '1.5px'}; + defaults.colorList = config.colorList || ['#ededed', '#d6e685', '#8cc665', '#44a340', '#1e6823']; + defaults.getColor = config.getColor || function (num) { + return defaults.colorList[num % defaults.colorList.length]; + }; + + this.defaults = defaults; +} + +Event.extend(Line, { + bindEvent: function () { + ; + }, + empty: function (msg) { + msg = msg || '暂无数据'; + this.container.html('
' + msg + '
'); + }, + _getSVG = function () { + var conf = this.defaults; + var container = this.container; + + var svg = d3.select(container).append("svg") + .attr({ + 'width': conf.width, + 'height': conf.height, + }); + + return svg; + }, + /** + * render data + * @public + * @param {[type]} data [description] + * @param {[type]} options [description] + * @return {[type]} [description] + */ + render: function (data, callback) { + if (!data || !data.length) { + return this.empty(this.config.emptyMessage); + } + this.data = data; + + var svg = this._getSVG(); + var conf = this.defaults; + var margin = conf.margin; + var width = conf.width - margin.left - margin.right; + var height = conf.height - margin.top - margin.bottom; + var min = conf.min; + var max = conf.max; + var gridNum = conf.gridNum; + var getColor = conf.getColor; + + var data = this.data; + + var paper = svg.append('g') + .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + + var x = d3.scale.ordinal() + .rangeBands([0, width], 0.5, 0.2); + + var y = d3.scale.linear() + .range([height, 0]); + + x.domain(data.map(function(d) { return d.date; })); + y.domain([min, max]); + + var xAxis = d3.svg.axis() + .scale(x) + .orient('bottom'); + + var yTicks = []; + var yStep = (max - min) / (gridNum - 1); + for (var i = 0; i < gridNum; i++) { + yTicks.push(min + yStep * i); + } + + var yAxis = d3.svg.axis() + .scale(y) + .tickValues(conf.yTickValues) + .tickFormat(d3.format(conf.yValueFormat)) + .orient('left'); + + var xAxisNode = paper.append('g') + .attr({ + 'class': 'x axis', + 'transform': 'translate(0,' + height + ')' + }) + .call(xAxis); + + xAxisNode.selectAll('.axis path, .axis line').style(conf.xAxisStyle); + + var yAxisNode = paper.append('g') + .attr({ 'class': 'y axis' }) + .call(yAxis); + + yAxisNode.selectAll('.axis path, .axis line').style(conf.yAxisStyle); + + var line = d3.svg.line() + .x(function(d) { return x(d.date) + x.rangeBand() / 2; }) + .y(function(d) { return y(d.value); }); + + paper.append('path') + .datum(data) + .attr({ + 'class': 'line', + 'd': line, + 'stroke': getColor(0), + }) + .style(conf.lineStyle); + + this.svg = svg; + this.paper = paper; + this.x = x; + this.y = y; + this.xAxis = xAxis; + this.yAxis = yAxis; + this.xAxisNode = xAxisNode; + this.yAxisNode = yAxisNode; + this.line = line; + + callback = callback || function () {}; + callback(); + } +}); + +module.exports = Line; From a8d0a9f835c8a046f739a6ce9ee0eebf8108ef4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=81=E6=9C=97?= Date: Thu, 11 Dec 2014 12:26:41 +0800 Subject: [PATCH 2/2] test --- Makefile | 2 + demos/charts/line/line_basic.js | 68 ++++++++++------- demos/charts/line/simple_line.js | 3 +- demos/node_modules/datav | 1 + src/charts/line/line_basic.js | 121 +++++++++++++++++++------------ src/charts/tree/tree_basic.js | 119 ++++++++++++++++++++++++++++++ 6 files changed, 237 insertions(+), 77 deletions(-) create mode 120000 demos/node_modules/datav create mode 100644 src/charts/tree/tree_basic.js diff --git a/Makefile b/Makefile index c3966ca..f338c7d 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,12 @@ TESTS = $(shell ls -S `find test/unit -type f -name "*.js" -print`) MOCHA = ./node_modules/.bin/mocha DOCV = ./node_modules/.bin/docv mkdir = mkdir test +PWD = $(shell pwd) install: @npm install . @cd demos; tnpm install . + @ln -s $(PWD)/src $(PWD)/demos/node_modules/datav release: @echo Release datav diff --git a/demos/charts/line/line_basic.js b/demos/charts/line/line_basic.js index 2b88c08..fc6bd26 100644 --- a/demos/charts/line/line_basic.js +++ b/demos/charts/line/line_basic.js @@ -1,35 +1,47 @@ var Line = require('datav/charts/line/line_basic'); var $ = require('jquery'); -var d3 = require('d3'); -var data = [ - {date: "2014-08-08", value: 1}, - {date: "2014-08-09", value: 2}, - {date: "2014-08-10", value: 3}, - {date: "2014-08-11", value: 4}, - {date: "2014-08-12", value: 5}, - {date: "2014-08-13", value: 3}, - {date: "2014-08-14", value: 2}, - {date: "2014-08-15", value: 5}, - {date: "2014-08-16", value: 1}, - {date: "2014-08-17", value: 1} +var dataList = [ + { + name: 'group1', + data: [ + {date: "2014-08-08", value: 1}, + {date: "2014-08-09", value: 2}, + {date: "2014-08-10", value: 3}, + {date: "2014-08-11", value: 4}, + {date: "2014-08-12", value: 5}, + {date: "2014-08-13", value: 3}, + {date: "2014-08-14", value: 2}, + {date: "2014-08-15", value: 5}, + {date: "2014-08-16", value: 1}, + {date: "2014-08-17", value: 1} + ], + }, + { + name: 'group2', + data: [ + {date: "2014-08-08", value: 4}, + {date: "2014-08-09", value: 5}, + {date: "2014-08-10", value: 7}, + {date: "2014-08-11", value: 8}, + {date: "2014-08-12", value: 3}, + {date: "2014-08-13", value: 6}, + {date: "2014-08-14", value: 7}, + {date: "2014-08-15", value: 3}, + {date: "2014-08-16", value: 5}, + {date: "2014-08-17", value: 2}, + {date: "2014-08-18", value: 7} + ] + } ]; -var valueExtent = d3.extent(data, function(d) { return d.value; }); - -var click = function (e, data) { - console.log(data); -}; - -var mouseover = function (e, data) { - console.log(data); -}; - -var line = new Line(('#preview_chart')[0], { - 'min': valueExtent[0], - 'max': valueExtent[1], - 'gridNum': 3 +$('#preview_chart').empty(); +var line = new Line($('#preview_chart'), { + 'width': 800, + 'height': 200, + 'yTickNum': 9, + 'min': 0 }); -line.render(data); +line.render(dataList); -module.exports = line; \ No newline at end of file +module.exports = line; diff --git a/demos/charts/line/simple_line.js b/demos/charts/line/simple_line.js index 54d6800..94e3b35 100644 --- a/demos/charts/line/simple_line.js +++ b/demos/charts/line/simple_line.js @@ -2,8 +2,7 @@ var SimpleLine = require('datav/charts/line/simple_line'); var $ = require('jquery'); var line = new SimpleLine({ - container: $('#preview_chart')[0], - + container: $('#preview_chart')[0] }); var data = [ diff --git a/demos/node_modules/datav b/demos/node_modules/datav new file mode 120000 index 0000000..5080ebd --- /dev/null +++ b/demos/node_modules/datav @@ -0,0 +1 @@ +/Users/wxtheseue/Documents/code/datav/src \ No newline at end of file diff --git a/src/charts/line/line_basic.js b/src/charts/line/line_basic.js index 35df8d7..479b749 100644 --- a/src/charts/line/line_basic.js +++ b/src/charts/line/line_basic.js @@ -1,19 +1,5 @@ /** - * Simple line chart - * - * @example - * - * line.render([ - * { name: 'Taobao', - * data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - * }, - * { name: 'Tmall', - * data: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], - * }, - * { name: 'Alipay', - * data: [20, 18, 2, 3, 7, 19, 29, 30, 10, 8] - * } - * ]); + * Line Basic chart */ var $ = require('jquery'); var d3 = require('d3'); @@ -21,7 +7,7 @@ var Event = require('../../core/event'); var util = require('../../core/util'); /** * - * Class SimpleLine + * Class Line Basic * @param {Object} config * { * container: {String|DomNode} @@ -37,23 +23,22 @@ var util = require('../../core/util'); */ function Line (container, config) { if (typeof container === 'string') { - container = $(container)[0]; + container = $(container); } this.container = container; var defaults = {}; - defaults.width = config.width || dom.width() || 400; - defaults.height = config.height || dom.height() || 200; + defaults.width = config.width || container.width() || 400; + defaults.height = config.height || container.height() || 200; defaults.margin = config.margin || {top: 20, right: 20, bottom: 30, left: 50}; - defaults.max = config.max || 100; - defaults.min = config.min || 0; - var max = defaults.max; - var max = defaults.max; - var valueStep = (defaults.max - defaults.min) / 3; - defaults.yTickValues = config.yTickValues || [min, min + valueStep, max - valueStep, max]; + defaults.max = config.max; + defaults.min = config.min; + defaults.yTickNum = config.yTickNum || 4; + defaults.xTickValues = config.xTickValues || []; + defaults.yTickValues = config.yTickValues || []; defaults.yValueFormat = config.yValueFormat || 'f'; defaults.xAxisStyle = config.xAxisStyle || {'fill': 'none', 'stroke': '#000', 'shape-rendering': 'crispEdges'}; - defaults.yAxisStyle = config.yAxisStyle || {'fill': 'none', 'stroke': '#000', 'stroke-width': '1px'}; + defaults.yAxisStyle = config.yAxisStyle || {'fill': 'none', 'stroke': '#000', 'shape-rendering': 'crispEdges'}; defaults.lineStyle = config.lineStyle || {'fill': 'none', 'stroke-width': '1.5px'}; defaults.colorList = config.colorList || ['#ededed', '#d6e685', '#8cc665', '#44a340', '#1e6823']; defaults.getColor = config.getColor || function (num) { @@ -71,11 +56,11 @@ Event.extend(Line, { msg = msg || '暂无数据'; this.container.html('
' + msg + '
'); }, - _getSVG = function () { + _getSVG: function () { var conf = this.defaults; var container = this.container; - var svg = d3.select(container).append("svg") + var svg = d3.select(container[0]).append("svg") .attr({ 'width': conf.width, 'height': conf.height, @@ -83,30 +68,55 @@ Event.extend(Line, { return svg; }, + // should add it to datavjs + _unionArray: function (arrays) { + if (arrays === []) { + return []; + } + + var array = d3.merge(arrays); + var unionArray = []; + $.each(array, function (i, el) { + if ($.inArray(el, unionArray) === -1) { + unionArray.push(el); + } + }); + + return unionArray; + }, + _getExtent: function (dataList) { + var min = d3.min(dataList, function(data) { return d3.min(data, function(item) { return item.value; }); }); + var max = d3.max(dataList, function(data) { return d3.max(data, function(item) { return item.value; }); }); + + return [min, max]; + }, /** * render data * @public * @param {[type]} data [description] - * @param {[type]} options [description] + * @param {[type]} callback [description] * @return {[type]} [description] */ - render: function (data, callback) { - if (!data || !data.length) { + render: function (source, callback) { + if (!source || !source.length) { return this.empty(this.config.emptyMessage); } - this.data = data; + var dataList = source.map(function (item) { + return item.data; + }); var svg = this._getSVG(); var conf = this.defaults; var margin = conf.margin; var width = conf.width - margin.left - margin.right; var height = conf.height - margin.top - margin.bottom; - var min = conf.min; - var max = conf.max; + var valueExtent = this._getExtent(dataList); var gridNum = conf.gridNum; var getColor = conf.getColor; - - var data = this.data; + var min = conf.min; + min = min === undefined ? valueExtent[0] : min; + var max = conf.max; + max = max === undefined ? valueExtent[1] : max; var paper = svg.append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); @@ -117,22 +127,35 @@ Event.extend(Line, { var y = d3.scale.linear() .range([height, 0]); - x.domain(data.map(function(d) { return d.date; })); + var xTickValues = conf.xTickValues; + if (xTickValues.length === 0) { + var xValueList = []; + for (var i = 0; i < dataList.length; i++) { + xValueList.push(dataList[i].map(function(d) { return d.date; })); + } + + xTickValues = this._unionArray(xValueList); + } + + x.domain(xTickValues); y.domain([min, max]); var xAxis = d3.svg.axis() .scale(x) .orient('bottom'); - var yTicks = []; - var yStep = (max - min) / (gridNum - 1); - for (var i = 0; i < gridNum; i++) { - yTicks.push(min + yStep * i); + var yTickValues = conf.yTickValues; + var yTickNum = conf.yTickNum; + if (yTickValues.length === 0) { + var yStep = (max - min) / (yTickNum - 1); + for (var i = 0; i < yTickNum; i++) { + yTickValues.push(min + yStep * i); + } } var yAxis = d3.svg.axis() .scale(y) - .tickValues(conf.yTickValues) + .tickValues(yTickValues) .tickFormat(d3.format(conf.yValueFormat)) .orient('left'); @@ -155,12 +178,16 @@ Event.extend(Line, { .x(function(d) { return x(d.date) + x.rangeBand() / 2; }) .y(function(d) { return y(d.value); }); - paper.append('path') - .datum(data) + var lineGroup = paper.selectAll(".line_group") + .data(dataList) + .enter().append("g") + .attr("class", "line_group"); + + lineGroup.append('path') .attr({ 'class': 'line', - 'd': line, - 'stroke': getColor(0), + 'd': function (d) { return line(d); }, + 'stroke': function (d, i) { return getColor(i); } }) .style(conf.lineStyle); @@ -172,7 +199,7 @@ Event.extend(Line, { this.yAxis = yAxis; this.xAxisNode = xAxisNode; this.yAxisNode = yAxisNode; - this.line = line; + this.lineGroup = lineGroup; callback = callback || function () {}; callback(); diff --git a/src/charts/tree/tree_basic.js b/src/charts/tree/tree_basic.js new file mode 100644 index 0000000..48f7c78 --- /dev/null +++ b/src/charts/tree/tree_basic.js @@ -0,0 +1,119 @@ +/** + * Tree Basic chart + */ +var $ = require('jquery'); +var d3 = require('d3'); +var Event = require('../../core/event'); +var util = require('../../core/util'); +/** + * + * Class Tree Basic + * @param {Object} config + * { + * container: {String|DomNode} + * color: {Color Object} + * className: {String} css class name + * width: {Number} + * height: {Number} + * margin: {Object} {left, right, top, bottom} + * xTicks: {Array} + * emptyMessage: {String} + * } + * + */ +function Tree (container, config) { + if (typeof container === 'string') { + container = $(container); + } + this.container = container; + + var defaults = {}; + defaults.width = config.width || container.width() || 400; + defaults.height = config.height || container.height() || 200; + defaults.margin = config.margin || {top: 20, right: 20, bottom: 30, left: 50}; + defaults.max = config.max; + defaults.min = config.min; + defaults.yTickNum = config.yTickNum || 4; + defaults.xTickValues = config.xTickValues || []; + defaults.yTickValues = config.yTickValues || []; + defaults.yValueFormat = config.yValueFormat || 'f'; + defaults.xAxisStyle = config.xAxisStyle || {'fill': 'none', 'stroke': '#000', 'shape-rendering': 'crispEdges'}; + defaults.yAxisStyle = config.yAxisStyle || {'fill': 'none', 'stroke': '#000', 'shape-rendering': 'crispEdges'}; + defaults.lineStyle = config.lineStyle || {'fill': 'none', 'stroke-width': '1.5px'}; + defaults.colorList = config.colorList || ['#ededed', '#d6e685', '#8cc665', '#44a340', '#1e6823']; + defaults.getColor = config.getColor || function (num) { + return defaults.colorList[num % defaults.colorList.length]; + }; + + this.defaults = defaults; +} + +Event.extend(Tree, { + bindEvent: function () { + ; + }, + empty: function (msg) { + msg = msg || '暂无数据'; + this.container.html('
' + msg + '
'); + }, + _getSVG: function () { + var conf = this.defaults; + var container = this.container; + + var svg = d3.select(container[0]).append("svg") + .attr({ + 'width': conf.width, + 'height': conf.height, + }); + + return svg; + }, + _createTree: function () { + var tree = d3.layout.tree().size([height, width]); + } + // should add it to datavjs + _unionArray: function (arrays) { + if (arrays === []) { + return []; + } + + var array = d3.merge(arrays); + var unionArray = []; + $.each(array, function (i, el) { + if ($.inArray(el, unionArray) === -1) { + unionArray.push(el); + } + }); + + return unionArray; + }, + /** + * render data + * @public + * @param {[type]} data [description] + * @param {[type]} callback [description] + * @return {[type]} [description] + */ + render: function (source, callback) { + if (!source || !source.length) { + return this.empty(this.config.emptyMessage); + } + var dataList = source.map(function (item) { + return item.data; + }); + + var svg = this._getSVG(); + var conf = this.defaults; + var margin = conf.margin; + var width = conf.width - margin.left - margin.right; + var height = conf.height - margin.top - margin.bottom; + + var tree = d3.layout.tree().size([height, width]); + + + callback = callback || function () {}; + callback(); + } +}); + +module.exports = Line;