diff --git a/README.md b/README.md index eb9fca20..e8e26936 100644 --- a/README.md +++ b/README.md @@ -7,138 +7,22 @@ DataV.js 是一个 JavaScript 的数据可视化库,致力于推动数据可 ![DataV logo](https://raw.github.com/TBEDP/datavjs/master/doc/assets/logo.png) - [API文档](http://tbedp.github.com/datavjs/index.html) -- 任意疑问,请移至底下联系人部分即可。 -## 安装 -目前处于开发中,并未release正式版本,如需使用,请采用如下方式: +## 说明 +此版本只演示与TEDP主版本有区别或新增的内容,如需查看原始主版本,请至 [DataV.js on Github](https://github.com/TBEDP/datavjs) . -```bash -$ git clone git://github.com/TBEDP/datavjs.git -``` - -需要注意的是 [example](https://github.com/TBEDP/datavjs/tree/master/example) 目录下的例子中有 `ajax` 存在,所以你需要一个服务器来托管这些静态文件。 - -你也可以试用基于 DataV.js 开发的 [Chrome插件](https://chrome.google.com/webstore/detail/datavjs/fkekhkndcgobgjbkclehjognobgdoppm),可以在任意网页中将表格可视化。 - -## Examples -- [Pie](http://datavlab.org/datavjs/#pie) -![Pie图](https://raw.github.com/TBEDP/datavjs/master/doc/assets/pie.jpg) -- [Treemap](http://datavlab.org/datavjs/#treemap) -![Treemap图](https://raw.github.com/TBEDP/datavjs/master/doc/assets/treemap.jpg) -- [Tree](http://datavlab.org/datavjs/#tree) -![Tree图](https://raw.github.com/TBEDP/datavjs/master/doc/assets/tree.jpg) -- [Stream](http://datavlab.org/datavjs/#stream) -![Stream图](https://raw.github.com/TBEDP/datavjs/master/doc/assets/stream.jpg) -- [ScatterplotMatrix](http://datavlab.org/datavjs/#scatterplotMatrix) -![ScatterplotMatrix图](https://raw.github.com/TBEDP/datavjs/master/doc/assets/scatterplotMatrix.jpg) -- [Force](http://datavlab.org/datavjs/#force) -![Force图](https://raw.github.com/TBEDP/datavjs/master/doc/assets/force.jpg) -- [Matrix](http://datavlab.org/datavjs/#matrix) -![Matrix图](https://raw.github.com/TBEDP/datavjs/master/doc/assets/matrix.jpg) -- [Bubble](http://datavlab.org/datavjs/#bubble) -![Bubble图](https://raw.github.com/TBEDP/datavjs/master/doc/assets/bubble.jpg) -- [Chord](http://datavlab.org/datavjs/#chord) -![Chord图](https://raw.github.com/TBEDP/datavjs/master/doc/assets/chord.jpg) -- [Gender 性别图](https://github.com/TBEDP/datavjs/tree/master/example/gender) +## New Examples +- [Gender 性别图](https://github.com/hlqf/datavjs/tree/master/example/gender) ![Gender](http://nfs.nodeblog.org/d/3/d317bbffe6cc085b63c653e02d4d5373.png) - -## Quick start -此处以Pie图为例。 - -### 引入依赖 - -```html - - -``` - -### 准备数据 - -```js -var source = [ - ['北京', 50265], - ['上海', 60555], - ['广州', 38544], - ['深圳', 27276], - ['西安', 20506], - ['昆明', 26916], - ['武汉', 17636], - ['拉萨', 977], - ['哈尔滨', 10406], - ['乌鲁木齐', 6695] -]; -``` - -### 渲染图表 - -```js -// 初始化组件 -var pie = new Pie("container", {width: 1000, tag: true}); -// 添加数据源 -pie.setSource(source); -// 渲染 -pie.render(); -``` - -### 结果: - -![Pie图](https://raw.github.com/TBEDP/datavjs/master/doc/assets/pie.jpg) - -## Requirements: -* [D3.js](https://github.com/mbostock/d3). -* [Raphael.js](http://raphaeljs.com/). -* [Sea.js](https://github.com/seajs/seajs). - -## Learn more? -- The example site: -- [API Docs](http://tbedp.github.com/datavjs/) - -## Contributors -Thanks goes to the people who have contributed code to this library, see the [GitHub Contributors](https://github.com/TBEDP/datavjs/graphs/contributors) page. - -Below is the output from `git-summary` - -```bash -$ git summary - - project : datavjs - repo age : 1 year - active : 159 days - commits : 431 - files : 276 - authors : - 305 Jackson Tian 70.8% - 46 jdk137 10.7% - 25 xie cong 5.8% - 18 gozo1234 4.2% - 11 Theseue 2.6% - 8 xiecong 1.9% - 8 wxtheseue 1.9% - 4 Jiang Dongke 0.9% - 3 郭方舟 0.7% - 2 unknown 0.5% - 1 arcthur 0.2% -``` - -## License - -DataV.js is available under the [MIT License](https://github.com/TBEDP/datavjs/blob/master/MIT-License). - -## Contact - -组件由淘宝数据产品部可视化小组以及浙大CAD&CG可视化与可视分析小组共同开发 -开发联系人,有问题可咨询: - -- 阿里旺旺 - - 朴灵[![朴灵 在线咨询](http://amos1.taobao.com/online.ww?v=2&uid=%E6%9C%B4%E7%81%B5&s=1)](http://amos1.taobao.com/msg.ww?v=2&uid=%E6%9C%B4%E7%81%B5&s=1) - - 宁朗[![宁朗 在线咨询](http://amos1.taobao.com/online.ww?v=2&uid=%E5%AE%81%E6%9C%97&s=1)](http://amos1.taobao.com/msg.ww?v=2&uid=%E5%AE%81%E6%9C%97&s=1) - - 法慧[![法慧 在线咨询](http://amos1.taobao.com/online.ww?v=2&uid=%E6%B3%95%E6%85%A7&s=1)](http://amos1.taobao.com/msg.ww?v=2&uid=%E6%B3%95%E6%85%A7&s=1) - - 解聪(浙大实习生)[![解聪 在线咨询](http://amos1.taobao.com/online.ww?v=2&uid=%E9%95%BF%E4%BA%AD%E7%9A%84%E8%8B%8F%E5%B9%95%E9%81%AE&s=1)](http://amos1.taobao.com/msg.ww?v=2&uid=%E9%95%BF%E4%BA%AD%E7%9A%84%E8%8B%8F%E5%B9%95%E9%81%AE&s=1) - - 黄芯芯(浙大实习生)[![黄芯芯 在线咨询](http://amos1.taobao.com/online.ww?v=2&uid=littlemonkey007&s=1)](http://amos1.taobao.com/msg.ww?v=2&uid=littlemonkey007&s=1) -- 阿里旺旺群:76480715 -- QQ群: 18164936 -- Google Group: [DataVLab](http://groups.google.com/group/datavlab) -- Gtalk: -- 浙大CAD&CG可视化与可视分析小组: [VAG Wiki](http://www.cad.zju.edu.cn/home/vagwiki/index.php) - -如有疑问,或发现Bug,也可[提交Bug](https://github.com/TBEDP/datavjs/issues/new) +- [Level 层级图](https://github.com/hlqf/datavjs/tree/master/example/level) +![Level](https://raw.github.com/hlqf/datavjs/master/doc/assets/level.jpg) +- [Level 多元素层级图](https://github.com/hlqf/datavjs/tree/master/example/level) +![Level](https://raw.github.com/hlqf/datavjs/master/doc/assets/level_duo.jpg) +- [ChineseMap图](https://github.com/hlqf/datavjs/tree/master/example/chinamap) +![ChineseMap](https://raw.github.com/hlqf/datavjs/master/doc/assets/chinesemap.jpg) +- [分层选择器](https://github.com/hlqf/datavjs/tree/master/example/tiflevel) +![TifLevel](https://raw.github.com/hlqf/datavjs/master/doc/assets/tiflevel.jpg) +- [柱状选择器](https://github.com/hlqf/datavjs/tree/master/example/tifbarlevel) +![TifLevel](https://raw.github.com/hlqf/datavjs/master/doc/assets/tifbarlevel.jpg) + +选择器用于既需要显示数据间的相关占比关系又需要合并或拆分查询条件的业务场景。 diff --git a/doc/assets/chinesemap.jpg b/doc/assets/chinesemap.jpg new file mode 100644 index 00000000..d3643a81 Binary files /dev/null and b/doc/assets/chinesemap.jpg differ diff --git a/doc/assets/level.jpg b/doc/assets/level.jpg new file mode 100644 index 00000000..4ecc0278 Binary files /dev/null and b/doc/assets/level.jpg differ diff --git a/doc/assets/level_duo.jpg b/doc/assets/level_duo.jpg new file mode 100644 index 00000000..0c30f30d Binary files /dev/null and b/doc/assets/level_duo.jpg differ diff --git a/doc/assets/tifbarlevel.jpg b/doc/assets/tifbarlevel.jpg new file mode 100644 index 00000000..2fa62978 Binary files /dev/null and b/doc/assets/tifbarlevel.jpg differ diff --git a/doc/assets/tiflevel.jpg b/doc/assets/tiflevel.jpg new file mode 100644 index 00000000..ab853d68 Binary files /dev/null and b/doc/assets/tiflevel.jpg differ diff --git a/example/level/level_shu.html b/example/level/level_shu.html new file mode 100644 index 00000000..c5dedafb --- /dev/null +++ b/example/level/level_shu.html @@ -0,0 +1,74 @@ + + + + + + Document + + + + + + +
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/example/tifbarlevel/tifbarlevel.html b/example/tifbarlevel/tifbarlevel.html new file mode 100644 index 00000000..c9539d8a --- /dev/null +++ b/example/tifbarlevel/tifbarlevel.html @@ -0,0 +1,33 @@ + + + + + Demo + + + + + + + +
+ + + \ No newline at end of file diff --git a/example/tifgender/tifgender.html b/example/tifgender/tifgender.html new file mode 100644 index 00000000..7bc7f084 --- /dev/null +++ b/example/tifgender/tifgender.html @@ -0,0 +1,31 @@ + + + + + Demo + + + + + +
+ + + \ No newline at end of file diff --git a/example/tiflevel/tiflevel.html b/example/tiflevel/tiflevel.html new file mode 100644 index 00000000..c067e565 --- /dev/null +++ b/example/tiflevel/tiflevel.html @@ -0,0 +1,64 @@ + + + + + + Document + + + + + + +
+
+ + + \ No newline at end of file diff --git a/lib/charts/level.js b/lib/charts/level.js new file mode 100644 index 00000000..975f877b --- /dev/null +++ b/lib/charts/level.js @@ -0,0 +1,266 @@ +/*! + * datavjs - lib/charts/level.js + * + * 层级图 + * + * Copyright(c) 2013 hlqf + * MIT Licensed + */ +(function (name, def) { + if (typeof define === 'function') { + define(def); + } else { + this[name] = def(function (id) { + return this[id]; + }); + } +})('LevelChart', function (require) { + var DataV = require('DataV'); + + //init level chart,set default params + var LevelChart = DataV.extend(DataV.Chart, { + initialize: function (node, options) { + this.type = 'LevelChart'; + this.node = this.checkContainer(node); + + this.defaults.labels = [ '低', '偏低', '中', '偏高', '高' ]; + this.defaults.width = 235; + this.defaults.height = 200; + + this.setOptions(options); + } + }); + + LevelChart.prototype.drawGridConsume = function (r, x, y, w, h, wv, hv, color, options) { + color = color || "#999999"; + var path = []; + var conf = this.defaults; + var rowHeight = h / hv, + columnWidth = w / wv; + for (var i = 0; i < hv; i++) { + if (i === hv - 1) { + r.path(["M", Math.round(x) - 0.5, Math.round(y + i * rowHeight) - 0.5, "H", Math.round(x + w) + 0.5]).attr({'stroke-width': 1, 'stroke': '#999999'}); + continue; + } + path = path.concat(["M", Math.round(x) + 0.5, Math.round(y + i * rowHeight) + 0.5, "H", Math.round(x + w) + 0.5]); + } + for (i = 0; i < wv; i++) { + path = path.concat(["M", Math.round(x + i * columnWidth) + 0.5, Math.round(y) + 0.5, "V", Math.round(y + h) + 0.5]); + } + if ((!options || !options.tipImg) && (!options || !options.nobase)) { + r.image('http://img03.taobaocdn.com/tps/i3/T1aS6GXhFgXXcm5wzo-77-19.png', x + w - 80, 0, 77, 19); + } else if (!!options.tipImg) { + r.image(options.tipImg, x + w - 80, 0, 77, 19); + } + return r.path(path.join(",")).attr({ stroke: color, opacity: 0.7, 'stroke-width': 1}); + } + + LevelChart.prototype.createCanvas = function () { + var conf = this.defaults; + this.canvas = new Raphael(this.node, conf.width, conf.height); + }; + + LevelChart.prototype.render = function (data, options) { + + if (options && options.labels) { + var labels = options.labels; + } else { + var labels = this.defaults.labels; + } + // var labesl = options.labels ? options.labels : this.defaults.labels; + var nobase = options && options.nobase; + var nopercent = options && options.nopercent; + + var conf = this.defaults; + var $node = $(this.node); + var width = conf.width; + var height = conf.height; + var leftGutter = 10; + var bottomGutter = 5; + var topGutter = 20; + var padding = 10; + + var r = Raphael($node[0], width, height); + var localMax = Math.max.apply(Math, data); + var globalMax = options && options.max || localMax || 100; + var globalBase = options && options.globalBase || [8, 17, 50, 17, 10]; + + if (!nobase && globalMax < 50) { + globalMax = 50; + } + + this.drawGridConsume(r, 0, topGutter + 0.5, width, height - topGutter - bottomGutter, 0, 6, '#cccbbb', options); + + var globalBasePlot = []; + + var columnWidth = Math.floor(width / labels.length) - leftGutter - padding; + var columnHeight = Math.floor(height - topGutter - bottomGutter); + + //判断是否需要分条 + var is2dFlag = 0; + for (var x = 0; x < data.length; x++) { + if (Object.prototype.toString.call(data[x]) === '[object Array]') { + is2dFlag = 1; + } + } + + if (is2dFlag === 1){ //当出现多维同柱对比时 + var x = []; + var y = []; + var tmpMax = []; + // 重赋多维基数默认值 + globalBase = Object.prototype.toString.call(globalBase) === '[object Array]' ? globalBase : [[ 10, 20, 30, 10, 15 ], [ 15, 28, 40, 20, 20 ]]; + + var globalMax; + for (var n in data) { + //获得多维数据中的最大值 + for (var i = 0; i < data[n].length; i++) { + tmpMax.push(Math.max.apply(Math, data[n])); + } + globalMax = Math.max.apply(Math, tmpMax); + } + + for (var n in data) { + var color = []; + for (var i = 0; i < data[n].length; i++) { + var k = i + n * data[n].length; + var dwidth = columnWidth / data.length; + + x.push(Math.floor((columnWidth + leftGutter + padding) * i) + 10 + dwidth * n); + var dheight = Math.ceil((columnHeight - 18) * (data[n][i] / globalMax)) * 0.75; + y.push(columnHeight * 6 / 7 - dheight + topGutter - 5.5); + var attr = {'font-family': 'Arial', 'font-size': 12, 'fill': '#666666'}; + var value = data[n][i]; + + r.text(x[i] + columnWidth / 2, height - topGutter, labels[i]).attr(attr); + var rect = r.rect(x[k], y[k], dwidth, dheight); + + var unit = ''; + if (!nopercent) { + unit = '%'; + } + + var text = r.text(x[k] + columnWidth / 4, y[k] - 10, value + unit).attr({'font-size': 10}); + if (n <= 2) { + color = ['#3fa9f5','#ff89a3']; + } else { + var c = 'rgb(300, 200, ' + n * 100 + ')'; + color.push(c); + } + + rect.attr({'fill': color[n], 'stroke': color[n], 'opacity': 0.7}); + + rect.indexI = i; + rect.indexN = n; + if (!nobase) { + // hover效果 + rect.hover(function () { + var li = this.indexI + this.indexN * labels.length; //标记在globalBase中当前柱状坐标的下标 + this.attr({'opacity': 1.0}); + var maxLen = labels.length - 1; + var x = globalBasePlot[this.indexI][0] + columnWidth; + //处理最右侧的元素 + x = this.indexI === maxLen ? x - columnWidth * 2 - 7 : x; + var y = globalBasePlot[li][1] - 10; + this.tooltipImage = r.image('http://img01.taobaocdn.com/tps/i1/T15H_GXiJjXXc.LCDe-40-21.gif', x, y, 40, 21); + if (this.indexI === maxLen) { + this.tooltipImage.rotate(180); + x -= maxLen; + } + this.tooltipText = r.text(x + 23, y + 11, globalBase[this.indexN][this.indexI] + '%').attr({'fill': '#fff', 'font-size': 12}); + }, function () { + this.attr({'opacity': 0.7}); + if (this.tooltipImage) { + this.tooltipImage.remove(); + } + if (this.tooltipText) { + this.tooltipText.remove(); + } + }); + + // 计算全网基数坐标 + globalBasePlot[k] = [x[k], columnHeight * 6 / 7 + - (Math.ceil((columnHeight - 18) * (globalBase[n][i] / globalMax)) * 0.75) + topGutter - 6, + x[k] + columnWidth / 2 , columnHeight * 6 / 7 + - (Math.ceil((columnHeight - 18) * (globalBase[n][i] / globalMax)) * 0.75) + topGutter - 6]; + } + } + } + } else { //当为单条时 + for (var i = 0; i < data.length; i++) { + // 计算依赖的值 + var x = Math.floor((columnWidth + leftGutter + padding) * i) + 10; + var dwidth = columnWidth; + // 与全局最高值之间做变换 + var dheight = Math.ceil((columnHeight - 18) * (data[i] / globalMax)) * 0.75; + var y = columnHeight * 6 / 7 - dheight + topGutter - 6; + var attr = {'font-family': 'Arial', 'font-size': 12, 'fill': '#666666'}; + var value = data[i]; + + // 画坐标轴 + r.text(x + columnWidth / 2, height - topGutter, labels[i]).attr(attr); + // 画柱状 + var rect = r.rect(x, y, dwidth, dheight); + var unit = ''; + if (!nopercent) { + unit = '%'; + } + var text = r.text(x + columnWidth / 2, y - 10, value + unit); + rect.attr({fill: '#3fa9f5', 'stroke': '#3fa9f5', 'opacity': 0.7}); + attr['font-size'] = 14; + text.attr(attr); + rect.indexI = i; + if (!nobase) { + // hover效果 + rect.hover(function () { + this.attr({'opacity': 1.0}); + var maxLen = labels.length - 1; + var x = globalBasePlot[this.indexI][0] + columnWidth; + //处理最右侧的元素 + x = this.indexI === maxLen ? x - columnWidth * 2 - 7 : x; + var y = globalBasePlot[this.indexI][1] - 10; + this.tooltipImage = r.image('http://img01.taobaocdn.com/tps/i1/T15H_GXiJjXXc.LCDe-40-21.gif', x, y, 40, 21); + if (this.indexI === maxLen) { + this.tooltipImage.rotate(180); + x -= maxLen; + } + this.tooltipText = r.text(x + 23, y + 11, globalBase[this.indexI] + '%').attr({'fill': '#fff', 'font-size': 12}); + }, function () { + this.attr({'opacity': 0.7}); + if (this.tooltipImage) { + this.tooltipImage.remove(); + } + if (this.tooltipText) { + this.tooltipText.remove(); + } + }); + // 计算全网基数坐标 + globalBasePlot[i] = [x, columnHeight * 6 / 7 + - (Math.ceil((columnHeight - 18) * (globalBase[i] / globalMax)) * 0.75) + topGutter - 6, + x + columnWidth , columnHeight * 6 / 7 + - (Math.ceil((columnHeight - 18) * (globalBase[i] / globalMax)) * 0.75) + topGutter - 6]; + } + } + } + + if (!nobase && is2dFlag === 0) { + // 画全网基数线,单维时 + var globalBasePath = []; + for (var i = 1; i < globalBasePlot.length; i++) { + globalBasePath.push('M' + globalBasePlot[i - 1].join(' ') + 'L' + globalBasePlot[i].join(' ')); + } + r.path(globalBasePath.join(",")).attr({'stroke': '#999999'}); + } else { + //多维时的全网基线之间不相连接,只画在柱状上 + var globalBasePath = []; + for (var i = 1; i < globalBasePlot.length + 1; i++) { + globalBasePath.push('M' + globalBasePlot[i - 1].join(' ')); + } + r.path(globalBasePath.join(",")).attr({'stroke': '#999999', 'stroke-dasharray': '-', 'stroke-width': 2}); + } + + }; + + return LevelChart; + +}); \ No newline at end of file diff --git a/lib/charts/tifbarlevel.js b/lib/charts/tifbarlevel.js new file mode 100644 index 00000000..87e4ea10 --- /dev/null +++ b/lib/charts/tifbarlevel.js @@ -0,0 +1,172 @@ +/** +* TifBarLevel +*/ +var TifBarLevel = function (config) { + // 4 params must need + var width = config.width || 320, + height = config.height || 190; + var container = typeof config.container === "string" ? document.getElementById(config.container) : config.container; // id or dom node + var data = config.data || [{name: "18", value: 40}, {name: "25", value: 60}]; + + // other optional params + var nullWidth = config.nullWidth || 2; + var nullColor = config.nullColor || 'gray'; + var highlightColor = config.highlightColor || '#3391d4'; + var clickHandle = config.clickHandle || function (d, i) { + //console.log(d.name); + }; + var margin = config.margin || { + "top": 1, + "left": 1, + "bottom": 1, + "right": 1 + }; + var color = this.color = typeof data[0].color !== 'undefined' ? + data.map(function (d) { return d.color; }) : + d3.range(data.length).map(function () { return "#3fa9f5"; }); + var wordSpacing = config.wordSpacing || 10; // distance between word and bar + + var titleDefaultStyle = config.titleDefaultStyle || { + 'cursor': 'pointer', + 'border': 'solid 1px #fff', + 'display': 'inline-block', + 'border-radius': 3, + "font-family": "微软雅黑", + "font-size": "12px", + 'padding': 1 + }; + + var w = width - margin.left - margin.right; + var h = height - margin.top - margin.bottom; + var barH = h / data.length; + + // data process; + var notNullCount = 0; + data.forEach(function (d) { + d.isNull = (d.value === null); + d.v = (d.isNull ? 0 : d.value); + if (!d.isNull) { + notNullCount += 1; + } + }); + var all = data.every(function (d) { + return !d.isNull; + }); + var sum = d3.sum(data, function (d) { return d.v; }); + var max = d3.max(data, function (d) { return d.v; }); + var ratioArr = this.ratioArr = data.map(function (d) { + if (d.isNull) { + d.ratio = 0; + } else { + d.ratio = sum === 0 ? 0 : (d.v / sum); + } + return d.ratio; + }); + var wArr = this.wArr = data.map(function (d) { + return max === 0 ? nullWidth : (w - nullWidth) * d.value / max + nullWidth; + }); + + //draw bar + $(container).css("position", "relative"); + var paper = this.paper = new Raphael(container, width, height); + var rects = this.rects = paper.set(); + data.forEach(function (d, i) { + var rect = paper.rect(margin.left, margin.top + barH * i, wArr[i], barH) + .attr({ + "r": 3, + "cursor": "pointer", + "fill": d.isNull ? nullColor : color[i], + "stroke-width": 1, + "stroke": "#fff" + }); + rects.push(rect); + }); + + // draw words + var ratioWord = this.ratioWord = paper.set(); + var titles = this.titles = []; + //var nameWord = this.nameWord = paper.set(); + //var nameBackground = this.nameBackground = paper.set(); + ratioArr.forEach(function (d, i) { + if (data[i].isNull) { + return; + } + var text = paper.text( + margin.left + wArr[i] + wordSpacing, //x + margin.top + barH * (i + 0.5), //y + Math.round(d * 1000) / 10 + "%" //text string + ).attr({ + "text-anchor": "start", + "fill": color[i] + }); + ratioWord.push(text); + }); + ratioWord.attr({ + "font-size": "14px" + }); + + data.forEach(function (d, i) { + var title = $('
' + d.name + '
').css(titleDefaultStyle); + if (!all && !d.isNull) { + title.css({ + 'border': 'solid 1px black', + 'background-color': highlightColor + }); + } + title.appendTo($(container)); + var tw = title.width(); + var th = title.outerHeight(); + title.css({ + 'position': 'absolute', + 'left': margin.left - wordSpacing - tw, + 'top': margin.top + barH * (i + 0.5) - th / 2, + 'text-align': 'right' + }); + titles.push(title); + }); + + //interaction + //hover + var getHoverIn = function (i) { + var index = i; + var needChange = !(!all && !data[i].isNull); + return function () { + if (needChange) { + titles[index].css({ + 'border': 'solid 1px black', + 'background-color': highlightColor + }); + } + }; + }; + var getHoverOut = function (i) { + var index = i; + var needChange = !(!all && !data[i].isNull); + return function () { + if (needChange) { + titles[index].css({ + 'border': 'solid 1px #fff', + 'background-color': '#fff' + }); + } + }; + }; + //click + var getClick = function (i) { + var index = i; + return function () { + clickHandle(data[index], index); + }; + }; + data.forEach(function (d, i) { + var hoverIn = getHoverIn(i); + var hoverOut = getHoverOut(i); + var click = getClick(i); + rects[i].hover(hoverIn, hoverOut); + rects[i].click(click); + titles[i].on("mouseenter", hoverIn); + titles[i].on("mouseleave", hoverOut); + titles[i].on("click", click); + }); + +}; \ No newline at end of file diff --git a/lib/charts/tifbarlevel2.js b/lib/charts/tifbarlevel2.js new file mode 100644 index 00000000..358f2ab9 --- /dev/null +++ b/lib/charts/tifbarlevel2.js @@ -0,0 +1,173 @@ +/** +* TifBarLevel +*/ +var TifBarLevel = function (config) { + // 4 params must need + var width = config.width || 320, + height = config.height || 190; + var container = typeof config.container === "string" ? document.getElementById(config.container) : config.container; // id or dom node + var data = config.data || [{name: "18", value: 40}, {name: "25", value: 60}]; + + // other optional params + var nullWidth = config.nullWidth || 2; + var nullColor = config.nullColor || 'gray'; + var highlightColor = config.highlightColor || '#3391d4'; + var clickHandle = config.clickHandle || function (d, i) { + //console.log(d.name); + }; + var margin = config.margin || { + "top": 1, + "left": 1, + "bottom": 1, + "right": 1 + }; + var color = this.color = typeof data[0].color !== 'undefined' ? + data.map(function (d) { return d.color; }) : + d3.range(data.length).map(function () { return "#3fa9f5"; }); + var wordSpacing = config.wordSpacing || 10; // distance between word and bar + + var titleDefaultStyle = config.titleDefaultStyle || { + 'cursor': 'pointer', + 'border': 'solid 1px #fff', + 'display': 'inline-block', + 'border-radius': 3, + "font-family": "微软雅黑", + "font-size": "12px", + 'padding': 1 + }; + + var w = height - margin.left - margin.right; + var h = width - margin.top - margin.bottom; + var barH = h / data.length; + + // data process; + var notNullCount = 0; + data.forEach(function (d) { + d.isNull = (d.value === null); + d.v = (d.isNull ? 0 : d.value); + if (!d.isNull) { + notNullCount += 1; + } + }); + var all = data.every(function (d) { + return !d.isNull; + }); + var sum = d3.sum(data, function (d) { return d.v; }); + var max = d3.max(data, function (d) { return d.v; }); + var ratioArr = this.ratioArr = data.map(function (d) { + if (d.isNull) { + d.ratio = 0; + } else { + d.ratio = sum === 0 ? 0 : (d.v / sum); + } + return d.ratio; + }); + var wArr = this.wArr = data.map(function (d) { + return max === 0 ? nullWidth : (w - nullWidth) * d.value / max + nullWidth; + }); + + //draw bar + $(container).css("position", "relative"); + var paper = this.paper = new Raphael(container, width, height); + var rects = this.rects = paper.set(); + data.forEach(function (d, i) { + var rect = paper.rect(margin.top + barH * i, margin.top + w - wArr[i], barH, wArr[i] + h / 3) + .attr({ + "r": 3, + "cursor": "pointer", + "fill": d.isNull ? nullColor : color[i], + "stroke-width": 1, + "stroke": "#fff" + }); + console.log(margin.left); + rects.push(rect); + }); + + // draw words + var ratioWord = this.ratioWord = paper.set(); + var titles = this.titles = []; + //var nameWord = this.nameWord = paper.set(); + //var nameBackground = this.nameBackground = paper.set(); + ratioArr.forEach(function (d, i) { + if (data[i].isNull) { + return; + } + var text = paper.text( + margin.top + barH * (i + 0.5) - wordSpacing * 1.5, + margin.top + w - wArr[i] - 15, + Math.round(d * 1000) / 10 + "%" //text string + ).attr({ + "text-anchor": "start", + "fill": color[i] + }); + ratioWord.push(text); + }); + ratioWord.attr({ + "font-size": "14px" + }); + + data.forEach(function (d, i) { + var title = $('
' + d.name + '
').css(titleDefaultStyle); + if (!all && !d.isNull) { + title.css({ + 'border': 'solid 1px black', + 'background-color': highlightColor + }); + } + title.appendTo($(container)); + var tw = title.width(); + var th = title.outerHeight(); + title.css({ + 'position': 'absolute', + 'left': margin.top + barH * (i + 0.5) - th, + 'top': h - margin.top * 1.3, + 'text-align': 'right' + }); + titles.push(title); + }); + + //interaction + //hover + var getHoverIn = function (i) { + var index = i; + var needChange = !(!all && !data[i].isNull); + return function () { + if (needChange) { + titles[index].css({ + 'border': 'solid 1px black', + 'background-color': highlightColor + }); + } + }; + }; + var getHoverOut = function (i) { + var index = i; + var needChange = !(!all && !data[i].isNull); + return function () { + if (needChange) { + titles[index].css({ + 'border': 'solid 1px #fff', + 'background-color': '#fff' + }); + } + }; + }; + //click + var getClick = function (i) { + var index = i; + return function () { + clickHandle(data[index], index); + }; + }; + data.forEach(function (d, i) { + var hoverIn = getHoverIn(i); + var hoverOut = getHoverOut(i); + var click = getClick(i); + rects[i].hover(hoverIn, hoverOut); + rects[i].click(click); + titles[i].on("mouseenter", hoverIn); + titles[i].on("mouseleave", hoverOut); + titles[i].on("click", click); + }); + +}; \ No newline at end of file diff --git a/lib/charts/tifgender.js b/lib/charts/tifgender.js new file mode 100644 index 00000000..aea8b5ef --- /dev/null +++ b/lib/charts/tifgender.js @@ -0,0 +1,170 @@ +var TifGender = function (config) { + // 4 params must need + var width = config.width || 320, + height = config.height || 190; + var container = typeof config.container === "string" ? document.getElementById(config.container) : config.container; // id or dom node + $(container).css({'position': 'relative'}); + var data = config.data || [{name: "男", value: 40}, {name: "女", value: 60}]; + + // other optional params + var margin = config.margin || { + "top": 1, + "left": 1, + "bottom": 1, + "right": 1 + }; + var highlightColor = config.highlightColor || '#3391d4'; + var clickHandle = config.clickHandle || function (d, i) { + //console.log(d.name); + }; + var color = this.color = typeof data[0].color !== 'undefined' ? + data.map(function (d) { return d.color; }) : + ["#3fa9f5", "#ff88a2", "#909dd0", "#909dd0", "#ff88a2", "#ff88a2", "#ff88a2"]; + + var rotateAngle = Math.PI / 6; + + var w = width - margin.left - margin.right; + var h = height - margin.top - margin.bottom; + var r = Math.min(w, h) / 2; + var center = [margin.left + w / 2, margin.top + h / 2]; + + // data process; + data.forEach(function (d) { + d.isNull = d.value === null; + d.v = d.isNull ? 0 : d.value; + }); + var all = data.every(function (d) { + return !d.isNull; + }); + var sum = data[0].v + data[1].v; + var ratio1 = all ? + (sum === 0 ? 0.5 : data[0].v / sum) : + (data[0].isNull ? 0 : 1); + ratio1 = Math.max(0.000001, Math.min(0.999999, ratio1));//between 0.001 and 0.999 to ensure arc would always be drawn. + var angle1 = Math.PI * 2 * ratio1; + var angleStart = -angle1 / 2 - rotateAngle; + var angleEnd = angleStart + angle1; + var p1 = [center[0] + r * Math.cos(angleStart), center[1] + r * Math.sin(angleStart)]; + var p2 = [center[0] + r * Math.cos(angleEnd), center[1] + r * Math.sin(angleEnd)]; + + //draw circle + var paper = this.paper = new Raphael(container, width, height); + var pie1 = this.pie1 = paper.path( + "M" + center[0] + "," + center[1] + + "L" + p1[0] + "," + p1[1] + + "A" + r + "," + r + " 0, " + (ratio1 > 0.5 ? "1" : "0") + "," + "1 " + + p2[0] + "," + p2[1] + "Z" + ); + var pie2 = this.pie2 = paper.path( + "M" + center[0] + "," + center[1] + + "L" + p1[0] + "," + p1[1] + + "A" + r + "," + r + " 0, " + (ratio1 > 0.5 ? "0" : "1") + "," + "0 " + + p2[0] + "," + p2[1] + "Z" + ); + pie1.attr({ + 'cursor': 'pointer', + 'stroke-width': 2, + 'stroke': '#fff', + 'fill': color[0] + }); + pie2.attr({ + 'cursor': 'pointer', + 'stroke-width': 2, + 'stroke': '#fff', + 'fill': color[1] + }); + var pies = this.pies = [pie1, pie2]; + + // draw ratios + var trans = [ + [r * 1.3 * Math.cos(rotateAngle) + 0.2 * r, -r * 1.3 * Math.sin(rotateAngle) - 0.1 * r], + [-r * 1.3 * Math.cos(rotateAngle) - 0.8 * r, r * 1.3 * Math.sin(rotateAngle) - 0.2 * r] + ]; + var ratios = this.ratios = [paper.set(), paper.set()]; + ratios.forEach(function (d, i) { + if (data[i].isNull) { + return; + } + var v = i === 0 ? Math.round(ratio1 * 100) : 100 - Math.round(ratio1 * 100); + var numberCount = v < 10 ? 1 : (v === 100 ? 3 : 2); + d.push( + paper.text(center[0], center[1] + 10, v).attr({ + "fill": color[i], + "font-size": "26px", + "text-anchor": "start" + }), + paper.text(center[0] + 15 * numberCount, center[1] + 13, "%").attr({ + "fill": color[i], + "font-size": "16px", + "text-anchor": "start" + }) + ); + d.attr({ + "transform": "translate(t" + trans[i][0] + "," + trans[i][1] + ")" + }); + }); + + // draw title + var titles = this.titles = []; + data.forEach(function (d, i) { + var t = titles[i] = $("
" + data[i].name + "
").css({ + 'position': 'absolute', + 'left': center[0] + trans[i][0], + 'top': center[1] + trans[i][1] - 27, + 'border-radius': 3, + 'padding': 1, + 'cursor': 'pointer', + 'font-size': 14 + }).appendTo($(container)); + if (!all && !data[i].isNull) { + t.css({ + 'border': 'solid 1px black', + 'background-color': highlightColor + }); + } + }); + + //interaction + //hover + var getHoverIn = function (i) { + var index = i; + var needChange = !(!all && !data[i].isNull); + return function () { + if (needChange) { + titles[index].css({ + 'border': 'solid 1px black', + 'background-color': highlightColor + }); + } + }; + }; + var getHoverOut = function (i) { + var index = i; + var needChange = !(!all && !data[i].isNull); + return function () { + if (needChange) { + titles[index].css({ + 'border': 'solid 1px #fff', + 'background-color': '#fff' + }); + } + }; + }; + //click + var getClick = function (i) { + var index = i; + return function () { + clickHandle(data[index], index); + }; + }; + data.forEach(function (d, i) { + var hoverIn = getHoverIn(i); + var hoverOut = getHoverOut(i); + var click = getClick(i); + pies[i].hover(hoverIn, hoverOut); + pies[i].click(click); + titles[i].on("mouseenter", hoverIn); + titles[i].on("mouseleave", hoverOut); + titles[i].on("click", click); + }); +}; \ No newline at end of file diff --git a/lib/charts/tiflevel.js b/lib/charts/tiflevel.js new file mode 100644 index 00000000..1f2d877f --- /dev/null +++ b/lib/charts/tiflevel.js @@ -0,0 +1,263 @@ +/** +* TifLevel for DataV.js +* +*/ + +var TifBuyerLevel = function (config) { + this.config = config; + // 4 params must need + var width = config.width || 320, + height = config.height || 190; + var container = typeof config.container === "string" ? document.getElementById(config.container) : config.container; // id or dom node + var data = config.data || [{name: "18", value: 40}, {name: "25", value: 60}]; + + // other optional params + var margin = config.margin || { + "top": 1, + "left": 1, + "bottom": 1, + "right": 1 + }; + var highlightColor = config.highlightColor || '#3391d4'; + var clickHandle = config.clickHandle || function (d, i) { + //console.log(d.name); + }; + var color = this.color = typeof data[0].color !== 'undefined' ? + data.map(function (d) { return d.color; }) : + d3.range(data.length).map(function () { return "#3fa9f5"; }); + var levelSpacing = config.levelSpacing || 20; // distance between first level words and bar, distance between first level word and second level word + var wordBox = config.wordBox || {w: 50, h: 30}; // bar word (includs name and ratio) box's width + var level2Shift = config.level2Shift || -10; + var showBox = config.showBox || false; + + var titleDefaultStyle = config.titleDefaultStyle || { + 'cursor': 'pointer', + 'border': 'solid 1px #fff', + 'display': 'inline-block', + 'border-radius': 3, + 'padding': 1 + }; + + var wordDefaultStyle = config.wordDefaultStyle || { + "font-size": "12px", + "font-family": "微软雅黑" + }; + + var w = width - margin.left - margin.right; + var h = height - margin.top - margin.bottom; + + // data process; + var notNullCount = 0; + data.forEach(function (d) { + d.isNull = (d.value === null); + d.v = (d.isNull ? 0 : d.value); + if (!d.isNull) { + notNullCount += 1; + } + }); + var all = data.every(function (d) { + return !d.isNull; + }); + var sum = d3.sum(data, function (d) { return d.v; }); + var ratioArr = this.ratioArr = data.map(function (d) { + if (d.isNull) { + d.ratio = 0; + } else { + d.ratio = sum === 0 ? (1 / notNullCount) : (d.v / sum); + } + return d.ratio; + }); + var wArr = this.wArr = data.map(function (d) { + return (w - data.length + 1) * d.ratio; + }); + var s = margin.left; + var leftArr = this.leftArr = wArr.map(function (d, i) { + s += wArr[i] + 1; // 1 is spacing between different bars; + return s - wArr[i] - 1; + }); + + //compute linkLine and word box left position + var shakeLevel = function (boundL, boundR, initArr, boxWidth) { + var wordBoxLeft = []; + var right = boundL; + var left = boundR; + //left to right + initArr.forEach(function (d, i) { + if (d >= right) { + wordBoxLeft[i] = d; + } else { + wordBoxLeft[i] = right; + } + right = wordBoxLeft[i] + boxWidth; + }); + if (right < boundR) { + return wordBoxLeft; + } + wordBoxLeft.reverse(); + wordBoxLeft.forEach(function (d, i) { + if (d + boxWidth < left) { + wordBoxLeft[i] = d; + } else { + wordBoxLeft[i] = left - boxWidth; + } + left = wordBoxLeft[i]; + }); + + wordBoxLeft.reverse(); + return wordBoxLeft; + }; + var oddBoxLeft = leftArr.filter(function (d, i) { + return i % 2 === 0; + }); + var evenBoxLeft = leftArr.filter(function (d, i) { + return i % 2 === 1; + }); + var linePos = []; + var boxPos = []; + // get first level box left position + oddBoxLeft = shakeLevel(margin.left, margin.left + w, oddBoxLeft, wordBox.w); + // get second level line position + evenBoxLeft.forEach(function (d, i) { + // last item and total data number is even; + if (i === evenBoxLeft.length - 1 && (leftArr.length % 2 === 0)) { + if (oddBoxLeft[i] + wordBox.w < d) { + } else { + evenBoxLeft[i] = oddBoxLeft[i] + wordBox.w + level2Shift; + } + return; + } + if (oddBoxLeft[i] + wordBox.w < d && oddBoxLeft[i + 1] >= d + wordBox.w) { + } else { + evenBoxLeft[i] = (oddBoxLeft[i] + wordBox.w + oddBoxLeft[i + 1]) / 2 + level2Shift; + } + }); + // get linePos + oddBoxLeft.forEach(function (d, i) { + linePos.push(d); + if (typeof evenBoxLeft[i] !== 'undefined') { + linePos.push(evenBoxLeft[i]); + } + }); + // get second level box left position + evenBoxLeft = shakeLevel(margin.left, margin.left + w, evenBoxLeft, wordBox.w); + // get boxPos + oddBoxLeft.forEach(function (d, i) { + boxPos.push(d); + if (typeof evenBoxLeft[i] !== 'undefined') { + boxPos.push(evenBoxLeft[i]); + } + }); + + //draw bar + var paper = this.paper = new Raphael(container, width, height); + var rects = this.rects = paper.set(); + data.forEach(function (d, i) { + var rect = paper.rect(leftArr[i], margin.top, wArr[i], h) + .attr({ + "r": 3, + "fill": color[i], + 'cursor': 'pointer', + "stroke": "none" + }); + rects.push(rect); + }); + + //draw test box and line + //line + var linkLines = this.linkLines = paper.set(); + var words = this.words = []; + linePos.forEach(function (d, i) { + var path = "M" + leftArr[i] + "," + (margin.top + h) + + " C" + leftArr[i] + "," + (margin.top + h + levelSpacing / 2) + + " " + d + "," + (margin.top + h + levelSpacing / 2) + + " " + d + "," + (margin.top + h + levelSpacing); + if (i % 2 === 1) { + path += "v" + (wordBox.h + levelSpacing / 2); + } + var line = paper.path(path).attr({ + "stroke-dasharray": ". " + }); + linkLines.push(line); + }); + // draw box + if (showBox) { + boxPos.forEach(function (d, i) { + var box = paper.rect( + d, //x + margin.top + h + levelSpacing + (i % 2 === 0 ? 0 : wordBox.h + levelSpacing / 2), //y + wordBox.w, //w + wordBox.h //h + ); + }); + } + + var titles = []; + $(container).css('position', 'relative'); + data.forEach(function (d, i) { + var wordSet = $("
"); + words.push(wordSet); + var title = $('
' + d.name + '
').css(titleDefaultStyle); + if (!all && !data[i].isNull) { + title.css({ + 'border': 'solid 1px black', + 'background-color': highlightColor + }); + } + titles.push(title); + wordSet.append(title); + if (!d.isNull) { + var value = $('
' + (Math.round(ratioArr[i] * 1000) / 10 + "%") + '
').css("color", color[i]); + wordSet.append(value); + } + wordSet.css(wordDefaultStyle).css({ + 'position': 'absolute', + 'left': boxPos[i], + 'top': margin.top + h + levelSpacing + (i % 2 === 0 ? 0 : wordBox.h + levelSpacing / 2) + }); + $(container).append(wordSet); + }); + + //interaction + //hover + var getHoverIn = function (i) { + var index = i; + var needChange = !(!all && !data[i].isNull); + return function () { + if (needChange) { + titles[index].css({ + 'border': 'solid 1px black', + 'background-color': highlightColor + }); + } + }; + }; + var getHoverOut = function (i) { + var index = i; + var needChange = !(!all && !data[i].isNull); + return function () { + if (needChange) { + titles[index].css({ + 'border': 'solid 1px #fff', + 'background-color': '#fff' + }); + } + }; + }; + //click + var getClick = function (i) { + var index = i; + return function () { + clickHandle(data[index], index); + }; + }; + data.forEach(function (d, i) { + var hoverIn = getHoverIn(i); + var hoverOut = getHoverOut(i); + var click = getClick(i); + rects[i].hover(hoverIn, hoverOut); + rects[i].click(click); + titles[i].on("mouseenter", hoverIn); + titles[i].on("mouseleave", hoverOut); + titles[i].on("click", click); + }); +};