mirror of
https://github.com/TBEDP/datavjs.git
synced 2025-12-08 19:45:52 +00:00
CRLF -> LF
This commit is contained in:
parent
c1e8f006e8
commit
3c71e8e49b
@ -1,308 +1,314 @@
|
||||
# DataV项目编码规范
|
||||
一套良好的编码规范如果在团队开发中被遵守,可以降低团队成员之间交流的成本,同时也降低犯错的几率。可以说编码规范只需花费20%的精力来遵循,却可以降低80%的犯低级错误几率。
|
||||
这一版的编码规范,主要查考了Douglas Crockford的JSLint的检查条件和过去的一些编码经验,整理而出。
|
||||
|
||||
## 命名空间
|
||||
项目的总命名空间为:**`DataV`**。
|
||||
除了Rapheal和D3自身的代码外,我们提交的任何方法都应该挂载在`DataV`命名空间中。
|
||||
例外:某些方法和功能可以独立抽取出来,适用于别的项目。但是也应该由一个命名空间来管理它们。
|
||||
|
||||
## 语法
|
||||
### 缩进规范
|
||||
由于该项目是普通的前端JavaScript项目,所以缩进采用4个空格的方式。禁止使用tab符号。
|
||||
推荐更改编辑器中的设置,使得按tab键可以自动插入4个空格。
|
||||
每一层级之间,要保证缩进是正确的。
|
||||
### 空格
|
||||
|
||||
* `=`前后应该存在一个空格
|
||||
* `(`前面应该存在一个空格
|
||||
* `)`后面应该存在一个空格
|
||||
* `{`前面应该存在一个空格
|
||||
|
||||
### 分号
|
||||
每一个申明或者定义,都应该以分号结尾。
|
||||
|
||||
var username = "Jackson";
|
||||
|
||||
var foo = function () {};
|
||||
每一行调用都应该明确以分号结尾。
|
||||
|
||||
foo();
|
||||
|
||||
### 大括号
|
||||
请明确使用`{}`来约束代码段。
|
||||
推荐:
|
||||
|
||||
if (condition) {
|
||||
foo = "bar";
|
||||
}
|
||||
不推荐:
|
||||
|
||||
if (condition) boo = "bar";
|
||||
|
||||
## 命名规范
|
||||
### 变量
|
||||
采用驼峰首字母小写,后续单词均需大写首字母,不得在中间包含下划线。
|
||||
每一个变量都应该通过`var`申明,以防止变量污染。
|
||||
正确:
|
||||
|
||||
var pipeChart;
|
||||
|
||||
错误:
|
||||
|
||||
var pipechart;
|
||||
var pipe_chart;
|
||||
|
||||
### 方法
|
||||
方法名同变量名,采用驼峰式命名。
|
||||
定义方法均通过`var foo = function () {};`而不是`function foo () {}`。
|
||||
|
||||
var foo = function () {
|
||||
// TODO
|
||||
};
|
||||
|
||||
方法名应该可以表示方法的行为和暗示方法的返回值。善用`set`、`get`、`is`、`has`等方法前缀。
|
||||
|
||||
### 类
|
||||
由于JavaScript中类和方法的关键字都是`function`,为了区别两者的混淆,类名的定义通常是大写首字母,以表示它是一个作为类来调用的。
|
||||
|
||||
var Person = function () {
|
||||
// TODO
|
||||
};
|
||||
|
||||
类的方法请尽量赋值在`prototype`属性上,使得通过原型链查找的方式降低内存占用。
|
||||
类的数据请尽量在构造函数中赋值,以加快执行时的查找速度。
|
||||
|
||||
var Person = function (age) {
|
||||
this.age = age; // 在构造函数中赋值数据
|
||||
};
|
||||
|
||||
// 在原型链中设置方法
|
||||
Person.prototype.getAge = function () {
|
||||
return this.age;
|
||||
};
|
||||
|
||||
对于私有方法,在前面添加下划线表示不推荐被外部调用。
|
||||
|
||||
Persion.prototype._sleep = function () {};
|
||||
|
||||
### 模块
|
||||
为了统一规划,我们的类均需通过模块的定义方式,与命名空间结合,以保证环境的干净。
|
||||
|
||||
(function () {
|
||||
var private = "I am private";
|
||||
var Person = function () {
|
||||
this.username = private;
|
||||
};
|
||||
|
||||
DataV.Person = Person;
|
||||
}());
|
||||
|
||||
## 注释
|
||||
注释对于团队而言是十分重要的,每一个方法都应该包含说明。
|
||||
必选的注释是方法的说明,方法的参数,和一个例子。
|
||||
任何诡异的地方,都应该附有简单的注释来描述它,以提醒后来人注意,以区别对待。
|
||||
|
||||
/**
|
||||
* Create chart panel by passin width and height
|
||||
* @param {string} type Type name
|
||||
* @param {number} width Chart panel's width value
|
||||
* @param {number} height Chart panel's height value
|
||||
* @example
|
||||
* createChart("pipe", 500, 500);
|
||||
*/
|
||||
var createChart = function (type, width, height) {
|
||||
// Trick comments
|
||||
trickCall();
|
||||
};
|
||||
|
||||
通过[jsdoc](http://code.google.com/p/jsdoc-toolkit/w/list)的注释,将利于我们后期导出API文档。
|
||||
|
||||
## 文件名
|
||||
文件名的方式也是通过驼峰式,但是以下划线分割单词,一律小写单词。如:
|
||||
|
||||
d3.js
|
||||
child_process.js
|
||||
|
||||
## JSLint扫描代码
|
||||
JSLint的调用方式如下:
|
||||
|
||||
node precommit.js file
|
||||
|
||||
配置方式存放在`config.json`中。
|
||||
|
||||
## 目录结构说明
|
||||
目前datav组件库的目录结构如下:
|
||||
|
||||
```
|
||||
JacksonTianmatoMacBook-Pro:datav.js jacksontian$ tree -d
|
||||
.
|
||||
├── bin
|
||||
├── deps
|
||||
├── doc
|
||||
├── example
|
||||
├── lib
|
||||
└── test
|
||||
```
|
||||
其中详细说明一下:
|
||||
|
||||
- `bin`目录用于存放一些可运行的脚本或者工具,例如`jslint`。
|
||||
- `deps`目录用于存放项目的依赖文件,其中有`raphael`和`d3`的相关文件。
|
||||
- `doc`目录用于用于存放项目的文档,如API生成文档或一些说明文档。
|
||||
- `example`目录存放案例代码。
|
||||
- `lib`目录为组件库具体代码,根据每个图的类型不同,存为对应文件。
|
||||
- `test`目录存放单元测试代码或自动化测试代码。
|
||||
|
||||
## 单元测试
|
||||
`DataV`项目采用[`QUnit`](http://docs.jquery.com/QUnit)作为测试框架,详细文档请见。
|
||||
简单测试例子:
|
||||
|
||||
```
|
||||
// 分模块
|
||||
module("DataV");
|
||||
// 测试用例
|
||||
test("Themes.get", function () {
|
||||
// 单个断言
|
||||
equal(DataV.Themes.get("inexsit"), undefined, "Should get undefined when key is inexsit");
|
||||
equal(DataV.Themes.get("COLOR_MODE"), "gradient", "Should get gradient when key is COLOR_MODE");
|
||||
equal(typeof DataV.Themes.get("COLOR_ARGS"), "object", "COLOR_ARGS should be an object");
|
||||
equal(DataV.Themes.get("COLOR_ARGS").length, 2, "COLOR_ARGS should have two items");
|
||||
});
|
||||
```
|
||||
|
||||
## 数据格式
|
||||
数据格式统一采用二维表的方式,结构比较类似数据库中的表。列名通过文档详细来描述,写在代码的[jsdoc](http://code.google.com/p/jsdoc-toolkit/w/list)的注释中。
|
||||
标准数据格式例子如下:
|
||||
|
||||
```
|
||||
/**
|
||||
* 设置数据源
|
||||
* Example 举个例字,假设下面的数组表示2个人在一年4个季度的消费。第一个人在4个季度里消费了1、2、3、9元。第二个人消费了3、4、6、3元。
|
||||
* [
|
||||
* [1,2,3,9],
|
||||
* [3,4,6,3]
|
||||
* ]
|
||||
* @param {Array} source 数据源数组.
|
||||
*/
|
||||
Stream.prototype.setSource = function (source) {
|
||||
// TODO
|
||||
};
|
||||
```
|
||||
所有的组件库的调用都是相同的接口`setSource`:
|
||||
|
||||
```
|
||||
var stream = new DataV.Stream("chart");
|
||||
stream.setSource(source);
|
||||
stream.render();
|
||||
```
|
||||
## 事件注入
|
||||
每一个组件都可能向外提供一些事件钩子,包括DOM事件,或者业务逻辑事件。绑定的方式十分简单:
|
||||
|
||||
```
|
||||
stream.on("click", function (event) {
|
||||
console.log(event);
|
||||
});
|
||||
|
||||
stream.on("dblclick", function (event) {
|
||||
alert("double click");
|
||||
});
|
||||
|
||||
stream.on("contextmenu", function (event) {
|
||||
alert("mouse right");
|
||||
});
|
||||
```
|
||||
|
||||
组件的内部实现是通过继承`EventProxy`提供自定义方式,在创建画布后,就绑定一些必要的事件到画布节点上,然后将事件触发出去。如果用户如上文,侦听了这些业务事件,将会调用执行。
|
||||
|
||||
```
|
||||
Stream.prototype.createCanvas = function () {
|
||||
var conf = this.defaults;
|
||||
this.canvas = Raphael(this.node, conf.width, conf.height);
|
||||
this.DOMNode = $(this.canvas.canvas);
|
||||
var that = this;
|
||||
this.DOMNode.click(function (event) {
|
||||
that.trigger("click", event);
|
||||
});
|
||||
this.DOMNode.dblclick(function (event) {
|
||||
that.trigger("dblclick", event);
|
||||
});
|
||||
this.DOMNode.bind("contextmenu", function (event) {
|
||||
that.trigger("contextmenu", event);
|
||||
});
|
||||
this.DOMNode.delegate("path", "click", function (event) {
|
||||
that.trigger("path_click", event);
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
## 常见错误
|
||||
### 判断一个对象是否是数组
|
||||
下面是宁朗写的:
|
||||
|
||||
```
|
||||
if (color.constructor !== Array) {
|
||||
throw new Error("The color should be Array");
|
||||
}
|
||||
```
|
||||
下面是Underscore的方法:
|
||||
|
||||
```
|
||||
_.isArray = nativeIsArray || function(obj) {
|
||||
return toString.call(obj) == '[object Array]';
|
||||
};
|
||||
```
|
||||
### 数组去重
|
||||
|
||||
```
|
||||
// make an array's every element unique by delete other same element
|
||||
Array.prototype.uniq = function () {
|
||||
var temp = {},
|
||||
len = this.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (typeof temp[this[i]] == "undefined") {
|
||||
temp[this[i]] = 1;
|
||||
}
|
||||
}
|
||||
this.length = 0;
|
||||
len = 0;
|
||||
for (var i in temp) {
|
||||
this[len++] = i;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
```
|
||||
这里可以调用underscore的uniq方法即可。节省N行代码量
|
||||
|
||||
### 元素是否存在数组中
|
||||
|
||||
```
|
||||
//check if a string is in an array
|
||||
var _strInArray = function (str, array) {
|
||||
var i = 0,
|
||||
l = 0;
|
||||
for (i = 0, l = array.length; i < l; i++) {
|
||||
if (array[i] === str) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
```
|
||||
换用`_.indexOf(array, value)`可以节省代码量,且返回值比boolean值更有价值
|
||||
|
||||
### meta
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
|
||||
希望大家潮一点,别在写上面那行代码了。下面的表达方式更简洁,且无任何副作用。
|
||||
|
||||
<meta charset="utf-8" />
|
||||
|
||||
### script标签的type属性
|
||||
|
||||
<script type="text/javascript" src="../../deps/d3.js"></script>
|
||||
|
||||
如果是默认的text/javascript的话,就可以直接省略掉。变成下面这样子:
|
||||
|
||||
# DataV项目编码规范
|
||||
一套良好的编码规范如果在团队开发中被遵守,可以降低团队成员之间交流的成本,同时也降低犯错的几率。可以说编码规范只需花费20%的精力来遵循,却可以降低80%的犯低级错误几率。
|
||||
这一版的编码规范,主要查考了Douglas Crockford的JSLint的检查条件和过去的一些编码经验,整理而出。
|
||||
|
||||
## 命名空间
|
||||
项目的总命名空间为:**`DataV`**。
|
||||
除了Rapheal和D3自身的代码外,我们提交的任何方法都应该挂载在`DataV`命名空间中。
|
||||
例外:某些方法和功能可以独立抽取出来,适用于别的项目。但是也应该由一个命名空间来管理它们。
|
||||
|
||||
## 语法
|
||||
### 缩进规范
|
||||
由于该项目是普通的前端JavaScript项目,所以缩进采用2个空格的方式。禁止使用tab符号。
|
||||
推荐更改编辑器中的设置,使得按tab键可以自动插入2个空格。
|
||||
每一层级之间,要保证缩进是正确的。
|
||||
### 空格
|
||||
|
||||
* `=`前后应该存在一个空格
|
||||
* `(`前面应该存在一个空格
|
||||
* `)`后面应该存在一个空格
|
||||
* `{`前面应该存在一个空格
|
||||
|
||||
### 分号
|
||||
每一个申明或者定义,都应该以分号结尾。
|
||||
|
||||
var username = "Jackson";
|
||||
|
||||
var foo = function () {};
|
||||
每一行调用都应该明确以分号结尾。
|
||||
|
||||
foo();
|
||||
|
||||
### 大括号
|
||||
请明确使用`{}`来约束代码段。
|
||||
推荐:
|
||||
|
||||
if (condition) {
|
||||
foo = "bar";
|
||||
}
|
||||
不推荐:
|
||||
|
||||
if (condition) boo = "bar";
|
||||
|
||||
## 命名规范
|
||||
### 变量
|
||||
采用驼峰首字母小写,后续单词均需大写首字母,不得在中间包含下划线。
|
||||
每一个变量都应该通过`var`申明,以防止变量污染。
|
||||
正确:
|
||||
|
||||
var pipeChart;
|
||||
|
||||
错误:
|
||||
|
||||
var pipechart;
|
||||
var pipe_chart;
|
||||
|
||||
### 方法
|
||||
方法名同变量名,采用驼峰式命名。
|
||||
定义方法均通过`var foo = function () {};`而不是`function foo () {}`。
|
||||
|
||||
var foo = function () {
|
||||
// TODO
|
||||
};
|
||||
|
||||
方法名应该可以表示方法的行为和暗示方法的返回值。善用`set`、`get`、`is`、`has`等方法前缀。
|
||||
|
||||
### 类
|
||||
由于JavaScript中类和方法的关键字都是`function`,为了区别两者的混淆,类名的定义通常是大写首字母,以表示它是一个作为类来调用的。
|
||||
|
||||
var Person = function () {
|
||||
// TODO
|
||||
};
|
||||
|
||||
类的方法请尽量赋值在`prototype`属性上,使得通过原型链查找的方式降低内存占用。
|
||||
类的数据请尽量在构造函数中赋值,以加快执行时的查找速度。
|
||||
|
||||
var Person = function (age) {
|
||||
this.age = age; // 在构造函数中赋值数据
|
||||
};
|
||||
|
||||
// 在原型链中设置方法
|
||||
Person.prototype.getAge = function () {
|
||||
return this.age;
|
||||
};
|
||||
|
||||
对于私有方法,在前面添加下划线表示不推荐被外部调用。
|
||||
|
||||
Persion.prototype._sleep = function () {};
|
||||
|
||||
### 模块
|
||||
为了统一规划,我们的类均需通过模块的定义方式,与命名空间结合,以保证环境的干净。
|
||||
|
||||
(function () {
|
||||
var private = "I am private";
|
||||
var Person = function () {
|
||||
this.username = private;
|
||||
};
|
||||
|
||||
DataV.Person = Person;
|
||||
}());
|
||||
|
||||
## 注释
|
||||
注释对于团队而言是十分重要的,每一个方法都应该包含说明。
|
||||
必选的注释是方法的说明,方法的参数,和一个例子。
|
||||
任何诡异的地方,都应该附有简单的注释来描述它,以提醒后来人注意,以区别对待。
|
||||
|
||||
/**
|
||||
* Create chart panel by passin width and height
|
||||
* @param {string} type Type name
|
||||
* @param {number} width Chart panel's width value
|
||||
* @param {number} height Chart panel's height value
|
||||
* @example
|
||||
* createChart("pipe", 500, 500);
|
||||
*/
|
||||
var createChart = function (type, width, height) {
|
||||
// Trick comments
|
||||
trickCall();
|
||||
};
|
||||
|
||||
通过[jsdoc](http://code.google.com/p/jsdoc-toolkit/w/list)的注释,将利于我们后期导出API文档。
|
||||
|
||||
## 文件名
|
||||
文件名的方式也是通过驼峰式,但是以下划线分割单词,一律小写单词。如:
|
||||
|
||||
d3.js
|
||||
child_process.js
|
||||
|
||||
## 文件内容
|
||||
### 换行符
|
||||
请用Unix的CRLF换行符
|
||||
### 文件编码
|
||||
请用UTF-8 without BOM作为文件内容的编码
|
||||
|
||||
## JSLint扫描代码
|
||||
JSLint的调用方式如下:
|
||||
|
||||
node precommit.js file
|
||||
|
||||
配置方式存放在`config.json`中。
|
||||
|
||||
## 目录结构说明
|
||||
目前datav组件库的目录结构如下:
|
||||
|
||||
```
|
||||
JacksonTianmatoMacBook-Pro:datav.js jacksontian$ tree -d
|
||||
.
|
||||
├── bin
|
||||
├── deps
|
||||
├── doc
|
||||
├── example
|
||||
├── lib
|
||||
└── test
|
||||
```
|
||||
其中详细说明一下:
|
||||
|
||||
- `bin`目录用于存放一些可运行的脚本或者工具,例如`jslint`。
|
||||
- `deps`目录用于存放项目的依赖文件,其中有`raphael`和`d3`的相关文件。
|
||||
- `doc`目录用于用于存放项目的文档,如API生成文档或一些说明文档。
|
||||
- `example`目录存放案例代码。
|
||||
- `lib`目录为组件库具体代码,根据每个图的类型不同,存为对应文件。
|
||||
- `test`目录存放单元测试代码或自动化测试代码。
|
||||
|
||||
## 单元测试
|
||||
`DataV`项目采用[`QUnit`](http://docs.jquery.com/QUnit)作为测试框架,详细文档请见。
|
||||
简单测试例子:
|
||||
|
||||
```
|
||||
// 分模块
|
||||
module("DataV");
|
||||
// 测试用例
|
||||
test("Themes.get", function () {
|
||||
// 单个断言
|
||||
equal(DataV.Themes.get("inexsit"), undefined, "Should get undefined when key is inexsit");
|
||||
equal(DataV.Themes.get("COLOR_MODE"), "gradient", "Should get gradient when key is COLOR_MODE");
|
||||
equal(typeof DataV.Themes.get("COLOR_ARGS"), "object", "COLOR_ARGS should be an object");
|
||||
equal(DataV.Themes.get("COLOR_ARGS").length, 2, "COLOR_ARGS should have two items");
|
||||
});
|
||||
```
|
||||
|
||||
## 数据格式
|
||||
数据格式统一采用二维表的方式,结构比较类似数据库中的表。列名通过文档详细来描述,写在代码的[jsdoc](http://code.google.com/p/jsdoc-toolkit/w/list)的注释中。
|
||||
标准数据格式例子如下:
|
||||
|
||||
```
|
||||
/**
|
||||
* 设置数据源
|
||||
* Example 举个例字,假设下面的数组表示2个人在一年4个季度的消费。第一个人在4个季度里消费了1、2、3、9元。第二个人消费了3、4、6、3元。
|
||||
* [
|
||||
* [1,2,3,9],
|
||||
* [3,4,6,3]
|
||||
* ]
|
||||
* @param {Array} source 数据源数组.
|
||||
*/
|
||||
Stream.prototype.setSource = function (source) {
|
||||
// TODO
|
||||
};
|
||||
```
|
||||
所有的组件库的调用都是相同的接口`setSource`:
|
||||
|
||||
```
|
||||
var stream = new DataV.Stream("chart");
|
||||
stream.setSource(source);
|
||||
stream.render();
|
||||
```
|
||||
## 事件注入
|
||||
每一个组件都可能向外提供一些事件钩子,包括DOM事件,或者业务逻辑事件。绑定的方式十分简单:
|
||||
|
||||
```
|
||||
stream.on("click", function (event) {
|
||||
console.log(event);
|
||||
});
|
||||
|
||||
stream.on("dblclick", function (event) {
|
||||
alert("double click");
|
||||
});
|
||||
|
||||
stream.on("contextmenu", function (event) {
|
||||
alert("mouse right");
|
||||
});
|
||||
```
|
||||
|
||||
组件的内部实现是通过继承`EventProxy`提供自定义方式,在创建画布后,就绑定一些必要的事件到画布节点上,然后将事件触发出去。如果用户如上文,侦听了这些业务事件,将会调用执行。
|
||||
|
||||
```
|
||||
Stream.prototype.createCanvas = function () {
|
||||
var conf = this.defaults;
|
||||
this.canvas = Raphael(this.node, conf.width, conf.height);
|
||||
this.DOMNode = $(this.canvas.canvas);
|
||||
var that = this;
|
||||
this.DOMNode.click(function (event) {
|
||||
that.trigger("click", event);
|
||||
});
|
||||
this.DOMNode.dblclick(function (event) {
|
||||
that.trigger("dblclick", event);
|
||||
});
|
||||
this.DOMNode.bind("contextmenu", function (event) {
|
||||
that.trigger("contextmenu", event);
|
||||
});
|
||||
this.DOMNode.delegate("path", "click", function (event) {
|
||||
that.trigger("path_click", event);
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
## 常见错误
|
||||
### 判断一个对象是否是数组
|
||||
下面是宁朗写的:
|
||||
|
||||
```
|
||||
if (color.constructor !== Array) {
|
||||
throw new Error("The color should be Array");
|
||||
}
|
||||
```
|
||||
下面是Underscore的方法:
|
||||
|
||||
```
|
||||
_.isArray = nativeIsArray || function(obj) {
|
||||
return toString.call(obj) == '[object Array]';
|
||||
};
|
||||
```
|
||||
### 数组去重
|
||||
|
||||
```
|
||||
// make an array's every element unique by delete other same element
|
||||
Array.prototype.uniq = function () {
|
||||
var temp = {},
|
||||
len = this.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (typeof temp[this[i]] == "undefined") {
|
||||
temp[this[i]] = 1;
|
||||
}
|
||||
}
|
||||
this.length = 0;
|
||||
len = 0;
|
||||
for (var i in temp) {
|
||||
this[len++] = i;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
```
|
||||
这里可以调用underscore的uniq方法即可。节省N行代码量
|
||||
|
||||
### 元素是否存在数组中
|
||||
|
||||
```
|
||||
//check if a string is in an array
|
||||
var _strInArray = function (str, array) {
|
||||
var i = 0,
|
||||
l = 0;
|
||||
for (i = 0, l = array.length; i < l; i++) {
|
||||
if (array[i] === str) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
```
|
||||
换用`_.indexOf(array, value)`可以节省代码量,且返回值比boolean值更有价值
|
||||
|
||||
### meta
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
|
||||
希望大家潮一点,别在写上面那行代码了。下面的表达方式更简洁,且无任何副作用。
|
||||
|
||||
<meta charset="utf-8" />
|
||||
|
||||
### script标签的type属性
|
||||
|
||||
<script type="text/javascript" src="../../deps/d3.js"></script>
|
||||
|
||||
如果是默认的text/javascript的话,就可以直接省略掉。变成下面这样子:
|
||||
|
||||
<script src="../../deps/d3.js"></script>
|
||||
@ -1,326 +1,326 @@
|
||||
//copy codes from d3.js, add 4 functions: tickAttr, tickTextAttr, minorTickAttr and domainAttr;
|
||||
//axis() changes, need a raphael paper object param, return raphael set object.
|
||||
//examples in ../examples/axis/ to know the usage.
|
||||
//a basic part for other data visualization format
|
||||
/*global d3*/
|
||||
/*!
|
||||
* Axis兼容定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Axis', function (require) {
|
||||
/**
|
||||
* function from d3, get scaleRange of an ordinal scale
|
||||
* @param {Array} domain ordinal scale's range
|
||||
*/
|
||||
function d3_scaleExtent(domain) {
|
||||
var start = domain[0], stop = domain[domain.length - 1];
|
||||
return start < stop ? [start, stop] : [stop, start];
|
||||
}
|
||||
|
||||
/**
|
||||
* function from d3, get scaleRange of a scale
|
||||
*/
|
||||
function d3_scaleRange(scale) {
|
||||
return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
|
||||
}
|
||||
|
||||
/**
|
||||
* function from d3, get subticks
|
||||
* @param scale, scale
|
||||
* @param ticks, major ticks of scale
|
||||
* @param m, number of subdivide
|
||||
*/
|
||||
function d3_svg_axisSubdivide(scale, ticks, m) {
|
||||
var subticks = [];
|
||||
if (m && ticks.length > 1) {
|
||||
var extent = d3_scaleExtent(scale.domain()),
|
||||
i = -1,
|
||||
n = ticks.length,
|
||||
d = (ticks[1] - ticks[0]) / ++m,
|
||||
j,
|
||||
v;
|
||||
while (++i < n) {
|
||||
for (j = m; --j > 0;) {
|
||||
if ((v = +ticks[i] - j * d) >= extent[0]) {
|
||||
subticks.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1];) {
|
||||
subticks.push(v);
|
||||
}
|
||||
}
|
||||
return subticks;
|
||||
}
|
||||
|
||||
var Axis = function () {
|
||||
var scale = d3.scale.linear(),
|
||||
orient = "bottom",
|
||||
tickMajorSize = 6,
|
||||
tickMinorSize = 6,
|
||||
tickEndSize = 6,
|
||||
tickPadding = 3,
|
||||
tickArguments_ = [10],
|
||||
tickFormat_,
|
||||
tickSubdivide = 0,
|
||||
|
||||
tickAttr_ = {},
|
||||
tickTextAttr_ = {},
|
||||
minorTickAttr_ = {},
|
||||
domainAttr_ = {};
|
||||
|
||||
/**
|
||||
* @param paper: raphael's paper object.
|
||||
* @return axisSet: raphael's set object.
|
||||
*/
|
||||
function axis(paper) {
|
||||
// Ticks for quantitative scale, or domain values for ordinal scale.
|
||||
var ticks = scale.ticks ? scale.ticks.apply(scale, tickArguments_) : scale.domain(),
|
||||
tickFormat = tickFormat_ === undefined ?
|
||||
(scale.tickFormat ?
|
||||
scale.tickFormat.apply(scale, tickArguments_)
|
||||
: String)
|
||||
: tickFormat_;
|
||||
|
||||
var subticks = d3_svg_axisSubdivide(scale, ticks, tickSubdivide);
|
||||
var range = d3_scaleRange(scale);
|
||||
|
||||
var axisSet = paper.set();
|
||||
|
||||
switch (orient) {
|
||||
case "bottom":
|
||||
subticks.forEach(function (d, i, arr) {
|
||||
var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickX + "," + tickMinorSize + "V0")
|
||||
.attr(minorTickAttr_));
|
||||
});
|
||||
ticks.forEach(function (d, i, arr) {
|
||||
var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickX + "," + tickMajorSize + "V0")
|
||||
.attr(tickAttr_));
|
||||
axisSet.push(paper
|
||||
.text(tickX, Math.max(tickMajorSize, 0) + tickPadding + 2,
|
||||
typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
|
||||
.attr({"text-anchor": "middle"})
|
||||
.attr(tickTextAttr_));
|
||||
});
|
||||
axisSet.push(paper
|
||||
.path("M" + range[0] + "," + tickEndSize + "V0H" + range[1] + "V" + tickEndSize)
|
||||
.attr(domainAttr_));
|
||||
break;
|
||||
|
||||
case "top":
|
||||
subticks.forEach(function (d, i, arr) {
|
||||
var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickX + "," + -tickMinorSize + "V0")
|
||||
.attr(minorTickAttr_));
|
||||
});
|
||||
ticks.forEach(function (d, i, arr) {
|
||||
var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickX + "," + -tickMajorSize + "V0")
|
||||
.attr(tickAttr_));
|
||||
axisSet.push(paper
|
||||
.text(tickX, -(Math.max(tickMajorSize, 0) + tickPadding + 2),
|
||||
typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
|
||||
.attr({"text-anchor": "middle"})
|
||||
.attr(tickTextAttr_));
|
||||
});
|
||||
axisSet.push(paper
|
||||
.path("M" + range[0] + "," + -tickEndSize + "V0H" + range[1] + "V" + -tickEndSize)
|
||||
.attr(domainAttr_));
|
||||
break;
|
||||
|
||||
case "left":
|
||||
subticks.forEach(function (d, i, arr) {
|
||||
var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + -tickMinorSize + "," + tickY + "H0")
|
||||
.attr(minorTickAttr_));
|
||||
});
|
||||
ticks.forEach(function (d, i, arr) {
|
||||
var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + -tickMajorSize + "," + tickY + "H0")
|
||||
.attr(tickAttr_));
|
||||
axisSet.push(paper
|
||||
.text(-(Math.max(tickMajorSize, 0) + tickPadding), tickY,
|
||||
typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
|
||||
.attr({"text-anchor": "end"})
|
||||
.attr(tickTextAttr_));
|
||||
});
|
||||
axisSet.push(paper
|
||||
.path("M" + -tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + -tickEndSize)
|
||||
.attr(domainAttr_));
|
||||
break;
|
||||
|
||||
case "right":
|
||||
subticks.forEach(function (d, i, arr) {
|
||||
var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickMinorSize + "," + tickY + "H0")
|
||||
.attr(minorTickAttr_));
|
||||
});
|
||||
ticks.forEach(function (d, i, arr) {
|
||||
var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickMajorSize + "," + tickY + "H0")
|
||||
.attr(tickAttr_));
|
||||
axisSet.push(paper
|
||||
.text(Math.max(tickMajorSize, 0) + tickPadding, tickY,
|
||||
typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
|
||||
.attr({"text-anchor": "start"})
|
||||
.attr(tickTextAttr_));
|
||||
});
|
||||
axisSet.push(paper
|
||||
.path("M" + tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + tickEndSize)
|
||||
.attr(domainAttr_));
|
||||
break;
|
||||
}
|
||||
|
||||
return axisSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* get or set axis' scale.
|
||||
*/
|
||||
axis.scale = function (x) {
|
||||
if (!arguments.length) {
|
||||
return scale;
|
||||
}
|
||||
scale = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' orinet: "bottom", "top", "left", "right", default orient is bottom.
|
||||
*/
|
||||
axis.orient = function (x) {
|
||||
if (!arguments.length) {
|
||||
return orient;
|
||||
}
|
||||
orient = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' ticks number.
|
||||
*/
|
||||
axis.ticks = function () {
|
||||
if (!arguments.length) {
|
||||
return tickArguments_;
|
||||
}
|
||||
tickArguments_ = arguments;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' ticks format function, it's a function change format style.
|
||||
* from one string format to another string format.
|
||||
*/
|
||||
axis.tickFormat = function (x) {
|
||||
if (!arguments.length) {
|
||||
return tickFormat_;
|
||||
}
|
||||
tickFormat_ = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' tick size(length of tick line, unit: px).
|
||||
* @param arguments.length === 0, get axis' major tick size.
|
||||
* @param arguments.length === 1, set axis' all tick sizes as x.
|
||||
* @param arguments.length === 2, get axis' major tick size as x, minor and end size as y.
|
||||
* @param arguments.length === 3, get axis' major tick size as x, minor size as y, end size as z.
|
||||
*/
|
||||
axis.tickSize = function (x, y, z) {
|
||||
if (!arguments.length) {
|
||||
return tickMajorSize;
|
||||
}
|
||||
var n = arguments.length - 1;
|
||||
tickMajorSize = +x;
|
||||
tickMinorSize = n > 1 ? +y : tickMajorSize;
|
||||
tickEndSize = n > 0 ? +arguments[n] : tickMajorSize;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' tick padding(the distance between tick text and axis).
|
||||
* @param x is a number, unit is px;
|
||||
*/
|
||||
axis.tickPadding = function (x) {
|
||||
if (!arguments.length) {
|
||||
return tickPadding;
|
||||
}
|
||||
tickPadding = +x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' sub tick divide number(divide number between two major ticks).
|
||||
*/
|
||||
axis.tickSubdivide = function (x) {
|
||||
if (!arguments.length) {
|
||||
return tickSubdivide;
|
||||
}
|
||||
tickSubdivide = +x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' tick attribute(Raphael format).
|
||||
*/
|
||||
axis.tickAttr = function (x) {
|
||||
if (!arguments.length) {
|
||||
return tickAttr_;
|
||||
}
|
||||
tickAttr_ = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' tick text attribute(Raphael format).
|
||||
*/
|
||||
axis.tickTextAttr = function (x) {
|
||||
if (!arguments.length) {
|
||||
return tickTextAttr_;
|
||||
}
|
||||
tickTextAttr_ = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' minor tick attribute(Raphael format).
|
||||
*/
|
||||
axis.minorTickAttr = function (x) {
|
||||
if (!arguments.length) {
|
||||
return minorTickAttr_;
|
||||
}
|
||||
minorTickAttr_ = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' domain(axis line) attribute(Raphael format).
|
||||
*/
|
||||
axis.domainAttr = function (x) {
|
||||
if (!arguments.length) {
|
||||
return domainAttr_;
|
||||
}
|
||||
domainAttr_ = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
return axis;
|
||||
};
|
||||
|
||||
return Axis;
|
||||
});
|
||||
//copy codes from d3.js, add 4 functions: tickAttr, tickTextAttr, minorTickAttr and domainAttr;
|
||||
//axis() changes, need a raphael paper object param, return raphael set object.
|
||||
//examples in ../examples/axis/ to know the usage.
|
||||
//a basic part for other data visualization format
|
||||
/*global d3*/
|
||||
/*!
|
||||
* Axis兼容定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Axis', function (require) {
|
||||
/**
|
||||
* function from d3, get scaleRange of an ordinal scale
|
||||
* @param {Array} domain ordinal scale's range
|
||||
*/
|
||||
function d3_scaleExtent(domain) {
|
||||
var start = domain[0], stop = domain[domain.length - 1];
|
||||
return start < stop ? [start, stop] : [stop, start];
|
||||
}
|
||||
|
||||
/**
|
||||
* function from d3, get scaleRange of a scale
|
||||
*/
|
||||
function d3_scaleRange(scale) {
|
||||
return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
|
||||
}
|
||||
|
||||
/**
|
||||
* function from d3, get subticks
|
||||
* @param scale, scale
|
||||
* @param ticks, major ticks of scale
|
||||
* @param m, number of subdivide
|
||||
*/
|
||||
function d3_svg_axisSubdivide(scale, ticks, m) {
|
||||
var subticks = [];
|
||||
if (m && ticks.length > 1) {
|
||||
var extent = d3_scaleExtent(scale.domain()),
|
||||
i = -1,
|
||||
n = ticks.length,
|
||||
d = (ticks[1] - ticks[0]) / ++m,
|
||||
j,
|
||||
v;
|
||||
while (++i < n) {
|
||||
for (j = m; --j > 0;) {
|
||||
if ((v = +ticks[i] - j * d) >= extent[0]) {
|
||||
subticks.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1];) {
|
||||
subticks.push(v);
|
||||
}
|
||||
}
|
||||
return subticks;
|
||||
}
|
||||
|
||||
var Axis = function () {
|
||||
var scale = d3.scale.linear(),
|
||||
orient = "bottom",
|
||||
tickMajorSize = 6,
|
||||
tickMinorSize = 6,
|
||||
tickEndSize = 6,
|
||||
tickPadding = 3,
|
||||
tickArguments_ = [10],
|
||||
tickFormat_,
|
||||
tickSubdivide = 0,
|
||||
|
||||
tickAttr_ = {},
|
||||
tickTextAttr_ = {},
|
||||
minorTickAttr_ = {},
|
||||
domainAttr_ = {};
|
||||
|
||||
/**
|
||||
* @param paper: raphael's paper object.
|
||||
* @return axisSet: raphael's set object.
|
||||
*/
|
||||
function axis(paper) {
|
||||
// Ticks for quantitative scale, or domain values for ordinal scale.
|
||||
var ticks = scale.ticks ? scale.ticks.apply(scale, tickArguments_) : scale.domain(),
|
||||
tickFormat = tickFormat_ === undefined ?
|
||||
(scale.tickFormat ?
|
||||
scale.tickFormat.apply(scale, tickArguments_)
|
||||
: String)
|
||||
: tickFormat_;
|
||||
|
||||
var subticks = d3_svg_axisSubdivide(scale, ticks, tickSubdivide);
|
||||
var range = d3_scaleRange(scale);
|
||||
|
||||
var axisSet = paper.set();
|
||||
|
||||
switch (orient) {
|
||||
case "bottom":
|
||||
subticks.forEach(function (d, i, arr) {
|
||||
var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickX + "," + tickMinorSize + "V0")
|
||||
.attr(minorTickAttr_));
|
||||
});
|
||||
ticks.forEach(function (d, i, arr) {
|
||||
var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickX + "," + tickMajorSize + "V0")
|
||||
.attr(tickAttr_));
|
||||
axisSet.push(paper
|
||||
.text(tickX, Math.max(tickMajorSize, 0) + tickPadding + 2,
|
||||
typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
|
||||
.attr({"text-anchor": "middle"})
|
||||
.attr(tickTextAttr_));
|
||||
});
|
||||
axisSet.push(paper
|
||||
.path("M" + range[0] + "," + tickEndSize + "V0H" + range[1] + "V" + tickEndSize)
|
||||
.attr(domainAttr_));
|
||||
break;
|
||||
|
||||
case "top":
|
||||
subticks.forEach(function (d, i, arr) {
|
||||
var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickX + "," + -tickMinorSize + "V0")
|
||||
.attr(minorTickAttr_));
|
||||
});
|
||||
ticks.forEach(function (d, i, arr) {
|
||||
var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickX + "," + -tickMajorSize + "V0")
|
||||
.attr(tickAttr_));
|
||||
axisSet.push(paper
|
||||
.text(tickX, -(Math.max(tickMajorSize, 0) + tickPadding + 2),
|
||||
typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
|
||||
.attr({"text-anchor": "middle"})
|
||||
.attr(tickTextAttr_));
|
||||
});
|
||||
axisSet.push(paper
|
||||
.path("M" + range[0] + "," + -tickEndSize + "V0H" + range[1] + "V" + -tickEndSize)
|
||||
.attr(domainAttr_));
|
||||
break;
|
||||
|
||||
case "left":
|
||||
subticks.forEach(function (d, i, arr) {
|
||||
var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + -tickMinorSize + "," + tickY + "H0")
|
||||
.attr(minorTickAttr_));
|
||||
});
|
||||
ticks.forEach(function (d, i, arr) {
|
||||
var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + -tickMajorSize + "," + tickY + "H0")
|
||||
.attr(tickAttr_));
|
||||
axisSet.push(paper
|
||||
.text(-(Math.max(tickMajorSize, 0) + tickPadding), tickY,
|
||||
typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
|
||||
.attr({"text-anchor": "end"})
|
||||
.attr(tickTextAttr_));
|
||||
});
|
||||
axisSet.push(paper
|
||||
.path("M" + -tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + -tickEndSize)
|
||||
.attr(domainAttr_));
|
||||
break;
|
||||
|
||||
case "right":
|
||||
subticks.forEach(function (d, i, arr) {
|
||||
var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickMinorSize + "," + tickY + "H0")
|
||||
.attr(minorTickAttr_));
|
||||
});
|
||||
ticks.forEach(function (d, i, arr) {
|
||||
var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
|
||||
axisSet.push(paper
|
||||
.path("M" + tickMajorSize + "," + tickY + "H0")
|
||||
.attr(tickAttr_));
|
||||
axisSet.push(paper
|
||||
.text(Math.max(tickMajorSize, 0) + tickPadding, tickY,
|
||||
typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
|
||||
.attr({"text-anchor": "start"})
|
||||
.attr(tickTextAttr_));
|
||||
});
|
||||
axisSet.push(paper
|
||||
.path("M" + tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + tickEndSize)
|
||||
.attr(domainAttr_));
|
||||
break;
|
||||
}
|
||||
|
||||
return axisSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* get or set axis' scale.
|
||||
*/
|
||||
axis.scale = function (x) {
|
||||
if (!arguments.length) {
|
||||
return scale;
|
||||
}
|
||||
scale = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' orinet: "bottom", "top", "left", "right", default orient is bottom.
|
||||
*/
|
||||
axis.orient = function (x) {
|
||||
if (!arguments.length) {
|
||||
return orient;
|
||||
}
|
||||
orient = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' ticks number.
|
||||
*/
|
||||
axis.ticks = function () {
|
||||
if (!arguments.length) {
|
||||
return tickArguments_;
|
||||
}
|
||||
tickArguments_ = arguments;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' ticks format function, it's a function change format style.
|
||||
* from one string format to another string format.
|
||||
*/
|
||||
axis.tickFormat = function (x) {
|
||||
if (!arguments.length) {
|
||||
return tickFormat_;
|
||||
}
|
||||
tickFormat_ = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' tick size(length of tick line, unit: px).
|
||||
* @param arguments.length === 0, get axis' major tick size.
|
||||
* @param arguments.length === 1, set axis' all tick sizes as x.
|
||||
* @param arguments.length === 2, get axis' major tick size as x, minor and end size as y.
|
||||
* @param arguments.length === 3, get axis' major tick size as x, minor size as y, end size as z.
|
||||
*/
|
||||
axis.tickSize = function (x, y, z) {
|
||||
if (!arguments.length) {
|
||||
return tickMajorSize;
|
||||
}
|
||||
var n = arguments.length - 1;
|
||||
tickMajorSize = +x;
|
||||
tickMinorSize = n > 1 ? +y : tickMajorSize;
|
||||
tickEndSize = n > 0 ? +arguments[n] : tickMajorSize;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' tick padding(the distance between tick text and axis).
|
||||
* @param x is a number, unit is px;
|
||||
*/
|
||||
axis.tickPadding = function (x) {
|
||||
if (!arguments.length) {
|
||||
return tickPadding;
|
||||
}
|
||||
tickPadding = +x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' sub tick divide number(divide number between two major ticks).
|
||||
*/
|
||||
axis.tickSubdivide = function (x) {
|
||||
if (!arguments.length) {
|
||||
return tickSubdivide;
|
||||
}
|
||||
tickSubdivide = +x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' tick attribute(Raphael format).
|
||||
*/
|
||||
axis.tickAttr = function (x) {
|
||||
if (!arguments.length) {
|
||||
return tickAttr_;
|
||||
}
|
||||
tickAttr_ = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' tick text attribute(Raphael format).
|
||||
*/
|
||||
axis.tickTextAttr = function (x) {
|
||||
if (!arguments.length) {
|
||||
return tickTextAttr_;
|
||||
}
|
||||
tickTextAttr_ = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' minor tick attribute(Raphael format).
|
||||
*/
|
||||
axis.minorTickAttr = function (x) {
|
||||
if (!arguments.length) {
|
||||
return minorTickAttr_;
|
||||
}
|
||||
minorTickAttr_ = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
/**
|
||||
* get or set axis' domain(axis line) attribute(Raphael format).
|
||||
*/
|
||||
axis.domainAttr = function (x) {
|
||||
if (!arguments.length) {
|
||||
return domainAttr_;
|
||||
}
|
||||
domainAttr_ = x;
|
||||
return axis;
|
||||
};
|
||||
|
||||
return axis;
|
||||
};
|
||||
|
||||
return Axis;
|
||||
});
|
||||
|
||||
1230
lib/charts/brush.js
1230
lib/charts/brush.js
File diff suppressed because it is too large
Load Diff
@ -1,349 +1,349 @@
|
||||
/*global Raphael, d3 */
|
||||
/*!
|
||||
* Bullet的兼容性定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Bullet', function (require) {
|
||||
var DataV = require('DataV');
|
||||
var Axis = require('Axis');
|
||||
|
||||
/**
|
||||
* Bullet构造函数
|
||||
* Options:
|
||||
*
|
||||
* - `width` 数字,图片宽度,默认为200,表示图片高200px
|
||||
* - `height` 数字,图片高度,默认为80
|
||||
* - `orient` string,图片方向,默认为"horizonal",表示水平方向。若为"vertical",则表示垂直方向
|
||||
* - `axisStyle` string, 坐标系类型,默认为"linear",表示坐标系为线性坐标系。若为"log",则表示对数坐标系
|
||||
* - `logBase` 数字, 采用对数坐标系时的对数基,默认为Math.E
|
||||
* - `tickDivide` 数字,表示坐标的标尺的分段数,默认为5
|
||||
* - `margin` 数字数组,表示图片上、右、下、左的边距,默认为 [20, 20, 20, 20]
|
||||
* - `centerBarRatio` 数字,表示中间的测度条的高度与背景条高度的比值, 默认为0.3
|
||||
* - `markerWidth` 数字,表示标记条的宽度, 默认为4,单位为像素
|
||||
* - `markerRatio` 数字,表示标记条的高度与背景条高度的比值,默认为0.7
|
||||
* - `titleRatio` 数字,表示子弹图title的高度与背景条高度的比值,默认为0.6,此时title与subtitle的比值
|
||||
* - `backgroundColor` string数组,表示背景条颜色的渐变数组,默认为["#666", "#ddd"],背景条颜色就是这两种颜色的渐变
|
||||
* - `measureColor` string数组,表示测度条颜色的渐变数组,默认为["steelblue", "#B0C4DE"],测度条颜色就是这两种颜色的渐变
|
||||
* - `markerColor` string,表示标记条的颜色,默认为"#000"
|
||||
*
|
||||
* Examples:
|
||||
* ```
|
||||
* //Create bullet in a dom node with id "chart", width is 500; height is 600px;
|
||||
* var bullet = new Bullet("chart", {
|
||||
* "width": 500,
|
||||
* "height": 600,
|
||||
* "margin": [10, 10, 20, 70]
|
||||
* });
|
||||
* //Create bullet with log base;
|
||||
* var log = new Bullet("chart2", {
|
||||
* width: 300,
|
||||
* height: 60,
|
||||
* margin: [10, 10, 20, 70],
|
||||
* backgroundColor: ["#66f", "#ddf"],
|
||||
* measureColor: ["#000", "#000"],
|
||||
* markerColor: "#44f",
|
||||
* axisStyle: "log",
|
||||
* logBase: 10
|
||||
* });
|
||||
* ```
|
||||
* @param {Mix} node The dom node or dom node Id
|
||||
* @param {Object} options options json object for determin stream style.
|
||||
*/
|
||||
var Bullet = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.type = "Bullet";
|
||||
this.node = this.checkContainer(node);
|
||||
// Properties
|
||||
this.defaults.orient = "horizonal"; // "horizonal", "vertical"
|
||||
this.defaults.axisStyle = "linear"; // "linear", "log"
|
||||
this.defaults.logBase = Math.E;
|
||||
this.defaults.tickDivide = 5;
|
||||
this.defaults.margin = [10, 10, 20, 80];//top, right, bottom, left
|
||||
this.defaults.centerBarRatio = 0.3;
|
||||
this.defaults.markerWidth = 4;
|
||||
this.defaults.markerRatio = 0.7;
|
||||
this.defaults.titleRatio = 0.6; //title's text height : subtitle's text height = 6:4
|
||||
this.defaults.backgroundColor = ["#666", "#ddd"]; //dark, light
|
||||
this.defaults.measureColor = ["steelblue", "#B0C4DE"]; //dark, light
|
||||
this.defaults.markerColor = "#000";
|
||||
|
||||
// canvas
|
||||
this.defaults.width = 200;
|
||||
this.defaults.height = 80;
|
||||
|
||||
this.setOptions(options);
|
||||
this.createCanvas();
|
||||
}
|
||||
});
|
||||
|
||||
/*!
|
||||
* 创建画布
|
||||
*/
|
||||
Bullet.prototype.createCanvas = function () {
|
||||
var conf = this.defaults;
|
||||
this.canvas = new Raphael(this.node, conf.width, conf.height);
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置数据源
|
||||
* Examples:
|
||||
* ```
|
||||
* bullet.setSource({
|
||||
* title: "Sample",
|
||||
* subtitle: "ratio",
|
||||
* ranges: [0, 0.5, 0.8, 1],
|
||||
* measures: [0.7, 0.9],
|
||||
* markers: [0.6],
|
||||
* rangeTitles: ["below 50%", "top 20% - 50%", "top 20%"],
|
||||
* measureTitles: ["value is 0.7", "value is 0.9"],
|
||||
* markerTitles: ["mean is 0.6"]
|
||||
* })
|
||||
* ```
|
||||
* @param {Object} source 数据源
|
||||
*/
|
||||
Bullet.prototype.setSource = function (source) {
|
||||
var conf = this.defaults,
|
||||
range,
|
||||
axisOrient;
|
||||
this.data = source;
|
||||
if (conf.orient === "horizonal") {
|
||||
axisOrient = "bottom";
|
||||
range = [conf.margin[3], conf.width - conf.margin[1]];
|
||||
} else if (conf.orient === "vertical") {
|
||||
axisOrient = "left";
|
||||
range = [conf.height - conf.margin[2], conf.margin[0]];
|
||||
}
|
||||
|
||||
if (conf.axisStyle === "linear") {
|
||||
this.scale = d3.scale.linear();
|
||||
} else if (conf.axisStyle === "log") {
|
||||
this.scale = d3.scale.log();
|
||||
}
|
||||
|
||||
this.data.min = this.data.ranges[0];
|
||||
this.data.max = this.data.ranges[this.data.ranges.length - 1];
|
||||
this.scale.domain([this.data.min, this.data.max])
|
||||
.range(range);
|
||||
|
||||
if (conf.axisStyle === "linear") {
|
||||
this.axis = Axis().scale(this.scale).orient(axisOrient).ticks(conf.tickDivide).domainAttr({"stroke": "none"});
|
||||
} else if (conf.axisStyle === "log") {
|
||||
this.logScale = d3.scale.linear()
|
||||
.domain([Math.log(this.data.min)/Math.log(conf.logBase), Math.log(this.data.max)/Math.log(conf.logBase)])
|
||||
.range(range);
|
||||
this.axis = Axis()
|
||||
.orient(axisOrient)
|
||||
.scale(this.logScale)
|
||||
.ticks(conf.tickDivide)
|
||||
.tickFormat(function (d) {return Math.round(Math.pow(conf.logBase, d));})
|
||||
.domainAttr({"stroke": "none"});
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* generate bullet dom path
|
||||
*/
|
||||
Bullet.prototype.generatePaths = function () {
|
||||
var conf = this.defaults;
|
||||
//get color function
|
||||
if (conf.backgroundColor) {
|
||||
this.color = d3.interpolateRgb.apply(null, [conf.backgroundColor[0], conf.backgroundColor[1]]);
|
||||
}
|
||||
if (conf.measureColor) {
|
||||
this.measureColor = d3.interpolateRgb.apply(null, [conf.measureColor[0], conf.measureColor[1]]);
|
||||
}
|
||||
|
||||
if (conf.orient === "horizonal") {
|
||||
this.paintHorizonal();
|
||||
} else if (conf.orient === "vertical") {
|
||||
this.paintVertical();
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* paint orient horizonal bullet
|
||||
*/
|
||||
Bullet.prototype.paintHorizonal = function () {
|
||||
var conf = this.defaults;
|
||||
var paper = this.canvas,
|
||||
data = this.data,
|
||||
m = conf.margin,
|
||||
ranges = [],
|
||||
measures = [],
|
||||
markers = [],
|
||||
rangeTitles = [],
|
||||
i,
|
||||
l,
|
||||
rect,
|
||||
titleRatio,
|
||||
w,
|
||||
h = conf.height - m[0] - m[2],
|
||||
left;
|
||||
|
||||
//axis
|
||||
this.axis(paper).attr({transform: "t" + 0 + ',' + (conf.height - m[2])});
|
||||
//color rect
|
||||
ranges = data.ranges;
|
||||
if (data.rangeTitles) {
|
||||
rangeTitles = data.rangeTitles;
|
||||
}
|
||||
left = m[3];
|
||||
for (i = 0, l = ranges.length - 1; i < l; i++) {
|
||||
w = this.scale(ranges[i + 1]) - this.scale(ranges[i]);
|
||||
rect = paper.rect(left, m[0], w, h)
|
||||
.attr({"stroke": "none",
|
||||
"fill": this.color(l === 1 ? 1 : i / (l - 1)),
|
||||
"title": rangeTitles[i] ? rangeTitles[i] : ""});
|
||||
left += w;
|
||||
}
|
||||
|
||||
//measure bar
|
||||
data.measures.forEach(function (d, i) {
|
||||
var mTitles = data.measureTitles;
|
||||
var mTitle = mTitles && mTitles[i] ? mTitles[i] : "";
|
||||
measures.push({measure: d, measureTitle: mTitle});
|
||||
});
|
||||
measures.sort(function (a, b) { return d3.ascending(a.measure, b.measure); });
|
||||
left = this.scale(data.min);
|
||||
for (i = 0, l = measures.length; i < l; i++) {
|
||||
value = Math.max(data.min, Math.min(data.max, measures[i].measure));
|
||||
w = this.scale(value) - left;
|
||||
paper.rect(left,
|
||||
m[0] + h * (1 - conf.centerBarRatio) / 2,
|
||||
w,
|
||||
h * conf.centerBarRatio)
|
||||
.attr({"stroke": "none", "fill": this.measureColor(l === 1 ? 1 : i / (l - 1)), "title": measures[i].measureTitle});
|
||||
left += w;
|
||||
}
|
||||
|
||||
//marker bar
|
||||
markers = data.markers;
|
||||
for (i = 0, l = markers.length; i < l; i++) {
|
||||
paper.rect(this.scale(markers[i]) - conf.markerWidth / 2,
|
||||
m[0] + h * (1 - conf.markerRatio) / 2,
|
||||
conf.markerWidth,
|
||||
h * conf.markerRatio)
|
||||
.attr({"stroke": "none", "fill": conf.markerColor,
|
||||
"title": data.markerTitles && data.markerTitles[i] ? data.markerTitles[i] : ""});
|
||||
}
|
||||
|
||||
//title
|
||||
if (data.title) {
|
||||
titleRatio = data.subtitle ? conf.titleRatio : 1;
|
||||
this.title = paper.text(m[3] - 5, m[0] + h / 2, data.title)
|
||||
.attr({"text-anchor": "end", "font-weight": "bold", "font-size": h * titleRatio * 0.9});
|
||||
}
|
||||
|
||||
//subtitle
|
||||
if (data.subtitle) {
|
||||
this.subtitle = paper.text(m[3] - 5, conf.height - m[2], data.subtitle)
|
||||
.attr({"text-anchor": "end", "font-size": h * (1 - conf.titleRatio) * 0.9});
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* paint orient vertical bullet
|
||||
*/
|
||||
Bullet.prototype.paintVertical = function () {
|
||||
var conf = this.defaults;
|
||||
var paper = this.canvas,
|
||||
data = this.data,
|
||||
m = conf.margin,
|
||||
ranges = [],
|
||||
measures = [],
|
||||
markers = [],
|
||||
rangeTitles = [],
|
||||
i,
|
||||
l,
|
||||
rect,
|
||||
titleRatio,
|
||||
w = conf.width - m[1] - m[3],
|
||||
h,
|
||||
bottom;
|
||||
|
||||
//axis
|
||||
this.axis(paper).attr({transform: "t" + m[3] + ',' + 0});
|
||||
|
||||
//color rect
|
||||
ranges = data.ranges;
|
||||
if (data.rangeTitles) {
|
||||
rangeTitles = data.rangeTitles;
|
||||
}
|
||||
bottom = conf.height - m[2];
|
||||
for (i = 0, l = ranges.length - 1; i < l; i++) {
|
||||
h = -this.scale(ranges[i + 1]) + this.scale(ranges[i]);
|
||||
rect = paper.rect(m[3], bottom - h, w, h)
|
||||
.attr({"stroke": "none",
|
||||
"fill": this.color(l === 1 ? 1 : i / (l - 1)),
|
||||
"title": rangeTitles[i] ? rangeTitles[i] : ""});
|
||||
bottom -= h;
|
||||
}
|
||||
|
||||
//measure bar
|
||||
data.measures.forEach(function (d, i) {
|
||||
var mTitles = data.measureTitles;
|
||||
var mTitle = mTitles && mTitles[i] ? mTitles[i] : "";
|
||||
measures.push({measure: d, measureTitle: mTitle});
|
||||
});
|
||||
measures.sort(function (a, b) { return d3.ascending(a.measure, b.measure); });
|
||||
bottom = this.scale(data.min);
|
||||
for (i = 0, l = measures.length; i < l; i++) {
|
||||
value = Math.max(data.min, Math.min(data.max, measures[i].measure));
|
||||
h = -this.scale(value) + bottom;
|
||||
paper.rect(m[3] + w * (1 - conf.centerBarRatio) / 2,
|
||||
bottom - h,
|
||||
w * conf.centerBarRatio,
|
||||
h)
|
||||
.attr({"stroke": "none", "fill": this.measureColor(l === 1 ? 1 : i / (l - 1)), "title": measures[i].measureTitle});
|
||||
bottom -= h;
|
||||
}
|
||||
|
||||
//marker bar
|
||||
markers = data.markers;
|
||||
for (i = 0, l = markers.length; i < l; i++) {
|
||||
paper.rect(m[3] + w * (1 - conf.markerRatio) / 2,
|
||||
this.scale(markers[i]) - conf.markerWidth / 2,
|
||||
w * conf.markerRatio,
|
||||
conf.markerWidth)
|
||||
.attr({"stroke": "none", "fill": conf.markerColor,
|
||||
"title": data.markerTitles && data.markerTitles[i] ? data.markerTitles[i] : ""});
|
||||
}
|
||||
|
||||
//title
|
||||
if (data.title) {
|
||||
titleRatio = data.subtitle ? conf.titleRatio : 1;
|
||||
m[0] *= 0.9; //some ratio adjust;
|
||||
this.title = paper.text((conf.width + m[3] - m[1])/ 2, m[0] * titleRatio / 2, data.title)
|
||||
.attr({"text-anchor": "middle", "font-weight": "bold", "font-size": m[0] * titleRatio * 0.8});
|
||||
}
|
||||
|
||||
//subtitle
|
||||
if (data.subtitle) {
|
||||
this.subtitle = paper.text((conf.width + m[3] - m[1])/ 2, m[0] * (1 - (1 - titleRatio) / 2), data.subtitle)
|
||||
.attr({"text-anchor": "middle", "font-size": m[0] * (1 - titleRatio) * 0.8});
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* clean canvas
|
||||
*/
|
||||
Bullet.prototype.clearCanvas = function () {
|
||||
this.canvas.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* render bullet
|
||||
*/
|
||||
Bullet.prototype.render = function (options) {
|
||||
this.setOptions(options);
|
||||
this.generatePaths();
|
||||
};
|
||||
|
||||
return Bullet;
|
||||
});
|
||||
|
||||
/*global Raphael, d3 */
|
||||
/*!
|
||||
* Bullet的兼容性定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Bullet', function (require) {
|
||||
var DataV = require('DataV');
|
||||
var Axis = require('Axis');
|
||||
|
||||
/**
|
||||
* Bullet构造函数
|
||||
* Options:
|
||||
*
|
||||
* - `width` 数字,图片宽度,默认为200,表示图片高200px
|
||||
* - `height` 数字,图片高度,默认为80
|
||||
* - `orient` string,图片方向,默认为"horizonal",表示水平方向。若为"vertical",则表示垂直方向
|
||||
* - `axisStyle` string, 坐标系类型,默认为"linear",表示坐标系为线性坐标系。若为"log",则表示对数坐标系
|
||||
* - `logBase` 数字, 采用对数坐标系时的对数基,默认为Math.E
|
||||
* - `tickDivide` 数字,表示坐标的标尺的分段数,默认为5
|
||||
* - `margin` 数字数组,表示图片上、右、下、左的边距,默认为 [20, 20, 20, 20]
|
||||
* - `centerBarRatio` 数字,表示中间的测度条的高度与背景条高度的比值, 默认为0.3
|
||||
* - `markerWidth` 数字,表示标记条的宽度, 默认为4,单位为像素
|
||||
* - `markerRatio` 数字,表示标记条的高度与背景条高度的比值,默认为0.7
|
||||
* - `titleRatio` 数字,表示子弹图title的高度与背景条高度的比值,默认为0.6,此时title与subtitle的比值
|
||||
* - `backgroundColor` string数组,表示背景条颜色的渐变数组,默认为["#666", "#ddd"],背景条颜色就是这两种颜色的渐变
|
||||
* - `measureColor` string数组,表示测度条颜色的渐变数组,默认为["steelblue", "#B0C4DE"],测度条颜色就是这两种颜色的渐变
|
||||
* - `markerColor` string,表示标记条的颜色,默认为"#000"
|
||||
*
|
||||
* Examples:
|
||||
* ```
|
||||
* //Create bullet in a dom node with id "chart", width is 500; height is 600px;
|
||||
* var bullet = new Bullet("chart", {
|
||||
* "width": 500,
|
||||
* "height": 600,
|
||||
* "margin": [10, 10, 20, 70]
|
||||
* });
|
||||
* //Create bullet with log base;
|
||||
* var log = new Bullet("chart2", {
|
||||
* width: 300,
|
||||
* height: 60,
|
||||
* margin: [10, 10, 20, 70],
|
||||
* backgroundColor: ["#66f", "#ddf"],
|
||||
* measureColor: ["#000", "#000"],
|
||||
* markerColor: "#44f",
|
||||
* axisStyle: "log",
|
||||
* logBase: 10
|
||||
* });
|
||||
* ```
|
||||
* @param {Mix} node The dom node or dom node Id
|
||||
* @param {Object} options options json object for determin stream style.
|
||||
*/
|
||||
var Bullet = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.type = "Bullet";
|
||||
this.node = this.checkContainer(node);
|
||||
// Properties
|
||||
this.defaults.orient = "horizonal"; // "horizonal", "vertical"
|
||||
this.defaults.axisStyle = "linear"; // "linear", "log"
|
||||
this.defaults.logBase = Math.E;
|
||||
this.defaults.tickDivide = 5;
|
||||
this.defaults.margin = [10, 10, 20, 80];//top, right, bottom, left
|
||||
this.defaults.centerBarRatio = 0.3;
|
||||
this.defaults.markerWidth = 4;
|
||||
this.defaults.markerRatio = 0.7;
|
||||
this.defaults.titleRatio = 0.6; //title's text height : subtitle's text height = 6:4
|
||||
this.defaults.backgroundColor = ["#666", "#ddd"]; //dark, light
|
||||
this.defaults.measureColor = ["steelblue", "#B0C4DE"]; //dark, light
|
||||
this.defaults.markerColor = "#000";
|
||||
|
||||
// canvas
|
||||
this.defaults.width = 200;
|
||||
this.defaults.height = 80;
|
||||
|
||||
this.setOptions(options);
|
||||
this.createCanvas();
|
||||
}
|
||||
});
|
||||
|
||||
/*!
|
||||
* 创建画布
|
||||
*/
|
||||
Bullet.prototype.createCanvas = function () {
|
||||
var conf = this.defaults;
|
||||
this.canvas = new Raphael(this.node, conf.width, conf.height);
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置数据源
|
||||
* Examples:
|
||||
* ```
|
||||
* bullet.setSource({
|
||||
* title: "Sample",
|
||||
* subtitle: "ratio",
|
||||
* ranges: [0, 0.5, 0.8, 1],
|
||||
* measures: [0.7, 0.9],
|
||||
* markers: [0.6],
|
||||
* rangeTitles: ["below 50%", "top 20% - 50%", "top 20%"],
|
||||
* measureTitles: ["value is 0.7", "value is 0.9"],
|
||||
* markerTitles: ["mean is 0.6"]
|
||||
* })
|
||||
* ```
|
||||
* @param {Object} source 数据源
|
||||
*/
|
||||
Bullet.prototype.setSource = function (source) {
|
||||
var conf = this.defaults,
|
||||
range,
|
||||
axisOrient;
|
||||
this.data = source;
|
||||
if (conf.orient === "horizonal") {
|
||||
axisOrient = "bottom";
|
||||
range = [conf.margin[3], conf.width - conf.margin[1]];
|
||||
} else if (conf.orient === "vertical") {
|
||||
axisOrient = "left";
|
||||
range = [conf.height - conf.margin[2], conf.margin[0]];
|
||||
}
|
||||
|
||||
if (conf.axisStyle === "linear") {
|
||||
this.scale = d3.scale.linear();
|
||||
} else if (conf.axisStyle === "log") {
|
||||
this.scale = d3.scale.log();
|
||||
}
|
||||
|
||||
this.data.min = this.data.ranges[0];
|
||||
this.data.max = this.data.ranges[this.data.ranges.length - 1];
|
||||
this.scale.domain([this.data.min, this.data.max])
|
||||
.range(range);
|
||||
|
||||
if (conf.axisStyle === "linear") {
|
||||
this.axis = Axis().scale(this.scale).orient(axisOrient).ticks(conf.tickDivide).domainAttr({"stroke": "none"});
|
||||
} else if (conf.axisStyle === "log") {
|
||||
this.logScale = d3.scale.linear()
|
||||
.domain([Math.log(this.data.min)/Math.log(conf.logBase), Math.log(this.data.max)/Math.log(conf.logBase)])
|
||||
.range(range);
|
||||
this.axis = Axis()
|
||||
.orient(axisOrient)
|
||||
.scale(this.logScale)
|
||||
.ticks(conf.tickDivide)
|
||||
.tickFormat(function (d) {return Math.round(Math.pow(conf.logBase, d));})
|
||||
.domainAttr({"stroke": "none"});
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* generate bullet dom path
|
||||
*/
|
||||
Bullet.prototype.generatePaths = function () {
|
||||
var conf = this.defaults;
|
||||
//get color function
|
||||
if (conf.backgroundColor) {
|
||||
this.color = d3.interpolateRgb.apply(null, [conf.backgroundColor[0], conf.backgroundColor[1]]);
|
||||
}
|
||||
if (conf.measureColor) {
|
||||
this.measureColor = d3.interpolateRgb.apply(null, [conf.measureColor[0], conf.measureColor[1]]);
|
||||
}
|
||||
|
||||
if (conf.orient === "horizonal") {
|
||||
this.paintHorizonal();
|
||||
} else if (conf.orient === "vertical") {
|
||||
this.paintVertical();
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* paint orient horizonal bullet
|
||||
*/
|
||||
Bullet.prototype.paintHorizonal = function () {
|
||||
var conf = this.defaults;
|
||||
var paper = this.canvas,
|
||||
data = this.data,
|
||||
m = conf.margin,
|
||||
ranges = [],
|
||||
measures = [],
|
||||
markers = [],
|
||||
rangeTitles = [],
|
||||
i,
|
||||
l,
|
||||
rect,
|
||||
titleRatio,
|
||||
w,
|
||||
h = conf.height - m[0] - m[2],
|
||||
left;
|
||||
|
||||
//axis
|
||||
this.axis(paper).attr({transform: "t" + 0 + ',' + (conf.height - m[2])});
|
||||
//color rect
|
||||
ranges = data.ranges;
|
||||
if (data.rangeTitles) {
|
||||
rangeTitles = data.rangeTitles;
|
||||
}
|
||||
left = m[3];
|
||||
for (i = 0, l = ranges.length - 1; i < l; i++) {
|
||||
w = this.scale(ranges[i + 1]) - this.scale(ranges[i]);
|
||||
rect = paper.rect(left, m[0], w, h)
|
||||
.attr({"stroke": "none",
|
||||
"fill": this.color(l === 1 ? 1 : i / (l - 1)),
|
||||
"title": rangeTitles[i] ? rangeTitles[i] : ""});
|
||||
left += w;
|
||||
}
|
||||
|
||||
//measure bar
|
||||
data.measures.forEach(function (d, i) {
|
||||
var mTitles = data.measureTitles;
|
||||
var mTitle = mTitles && mTitles[i] ? mTitles[i] : "";
|
||||
measures.push({measure: d, measureTitle: mTitle});
|
||||
});
|
||||
measures.sort(function (a, b) { return d3.ascending(a.measure, b.measure); });
|
||||
left = this.scale(data.min);
|
||||
for (i = 0, l = measures.length; i < l; i++) {
|
||||
value = Math.max(data.min, Math.min(data.max, measures[i].measure));
|
||||
w = this.scale(value) - left;
|
||||
paper.rect(left,
|
||||
m[0] + h * (1 - conf.centerBarRatio) / 2,
|
||||
w,
|
||||
h * conf.centerBarRatio)
|
||||
.attr({"stroke": "none", "fill": this.measureColor(l === 1 ? 1 : i / (l - 1)), "title": measures[i].measureTitle});
|
||||
left += w;
|
||||
}
|
||||
|
||||
//marker bar
|
||||
markers = data.markers;
|
||||
for (i = 0, l = markers.length; i < l; i++) {
|
||||
paper.rect(this.scale(markers[i]) - conf.markerWidth / 2,
|
||||
m[0] + h * (1 - conf.markerRatio) / 2,
|
||||
conf.markerWidth,
|
||||
h * conf.markerRatio)
|
||||
.attr({"stroke": "none", "fill": conf.markerColor,
|
||||
"title": data.markerTitles && data.markerTitles[i] ? data.markerTitles[i] : ""});
|
||||
}
|
||||
|
||||
//title
|
||||
if (data.title) {
|
||||
titleRatio = data.subtitle ? conf.titleRatio : 1;
|
||||
this.title = paper.text(m[3] - 5, m[0] + h / 2, data.title)
|
||||
.attr({"text-anchor": "end", "font-weight": "bold", "font-size": h * titleRatio * 0.9});
|
||||
}
|
||||
|
||||
//subtitle
|
||||
if (data.subtitle) {
|
||||
this.subtitle = paper.text(m[3] - 5, conf.height - m[2], data.subtitle)
|
||||
.attr({"text-anchor": "end", "font-size": h * (1 - conf.titleRatio) * 0.9});
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* paint orient vertical bullet
|
||||
*/
|
||||
Bullet.prototype.paintVertical = function () {
|
||||
var conf = this.defaults;
|
||||
var paper = this.canvas,
|
||||
data = this.data,
|
||||
m = conf.margin,
|
||||
ranges = [],
|
||||
measures = [],
|
||||
markers = [],
|
||||
rangeTitles = [],
|
||||
i,
|
||||
l,
|
||||
rect,
|
||||
titleRatio,
|
||||
w = conf.width - m[1] - m[3],
|
||||
h,
|
||||
bottom;
|
||||
|
||||
//axis
|
||||
this.axis(paper).attr({transform: "t" + m[3] + ',' + 0});
|
||||
|
||||
//color rect
|
||||
ranges = data.ranges;
|
||||
if (data.rangeTitles) {
|
||||
rangeTitles = data.rangeTitles;
|
||||
}
|
||||
bottom = conf.height - m[2];
|
||||
for (i = 0, l = ranges.length - 1; i < l; i++) {
|
||||
h = -this.scale(ranges[i + 1]) + this.scale(ranges[i]);
|
||||
rect = paper.rect(m[3], bottom - h, w, h)
|
||||
.attr({"stroke": "none",
|
||||
"fill": this.color(l === 1 ? 1 : i / (l - 1)),
|
||||
"title": rangeTitles[i] ? rangeTitles[i] : ""});
|
||||
bottom -= h;
|
||||
}
|
||||
|
||||
//measure bar
|
||||
data.measures.forEach(function (d, i) {
|
||||
var mTitles = data.measureTitles;
|
||||
var mTitle = mTitles && mTitles[i] ? mTitles[i] : "";
|
||||
measures.push({measure: d, measureTitle: mTitle});
|
||||
});
|
||||
measures.sort(function (a, b) { return d3.ascending(a.measure, b.measure); });
|
||||
bottom = this.scale(data.min);
|
||||
for (i = 0, l = measures.length; i < l; i++) {
|
||||
value = Math.max(data.min, Math.min(data.max, measures[i].measure));
|
||||
h = -this.scale(value) + bottom;
|
||||
paper.rect(m[3] + w * (1 - conf.centerBarRatio) / 2,
|
||||
bottom - h,
|
||||
w * conf.centerBarRatio,
|
||||
h)
|
||||
.attr({"stroke": "none", "fill": this.measureColor(l === 1 ? 1 : i / (l - 1)), "title": measures[i].measureTitle});
|
||||
bottom -= h;
|
||||
}
|
||||
|
||||
//marker bar
|
||||
markers = data.markers;
|
||||
for (i = 0, l = markers.length; i < l; i++) {
|
||||
paper.rect(m[3] + w * (1 - conf.markerRatio) / 2,
|
||||
this.scale(markers[i]) - conf.markerWidth / 2,
|
||||
w * conf.markerRatio,
|
||||
conf.markerWidth)
|
||||
.attr({"stroke": "none", "fill": conf.markerColor,
|
||||
"title": data.markerTitles && data.markerTitles[i] ? data.markerTitles[i] : ""});
|
||||
}
|
||||
|
||||
//title
|
||||
if (data.title) {
|
||||
titleRatio = data.subtitle ? conf.titleRatio : 1;
|
||||
m[0] *= 0.9; //some ratio adjust;
|
||||
this.title = paper.text((conf.width + m[3] - m[1])/ 2, m[0] * titleRatio / 2, data.title)
|
||||
.attr({"text-anchor": "middle", "font-weight": "bold", "font-size": m[0] * titleRatio * 0.8});
|
||||
}
|
||||
|
||||
//subtitle
|
||||
if (data.subtitle) {
|
||||
this.subtitle = paper.text((conf.width + m[3] - m[1])/ 2, m[0] * (1 - (1 - titleRatio) / 2), data.subtitle)
|
||||
.attr({"text-anchor": "middle", "font-size": m[0] * (1 - titleRatio) * 0.8});
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* clean canvas
|
||||
*/
|
||||
Bullet.prototype.clearCanvas = function () {
|
||||
this.canvas.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* render bullet
|
||||
*/
|
||||
Bullet.prototype.render = function (options) {
|
||||
this.setOptions(options);
|
||||
this.generatePaths();
|
||||
};
|
||||
|
||||
return Bullet;
|
||||
});
|
||||
|
||||
|
||||
@ -1,424 +1,424 @@
|
||||
/*global EventProxy, d3, Raphael, self, packages, $ */
|
||||
|
||||
/*!
|
||||
* Bundle的兼容性定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Bundle', function (require) {
|
||||
var DataV = require('DataV');
|
||||
|
||||
/**
|
||||
* 构造函数,node参数表示在html的哪个容器中绘制该组件
|
||||
* options对象为用户自定义的组件的属性,比如画布大小
|
||||
*/
|
||||
var Bundle = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.type = "Bundle";
|
||||
this.node = this.checkContainer(node);
|
||||
this.json = {};
|
||||
|
||||
// 图的半径
|
||||
this.defaults.diameter = 960;
|
||||
this.defaults.radius = this.defaults.diameter / 2;
|
||||
this.defaults.innerRadius = this.defaults.radius - 120;
|
||||
this.defaults.tension = 0.85;
|
||||
|
||||
this.defaults.color = {
|
||||
defaultLineColor: "#4065AF",
|
||||
defaultWordColor: "#000000",
|
||||
lineHoverColor: "#02B0ED",
|
||||
nodeHoverColor: "#02B0ED",
|
||||
importNodesColor: "#5DA714", //被引用的节点
|
||||
exportNodesColor: "#FE3919" //引用当前节点的节点
|
||||
};
|
||||
|
||||
this.setOptions(options);
|
||||
this.createCanvas();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 设置用户自定义属性
|
||||
*/
|
||||
Bundle.prototype.setOptions = function (options) {
|
||||
if (options) {
|
||||
var prop;
|
||||
for (prop in options) {
|
||||
if (options.hasOwnProperty(prop)) {
|
||||
this.defaults[prop] = options[prop];
|
||||
if (prop === "diameter") {
|
||||
this.defaults.radius = this.defaults.diameter / 2;
|
||||
this.defaults.innerRadius = this.defaults.radius - 120;
|
||||
} else if (prop === "radius") {
|
||||
this.defaults.diameter = this.defaults.radius * 2;
|
||||
this.defaults.innerRadius = this.defaults.radius - 120;
|
||||
} else if (prop === "innerRadius") {
|
||||
this.defaults.radius = this.defaults.innerRadius + 120;
|
||||
this.defaults.diameter = this.defaults.radius * 2;
|
||||
} else if (prop === "width") {
|
||||
this.defaults.diameter = this.defaults.width;
|
||||
this.defaults.radius = this.defaults.diameter / 2;
|
||||
this.defaults.innerRadius = this.defaults.radius - 120;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 对原始数据进行处理
|
||||
* TODO: 改进为获取值时运算
|
||||
*/
|
||||
Bundle.prototype.setSource = function (source) {
|
||||
if (source[0] && source[0] instanceof Array) {
|
||||
// csv or 2d array source
|
||||
if (source[0][0] === "name") {
|
||||
source = source.slice(1); // 从第一行开始,第0行舍去
|
||||
}
|
||||
var nData = [];
|
||||
var imports = [];
|
||||
//var isNode = true;
|
||||
var nodeNum;
|
||||
var that = this;
|
||||
source.forEach(function (d, i) {
|
||||
if (d[0] === "") {
|
||||
throw new Error("name can not be empty(line:" + (i + 1) + ").");
|
||||
}
|
||||
if (d[1] !== "") {
|
||||
imports = d[1].split(" ");
|
||||
}
|
||||
nData[i] = {
|
||||
name: d[0],
|
||||
imports: imports
|
||||
};
|
||||
});
|
||||
this.json = nData;
|
||||
} else {
|
||||
// json source
|
||||
this.json = source;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建画布
|
||||
*/
|
||||
Bundle.prototype.createCanvas = function () {
|
||||
var conf = this.defaults;
|
||||
this.canvas = new Raphael(this.node, conf.diameter, conf.diameter);
|
||||
|
||||
//var c = this.canvas.circle(50, 50, 40);
|
||||
};
|
||||
|
||||
/**
|
||||
* 布局
|
||||
*/
|
||||
Bundle.prototype.layout = function () {
|
||||
var packages = {
|
||||
// Lazily construct the package hierarchy from class names.
|
||||
root: function (classes) {
|
||||
var map = {};
|
||||
function construct(name, data) {
|
||||
var node = map[name], i;
|
||||
if (!node) {
|
||||
node = map[name] = data || {name: name, children: []};
|
||||
if (name.length) {
|
||||
node.parent = construct(name.substring(0, i = name.lastIndexOf(".")));
|
||||
node.parent.children.push(node);
|
||||
node.key = name.substring(i + 1);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
classes.forEach(function (d) {
|
||||
construct(d.name, d);
|
||||
});
|
||||
|
||||
return map[""];
|
||||
},
|
||||
|
||||
// Return a list of imports for the given array of nodes.
|
||||
imports: function (nodes) {
|
||||
var map = {},
|
||||
imports = [];
|
||||
|
||||
// Compute a map from name to node.
|
||||
nodes.forEach(function (d) {
|
||||
map[d.name] = d;
|
||||
});
|
||||
|
||||
// For each import, construct a link from the source to target node.
|
||||
nodes.forEach(function (d) {
|
||||
if (d.imports) {
|
||||
d.imports.forEach(function (i) {imports.push({source: map[d.name], target: map[i]});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return imports;
|
||||
}
|
||||
};
|
||||
|
||||
var cluster = d3.layout.cluster()
|
||||
.size([360, this.defaults.innerRadius]) //.size(角度,半径)
|
||||
.sort(null)
|
||||
.value(function (d) {
|
||||
return d.size;
|
||||
});
|
||||
this.nodes = cluster.nodes(packages.root(this.json));
|
||||
this.links = packages.imports(this.nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* 渲染弦图
|
||||
*/
|
||||
Bundle.prototype.render = function () {
|
||||
this.layout();
|
||||
this.generatePaths();
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成路径
|
||||
*/
|
||||
Bundle.prototype.generatePaths = function (options) {
|
||||
var that = this;
|
||||
|
||||
var canvas = this.canvas;
|
||||
var rNodes = canvas.set();
|
||||
var rLinks = canvas.set();
|
||||
|
||||
var bundle = d3.layout.bundle();
|
||||
|
||||
var line = d3.svg.line.radial()
|
||||
.interpolate("bundle")
|
||||
.tension(this.defaults.tension)
|
||||
.radius(function (d) {
|
||||
return d.y;
|
||||
})
|
||||
.angle(function (d) {
|
||||
return d.x / 180 * Math.PI;
|
||||
});
|
||||
|
||||
//定义图中的弦和节点
|
||||
var nodes = this.nodes;
|
||||
var links = this.links;
|
||||
var linksCount = links.length;
|
||||
var paths = bundle(links);
|
||||
|
||||
var locateStr = ""; //对文字进行平移和旋转
|
||||
var locateBBox = ""; //对文字的bounding box进行平移和旋转
|
||||
var r = 0;
|
||||
var angle = 0;
|
||||
var xTrans = 0;
|
||||
var yTrans = 0;
|
||||
var anchor; //text-anchor: start or end
|
||||
var rotateStr = "";
|
||||
|
||||
//element data cache
|
||||
var nodeRelatedElements = {};// {key: {targetLink: [], sourceLink: [], targetNode: [], sourceNode: []}}
|
||||
var nodeElements = {}; //{key: Els}
|
||||
var bBoxElements = {}; //{key: Els}
|
||||
|
||||
var i,
|
||||
j,
|
||||
key,
|
||||
textEl,
|
||||
bBox,
|
||||
bBoxNew,
|
||||
tCenterX,
|
||||
tCenterY,
|
||||
bBoxEl,
|
||||
linkEl;
|
||||
|
||||
var mouseoverLink = function () {
|
||||
var current = this;
|
||||
//var color = that.data("color");
|
||||
if (rLinks.preLink) {
|
||||
rLinks.preLink.attr("stroke", that.defaults.color.defaultLineColor)
|
||||
.attr("stroke-width", 1)
|
||||
.attr("stroke-opacity", 0.6);
|
||||
|
||||
}
|
||||
rLinks.preLink = this;
|
||||
|
||||
current.attr("stroke", that.defaults.color.lineHoverColor)
|
||||
.attr("stroke-width", 2)
|
||||
.attr("stroke-opacity", 1.0)
|
||||
.toFront(); //把当前弦移到画布最上层
|
||||
};
|
||||
|
||||
var mouseoverNode = function () {
|
||||
var relatedEl = this.data("relatedElements");
|
||||
//高亮所选节点的文字颜色
|
||||
this.data("relatedNode").attr({"fill": that.defaults.color.nodeHoverColor,
|
||||
"fill-opacity": 1.0, "font-weight": "600"});
|
||||
//将包围盒颜色设为透明
|
||||
this.attr({"fill": that.defaults.color.nodeHoverColor, "fill-opacity": 0.0/*, "font-weight": "600"*/});
|
||||
|
||||
relatedEl.sourceLink.forEach(function (d) { //set green
|
||||
d.attr({"stroke": that.defaults.color.importNodesColor, "stroke-width": 1, "stroke-opacity": 0.9})
|
||||
.toFront();
|
||||
});
|
||||
relatedEl.sourceNode.forEach(function (d) {
|
||||
d.attr({"fill": that.defaults.color.importNodesColor, "font-weight": "600"});
|
||||
});
|
||||
relatedEl.targetLink.forEach(function (d) { //set red
|
||||
d.attr({"stroke": that.defaults.color.exportNodesColor, "stroke-width": 1, "stroke-opacity": 0.9})
|
||||
.toFront();
|
||||
});
|
||||
relatedEl.targetNode.forEach(function (d) {
|
||||
d.attr({"fill": that.defaults.color.exportNodesColor, "font-weight": "600"});
|
||||
});
|
||||
};
|
||||
|
||||
var mouseoutNode = function () {
|
||||
var relatedEl = this.data("relatedElements");
|
||||
this.data("relatedNode").attr({"fill": that.defaults.color.defaultWordColor,
|
||||
"font-weight": "400", "fill-opacity": 1.0});
|
||||
relatedEl.targetLink.forEach(function (d) {
|
||||
d.attr({"stroke": that.defaults.color.defaultLineColor, "stroke-width": 1, "stroke-opacity": 0.6});
|
||||
});
|
||||
relatedEl.targetNode.forEach(function (d) {
|
||||
d.attr({"fill": that.defaults.color.defaultWordColor, "font-weight": "400"});
|
||||
});
|
||||
relatedEl.sourceLink.forEach(function (d) {
|
||||
d.attr({"stroke": that.defaults.color.defaultLineColor, "stroke-width": 1, "stroke-opacity": 0.6});
|
||||
});
|
||||
relatedEl.sourceNode.forEach(function (d) {
|
||||
d.attr({"fill": that.defaults.color.defaultWordColor, "font-weight": "400"});
|
||||
});
|
||||
};
|
||||
|
||||
for (j = 0; j < nodes.length; j++) {
|
||||
//若为叶子节点
|
||||
if (!nodes[j].children) {
|
||||
locateStr = "T" + that.defaults.radius + "," + that.defaults.radius + "R"; //使用大写T、R、S--绝对,not相对
|
||||
|
||||
//半径: add a padding between lines and words
|
||||
r = nodes[j].y + 20;
|
||||
|
||||
//计算旋转角度和水平、竖直方向所需平移的距离
|
||||
angle = (nodes[j].x - 90) * Math.PI / 180;
|
||||
xTrans = r * Math.cos(angle);
|
||||
yTrans = r * Math.sin(angle);
|
||||
|
||||
//计算text-anchor
|
||||
if (nodes[j].x < 180) {
|
||||
anchor = "start";
|
||||
} else {
|
||||
anchor = "end";
|
||||
}
|
||||
|
||||
//计算文字方向是否需要旋转180度
|
||||
if (nodes[j].x < 180) {
|
||||
rotateStr = "";
|
||||
} else {
|
||||
rotateStr = "R180";
|
||||
}
|
||||
|
||||
//计算文字需要如何经过平移和旋转被排列在圆周上
|
||||
locateStr += (nodes[j].x - 90) + rotateStr + "T" + xTrans + "," + yTrans;
|
||||
|
||||
//绘制文字
|
||||
textEl = canvas.text()
|
||||
.attr("font", "11px arial")
|
||||
.data("color", that.defaults.color)
|
||||
.attr("text", nodes[j].key)
|
||||
//.attr("title", nodes[j].size)
|
||||
.transform(locateStr)
|
||||
.attr("text-anchor", anchor)
|
||||
.attr("fill", that.defaults.color.defaultWordColor);
|
||||
|
||||
//获取旋转平移之前文字的bounding box
|
||||
bBox = textEl.getBBox(true);
|
||||
|
||||
//canvas.rect(bBox.x, bBox.y, bBox.width, bBox.height);
|
||||
//获取旋转平移之后文字的bounding box
|
||||
bBoxNew = textEl.getBBox();
|
||||
//adjust vml box center
|
||||
if (Raphael.vml) {
|
||||
//vml's word bbox is not related to text-anchor, always middle;
|
||||
//svg's word bbox is related to text-anchor;
|
||||
bBoxNew.x = bBoxNew.x + bBox.width / 2 * Math.cos(angle);
|
||||
bBoxNew.y = bBoxNew.y + bBox.width / 2 * Math.sin(angle);
|
||||
}
|
||||
//canvas.rect(bBoxNew.x, bBoxNew.y, bBoxNew.width, bBoxNew.height);
|
||||
|
||||
//新旧bounding box的中心坐标变化
|
||||
tCenterX = bBoxNew.x + bBoxNew.width / 2 - bBox.x - bBox.width / 2;
|
||||
tCenterY = bBoxNew.y + bBoxNew.height / 2 - bBox.y - bBox.height / 2;
|
||||
//对bounding box进行平移和旋转
|
||||
locateBBox = "T" + tCenterX + "," + tCenterY + "R" + (nodes[j].x - 90) + rotateStr;
|
||||
|
||||
// 包围盒
|
||||
bBoxEl = canvas.rect(bBox.x, bBox.y, bBox.width, bBox.height)
|
||||
.transform(locateBBox)
|
||||
.data("relatedNode", textEl)
|
||||
.attr({"fill": "#fff", "opacity": 0.01});
|
||||
|
||||
key = nodes[j].key;
|
||||
nodeElements[key] = textEl;
|
||||
bBoxElements[key] = bBoxEl;
|
||||
nodeRelatedElements[key] = {targetLink: [], sourceLink: [], targetNode: [], sourceNode: []};
|
||||
|
||||
rNodes.push(textEl);
|
||||
}
|
||||
}
|
||||
|
||||
//绘制曲线
|
||||
for (i = 0; i < linksCount; i++) {
|
||||
var l = paths[i];
|
||||
|
||||
//对paths数组中的每一项进行计算,由路径节点信息得到坐标值
|
||||
var spline = line(l);
|
||||
var sourceKey = links[i].source.key;
|
||||
var targetKey = links[i].target.key;
|
||||
var tips = "link source: " + sourceKey + "\n"
|
||||
+ "link target: " + targetKey;
|
||||
|
||||
linkEl = canvas.path(spline)
|
||||
//.attr("stroke", that.defaults.defaultLineColor)
|
||||
.attr("stroke-opacity", 0.6)
|
||||
.attr("title", tips)
|
||||
.attr("d", spline)
|
||||
.attr("stroke", that.defaults.color.defaultLineColor)
|
||||
.translate(that.defaults.radius, that.defaults.radius)
|
||||
.mouseover(mouseoverLink);
|
||||
//.mouseout(mouseoutLink);
|
||||
linkEl[0].el = linkEl;
|
||||
|
||||
nodeRelatedElements[sourceKey].targetLink.push(linkEl);
|
||||
nodeRelatedElements[sourceKey].targetNode.push(nodeElements[targetKey]);
|
||||
nodeRelatedElements[targetKey].sourceLink.push(linkEl);
|
||||
nodeRelatedElements[targetKey].sourceNode.push(nodeElements[sourceKey]);
|
||||
rLinks.push(linkEl);
|
||||
}
|
||||
|
||||
$(this.canvas.canvas).mousemove(function (e) {
|
||||
if(!e.target.el && rLinks.preLink){
|
||||
rLinks.preLink.attr("stroke", that.defaults.color.defaultLineColor)
|
||||
.attr("stroke-width", 1)
|
||||
.attr("stroke-opacity", 0.6);
|
||||
rLinks.preLink = undefined;
|
||||
//console.log("a");
|
||||
}
|
||||
});
|
||||
|
||||
//bind text words hover event
|
||||
for (key in bBoxElements) {
|
||||
if (bBoxElements.hasOwnProperty(key)) {
|
||||
bBoxElements[key].data("relatedElements", nodeRelatedElements[key])
|
||||
.mouseover(mouseoverNode)
|
||||
.mouseout(mouseoutNode);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return Bundle;
|
||||
});
|
||||
/*global EventProxy, d3, Raphael, self, packages, $ */
|
||||
|
||||
/*!
|
||||
* Bundle的兼容性定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Bundle', function (require) {
|
||||
var DataV = require('DataV');
|
||||
|
||||
/**
|
||||
* 构造函数,node参数表示在html的哪个容器中绘制该组件
|
||||
* options对象为用户自定义的组件的属性,比如画布大小
|
||||
*/
|
||||
var Bundle = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.type = "Bundle";
|
||||
this.node = this.checkContainer(node);
|
||||
this.json = {};
|
||||
|
||||
// 图的半径
|
||||
this.defaults.diameter = 960;
|
||||
this.defaults.radius = this.defaults.diameter / 2;
|
||||
this.defaults.innerRadius = this.defaults.radius - 120;
|
||||
this.defaults.tension = 0.85;
|
||||
|
||||
this.defaults.color = {
|
||||
defaultLineColor: "#4065AF",
|
||||
defaultWordColor: "#000000",
|
||||
lineHoverColor: "#02B0ED",
|
||||
nodeHoverColor: "#02B0ED",
|
||||
importNodesColor: "#5DA714", //被引用的节点
|
||||
exportNodesColor: "#FE3919" //引用当前节点的节点
|
||||
};
|
||||
|
||||
this.setOptions(options);
|
||||
this.createCanvas();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 设置用户自定义属性
|
||||
*/
|
||||
Bundle.prototype.setOptions = function (options) {
|
||||
if (options) {
|
||||
var prop;
|
||||
for (prop in options) {
|
||||
if (options.hasOwnProperty(prop)) {
|
||||
this.defaults[prop] = options[prop];
|
||||
if (prop === "diameter") {
|
||||
this.defaults.radius = this.defaults.diameter / 2;
|
||||
this.defaults.innerRadius = this.defaults.radius - 120;
|
||||
} else if (prop === "radius") {
|
||||
this.defaults.diameter = this.defaults.radius * 2;
|
||||
this.defaults.innerRadius = this.defaults.radius - 120;
|
||||
} else if (prop === "innerRadius") {
|
||||
this.defaults.radius = this.defaults.innerRadius + 120;
|
||||
this.defaults.diameter = this.defaults.radius * 2;
|
||||
} else if (prop === "width") {
|
||||
this.defaults.diameter = this.defaults.width;
|
||||
this.defaults.radius = this.defaults.diameter / 2;
|
||||
this.defaults.innerRadius = this.defaults.radius - 120;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 对原始数据进行处理
|
||||
* TODO: 改进为获取值时运算
|
||||
*/
|
||||
Bundle.prototype.setSource = function (source) {
|
||||
if (source[0] && source[0] instanceof Array) {
|
||||
// csv or 2d array source
|
||||
if (source[0][0] === "name") {
|
||||
source = source.slice(1); // 从第一行开始,第0行舍去
|
||||
}
|
||||
var nData = [];
|
||||
var imports = [];
|
||||
//var isNode = true;
|
||||
var nodeNum;
|
||||
var that = this;
|
||||
source.forEach(function (d, i) {
|
||||
if (d[0] === "") {
|
||||
throw new Error("name can not be empty(line:" + (i + 1) + ").");
|
||||
}
|
||||
if (d[1] !== "") {
|
||||
imports = d[1].split(" ");
|
||||
}
|
||||
nData[i] = {
|
||||
name: d[0],
|
||||
imports: imports
|
||||
};
|
||||
});
|
||||
this.json = nData;
|
||||
} else {
|
||||
// json source
|
||||
this.json = source;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建画布
|
||||
*/
|
||||
Bundle.prototype.createCanvas = function () {
|
||||
var conf = this.defaults;
|
||||
this.canvas = new Raphael(this.node, conf.diameter, conf.diameter);
|
||||
|
||||
//var c = this.canvas.circle(50, 50, 40);
|
||||
};
|
||||
|
||||
/**
|
||||
* 布局
|
||||
*/
|
||||
Bundle.prototype.layout = function () {
|
||||
var packages = {
|
||||
// Lazily construct the package hierarchy from class names.
|
||||
root: function (classes) {
|
||||
var map = {};
|
||||
function construct(name, data) {
|
||||
var node = map[name], i;
|
||||
if (!node) {
|
||||
node = map[name] = data || {name: name, children: []};
|
||||
if (name.length) {
|
||||
node.parent = construct(name.substring(0, i = name.lastIndexOf(".")));
|
||||
node.parent.children.push(node);
|
||||
node.key = name.substring(i + 1);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
classes.forEach(function (d) {
|
||||
construct(d.name, d);
|
||||
});
|
||||
|
||||
return map[""];
|
||||
},
|
||||
|
||||
// Return a list of imports for the given array of nodes.
|
||||
imports: function (nodes) {
|
||||
var map = {},
|
||||
imports = [];
|
||||
|
||||
// Compute a map from name to node.
|
||||
nodes.forEach(function (d) {
|
||||
map[d.name] = d;
|
||||
});
|
||||
|
||||
// For each import, construct a link from the source to target node.
|
||||
nodes.forEach(function (d) {
|
||||
if (d.imports) {
|
||||
d.imports.forEach(function (i) {imports.push({source: map[d.name], target: map[i]});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return imports;
|
||||
}
|
||||
};
|
||||
|
||||
var cluster = d3.layout.cluster()
|
||||
.size([360, this.defaults.innerRadius]) //.size(角度,半径)
|
||||
.sort(null)
|
||||
.value(function (d) {
|
||||
return d.size;
|
||||
});
|
||||
this.nodes = cluster.nodes(packages.root(this.json));
|
||||
this.links = packages.imports(this.nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* 渲染弦图
|
||||
*/
|
||||
Bundle.prototype.render = function () {
|
||||
this.layout();
|
||||
this.generatePaths();
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成路径
|
||||
*/
|
||||
Bundle.prototype.generatePaths = function (options) {
|
||||
var that = this;
|
||||
|
||||
var canvas = this.canvas;
|
||||
var rNodes = canvas.set();
|
||||
var rLinks = canvas.set();
|
||||
|
||||
var bundle = d3.layout.bundle();
|
||||
|
||||
var line = d3.svg.line.radial()
|
||||
.interpolate("bundle")
|
||||
.tension(this.defaults.tension)
|
||||
.radius(function (d) {
|
||||
return d.y;
|
||||
})
|
||||
.angle(function (d) {
|
||||
return d.x / 180 * Math.PI;
|
||||
});
|
||||
|
||||
//定义图中的弦和节点
|
||||
var nodes = this.nodes;
|
||||
var links = this.links;
|
||||
var linksCount = links.length;
|
||||
var paths = bundle(links);
|
||||
|
||||
var locateStr = ""; //对文字进行平移和旋转
|
||||
var locateBBox = ""; //对文字的bounding box进行平移和旋转
|
||||
var r = 0;
|
||||
var angle = 0;
|
||||
var xTrans = 0;
|
||||
var yTrans = 0;
|
||||
var anchor; //text-anchor: start or end
|
||||
var rotateStr = "";
|
||||
|
||||
//element data cache
|
||||
var nodeRelatedElements = {};// {key: {targetLink: [], sourceLink: [], targetNode: [], sourceNode: []}}
|
||||
var nodeElements = {}; //{key: Els}
|
||||
var bBoxElements = {}; //{key: Els}
|
||||
|
||||
var i,
|
||||
j,
|
||||
key,
|
||||
textEl,
|
||||
bBox,
|
||||
bBoxNew,
|
||||
tCenterX,
|
||||
tCenterY,
|
||||
bBoxEl,
|
||||
linkEl;
|
||||
|
||||
var mouseoverLink = function () {
|
||||
var current = this;
|
||||
//var color = that.data("color");
|
||||
if (rLinks.preLink) {
|
||||
rLinks.preLink.attr("stroke", that.defaults.color.defaultLineColor)
|
||||
.attr("stroke-width", 1)
|
||||
.attr("stroke-opacity", 0.6);
|
||||
|
||||
}
|
||||
rLinks.preLink = this;
|
||||
|
||||
current.attr("stroke", that.defaults.color.lineHoverColor)
|
||||
.attr("stroke-width", 2)
|
||||
.attr("stroke-opacity", 1.0)
|
||||
.toFront(); //把当前弦移到画布最上层
|
||||
};
|
||||
|
||||
var mouseoverNode = function () {
|
||||
var relatedEl = this.data("relatedElements");
|
||||
//高亮所选节点的文字颜色
|
||||
this.data("relatedNode").attr({"fill": that.defaults.color.nodeHoverColor,
|
||||
"fill-opacity": 1.0, "font-weight": "600"});
|
||||
//将包围盒颜色设为透明
|
||||
this.attr({"fill": that.defaults.color.nodeHoverColor, "fill-opacity": 0.0/*, "font-weight": "600"*/});
|
||||
|
||||
relatedEl.sourceLink.forEach(function (d) { //set green
|
||||
d.attr({"stroke": that.defaults.color.importNodesColor, "stroke-width": 1, "stroke-opacity": 0.9})
|
||||
.toFront();
|
||||
});
|
||||
relatedEl.sourceNode.forEach(function (d) {
|
||||
d.attr({"fill": that.defaults.color.importNodesColor, "font-weight": "600"});
|
||||
});
|
||||
relatedEl.targetLink.forEach(function (d) { //set red
|
||||
d.attr({"stroke": that.defaults.color.exportNodesColor, "stroke-width": 1, "stroke-opacity": 0.9})
|
||||
.toFront();
|
||||
});
|
||||
relatedEl.targetNode.forEach(function (d) {
|
||||
d.attr({"fill": that.defaults.color.exportNodesColor, "font-weight": "600"});
|
||||
});
|
||||
};
|
||||
|
||||
var mouseoutNode = function () {
|
||||
var relatedEl = this.data("relatedElements");
|
||||
this.data("relatedNode").attr({"fill": that.defaults.color.defaultWordColor,
|
||||
"font-weight": "400", "fill-opacity": 1.0});
|
||||
relatedEl.targetLink.forEach(function (d) {
|
||||
d.attr({"stroke": that.defaults.color.defaultLineColor, "stroke-width": 1, "stroke-opacity": 0.6});
|
||||
});
|
||||
relatedEl.targetNode.forEach(function (d) {
|
||||
d.attr({"fill": that.defaults.color.defaultWordColor, "font-weight": "400"});
|
||||
});
|
||||
relatedEl.sourceLink.forEach(function (d) {
|
||||
d.attr({"stroke": that.defaults.color.defaultLineColor, "stroke-width": 1, "stroke-opacity": 0.6});
|
||||
});
|
||||
relatedEl.sourceNode.forEach(function (d) {
|
||||
d.attr({"fill": that.defaults.color.defaultWordColor, "font-weight": "400"});
|
||||
});
|
||||
};
|
||||
|
||||
for (j = 0; j < nodes.length; j++) {
|
||||
//若为叶子节点
|
||||
if (!nodes[j].children) {
|
||||
locateStr = "T" + that.defaults.radius + "," + that.defaults.radius + "R"; //使用大写T、R、S--绝对,not相对
|
||||
|
||||
//半径: add a padding between lines and words
|
||||
r = nodes[j].y + 20;
|
||||
|
||||
//计算旋转角度和水平、竖直方向所需平移的距离
|
||||
angle = (nodes[j].x - 90) * Math.PI / 180;
|
||||
xTrans = r * Math.cos(angle);
|
||||
yTrans = r * Math.sin(angle);
|
||||
|
||||
//计算text-anchor
|
||||
if (nodes[j].x < 180) {
|
||||
anchor = "start";
|
||||
} else {
|
||||
anchor = "end";
|
||||
}
|
||||
|
||||
//计算文字方向是否需要旋转180度
|
||||
if (nodes[j].x < 180) {
|
||||
rotateStr = "";
|
||||
} else {
|
||||
rotateStr = "R180";
|
||||
}
|
||||
|
||||
//计算文字需要如何经过平移和旋转被排列在圆周上
|
||||
locateStr += (nodes[j].x - 90) + rotateStr + "T" + xTrans + "," + yTrans;
|
||||
|
||||
//绘制文字
|
||||
textEl = canvas.text()
|
||||
.attr("font", "11px arial")
|
||||
.data("color", that.defaults.color)
|
||||
.attr("text", nodes[j].key)
|
||||
//.attr("title", nodes[j].size)
|
||||
.transform(locateStr)
|
||||
.attr("text-anchor", anchor)
|
||||
.attr("fill", that.defaults.color.defaultWordColor);
|
||||
|
||||
//获取旋转平移之前文字的bounding box
|
||||
bBox = textEl.getBBox(true);
|
||||
|
||||
//canvas.rect(bBox.x, bBox.y, bBox.width, bBox.height);
|
||||
//获取旋转平移之后文字的bounding box
|
||||
bBoxNew = textEl.getBBox();
|
||||
//adjust vml box center
|
||||
if (Raphael.vml) {
|
||||
//vml's word bbox is not related to text-anchor, always middle;
|
||||
//svg's word bbox is related to text-anchor;
|
||||
bBoxNew.x = bBoxNew.x + bBox.width / 2 * Math.cos(angle);
|
||||
bBoxNew.y = bBoxNew.y + bBox.width / 2 * Math.sin(angle);
|
||||
}
|
||||
//canvas.rect(bBoxNew.x, bBoxNew.y, bBoxNew.width, bBoxNew.height);
|
||||
|
||||
//新旧bounding box的中心坐标变化
|
||||
tCenterX = bBoxNew.x + bBoxNew.width / 2 - bBox.x - bBox.width / 2;
|
||||
tCenterY = bBoxNew.y + bBoxNew.height / 2 - bBox.y - bBox.height / 2;
|
||||
//对bounding box进行平移和旋转
|
||||
locateBBox = "T" + tCenterX + "," + tCenterY + "R" + (nodes[j].x - 90) + rotateStr;
|
||||
|
||||
// 包围盒
|
||||
bBoxEl = canvas.rect(bBox.x, bBox.y, bBox.width, bBox.height)
|
||||
.transform(locateBBox)
|
||||
.data("relatedNode", textEl)
|
||||
.attr({"fill": "#fff", "opacity": 0.01});
|
||||
|
||||
key = nodes[j].key;
|
||||
nodeElements[key] = textEl;
|
||||
bBoxElements[key] = bBoxEl;
|
||||
nodeRelatedElements[key] = {targetLink: [], sourceLink: [], targetNode: [], sourceNode: []};
|
||||
|
||||
rNodes.push(textEl);
|
||||
}
|
||||
}
|
||||
|
||||
//绘制曲线
|
||||
for (i = 0; i < linksCount; i++) {
|
||||
var l = paths[i];
|
||||
|
||||
//对paths数组中的每一项进行计算,由路径节点信息得到坐标值
|
||||
var spline = line(l);
|
||||
var sourceKey = links[i].source.key;
|
||||
var targetKey = links[i].target.key;
|
||||
var tips = "link source: " + sourceKey + "\n"
|
||||
+ "link target: " + targetKey;
|
||||
|
||||
linkEl = canvas.path(spline)
|
||||
//.attr("stroke", that.defaults.defaultLineColor)
|
||||
.attr("stroke-opacity", 0.6)
|
||||
.attr("title", tips)
|
||||
.attr("d", spline)
|
||||
.attr("stroke", that.defaults.color.defaultLineColor)
|
||||
.translate(that.defaults.radius, that.defaults.radius)
|
||||
.mouseover(mouseoverLink);
|
||||
//.mouseout(mouseoutLink);
|
||||
linkEl[0].el = linkEl;
|
||||
|
||||
nodeRelatedElements[sourceKey].targetLink.push(linkEl);
|
||||
nodeRelatedElements[sourceKey].targetNode.push(nodeElements[targetKey]);
|
||||
nodeRelatedElements[targetKey].sourceLink.push(linkEl);
|
||||
nodeRelatedElements[targetKey].sourceNode.push(nodeElements[sourceKey]);
|
||||
rLinks.push(linkEl);
|
||||
}
|
||||
|
||||
$(this.canvas.canvas).mousemove(function (e) {
|
||||
if(!e.target.el && rLinks.preLink){
|
||||
rLinks.preLink.attr("stroke", that.defaults.color.defaultLineColor)
|
||||
.attr("stroke-width", 1)
|
||||
.attr("stroke-opacity", 0.6);
|
||||
rLinks.preLink = undefined;
|
||||
//console.log("a");
|
||||
}
|
||||
});
|
||||
|
||||
//bind text words hover event
|
||||
for (key in bBoxElements) {
|
||||
if (bBoxElements.hasOwnProperty(key)) {
|
||||
bBoxElements[key].data("relatedElements", nodeRelatedElements[key])
|
||||
.mouseover(mouseoverNode)
|
||||
.mouseout(mouseoutNode);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return Bundle;
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,481 +1,481 @@
|
||||
/*global Raphael, d3, $, define */
|
||||
/*!
|
||||
* Chord的兼容性定义
|
||||
*/
|
||||
;(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];
|
||||
});
|
||||
}
|
||||
})('Chord', function (require) {
|
||||
var DataV = require('DataV');
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param {Object} node 表示在html的哪个容器中绘制该组件
|
||||
* @param {Object} options 为用户自定义的组件的属性,比如画布大小
|
||||
*/
|
||||
var Chord = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.type = "Chord";
|
||||
this.node = this.checkContainer(node);
|
||||
this.matrix = [];
|
||||
this.groupNames = []; //数组:记录每个group的名字
|
||||
|
||||
//图的大小设置
|
||||
this.defaults.legend = true;
|
||||
this.defaults.width = 800;
|
||||
this.defaults.height = 800;
|
||||
|
||||
//设置用户指定的属性
|
||||
this.setOptions(options);
|
||||
|
||||
this.legendArea = [20, (this.defaults.height - 20 - 220), 200, 220];
|
||||
if (this.defaults.legend) {
|
||||
this.xOffset = this.legendArea[2];
|
||||
} else {
|
||||
this.xOffset = 0;
|
||||
}
|
||||
|
||||
this.defaults.innerRadius = Math.min((this.defaults.width - this.xOffset), this.defaults.height) * 0.38;
|
||||
this.defaults.outerRadius = this.defaults.innerRadius * 1.10;
|
||||
//创建画布
|
||||
this.createCanvas();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 创建画布
|
||||
*/
|
||||
Chord.prototype.createCanvas = function () {
|
||||
this.canvas = new Raphael(this.node, this.defaults.width, this.defaults.height);
|
||||
canvasStyle = this.node.style;
|
||||
canvasStyle.position = "relative";
|
||||
this.floatTag = DataV.FloatTag()(this.node);
|
||||
this.floatTag.css({
|
||||
"visibility": "hidden"
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取颜色
|
||||
* @param {Number} i 元素类别编号
|
||||
* @return {String} 返回颜色值
|
||||
*/
|
||||
Chord.prototype.getColor = function (i) {
|
||||
var color = DataV.getColor();
|
||||
return color[i % color.length][0];
|
||||
};
|
||||
|
||||
/**
|
||||
* 绘制弦图
|
||||
*/
|
||||
Chord.prototype.render = function () {
|
||||
this.layout();
|
||||
if (this.defaults.legend) {
|
||||
this.legend();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 绘制图例
|
||||
*/
|
||||
Chord.prototype.legend = function () {
|
||||
var that = this;
|
||||
var paper = this.canvas;
|
||||
var legendArea = this.legendArea;
|
||||
var rectBn = paper.set();
|
||||
this.underBn = [];
|
||||
var underBn = this.underBn;
|
||||
for (i = 0; i <= this.groupNum; i++) {
|
||||
//底框
|
||||
underBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
|
||||
"fill": "#ebebeb",
|
||||
"stroke": "none"
|
||||
//"r": 3
|
||||
}).hide());
|
||||
//色框
|
||||
paper.rect(legendArea[0] + 10 + 3, legendArea[1] + 10 + (20 + 3) * i + 6, 16, 8).attr({
|
||||
"fill": this.getColor(i),
|
||||
"stroke": "none"
|
||||
});
|
||||
//文字
|
||||
paper.text(legendArea[0] + 10 + 3 + 16 + 8, legendArea[1] + 10 + (20 + 3) * i + 10, this.groupNames[i]).attr({
|
||||
"fill": "black",
|
||||
"fill-opacity": 1,
|
||||
"font-family": "Verdana",
|
||||
"font-size": 12
|
||||
}).attr({
|
||||
"text-anchor": "start"
|
||||
});
|
||||
//选框
|
||||
rectBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
|
||||
"fill": "white",
|
||||
"fill-opacity": 0,
|
||||
"stroke": "none"
|
||||
//"r": 3
|
||||
})).data("clicked", 0);
|
||||
}
|
||||
rectBn.forEach(function (d, i) {
|
||||
d.mouseover(function () {
|
||||
if (d.data("clicked") === 0) {
|
||||
underBn[i].attr('opacity', 0.5);
|
||||
underBn[i].show();
|
||||
}
|
||||
}).mouseout(function () {
|
||||
if (d.data("clicked") === 0) {
|
||||
underBn[i].hide();
|
||||
}
|
||||
});
|
||||
d.click(function () {
|
||||
for (j = 0; j < underBn.length; j++) {
|
||||
if (j === i) {
|
||||
underBn[j].show();
|
||||
} else {
|
||||
underBn[j].hide();
|
||||
}
|
||||
}
|
||||
rectBn.forEach(function (eachBn) {
|
||||
if (eachBn !== d) {
|
||||
eachBn.data("clicked", 0);
|
||||
}
|
||||
});
|
||||
if (d.data("clicked") === 0) {
|
||||
underBn[i].attr('opacity', 1);
|
||||
underBn[i].show();
|
||||
that.chordGroups.forEach(function (d) {
|
||||
if (d.data('source') !== i && d.data('target') !== i) {
|
||||
d.attr({
|
||||
'fill-opacity': 0.1
|
||||
});
|
||||
} else {
|
||||
d.attr({
|
||||
'fill-opacity': 0.6
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
d.data("clicked", 1);
|
||||
} else if (d.data("clicked") === 1) {
|
||||
underBn[i].hide();
|
||||
d.data("clicked", 0);
|
||||
that.chordGroups.forEach(function (d) {
|
||||
d.attr({
|
||||
'fill-opacity': 0.6
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*对原始数据进行处理
|
||||
* @param {Array} table 将要被绘制成饼图的二维表数据
|
||||
*/
|
||||
Chord.prototype.setSource = function (table) {
|
||||
if (table[0][0] !== null) {
|
||||
var t;
|
||||
for (t = 0; t < table[0].length; t++) {
|
||||
this.groupNames[t] = table[0][t];
|
||||
}
|
||||
table = table.slice(1); // 从第一行开始,第0行舍去
|
||||
}
|
||||
|
||||
var group = [];
|
||||
this.groupNum = table[0].length;
|
||||
var groupNum = this.groupNum;
|
||||
var that = this;
|
||||
table.forEach(function (d, i) {
|
||||
if (d.length !== groupNum || table.length !== groupNum) {
|
||||
throw new Error("The source data should be an n * n matrix!");
|
||||
}
|
||||
|
||||
group[i] = [];
|
||||
var s;
|
||||
for (s = 0; s < groupNum; s++) {
|
||||
group[i][s] = Number(d[s]);
|
||||
}
|
||||
});
|
||||
|
||||
var r;
|
||||
for (r = 0; r < groupNum; r++) {
|
||||
that.matrix[r] = group[r];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*创建chord布局
|
||||
*/
|
||||
Chord.prototype.layout = function () {
|
||||
var floatTag = this.floatTag;
|
||||
var that = this;
|
||||
|
||||
that.canvas.clear();
|
||||
/*var see = [
|
||||
[11975, 5871, 8916, 2868],
|
||||
[1951, 10048, 2060, 6171],
|
||||
[8010, 16145, 8090, 8045],
|
||||
[1013, 990, 940, 6907]
|
||||
];*/
|
||||
|
||||
var chordLayout = d3.layout.chord().padding(0.05) //chord segments之间的padding间隔
|
||||
.sortSubgroups(d3.descending) //chord segments细分后的排序规则
|
||||
.matrix(that.matrix);
|
||||
|
||||
/*var fillColor = d3.scale.ordinal()
|
||||
.domain(d3.range(4))
|
||||
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);*/
|
||||
|
||||
//groups数组:获取每个组的起始角度、数值、索引等属性
|
||||
var groups = chordLayout.groups();
|
||||
|
||||
//由内外半径、起始角度计算路径字符串
|
||||
var pathCalc = d3.svg.arc().innerRadius(that.defaults.innerRadius).outerRadius(that.defaults.outerRadius).startAngle(function (d) {
|
||||
return d.startAngle;
|
||||
}).endAngle(function (d) {
|
||||
return d.endAngle;
|
||||
});
|
||||
|
||||
var chords = chordLayout.chords();
|
||||
|
||||
//计算弦的路径曲线
|
||||
var chordCalc = d3.svg.chord().radius(that.defaults.innerRadius);
|
||||
|
||||
//Raphael: Paper.path()
|
||||
var donutEle;
|
||||
//获取每个环形的字符串表示
|
||||
var spline;
|
||||
//表示每条弦的element
|
||||
var chordEle;
|
||||
//每条弦的字符串表示
|
||||
var belt;
|
||||
|
||||
var num; //每个group分割小格数
|
||||
var unitAngle; //每个group所占的角度
|
||||
var angle;
|
||||
var radian;
|
||||
var tickLine;
|
||||
var tickStr; //每个tick的路径
|
||||
var xTrans, yTrans;
|
||||
var aX, aY, bX, bY; //每个tick起始端点的坐标
|
||||
var anchor;
|
||||
var rotateStr;
|
||||
var wordStr;
|
||||
var word;
|
||||
var textEl;
|
||||
var wXTrans, wYTrans;
|
||||
var tips;
|
||||
var minValue = 1000;
|
||||
that.chordGroups = that.canvas.set();
|
||||
that.donutGroups = that.canvas.set();
|
||||
|
||||
$(this.node).append(this.floatTag);
|
||||
|
||||
//计算某条弦被赋值为target或source的颜色
|
||||
var colorCalc = function (index) {
|
||||
var i = chords[index].target.value > chords[index].source.value ? chords[index].target.index : chords[index].source.index;
|
||||
return i;
|
||||
};
|
||||
|
||||
//添加透明效果
|
||||
|
||||
var mouseOverDonut = function () {
|
||||
floatTag.html('<div style = "text-align: center;margin:auto;color:'
|
||||
//+ jqNode.color
|
||||
+
|
||||
"#ffffff" + '">' + this.data('text') + '</div>');
|
||||
floatTag.css({
|
||||
"visibility": "visible"
|
||||
});
|
||||
that.underBn.forEach(function (d) {
|
||||
d.hide();
|
||||
});
|
||||
index = this.data("donutIndex");
|
||||
that.chordGroups.forEach(function (d) {
|
||||
if (d.data('source') !== index && d.data('target') !== index) {
|
||||
d.attr({
|
||||
'fill-opacity': 0.1
|
||||
});
|
||||
} else {
|
||||
d.attr({
|
||||
'fill-opacity': 0.6
|
||||
});
|
||||
}
|
||||
});
|
||||
//fade(this.data("donutIndex"), 0.2);
|
||||
that.underBn[index].attr('opacity', 0.5).show();
|
||||
|
||||
};
|
||||
|
||||
var mouseOutDonut = function () {
|
||||
floatTag.css({
|
||||
"visibility": "hidden"
|
||||
});
|
||||
index = this.data("donutIndex");
|
||||
that.chordGroups.forEach(function (d) {
|
||||
if (d.data('source') !== index && d.data('target') !== index) {
|
||||
d.attr({
|
||||
'fill-opacity': 0.6
|
||||
});
|
||||
}
|
||||
});
|
||||
//fade(this.data("donutIndex"), 0.6);
|
||||
that.underBn[index].hide();
|
||||
};
|
||||
|
||||
var mouseoverChord = function () {
|
||||
floatTag.html('<div style="text-align: center;margin:auto;color:#ffffff">' + this.data('text') + '</div>');
|
||||
floatTag.css({
|
||||
"visibility": "visible"
|
||||
});
|
||||
that.underBn.forEach(function (d) {
|
||||
d.hide();
|
||||
});
|
||||
that.chordGroups.forEach(function (d) {
|
||||
d.attr("fill-opacity", 0.1);
|
||||
});
|
||||
if (navigator.appName !== "Microsoft Internet Explorer") {
|
||||
this.toFront(); //把当前弦移到画布最上层
|
||||
}
|
||||
this.attr("fill-opacity", 0.7);
|
||||
that.underBn[this.data('source')].attr('opacity', 0.5).show();
|
||||
};
|
||||
|
||||
var mouseoutChord = function () {
|
||||
floatTag.css({
|
||||
"visibility": "hidden"
|
||||
});
|
||||
//alert("***");
|
||||
that.chordGroups.forEach(function (d) {
|
||||
d.attr("fill-opacity", 0.6);
|
||||
});
|
||||
//this.attr("fill-opacity", 0.6);
|
||||
that.underBn[this.data('source')].hide();
|
||||
};
|
||||
|
||||
//画弦*********************************************************
|
||||
var t;
|
||||
for (t = 0; t <= chords.length - 1; t++) {
|
||||
//alert(chords.length);
|
||||
belt = chordCalc(chords[t]);
|
||||
//hover到弦上时的效果
|
||||
tips = that.groupNames[chords[t].source.index] + " to " + that.groupNames[chords[t].target.index] + ": " + that.matrix[chords[t].source.index][chords[t].target.index] + "," + that.groupNames[chords[t].target.index] + " to " + that.groupNames[chords[t].source.index] + ": " + that.matrix[chords[t].target.index][chords[t].source.index];
|
||||
|
||||
chordEle = that.canvas.path(belt).
|
||||
translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, that.defaults.height / 2).attr({
|
||||
"path": belt,
|
||||
"fill": that.getColor(colorCalc(t)),
|
||||
"fill-opacity": 0.6,
|
||||
"stroke": "#d6d6d6",
|
||||
"stroke-opacity": 0.1
|
||||
}).hover(mouseoverChord, mouseoutChord).data("source", chords[t].source.index).data("target", chords[t].target.index);
|
||||
//.attr("fill", fillColor(chords[t].target.index))
|
||||
chordEle.data('text', tips);
|
||||
that.chordGroups.push(chordEle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//画圆弧*********************************************************
|
||||
var i, r;
|
||||
var donutName;
|
||||
var nameStr;
|
||||
var nameX, nameY;
|
||||
var ro, a;
|
||||
var sum = 0;
|
||||
for (r = 0; r <= groups.length - 1; r++) {
|
||||
sum += groups[r].value;
|
||||
}
|
||||
|
||||
for (i = 0; i <= groups.length - 1; i++) {
|
||||
//画外圈的pie图**************************************
|
||||
//计算每个group的path
|
||||
spline = pathCalc(groups[i]);
|
||||
tips = that.groupNames[i] + ": " + Math.round(groups[i].value) + " " + (groups[i].value * 100 / sum).toFixed(2) + "%";
|
||||
|
||||
donutEle = that.canvas.path(spline).translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, that.defaults.height / 2).data("donutIndex", i).attr({
|
||||
"path": spline,
|
||||
"fill": that.getColor(i),
|
||||
"stroke": that.getColor(i)
|
||||
}).mouseover(mouseOverDonut).mouseout(mouseOutDonut);
|
||||
donutEle.data('text', tips);
|
||||
that.donutGroups.push(donutEle);
|
||||
|
||||
//每个donut上显示名称
|
||||
ro = groups[i].startAngle * 180 / Math.PI - 86 + 90;
|
||||
a = (groups[i].startAngle * 180 / Math.PI - 86) * Math.PI / 180;
|
||||
nameX = ((that.defaults.outerRadius - that.defaults.innerRadius) / 2 + that.defaults.innerRadius) * Math.cos(a);
|
||||
nameY = ((that.defaults.outerRadius - that.defaults.innerRadius) / 2 + that.defaults.innerRadius) * Math.sin(a);
|
||||
nameStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "R" + ro + "T" + nameX + "," + nameY;
|
||||
|
||||
if ((groups[i].endAngle - groups[i].startAngle) * 180 / Math.PI > 10) {
|
||||
donutName = that.canvas.text().attr("font", "12px Verdana").attr("text", that.groupNames[i]).transform(nameStr);
|
||||
}
|
||||
|
||||
//画刻度和刻度值**************************************
|
||||
num = groups[i].value / 5000;
|
||||
//最细分的每个小格代表的数值大小
|
||||
unitAngle = (groups[i].endAngle - groups[i].startAngle) * 180 / Math.PI / num;
|
||||
|
||||
var j;
|
||||
for (j = 0; j <= num; j++) {
|
||||
//计算旋转角度和水平、竖直方向所需平移的距离
|
||||
radian = ((groups[i].startAngle * 180 / Math.PI - 90) + j * unitAngle);
|
||||
angle = radian * Math.PI / 180;
|
||||
xTrans = that.defaults.outerRadius * Math.cos(angle);
|
||||
yTrans = that.defaults.outerRadius * Math.sin(angle);
|
||||
|
||||
tickStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "T" + xTrans + "," + yTrans;
|
||||
|
||||
//刻度线的起点终点坐标
|
||||
aX = ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + xTrans;
|
||||
aY = that.defaults.height / 2 + yTrans;
|
||||
bX = ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + (that.defaults.outerRadius + 6) * Math.cos(angle);
|
||||
bY = that.defaults.height / 2 + (that.defaults.outerRadius + 6) * Math.sin(angle);
|
||||
|
||||
tickLine = "M" + aX + "," + aY + "L" + bX + "," + bY;
|
||||
that.canvas.path(tickLine).attr({
|
||||
'stroke': "#929292",
|
||||
"stroke-width": '1px'
|
||||
}); //绘制刻度
|
||||
|
||||
//每隔五个刻度,绘制一次文字
|
||||
if (j % 2 === 0) {
|
||||
//计算text-anchor
|
||||
if (radian + 90 < 180) {
|
||||
anchor = "start";
|
||||
} else {
|
||||
anchor = "end";
|
||||
}
|
||||
|
||||
//计算文字方向是否需要旋转180度
|
||||
if (radian + 90 < 180) {
|
||||
rotateStr = null;
|
||||
} else {
|
||||
rotateStr = "R180";
|
||||
}
|
||||
|
||||
wXTrans = (that.defaults.outerRadius + 10) * Math.cos(angle);
|
||||
wYTrans = (that.defaults.outerRadius + 10) * Math.sin(angle);
|
||||
|
||||
word = j % 2 ? "" : Math.round(((groups[i].value / num) * j) / 1000);
|
||||
|
||||
wordStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "R" + radian
|
||||
/*(groups[i].startAngle * 180 / Math.PI - 90)*/ + rotateStr + "T" + wXTrans + "," + wYTrans;
|
||||
|
||||
//绘制文字
|
||||
textEl = that.canvas.text(0, 0, word).attr("font", "12px Verdana").transform(wordStr).attr("text-anchor", anchor).attr('fill', "#929292");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*this.canvas.text().attr("font", "12px arial").translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, this.defaults.height).attr("text", "The unit of the scale on the periphery is 1000. \n 刻度值的单位为1000。");
|
||||
*/
|
||||
};
|
||||
|
||||
return Chord;
|
||||
/*global Raphael, d3, $, define */
|
||||
/*!
|
||||
* Chord的兼容性定义
|
||||
*/
|
||||
;(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];
|
||||
});
|
||||
}
|
||||
})('Chord', function (require) {
|
||||
var DataV = require('DataV');
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param {Object} node 表示在html的哪个容器中绘制该组件
|
||||
* @param {Object} options 为用户自定义的组件的属性,比如画布大小
|
||||
*/
|
||||
var Chord = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.type = "Chord";
|
||||
this.node = this.checkContainer(node);
|
||||
this.matrix = [];
|
||||
this.groupNames = []; //数组:记录每个group的名字
|
||||
|
||||
//图的大小设置
|
||||
this.defaults.legend = true;
|
||||
this.defaults.width = 800;
|
||||
this.defaults.height = 800;
|
||||
|
||||
//设置用户指定的属性
|
||||
this.setOptions(options);
|
||||
|
||||
this.legendArea = [20, (this.defaults.height - 20 - 220), 200, 220];
|
||||
if (this.defaults.legend) {
|
||||
this.xOffset = this.legendArea[2];
|
||||
} else {
|
||||
this.xOffset = 0;
|
||||
}
|
||||
|
||||
this.defaults.innerRadius = Math.min((this.defaults.width - this.xOffset), this.defaults.height) * 0.38;
|
||||
this.defaults.outerRadius = this.defaults.innerRadius * 1.10;
|
||||
//创建画布
|
||||
this.createCanvas();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 创建画布
|
||||
*/
|
||||
Chord.prototype.createCanvas = function () {
|
||||
this.canvas = new Raphael(this.node, this.defaults.width, this.defaults.height);
|
||||
canvasStyle = this.node.style;
|
||||
canvasStyle.position = "relative";
|
||||
this.floatTag = DataV.FloatTag()(this.node);
|
||||
this.floatTag.css({
|
||||
"visibility": "hidden"
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取颜色
|
||||
* @param {Number} i 元素类别编号
|
||||
* @return {String} 返回颜色值
|
||||
*/
|
||||
Chord.prototype.getColor = function (i) {
|
||||
var color = DataV.getColor();
|
||||
return color[i % color.length][0];
|
||||
};
|
||||
|
||||
/**
|
||||
* 绘制弦图
|
||||
*/
|
||||
Chord.prototype.render = function () {
|
||||
this.layout();
|
||||
if (this.defaults.legend) {
|
||||
this.legend();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 绘制图例
|
||||
*/
|
||||
Chord.prototype.legend = function () {
|
||||
var that = this;
|
||||
var paper = this.canvas;
|
||||
var legendArea = this.legendArea;
|
||||
var rectBn = paper.set();
|
||||
this.underBn = [];
|
||||
var underBn = this.underBn;
|
||||
for (i = 0; i <= this.groupNum; i++) {
|
||||
//底框
|
||||
underBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
|
||||
"fill": "#ebebeb",
|
||||
"stroke": "none"
|
||||
//"r": 3
|
||||
}).hide());
|
||||
//色框
|
||||
paper.rect(legendArea[0] + 10 + 3, legendArea[1] + 10 + (20 + 3) * i + 6, 16, 8).attr({
|
||||
"fill": this.getColor(i),
|
||||
"stroke": "none"
|
||||
});
|
||||
//文字
|
||||
paper.text(legendArea[0] + 10 + 3 + 16 + 8, legendArea[1] + 10 + (20 + 3) * i + 10, this.groupNames[i]).attr({
|
||||
"fill": "black",
|
||||
"fill-opacity": 1,
|
||||
"font-family": "Verdana",
|
||||
"font-size": 12
|
||||
}).attr({
|
||||
"text-anchor": "start"
|
||||
});
|
||||
//选框
|
||||
rectBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
|
||||
"fill": "white",
|
||||
"fill-opacity": 0,
|
||||
"stroke": "none"
|
||||
//"r": 3
|
||||
})).data("clicked", 0);
|
||||
}
|
||||
rectBn.forEach(function (d, i) {
|
||||
d.mouseover(function () {
|
||||
if (d.data("clicked") === 0) {
|
||||
underBn[i].attr('opacity', 0.5);
|
||||
underBn[i].show();
|
||||
}
|
||||
}).mouseout(function () {
|
||||
if (d.data("clicked") === 0) {
|
||||
underBn[i].hide();
|
||||
}
|
||||
});
|
||||
d.click(function () {
|
||||
for (j = 0; j < underBn.length; j++) {
|
||||
if (j === i) {
|
||||
underBn[j].show();
|
||||
} else {
|
||||
underBn[j].hide();
|
||||
}
|
||||
}
|
||||
rectBn.forEach(function (eachBn) {
|
||||
if (eachBn !== d) {
|
||||
eachBn.data("clicked", 0);
|
||||
}
|
||||
});
|
||||
if (d.data("clicked") === 0) {
|
||||
underBn[i].attr('opacity', 1);
|
||||
underBn[i].show();
|
||||
that.chordGroups.forEach(function (d) {
|
||||
if (d.data('source') !== i && d.data('target') !== i) {
|
||||
d.attr({
|
||||
'fill-opacity': 0.1
|
||||
});
|
||||
} else {
|
||||
d.attr({
|
||||
'fill-opacity': 0.6
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
d.data("clicked", 1);
|
||||
} else if (d.data("clicked") === 1) {
|
||||
underBn[i].hide();
|
||||
d.data("clicked", 0);
|
||||
that.chordGroups.forEach(function (d) {
|
||||
d.attr({
|
||||
'fill-opacity': 0.6
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*对原始数据进行处理
|
||||
* @param {Array} table 将要被绘制成饼图的二维表数据
|
||||
*/
|
||||
Chord.prototype.setSource = function (table) {
|
||||
if (table[0][0] !== null) {
|
||||
var t;
|
||||
for (t = 0; t < table[0].length; t++) {
|
||||
this.groupNames[t] = table[0][t];
|
||||
}
|
||||
table = table.slice(1); // 从第一行开始,第0行舍去
|
||||
}
|
||||
|
||||
var group = [];
|
||||
this.groupNum = table[0].length;
|
||||
var groupNum = this.groupNum;
|
||||
var that = this;
|
||||
table.forEach(function (d, i) {
|
||||
if (d.length !== groupNum || table.length !== groupNum) {
|
||||
throw new Error("The source data should be an n * n matrix!");
|
||||
}
|
||||
|
||||
group[i] = [];
|
||||
var s;
|
||||
for (s = 0; s < groupNum; s++) {
|
||||
group[i][s] = Number(d[s]);
|
||||
}
|
||||
});
|
||||
|
||||
var r;
|
||||
for (r = 0; r < groupNum; r++) {
|
||||
that.matrix[r] = group[r];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*创建chord布局
|
||||
*/
|
||||
Chord.prototype.layout = function () {
|
||||
var floatTag = this.floatTag;
|
||||
var that = this;
|
||||
|
||||
that.canvas.clear();
|
||||
/*var see = [
|
||||
[11975, 5871, 8916, 2868],
|
||||
[1951, 10048, 2060, 6171],
|
||||
[8010, 16145, 8090, 8045],
|
||||
[1013, 990, 940, 6907]
|
||||
];*/
|
||||
|
||||
var chordLayout = d3.layout.chord().padding(0.05) //chord segments之间的padding间隔
|
||||
.sortSubgroups(d3.descending) //chord segments细分后的排序规则
|
||||
.matrix(that.matrix);
|
||||
|
||||
/*var fillColor = d3.scale.ordinal()
|
||||
.domain(d3.range(4))
|
||||
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);*/
|
||||
|
||||
//groups数组:获取每个组的起始角度、数值、索引等属性
|
||||
var groups = chordLayout.groups();
|
||||
|
||||
//由内外半径、起始角度计算路径字符串
|
||||
var pathCalc = d3.svg.arc().innerRadius(that.defaults.innerRadius).outerRadius(that.defaults.outerRadius).startAngle(function (d) {
|
||||
return d.startAngle;
|
||||
}).endAngle(function (d) {
|
||||
return d.endAngle;
|
||||
});
|
||||
|
||||
var chords = chordLayout.chords();
|
||||
|
||||
//计算弦的路径曲线
|
||||
var chordCalc = d3.svg.chord().radius(that.defaults.innerRadius);
|
||||
|
||||
//Raphael: Paper.path()
|
||||
var donutEle;
|
||||
//获取每个环形的字符串表示
|
||||
var spline;
|
||||
//表示每条弦的element
|
||||
var chordEle;
|
||||
//每条弦的字符串表示
|
||||
var belt;
|
||||
|
||||
var num; //每个group分割小格数
|
||||
var unitAngle; //每个group所占的角度
|
||||
var angle;
|
||||
var radian;
|
||||
var tickLine;
|
||||
var tickStr; //每个tick的路径
|
||||
var xTrans, yTrans;
|
||||
var aX, aY, bX, bY; //每个tick起始端点的坐标
|
||||
var anchor;
|
||||
var rotateStr;
|
||||
var wordStr;
|
||||
var word;
|
||||
var textEl;
|
||||
var wXTrans, wYTrans;
|
||||
var tips;
|
||||
var minValue = 1000;
|
||||
that.chordGroups = that.canvas.set();
|
||||
that.donutGroups = that.canvas.set();
|
||||
|
||||
$(this.node).append(this.floatTag);
|
||||
|
||||
//计算某条弦被赋值为target或source的颜色
|
||||
var colorCalc = function (index) {
|
||||
var i = chords[index].target.value > chords[index].source.value ? chords[index].target.index : chords[index].source.index;
|
||||
return i;
|
||||
};
|
||||
|
||||
//添加透明效果
|
||||
|
||||
var mouseOverDonut = function () {
|
||||
floatTag.html('<div style = "text-align: center;margin:auto;color:'
|
||||
//+ jqNode.color
|
||||
+
|
||||
"#ffffff" + '">' + this.data('text') + '</div>');
|
||||
floatTag.css({
|
||||
"visibility": "visible"
|
||||
});
|
||||
that.underBn.forEach(function (d) {
|
||||
d.hide();
|
||||
});
|
||||
index = this.data("donutIndex");
|
||||
that.chordGroups.forEach(function (d) {
|
||||
if (d.data('source') !== index && d.data('target') !== index) {
|
||||
d.attr({
|
||||
'fill-opacity': 0.1
|
||||
});
|
||||
} else {
|
||||
d.attr({
|
||||
'fill-opacity': 0.6
|
||||
});
|
||||
}
|
||||
});
|
||||
//fade(this.data("donutIndex"), 0.2);
|
||||
that.underBn[index].attr('opacity', 0.5).show();
|
||||
|
||||
};
|
||||
|
||||
var mouseOutDonut = function () {
|
||||
floatTag.css({
|
||||
"visibility": "hidden"
|
||||
});
|
||||
index = this.data("donutIndex");
|
||||
that.chordGroups.forEach(function (d) {
|
||||
if (d.data('source') !== index && d.data('target') !== index) {
|
||||
d.attr({
|
||||
'fill-opacity': 0.6
|
||||
});
|
||||
}
|
||||
});
|
||||
//fade(this.data("donutIndex"), 0.6);
|
||||
that.underBn[index].hide();
|
||||
};
|
||||
|
||||
var mouseoverChord = function () {
|
||||
floatTag.html('<div style="text-align: center;margin:auto;color:#ffffff">' + this.data('text') + '</div>');
|
||||
floatTag.css({
|
||||
"visibility": "visible"
|
||||
});
|
||||
that.underBn.forEach(function (d) {
|
||||
d.hide();
|
||||
});
|
||||
that.chordGroups.forEach(function (d) {
|
||||
d.attr("fill-opacity", 0.1);
|
||||
});
|
||||
if (navigator.appName !== "Microsoft Internet Explorer") {
|
||||
this.toFront(); //把当前弦移到画布最上层
|
||||
}
|
||||
this.attr("fill-opacity", 0.7);
|
||||
that.underBn[this.data('source')].attr('opacity', 0.5).show();
|
||||
};
|
||||
|
||||
var mouseoutChord = function () {
|
||||
floatTag.css({
|
||||
"visibility": "hidden"
|
||||
});
|
||||
//alert("***");
|
||||
that.chordGroups.forEach(function (d) {
|
||||
d.attr("fill-opacity", 0.6);
|
||||
});
|
||||
//this.attr("fill-opacity", 0.6);
|
||||
that.underBn[this.data('source')].hide();
|
||||
};
|
||||
|
||||
//画弦*********************************************************
|
||||
var t;
|
||||
for (t = 0; t <= chords.length - 1; t++) {
|
||||
//alert(chords.length);
|
||||
belt = chordCalc(chords[t]);
|
||||
//hover到弦上时的效果
|
||||
tips = that.groupNames[chords[t].source.index] + " to " + that.groupNames[chords[t].target.index] + ": " + that.matrix[chords[t].source.index][chords[t].target.index] + "," + that.groupNames[chords[t].target.index] + " to " + that.groupNames[chords[t].source.index] + ": " + that.matrix[chords[t].target.index][chords[t].source.index];
|
||||
|
||||
chordEle = that.canvas.path(belt).
|
||||
translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, that.defaults.height / 2).attr({
|
||||
"path": belt,
|
||||
"fill": that.getColor(colorCalc(t)),
|
||||
"fill-opacity": 0.6,
|
||||
"stroke": "#d6d6d6",
|
||||
"stroke-opacity": 0.1
|
||||
}).hover(mouseoverChord, mouseoutChord).data("source", chords[t].source.index).data("target", chords[t].target.index);
|
||||
//.attr("fill", fillColor(chords[t].target.index))
|
||||
chordEle.data('text', tips);
|
||||
that.chordGroups.push(chordEle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//画圆弧*********************************************************
|
||||
var i, r;
|
||||
var donutName;
|
||||
var nameStr;
|
||||
var nameX, nameY;
|
||||
var ro, a;
|
||||
var sum = 0;
|
||||
for (r = 0; r <= groups.length - 1; r++) {
|
||||
sum += groups[r].value;
|
||||
}
|
||||
|
||||
for (i = 0; i <= groups.length - 1; i++) {
|
||||
//画外圈的pie图**************************************
|
||||
//计算每个group的path
|
||||
spline = pathCalc(groups[i]);
|
||||
tips = that.groupNames[i] + ": " + Math.round(groups[i].value) + " " + (groups[i].value * 100 / sum).toFixed(2) + "%";
|
||||
|
||||
donutEle = that.canvas.path(spline).translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, that.defaults.height / 2).data("donutIndex", i).attr({
|
||||
"path": spline,
|
||||
"fill": that.getColor(i),
|
||||
"stroke": that.getColor(i)
|
||||
}).mouseover(mouseOverDonut).mouseout(mouseOutDonut);
|
||||
donutEle.data('text', tips);
|
||||
that.donutGroups.push(donutEle);
|
||||
|
||||
//每个donut上显示名称
|
||||
ro = groups[i].startAngle * 180 / Math.PI - 86 + 90;
|
||||
a = (groups[i].startAngle * 180 / Math.PI - 86) * Math.PI / 180;
|
||||
nameX = ((that.defaults.outerRadius - that.defaults.innerRadius) / 2 + that.defaults.innerRadius) * Math.cos(a);
|
||||
nameY = ((that.defaults.outerRadius - that.defaults.innerRadius) / 2 + that.defaults.innerRadius) * Math.sin(a);
|
||||
nameStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "R" + ro + "T" + nameX + "," + nameY;
|
||||
|
||||
if ((groups[i].endAngle - groups[i].startAngle) * 180 / Math.PI > 10) {
|
||||
donutName = that.canvas.text().attr("font", "12px Verdana").attr("text", that.groupNames[i]).transform(nameStr);
|
||||
}
|
||||
|
||||
//画刻度和刻度值**************************************
|
||||
num = groups[i].value / 5000;
|
||||
//最细分的每个小格代表的数值大小
|
||||
unitAngle = (groups[i].endAngle - groups[i].startAngle) * 180 / Math.PI / num;
|
||||
|
||||
var j;
|
||||
for (j = 0; j <= num; j++) {
|
||||
//计算旋转角度和水平、竖直方向所需平移的距离
|
||||
radian = ((groups[i].startAngle * 180 / Math.PI - 90) + j * unitAngle);
|
||||
angle = radian * Math.PI / 180;
|
||||
xTrans = that.defaults.outerRadius * Math.cos(angle);
|
||||
yTrans = that.defaults.outerRadius * Math.sin(angle);
|
||||
|
||||
tickStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "T" + xTrans + "," + yTrans;
|
||||
|
||||
//刻度线的起点终点坐标
|
||||
aX = ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + xTrans;
|
||||
aY = that.defaults.height / 2 + yTrans;
|
||||
bX = ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + (that.defaults.outerRadius + 6) * Math.cos(angle);
|
||||
bY = that.defaults.height / 2 + (that.defaults.outerRadius + 6) * Math.sin(angle);
|
||||
|
||||
tickLine = "M" + aX + "," + aY + "L" + bX + "," + bY;
|
||||
that.canvas.path(tickLine).attr({
|
||||
'stroke': "#929292",
|
||||
"stroke-width": '1px'
|
||||
}); //绘制刻度
|
||||
|
||||
//每隔五个刻度,绘制一次文字
|
||||
if (j % 2 === 0) {
|
||||
//计算text-anchor
|
||||
if (radian + 90 < 180) {
|
||||
anchor = "start";
|
||||
} else {
|
||||
anchor = "end";
|
||||
}
|
||||
|
||||
//计算文字方向是否需要旋转180度
|
||||
if (radian + 90 < 180) {
|
||||
rotateStr = null;
|
||||
} else {
|
||||
rotateStr = "R180";
|
||||
}
|
||||
|
||||
wXTrans = (that.defaults.outerRadius + 10) * Math.cos(angle);
|
||||
wYTrans = (that.defaults.outerRadius + 10) * Math.sin(angle);
|
||||
|
||||
word = j % 2 ? "" : Math.round(((groups[i].value / num) * j) / 1000);
|
||||
|
||||
wordStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "R" + radian
|
||||
/*(groups[i].startAngle * 180 / Math.PI - 90)*/ + rotateStr + "T" + wXTrans + "," + wYTrans;
|
||||
|
||||
//绘制文字
|
||||
textEl = that.canvas.text(0, 0, word).attr("font", "12px Verdana").transform(wordStr).attr("text-anchor", anchor).attr('fill', "#929292");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*this.canvas.text().attr("font", "12px arial").translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, this.defaults.height).attr("text", "The unit of the scale on the periphery is 1000. \n 刻度值的单位为1000。");
|
||||
*/
|
||||
};
|
||||
|
||||
return Chord;
|
||||
});
|
||||
1020
lib/charts/column.js
1020
lib/charts/column.js
File diff suppressed because it is too large
Load Diff
@ -1,261 +1,261 @@
|
||||
/*global EventProxy, d3, Raphael, $ */
|
||||
/*!
|
||||
* Flow的兼容性定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Flow', function (require) {
|
||||
var DataV = require('DataV');
|
||||
var theme = DataV.Themes;
|
||||
|
||||
/**
|
||||
* Flow构造函数
|
||||
*/
|
||||
var Flow = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.type = "Flow";
|
||||
this.node = this.checkContainer(node);
|
||||
|
||||
// Properties
|
||||
this.font = {};
|
||||
|
||||
// Canvas
|
||||
this.defaults.width = 500;
|
||||
this.defaults.height = 400;
|
||||
this.defaults.deep = 150;
|
||||
this.defaults.radius = 50;
|
||||
this.defaults.xStep = 300;
|
||||
this.defaults.xStart = 200;
|
||||
this.defaults.yStart = 50;
|
||||
|
||||
this.setOptions(options);
|
||||
this.createCanvas();
|
||||
}
|
||||
});
|
||||
|
||||
Flow.prototype.setSource = function (source) {
|
||||
var conf = this.defaults;
|
||||
|
||||
this.rawData = source;
|
||||
this.source = this.remapSource(source);
|
||||
};
|
||||
|
||||
Flow.prototype.remapSource = function (data) {
|
||||
console.log(data);
|
||||
var dataLength = data.length;
|
||||
var remapData = [];
|
||||
|
||||
var total;
|
||||
var i;
|
||||
for (i = 0; i < dataLength; i++){
|
||||
if (!data[i][3]) {
|
||||
total = data[i][2];
|
||||
remapData.push({id: data[i][0], name: data[i][1], value: data[i][2], pid: data[i][3], child: [], deep: 0});
|
||||
} else {
|
||||
remapData.push({id: data[i][0], name: data[i][1], value: data[i][2], pid: data[i][3], child: [], deep: 0});
|
||||
}
|
||||
}
|
||||
|
||||
var conf = this.defaults;
|
||||
var width = conf.width;
|
||||
var height = conf.height;
|
||||
var radius = conf.radius;
|
||||
var xStep = conf.xStep;
|
||||
var xStart = conf.xStart;
|
||||
var yStart = conf.yStart;
|
||||
var depth = 0;
|
||||
|
||||
for (i = 0; i < dataLength; i++){
|
||||
if (remapData[i].pid) {
|
||||
remapData[i].deep = remapData[remapData[i].pid - 1].deep + 1;
|
||||
remapData[remapData[i].pid - 1].child.push(remapData[i].id - 1);
|
||||
if (remapData[i].deep > depth) {
|
||||
depth = remapData[i].deep;
|
||||
}
|
||||
}
|
||||
// remapData[remapData[i].pid].child.push(remapData[i].id);
|
||||
}
|
||||
|
||||
this.depth = depth;
|
||||
radius = Math.min(Math.min((width - xStep * (depth - 1) - xStart * 2) / depth, height*0.55), radius);
|
||||
console.log("r:" + radius);
|
||||
for (i = 0; i < dataLength; i++){
|
||||
remapData[i].percent = remapData[i].value / total;
|
||||
remapData[i].radius = radius * remapData[i].percent;
|
||||
}
|
||||
return remapData;
|
||||
// return data;
|
||||
};
|
||||
|
||||
Flow.prototype.layout = function () {
|
||||
var conf = this.defaults;
|
||||
var width = conf.width;
|
||||
var height = conf.height;
|
||||
var xStart = conf.xStart;
|
||||
var yStart = conf.yStart;
|
||||
var xStep = conf.xStep;
|
||||
var remapData = this.source;
|
||||
|
||||
//console.log(this.source);
|
||||
var circleData = [];
|
||||
|
||||
circleData.push({x: width * 0.24, y: height * 0.42, radius: Math.max(10, remapData[0].radius), deep: remapData[0].deep, name: remapData[0].name, value: remapData[0].value});
|
||||
circleData.push({x: width * 0.5, y: height * 0.245, radius: Math.max(10, remapData[1].radius), deep: remapData[1].deep, name: remapData[1].name, value: remapData[1].value});
|
||||
circleData.push({x: width * 0.5, y: height * 0.6, radius: Math.max(10, remapData[2].radius), deep: remapData[2].deep, name: remapData[2].name, value: remapData[2].value});
|
||||
circleData.push({x: width * 0.72, y: height * 0.5, radius: Math.max(10, remapData[3].radius), deep: remapData[3].deep, name: remapData[3].name, value: remapData[3].value});
|
||||
circleData.push({x: width * 0.72, y: height * 0.817, radius: Math.max(10, remapData[4].radius), deep: remapData[4].deep, name: remapData[4].name, value: remapData[4].value});
|
||||
|
||||
for (i = 0;i < circleData.length; i++) {
|
||||
console.log(circleData[i].x);
|
||||
}
|
||||
|
||||
this.circleData = circleData;
|
||||
};
|
||||
|
||||
Flow.prototype.getColor = function () {
|
||||
|
||||
var colorMatrix = DataV.getColor();
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
// Tree.prototype.getFont = function () {
|
||||
// //var conf = this.defaults;
|
||||
|
||||
// return DataV.getFont();
|
||||
// };
|
||||
|
||||
Flow.prototype.createCanvas = function () {
|
||||
var conf = this.defaults;
|
||||
this.canvas = new Raphael(this.node, conf.width, conf.height);
|
||||
|
||||
this.DOMNode = $(this.canvas.canvas);
|
||||
var that = this;
|
||||
this.DOMNode.click(function (event) {
|
||||
that.trigger("click", event);
|
||||
});
|
||||
this.DOMNode.dblclick(function (event) {
|
||||
that.trigger("dblclick", event);
|
||||
});
|
||||
|
||||
var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
|
||||
this.DOMNode.bind(mousewheel, function (event) {
|
||||
that.trigger("mousewheel", event);
|
||||
});
|
||||
|
||||
this.DOMNode.bind("contextmenu", function (event) {
|
||||
that.trigger("contextmenu", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "click", function (event) {
|
||||
that.trigger("circle_click", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "mouseover", function (event) {
|
||||
that.trigger("circle_mouseover", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "mouseout", function (event) {
|
||||
that.trigger("circle_mouseout", event);
|
||||
});
|
||||
|
||||
//console.log(this.canvas);
|
||||
};
|
||||
|
||||
|
||||
Flow.prototype.getLinkPath = function (fx, fy, tx, ty) {
|
||||
var conf = this.defaults;
|
||||
|
||||
var c1x = fx + (tx - fx) / 1.5;
|
||||
var c1y = fy;
|
||||
var c2x = tx - (tx - fx) / 4;
|
||||
var c2y = ty - (ty - fy) / 2;
|
||||
|
||||
var link_path = [["M", fx, fy],
|
||||
["S", c1x, c1y, tx, ty]];
|
||||
|
||||
return link_path;
|
||||
};
|
||||
|
||||
Flow.prototype.generatePaths = function () {
|
||||
var canvas = this.canvas;
|
||||
var source = this.source;
|
||||
var conf = this.defaults;
|
||||
var radius = conf.radius;
|
||||
//canvas.clear();
|
||||
// var font = this.getFont();
|
||||
var font_family = '微软雅黑';
|
||||
var font_size = 8;
|
||||
var depth = this.depth;
|
||||
var crilceData = this.circleData;
|
||||
var l = crilceData.length;
|
||||
var getLinkPath = this.getLinkPath;
|
||||
|
||||
canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[0].x, crilceData[0].y, crilceData[1].x, crilceData[1].y)});
|
||||
canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[0].x, crilceData[0].y, crilceData[2].x, crilceData[2].y)});
|
||||
canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[2].x, crilceData[2].y, crilceData[3].x, crilceData[3].y)});
|
||||
canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[2].x, crilceData[2].y, crilceData[4].x, crilceData[4].y)});
|
||||
|
||||
var i, d;
|
||||
var thisRadius;
|
||||
var thisColor;
|
||||
var titelPath = [];
|
||||
var valuePath = [];
|
||||
for (i = 0 ; i < l ; i++) {
|
||||
d = crilceData[i];
|
||||
thisRadius = Math.max(27, d.radius);
|
||||
if (i === 1) {
|
||||
thisColor = "#b4e481";
|
||||
} else if (i === 4) {
|
||||
thisColor = "#cd3a19";
|
||||
} else {
|
||||
thisColor = "#ffe79d";
|
||||
}
|
||||
canvas.circle(d.x, d.y, thisRadius)
|
||||
.attr({"stroke": "none", fill: thisColor});
|
||||
titelPath.push(canvas.text(0, 0, d.name).attr({'font-size': 12}));
|
||||
if (i < l - 2) {
|
||||
titelPath[i].transform("t" + d.x + "," + (d.y - thisRadius - titelPath[i].getBBox().height/2 - 5));
|
||||
} else {
|
||||
titelPath[i].transform("t" + d.x + "," + (d.y - thisRadius - titelPath[i].getBBox().height/2 - 5)).attr({"text-anchor": "start"});
|
||||
}
|
||||
|
||||
if (i < l - 1) {
|
||||
valuePath.push(canvas.text(d.x, d.y, d.value + "人").attr({'font-size': 12}));
|
||||
} else {
|
||||
valuePath.push(canvas.text(d.x, d.y, d.value + "人").attr({fill: "#ffffff", 'font-size': 12}));
|
||||
}
|
||||
}
|
||||
|
||||
var n = 0;
|
||||
|
||||
var node;
|
||||
var num = 0;
|
||||
|
||||
var nodes = canvas.set();
|
||||
var path = [];
|
||||
var textpath = [];
|
||||
|
||||
var tree = this;
|
||||
};
|
||||
|
||||
Flow.prototype.render = function (options) {
|
||||
var st = new Date().getTime();
|
||||
this.canvas.clear();
|
||||
this.setOptions(options);
|
||||
this.layout();
|
||||
var st2 = new Date().getTime();
|
||||
console.log(st2 - st);
|
||||
|
||||
this.generatePaths();
|
||||
var et = new Date().getTime();
|
||||
console.log(et - st2);
|
||||
//this.canvas.renderfix();
|
||||
};
|
||||
|
||||
return Flow;
|
||||
});
|
||||
/*global EventProxy, d3, Raphael, $ */
|
||||
/*!
|
||||
* Flow的兼容性定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Flow', function (require) {
|
||||
var DataV = require('DataV');
|
||||
var theme = DataV.Themes;
|
||||
|
||||
/**
|
||||
* Flow构造函数
|
||||
*/
|
||||
var Flow = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.type = "Flow";
|
||||
this.node = this.checkContainer(node);
|
||||
|
||||
// Properties
|
||||
this.font = {};
|
||||
|
||||
// Canvas
|
||||
this.defaults.width = 500;
|
||||
this.defaults.height = 400;
|
||||
this.defaults.deep = 150;
|
||||
this.defaults.radius = 50;
|
||||
this.defaults.xStep = 300;
|
||||
this.defaults.xStart = 200;
|
||||
this.defaults.yStart = 50;
|
||||
|
||||
this.setOptions(options);
|
||||
this.createCanvas();
|
||||
}
|
||||
});
|
||||
|
||||
Flow.prototype.setSource = function (source) {
|
||||
var conf = this.defaults;
|
||||
|
||||
this.rawData = source;
|
||||
this.source = this.remapSource(source);
|
||||
};
|
||||
|
||||
Flow.prototype.remapSource = function (data) {
|
||||
console.log(data);
|
||||
var dataLength = data.length;
|
||||
var remapData = [];
|
||||
|
||||
var total;
|
||||
var i;
|
||||
for (i = 0; i < dataLength; i++){
|
||||
if (!data[i][3]) {
|
||||
total = data[i][2];
|
||||
remapData.push({id: data[i][0], name: data[i][1], value: data[i][2], pid: data[i][3], child: [], deep: 0});
|
||||
} else {
|
||||
remapData.push({id: data[i][0], name: data[i][1], value: data[i][2], pid: data[i][3], child: [], deep: 0});
|
||||
}
|
||||
}
|
||||
|
||||
var conf = this.defaults;
|
||||
var width = conf.width;
|
||||
var height = conf.height;
|
||||
var radius = conf.radius;
|
||||
var xStep = conf.xStep;
|
||||
var xStart = conf.xStart;
|
||||
var yStart = conf.yStart;
|
||||
var depth = 0;
|
||||
|
||||
for (i = 0; i < dataLength; i++){
|
||||
if (remapData[i].pid) {
|
||||
remapData[i].deep = remapData[remapData[i].pid - 1].deep + 1;
|
||||
remapData[remapData[i].pid - 1].child.push(remapData[i].id - 1);
|
||||
if (remapData[i].deep > depth) {
|
||||
depth = remapData[i].deep;
|
||||
}
|
||||
}
|
||||
// remapData[remapData[i].pid].child.push(remapData[i].id);
|
||||
}
|
||||
|
||||
this.depth = depth;
|
||||
radius = Math.min(Math.min((width - xStep * (depth - 1) - xStart * 2) / depth, height*0.55), radius);
|
||||
console.log("r:" + radius);
|
||||
for (i = 0; i < dataLength; i++){
|
||||
remapData[i].percent = remapData[i].value / total;
|
||||
remapData[i].radius = radius * remapData[i].percent;
|
||||
}
|
||||
return remapData;
|
||||
// return data;
|
||||
};
|
||||
|
||||
Flow.prototype.layout = function () {
|
||||
var conf = this.defaults;
|
||||
var width = conf.width;
|
||||
var height = conf.height;
|
||||
var xStart = conf.xStart;
|
||||
var yStart = conf.yStart;
|
||||
var xStep = conf.xStep;
|
||||
var remapData = this.source;
|
||||
|
||||
//console.log(this.source);
|
||||
var circleData = [];
|
||||
|
||||
circleData.push({x: width * 0.24, y: height * 0.42, radius: Math.max(10, remapData[0].radius), deep: remapData[0].deep, name: remapData[0].name, value: remapData[0].value});
|
||||
circleData.push({x: width * 0.5, y: height * 0.245, radius: Math.max(10, remapData[1].radius), deep: remapData[1].deep, name: remapData[1].name, value: remapData[1].value});
|
||||
circleData.push({x: width * 0.5, y: height * 0.6, radius: Math.max(10, remapData[2].radius), deep: remapData[2].deep, name: remapData[2].name, value: remapData[2].value});
|
||||
circleData.push({x: width * 0.72, y: height * 0.5, radius: Math.max(10, remapData[3].radius), deep: remapData[3].deep, name: remapData[3].name, value: remapData[3].value});
|
||||
circleData.push({x: width * 0.72, y: height * 0.817, radius: Math.max(10, remapData[4].radius), deep: remapData[4].deep, name: remapData[4].name, value: remapData[4].value});
|
||||
|
||||
for (i = 0;i < circleData.length; i++) {
|
||||
console.log(circleData[i].x);
|
||||
}
|
||||
|
||||
this.circleData = circleData;
|
||||
};
|
||||
|
||||
Flow.prototype.getColor = function () {
|
||||
|
||||
var colorMatrix = DataV.getColor();
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
// Tree.prototype.getFont = function () {
|
||||
// //var conf = this.defaults;
|
||||
|
||||
// return DataV.getFont();
|
||||
// };
|
||||
|
||||
Flow.prototype.createCanvas = function () {
|
||||
var conf = this.defaults;
|
||||
this.canvas = new Raphael(this.node, conf.width, conf.height);
|
||||
|
||||
this.DOMNode = $(this.canvas.canvas);
|
||||
var that = this;
|
||||
this.DOMNode.click(function (event) {
|
||||
that.trigger("click", event);
|
||||
});
|
||||
this.DOMNode.dblclick(function (event) {
|
||||
that.trigger("dblclick", event);
|
||||
});
|
||||
|
||||
var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
|
||||
this.DOMNode.bind(mousewheel, function (event) {
|
||||
that.trigger("mousewheel", event);
|
||||
});
|
||||
|
||||
this.DOMNode.bind("contextmenu", function (event) {
|
||||
that.trigger("contextmenu", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "click", function (event) {
|
||||
that.trigger("circle_click", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "mouseover", function (event) {
|
||||
that.trigger("circle_mouseover", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "mouseout", function (event) {
|
||||
that.trigger("circle_mouseout", event);
|
||||
});
|
||||
|
||||
//console.log(this.canvas);
|
||||
};
|
||||
|
||||
|
||||
Flow.prototype.getLinkPath = function (fx, fy, tx, ty) {
|
||||
var conf = this.defaults;
|
||||
|
||||
var c1x = fx + (tx - fx) / 1.5;
|
||||
var c1y = fy;
|
||||
var c2x = tx - (tx - fx) / 4;
|
||||
var c2y = ty - (ty - fy) / 2;
|
||||
|
||||
var link_path = [["M", fx, fy],
|
||||
["S", c1x, c1y, tx, ty]];
|
||||
|
||||
return link_path;
|
||||
};
|
||||
|
||||
Flow.prototype.generatePaths = function () {
|
||||
var canvas = this.canvas;
|
||||
var source = this.source;
|
||||
var conf = this.defaults;
|
||||
var radius = conf.radius;
|
||||
//canvas.clear();
|
||||
// var font = this.getFont();
|
||||
var font_family = '微软雅黑';
|
||||
var font_size = 8;
|
||||
var depth = this.depth;
|
||||
var crilceData = this.circleData;
|
||||
var l = crilceData.length;
|
||||
var getLinkPath = this.getLinkPath;
|
||||
|
||||
canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[0].x, crilceData[0].y, crilceData[1].x, crilceData[1].y)});
|
||||
canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[0].x, crilceData[0].y, crilceData[2].x, crilceData[2].y)});
|
||||
canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[2].x, crilceData[2].y, crilceData[3].x, crilceData[3].y)});
|
||||
canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[2].x, crilceData[2].y, crilceData[4].x, crilceData[4].y)});
|
||||
|
||||
var i, d;
|
||||
var thisRadius;
|
||||
var thisColor;
|
||||
var titelPath = [];
|
||||
var valuePath = [];
|
||||
for (i = 0 ; i < l ; i++) {
|
||||
d = crilceData[i];
|
||||
thisRadius = Math.max(27, d.radius);
|
||||
if (i === 1) {
|
||||
thisColor = "#b4e481";
|
||||
} else if (i === 4) {
|
||||
thisColor = "#cd3a19";
|
||||
} else {
|
||||
thisColor = "#ffe79d";
|
||||
}
|
||||
canvas.circle(d.x, d.y, thisRadius)
|
||||
.attr({"stroke": "none", fill: thisColor});
|
||||
titelPath.push(canvas.text(0, 0, d.name).attr({'font-size': 12}));
|
||||
if (i < l - 2) {
|
||||
titelPath[i].transform("t" + d.x + "," + (d.y - thisRadius - titelPath[i].getBBox().height/2 - 5));
|
||||
} else {
|
||||
titelPath[i].transform("t" + d.x + "," + (d.y - thisRadius - titelPath[i].getBBox().height/2 - 5)).attr({"text-anchor": "start"});
|
||||
}
|
||||
|
||||
if (i < l - 1) {
|
||||
valuePath.push(canvas.text(d.x, d.y, d.value + "人").attr({'font-size': 12}));
|
||||
} else {
|
||||
valuePath.push(canvas.text(d.x, d.y, d.value + "人").attr({fill: "#ffffff", 'font-size': 12}));
|
||||
}
|
||||
}
|
||||
|
||||
var n = 0;
|
||||
|
||||
var node;
|
||||
var num = 0;
|
||||
|
||||
var nodes = canvas.set();
|
||||
var path = [];
|
||||
var textpath = [];
|
||||
|
||||
var tree = this;
|
||||
};
|
||||
|
||||
Flow.prototype.render = function (options) {
|
||||
var st = new Date().getTime();
|
||||
this.canvas.clear();
|
||||
this.setOptions(options);
|
||||
this.layout();
|
||||
var st2 = new Date().getTime();
|
||||
console.log(st2 - st);
|
||||
|
||||
this.generatePaths();
|
||||
var et = new Date().getTime();
|
||||
console.log(et - st2);
|
||||
//this.canvas.renderfix();
|
||||
};
|
||||
|
||||
return Flow;
|
||||
});
|
||||
|
||||
1420
lib/charts/force.js
1420
lib/charts/force.js
File diff suppressed because it is too large
Load Diff
1176
lib/charts/line.js
1176
lib/charts/line.js
File diff suppressed because it is too large
Load Diff
@ -1,434 +1,434 @@
|
||||
/*global EventProxy, d3, Raphael, $ */
|
||||
/*!
|
||||
* Matrix的兼容定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Matrix', function (require) {
|
||||
var DataV = require('DataV');
|
||||
var theme = DataV.Themes;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
var Matrix = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.type = "Matrix";
|
||||
this.node = this.checkContainer(node);
|
||||
|
||||
// Properties
|
||||
this.font = {};
|
||||
|
||||
// Canvas
|
||||
this.defaults.width = 1200;
|
||||
this.defaults.height = 1200;
|
||||
this.defaults.axisWidth = 40;
|
||||
|
||||
this.setOptions(options);
|
||||
this.createCanvas();
|
||||
this.move = false;
|
||||
}
|
||||
});
|
||||
|
||||
Matrix.prototype.getDataTable = function (table) {
|
||||
var title = table[0];
|
||||
table = table.slice(1);
|
||||
|
||||
var titleLength = title.length;
|
||||
var tableWidth = table[0].length;
|
||||
var tableHeight = table.length;
|
||||
|
||||
this.tableWidth = tableWidth;
|
||||
this.tableHeight = tableHeight;
|
||||
|
||||
//for symmetric matrix
|
||||
if (tableWidth !== title.length || tableHeight !== title.length) {
|
||||
throw new Error("This matrix is not symmetric matrix!!!");
|
||||
} else {
|
||||
this.tableWidth = tableWidth;
|
||||
this.tableHeight = tableHeight;
|
||||
}
|
||||
|
||||
this.title = title;
|
||||
return table;
|
||||
};
|
||||
|
||||
Matrix.prototype.setSource = function (source) {
|
||||
var conf = this.defaults;
|
||||
|
||||
this.source = this.getDataTable(source);
|
||||
this.hasSort = false;
|
||||
// this.source = this.remapSource(source);
|
||||
};
|
||||
|
||||
Matrix.prototype.layout = function () {
|
||||
var conf = this.defaults;
|
||||
var width = conf.width;
|
||||
var height = conf.height;
|
||||
var tableWidth = this.tableWidth;
|
||||
var tableHeight = this.tableHeight;
|
||||
var axisWidth = conf.axisWidth;
|
||||
|
||||
this.cellWidth = Math.min((width - axisWidth) / tableWidth, (height - axisWidth) / tableHeight);
|
||||
|
||||
var startX;
|
||||
var startY;
|
||||
var bRectWidth;
|
||||
var matrixWidth;
|
||||
|
||||
if (width > height) {
|
||||
startX = (width - height)/2 + axisWidth;
|
||||
startY = axisWidth;
|
||||
bRectWidth = height - axisWidth;
|
||||
matrixWidth = bRectWidth - axisWidth;
|
||||
} else if (height > width) {
|
||||
startX = axisWidth;
|
||||
startY = (height - width) + axisWidth;
|
||||
bRectWidth = width - axisWidth;
|
||||
} else {
|
||||
startX = axisWidth;
|
||||
startY = axisWidth;
|
||||
bRectWidth = width - axisWidth;
|
||||
matrixWidth = bRectWidth - axisWidth;
|
||||
}
|
||||
|
||||
this.startX = startX;
|
||||
this.startY = startY;
|
||||
this.bRectWidth = bRectWidth;
|
||||
this.matrixWidth = matrixWidth;
|
||||
};
|
||||
|
||||
Matrix.prototype.getColor = function (i) {
|
||||
var colorMatrix = DataV.getColor();
|
||||
var length = colorMatrix.length;
|
||||
var num = i % length;
|
||||
//var color = '#939598';
|
||||
var color = '#FFFFFF';
|
||||
|
||||
if (num !== 0) {
|
||||
color = colorMatrix[num][0];
|
||||
}
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
Matrix.prototype.createCanvas = function () {
|
||||
var conf = this.defaults;
|
||||
this.canvas = new Raphael(this.node, conf.width, conf.height);
|
||||
|
||||
this.DOMNode = $(this.canvas.canvas);
|
||||
var that = this;
|
||||
this.DOMNode.click(function (event) {
|
||||
that.trigger("click", event);
|
||||
that.update();
|
||||
});
|
||||
this.DOMNode.dblclick(function (event) {
|
||||
that.trigger("dblclick", event);
|
||||
});
|
||||
|
||||
var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
|
||||
this.DOMNode.bind(mousewheel, function (event) {
|
||||
that.trigger("mousewheel", event);
|
||||
});
|
||||
|
||||
this.DOMNode.bind("contextmenu", function (event) {
|
||||
that.trigger("contextmenu", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "click", function (event) {
|
||||
that.trigger("circle_click", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "mouseover", function (event) {
|
||||
that.trigger("circle_mouseover", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "mouseout", function (event) {
|
||||
that.trigger("circle_mouseout", event);
|
||||
});
|
||||
};
|
||||
|
||||
Matrix.prototype.generatePaths = function () {
|
||||
var canvas = this.canvas;
|
||||
var source = this.source;
|
||||
var conf = this.defaults;
|
||||
var width = conf.width;
|
||||
var height = conf.height;
|
||||
var startX = this.startX;
|
||||
var startY = this.startY;
|
||||
var cellWidth = this.cellWidth;
|
||||
var tableWidth = this.tableWidth;
|
||||
var tableHeight = this.tableHeight;
|
||||
var bRectWidth = this.bRectWidth;
|
||||
var matrixWidth = this.matrixWidth;
|
||||
|
||||
//canvas.clear();
|
||||
// var color = this.getColor();
|
||||
// var font = this.getFont();
|
||||
var font_family = '微软雅黑';
|
||||
var font_size = 8;
|
||||
|
||||
var title = this.title;
|
||||
|
||||
var row = [];
|
||||
var columnLine = [];
|
||||
var columnText = [];
|
||||
|
||||
var backgroundRect = canvas.rect(startX, startY, bRectWidth, bRectWidth);
|
||||
//backgroundRect.attr({fill: "#939598", stroke: "none", "fill-opacity": 0.8});
|
||||
backgroundRect.attr({fill: "#ffffff", stroke: "none", "fill-opacity": 0.8});
|
||||
backgroundRect.toBack();
|
||||
|
||||
var sort;
|
||||
if (this.hasSort) {
|
||||
sort = this.sort;
|
||||
}
|
||||
var i, j, a, b, color, rect;
|
||||
var rects = []; //for column change move rect
|
||||
for (i = 0; i < tableHeight; i++) {
|
||||
if (!this.hasSort){
|
||||
a = i;
|
||||
} else {
|
||||
for (j = 0; j < sort.length; j++) {
|
||||
if (sort[j] === i) {
|
||||
a = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
var rowRect = canvas.set();
|
||||
canvas.path("M" + startX + " " + (startY + cellWidth * i) + "L" + (startX + matrixWidth + 10 + cellWidth) + " "
|
||||
+ (startY + cellWidth * i)).attr({stroke: "#D1D1D1", "stroke-width": 1});
|
||||
rowRect.push(canvas.text(-20, cellWidth / 2, title[i])
|
||||
.attr({"fill": "#000000",
|
||||
"fill-opacity": 0.7,
|
||||
"font-family": "Verdana",
|
||||
//"font-weight": "bold",
|
||||
"font-size": 12}));
|
||||
|
||||
for (j = 0; j < tableWidth; j++) {
|
||||
if (!this.hasSort) {
|
||||
color = this.getColor(source[i][j]);
|
||||
} else {
|
||||
color = this.getColor(source[i][sort[j]]);
|
||||
}
|
||||
rect = canvas.rect(cellWidth * j, 0, cellWidth, cellWidth)
|
||||
.attr({stroke: "none", fill: color, "fill-opacity": 0.8});
|
||||
rowRect.push(rect);
|
||||
rects.push(rect);
|
||||
}
|
||||
|
||||
rowRect.transform("t" + startX + ", " + (startY + cellWidth * a));
|
||||
row.push(rowRect);
|
||||
}
|
||||
|
||||
canvas.path("M" + startX + " " + (startY + cellWidth * tableHeight) + "L" + (startX + matrixWidth + 10 + cellWidth) + " "
|
||||
+ (startY + cellWidth * tableHeight)).attr({stroke: "#D1D1D1", "stroke-width": 1});
|
||||
|
||||
for (i = 0; i < tableWidth; i++) {
|
||||
// var columnLine = canvas.set();
|
||||
// var columnText = canvas.set();
|
||||
if (!this.hasSort){
|
||||
a = i;
|
||||
} else {
|
||||
for (j = 0; j < sort.length; j++) {
|
||||
if (sort[j] === i) {
|
||||
a = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
columnLine.push(canvas.path("M0 0L0 " + matrixWidth + 10 + cellWidth)
|
||||
.attr({stroke: "#D1D1D1", "stroke-width": 1})
|
||||
.transform("t" + (startX + cellWidth * a) + ", " + startY));
|
||||
columnText.push(canvas.text(cellWidth / 2, -20, title[i])
|
||||
.attr({"fill": "#000000",
|
||||
"fill-opacity": 0.7,
|
||||
"font-family": "Verdana",
|
||||
//"font-weight": "bold",
|
||||
"font-size": 12})
|
||||
.transform("t" + (startX + cellWidth * a) + ", " + startY + "r90"));
|
||||
}
|
||||
|
||||
columnLine.push(canvas.path("M0 0L0 " + matrixWidth + 10 + cellWidth)
|
||||
.attr({stroke: "#D1D1D1", "stroke-width": 1})
|
||||
.transform("t" + (startX + cellWidth * tableWidth) + ", " + startY));
|
||||
|
||||
this.row = row;
|
||||
this.columnText = columnText;
|
||||
this.columnLine = columnLine;
|
||||
this.rects = rects;
|
||||
};
|
||||
|
||||
Matrix.prototype.getSort = function (source) {
|
||||
var sumQueue = [];
|
||||
var sort = [];
|
||||
var rowData;
|
||||
var rowLength;
|
||||
var sum;
|
||||
var means;
|
||||
var matrixD = [];
|
||||
var quareSum;
|
||||
var rowquareSum = [];
|
||||
|
||||
var i, j, k;
|
||||
for (i = 0 ; i < source.length ; i++) {
|
||||
rowData = source[i];
|
||||
rowLength = rowData.length;
|
||||
sum = 0;
|
||||
quareSum = 0;
|
||||
|
||||
for (j = 0 ; j < rowLength ; j++) {
|
||||
sum = sum + rowData[j];
|
||||
}
|
||||
|
||||
means = sum / rowLength;
|
||||
for (j = 0 ; j < rowLength ; j++) {
|
||||
rowData[j] = rowData[j] - means;
|
||||
quareSum = quareSum + Math.pow(rowData[j], 2);
|
||||
}
|
||||
|
||||
quareSum = Math.sqrt(quareSum);
|
||||
|
||||
rowquareSum.push(quareSum);
|
||||
matrixD.push(rowData);
|
||||
}
|
||||
|
||||
var rowI;
|
||||
var rowJ;
|
||||
var matrixR = [];
|
||||
|
||||
for (i = 0 ; i < source.length ; i++) {
|
||||
matrixR[i] = [];
|
||||
for (j = 0 ; j < source.length ; j++) {
|
||||
matrixR[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0 ; i < source.length ; i++) {
|
||||
rowI = matrixD[i];
|
||||
matrixR[i][i] = source[i][i];
|
||||
for (j = i + 1 ; j < source.length ; j++) {
|
||||
sum = 0;
|
||||
rowJ = matrixD[j];
|
||||
for (k = 0; k < rowLength; k++) {
|
||||
sum = sum + rowI[k] * rowJ[k];
|
||||
}
|
||||
|
||||
sum = sum / (rowquareSum[i] * rowquareSum[j]);
|
||||
matrixR[i][j] = sum;
|
||||
matrixR[j][i] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return matrixR;
|
||||
};
|
||||
|
||||
Matrix.prototype.update = function () {
|
||||
var i, j;
|
||||
var source = [];
|
||||
for(i = 0; i < this.source.length ; i++){
|
||||
source[i] = this.source[i].concat();
|
||||
}
|
||||
|
||||
var sort = [];
|
||||
for (i = 0; i < source[0].length; i++) {
|
||||
sort.push(i);
|
||||
}
|
||||
|
||||
if (this.hasSort) {
|
||||
this.sort = sort;
|
||||
this.hasSort = false;
|
||||
} else {
|
||||
var getSort = this.getSort;
|
||||
var i, j;
|
||||
var pt;
|
||||
var nowSort = [];
|
||||
var iterations = 12;
|
||||
|
||||
for (i = 0; i < iterations; i++) {
|
||||
source = getSort(source);
|
||||
}
|
||||
|
||||
nowSort = source[0];
|
||||
|
||||
var a, b;
|
||||
for (i = 1; i < sort.length; i++) {
|
||||
a = sort[i];
|
||||
for (j = i + 1; j < sort.length; j++) {
|
||||
b = sort[j];
|
||||
if (nowSort[a] < nowSort[b]) {
|
||||
pt = sort[i];
|
||||
sort[i] = sort[j];
|
||||
sort[j] = pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
sort = [0,7,5,2,8,3,1,9,6,14,15,4,13,10,16,11,12];
|
||||
this.sort = sort;
|
||||
this.hasSort = true;
|
||||
}
|
||||
|
||||
if (!this.move) {
|
||||
this.move = true;
|
||||
var rects = this.rects;
|
||||
var num;
|
||||
var startX = this.startX;
|
||||
var startY = this.startY;
|
||||
var cellWidth = this.cellWidth;
|
||||
|
||||
var rowAnim;
|
||||
var columnLineAnim;
|
||||
var columnTextAnim;
|
||||
var anim;
|
||||
|
||||
for (i = 0; i < sort.length; i++) {
|
||||
num = sort[i];
|
||||
// if (num != i) {
|
||||
rowAnim = Raphael.animation({transform: ["t", startX, (startY + cellWidth * i)]}, 200, "<>");
|
||||
this.row[num].animate(rowAnim.delay(100 * i));
|
||||
// }
|
||||
}
|
||||
|
||||
var that = this;
|
||||
var moveEnd = function () {
|
||||
that.move = false;
|
||||
};
|
||||
|
||||
for (i = 0; i < sort.length; i++) {
|
||||
num = sort[i];
|
||||
// if (num != i) {
|
||||
//columnLineAnim = Raphael.animation({transform: ["t", (startX + cellWidth * i), startY]}, 1000, "<>");
|
||||
columnTextAnim = Raphael.animation({transform: ["t", (startX + cellWidth * i), startY, "r", 90]},
|
||||
200, "<>");
|
||||
//this.columnLine[num].animate(columnLineAnim.delay(500 * (i + sort.length + 1)));
|
||||
this.columnText[num].animate(columnTextAnim.delay(100 * (i + sort.length + 1)));
|
||||
|
||||
for (j = 0; j < sort.length; j++) {
|
||||
if (i === sort.length - 1 && j === sort.length - 1) {
|
||||
anim = Raphael.animation({'x': cellWidth * i}, 200, "<>", moveEnd);
|
||||
} else {
|
||||
anim = Raphael.animation({'x': cellWidth * i}, 200, "<>");
|
||||
}
|
||||
rects[j * sort.length + num].animate(anim.delay(100 * (i + sort.length + 1)));
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Matrix.prototype.render = function (options) {
|
||||
if (!this.move) {
|
||||
this.canvas.clear();
|
||||
this.setOptions(options);
|
||||
this.layout();
|
||||
this.generatePaths();
|
||||
}
|
||||
};
|
||||
|
||||
return Matrix;
|
||||
/*global EventProxy, d3, Raphael, $ */
|
||||
/*!
|
||||
* Matrix的兼容定义
|
||||
*/
|
||||
;(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];});
|
||||
}
|
||||
})('Matrix', function (require) {
|
||||
var DataV = require('DataV');
|
||||
var theme = DataV.Themes;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
var Matrix = DataV.extend(DataV.Chart, {
|
||||
initialize: function (node, options) {
|
||||
this.type = "Matrix";
|
||||
this.node = this.checkContainer(node);
|
||||
|
||||
// Properties
|
||||
this.font = {};
|
||||
|
||||
// Canvas
|
||||
this.defaults.width = 1200;
|
||||
this.defaults.height = 1200;
|
||||
this.defaults.axisWidth = 40;
|
||||
|
||||
this.setOptions(options);
|
||||
this.createCanvas();
|
||||
this.move = false;
|
||||
}
|
||||
});
|
||||
|
||||
Matrix.prototype.getDataTable = function (table) {
|
||||
var title = table[0];
|
||||
table = table.slice(1);
|
||||
|
||||
var titleLength = title.length;
|
||||
var tableWidth = table[0].length;
|
||||
var tableHeight = table.length;
|
||||
|
||||
this.tableWidth = tableWidth;
|
||||
this.tableHeight = tableHeight;
|
||||
|
||||
//for symmetric matrix
|
||||
if (tableWidth !== title.length || tableHeight !== title.length) {
|
||||
throw new Error("This matrix is not symmetric matrix!!!");
|
||||
} else {
|
||||
this.tableWidth = tableWidth;
|
||||
this.tableHeight = tableHeight;
|
||||
}
|
||||
|
||||
this.title = title;
|
||||
return table;
|
||||
};
|
||||
|
||||
Matrix.prototype.setSource = function (source) {
|
||||
var conf = this.defaults;
|
||||
|
||||
this.source = this.getDataTable(source);
|
||||
this.hasSort = false;
|
||||
// this.source = this.remapSource(source);
|
||||
};
|
||||
|
||||
Matrix.prototype.layout = function () {
|
||||
var conf = this.defaults;
|
||||
var width = conf.width;
|
||||
var height = conf.height;
|
||||
var tableWidth = this.tableWidth;
|
||||
var tableHeight = this.tableHeight;
|
||||
var axisWidth = conf.axisWidth;
|
||||
|
||||
this.cellWidth = Math.min((width - axisWidth) / tableWidth, (height - axisWidth) / tableHeight);
|
||||
|
||||
var startX;
|
||||
var startY;
|
||||
var bRectWidth;
|
||||
var matrixWidth;
|
||||
|
||||
if (width > height) {
|
||||
startX = (width - height)/2 + axisWidth;
|
||||
startY = axisWidth;
|
||||
bRectWidth = height - axisWidth;
|
||||
matrixWidth = bRectWidth - axisWidth;
|
||||
} else if (height > width) {
|
||||
startX = axisWidth;
|
||||
startY = (height - width) + axisWidth;
|
||||
bRectWidth = width - axisWidth;
|
||||
} else {
|
||||
startX = axisWidth;
|
||||
startY = axisWidth;
|
||||
bRectWidth = width - axisWidth;
|
||||
matrixWidth = bRectWidth - axisWidth;
|
||||
}
|
||||
|
||||
this.startX = startX;
|
||||
this.startY = startY;
|
||||
this.bRectWidth = bRectWidth;
|
||||
this.matrixWidth = matrixWidth;
|
||||
};
|
||||
|
||||
Matrix.prototype.getColor = function (i) {
|
||||
var colorMatrix = DataV.getColor();
|
||||
var length = colorMatrix.length;
|
||||
var num = i % length;
|
||||
//var color = '#939598';
|
||||
var color = '#FFFFFF';
|
||||
|
||||
if (num !== 0) {
|
||||
color = colorMatrix[num][0];
|
||||
}
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
Matrix.prototype.createCanvas = function () {
|
||||
var conf = this.defaults;
|
||||
this.canvas = new Raphael(this.node, conf.width, conf.height);
|
||||
|
||||
this.DOMNode = $(this.canvas.canvas);
|
||||
var that = this;
|
||||
this.DOMNode.click(function (event) {
|
||||
that.trigger("click", event);
|
||||
that.update();
|
||||
});
|
||||
this.DOMNode.dblclick(function (event) {
|
||||
that.trigger("dblclick", event);
|
||||
});
|
||||
|
||||
var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
|
||||
this.DOMNode.bind(mousewheel, function (event) {
|
||||
that.trigger("mousewheel", event);
|
||||
});
|
||||
|
||||
this.DOMNode.bind("contextmenu", function (event) {
|
||||
that.trigger("contextmenu", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "click", function (event) {
|
||||
that.trigger("circle_click", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "mouseover", function (event) {
|
||||
that.trigger("circle_mouseover", event);
|
||||
});
|
||||
|
||||
this.DOMNode.delegate("circle", "mouseout", function (event) {
|
||||
that.trigger("circle_mouseout", event);
|
||||
});
|
||||
};
|
||||
|
||||
Matrix.prototype.generatePaths = function () {
|
||||
var canvas = this.canvas;
|
||||
var source = this.source;
|
||||
var conf = this.defaults;
|
||||
var width = conf.width;
|
||||
var height = conf.height;
|
||||
var startX = this.startX;
|
||||
var startY = this.startY;
|
||||
var cellWidth = this.cellWidth;
|
||||
var tableWidth = this.tableWidth;
|
||||
var tableHeight = this.tableHeight;
|
||||
var bRectWidth = this.bRectWidth;
|
||||
var matrixWidth = this.matrixWidth;
|
||||
|
||||
//canvas.clear();
|
||||
// var color = this.getColor();
|
||||
// var font = this.getFont();
|
||||
var font_family = '微软雅黑';
|
||||
var font_size = 8;
|
||||
|
||||
var title = this.title;
|
||||
|
||||
var row = [];
|
||||
var columnLine = [];
|
||||
var columnText = [];
|
||||
|
||||
var backgroundRect = canvas.rect(startX, startY, bRectWidth, bRectWidth);
|
||||
//backgroundRect.attr({fill: "#939598", stroke: "none", "fill-opacity": 0.8});
|
||||
backgroundRect.attr({fill: "#ffffff", stroke: "none", "fill-opacity": 0.8});
|
||||
backgroundRect.toBack();
|
||||
|
||||
var sort;
|
||||
if (this.hasSort) {
|
||||
sort = this.sort;
|
||||
}
|
||||
var i, j, a, b, color, rect;
|
||||
var rects = []; //for column change move rect
|
||||
for (i = 0; i < tableHeight; i++) {
|
||||
if (!this.hasSort){
|
||||
a = i;
|
||||
} else {
|
||||
for (j = 0; j < sort.length; j++) {
|
||||
if (sort[j] === i) {
|
||||
a = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
var rowRect = canvas.set();
|
||||
canvas.path("M" + startX + " " + (startY + cellWidth * i) + "L" + (startX + matrixWidth + 10 + cellWidth) + " "
|
||||
+ (startY + cellWidth * i)).attr({stroke: "#D1D1D1", "stroke-width": 1});
|
||||
rowRect.push(canvas.text(-20, cellWidth / 2, title[i])
|
||||
.attr({"fill": "#000000",
|
||||
"fill-opacity": 0.7,
|
||||
"font-family": "Verdana",
|
||||
//"font-weight": "bold",
|
||||
"font-size": 12}));
|
||||
|
||||
for (j = 0; j < tableWidth; j++) {
|
||||
if (!this.hasSort) {
|
||||
color = this.getColor(source[i][j]);
|
||||
} else {
|
||||
color = this.getColor(source[i][sort[j]]);
|
||||
}
|
||||
rect = canvas.rect(cellWidth * j, 0, cellWidth, cellWidth)
|
||||
.attr({stroke: "none", fill: color, "fill-opacity": 0.8});
|
||||
rowRect.push(rect);
|
||||
rects.push(rect);
|
||||
}
|
||||
|
||||
rowRect.transform("t" + startX + ", " + (startY + cellWidth * a));
|
||||
row.push(rowRect);
|
||||
}
|
||||
|
||||
canvas.path("M" + startX + " " + (startY + cellWidth * tableHeight) + "L" + (startX + matrixWidth + 10 + cellWidth) + " "
|
||||
+ (startY + cellWidth * tableHeight)).attr({stroke: "#D1D1D1", "stroke-width": 1});
|
||||
|
||||
for (i = 0; i < tableWidth; i++) {
|
||||
// var columnLine = canvas.set();
|
||||
// var columnText = canvas.set();
|
||||
if (!this.hasSort){
|
||||
a = i;
|
||||
} else {
|
||||
for (j = 0; j < sort.length; j++) {
|
||||
if (sort[j] === i) {
|
||||
a = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
columnLine.push(canvas.path("M0 0L0 " + matrixWidth + 10 + cellWidth)
|
||||
.attr({stroke: "#D1D1D1", "stroke-width": 1})
|
||||
.transform("t" + (startX + cellWidth * a) + ", " + startY));
|
||||
columnText.push(canvas.text(cellWidth / 2, -20, title[i])
|
||||
.attr({"fill": "#000000",
|
||||
"fill-opacity": 0.7,
|
||||
"font-family": "Verdana",
|
||||
//"font-weight": "bold",
|
||||
"font-size": 12})
|
||||
.transform("t" + (startX + cellWidth * a) + ", " + startY + "r90"));
|
||||
}
|
||||
|
||||
columnLine.push(canvas.path("M0 0L0 " + matrixWidth + 10 + cellWidth)
|
||||
.attr({stroke: "#D1D1D1", "stroke-width": 1})
|
||||
.transform("t" + (startX + cellWidth * tableWidth) + ", " + startY));
|
||||
|
||||
this.row = row;
|
||||
this.columnText = columnText;
|
||||
this.columnLine = columnLine;
|
||||
this.rects = rects;
|
||||
};
|
||||
|
||||
Matrix.prototype.getSort = function (source) {
|
||||
var sumQueue = [];
|
||||
var sort = [];
|
||||
var rowData;
|
||||
var rowLength;
|
||||
var sum;
|
||||
var means;
|
||||
var matrixD = [];
|
||||
var quareSum;
|
||||
var rowquareSum = [];
|
||||
|
||||
var i, j, k;
|
||||
for (i = 0 ; i < source.length ; i++) {
|
||||
rowData = source[i];
|
||||
rowLength = rowData.length;
|
||||
sum = 0;
|
||||
quareSum = 0;
|
||||
|
||||
for (j = 0 ; j < rowLength ; j++) {
|
||||
sum = sum + rowData[j];
|
||||
}
|
||||
|
||||
means = sum / rowLength;
|
||||
for (j = 0 ; j < rowLength ; j++) {
|
||||
rowData[j] = rowData[j] - means;
|
||||
quareSum = quareSum + Math.pow(rowData[j], 2);
|
||||
}
|
||||
|
||||
quareSum = Math.sqrt(quareSum);
|
||||
|
||||
rowquareSum.push(quareSum);
|
||||
matrixD.push(rowData);
|
||||
}
|
||||
|
||||
var rowI;
|
||||
var rowJ;
|
||||
var matrixR = [];
|
||||
|
||||
for (i = 0 ; i < source.length ; i++) {
|
||||
matrixR[i] = [];
|
||||
for (j = 0 ; j < source.length ; j++) {
|
||||
matrixR[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0 ; i < source.length ; i++) {
|
||||
rowI = matrixD[i];
|
||||
matrixR[i][i] = source[i][i];
|
||||
for (j = i + 1 ; j < source.length ; j++) {
|
||||
sum = 0;
|
||||
rowJ = matrixD[j];
|
||||
for (k = 0; k < rowLength; k++) {
|
||||
sum = sum + rowI[k] * rowJ[k];
|
||||
}
|
||||
|
||||
sum = sum / (rowquareSum[i] * rowquareSum[j]);
|
||||
matrixR[i][j] = sum;
|
||||
matrixR[j][i] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return matrixR;
|
||||
};
|
||||
|
||||
Matrix.prototype.update = function () {
|
||||
var i, j;
|
||||
var source = [];
|
||||
for(i = 0; i < this.source.length ; i++){
|
||||
source[i] = this.source[i].concat();
|
||||
}
|
||||
|
||||
var sort = [];
|
||||
for (i = 0; i < source[0].length; i++) {
|
||||
sort.push(i);
|
||||
}
|
||||
|
||||
if (this.hasSort) {
|
||||
this.sort = sort;
|
||||
this.hasSort = false;
|
||||
} else {
|
||||
var getSort = this.getSort;
|
||||
var i, j;
|
||||
var pt;
|
||||
var nowSort = [];
|
||||
var iterations = 12;
|
||||
|
||||
for (i = 0; i < iterations; i++) {
|
||||
source = getSort(source);
|
||||
}
|
||||
|
||||
nowSort = source[0];
|
||||
|
||||
var a, b;
|
||||
for (i = 1; i < sort.length; i++) {
|
||||
a = sort[i];
|
||||
for (j = i + 1; j < sort.length; j++) {
|
||||
b = sort[j];
|
||||
if (nowSort[a] < nowSort[b]) {
|
||||
pt = sort[i];
|
||||
sort[i] = sort[j];
|
||||
sort[j] = pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
sort = [0,7,5,2,8,3,1,9,6,14,15,4,13,10,16,11,12];
|
||||
this.sort = sort;
|
||||
this.hasSort = true;
|
||||
}
|
||||
|
||||
if (!this.move) {
|
||||
this.move = true;
|
||||
var rects = this.rects;
|
||||
var num;
|
||||
var startX = this.startX;
|
||||
var startY = this.startY;
|
||||
var cellWidth = this.cellWidth;
|
||||
|
||||
var rowAnim;
|
||||
var columnLineAnim;
|
||||
var columnTextAnim;
|
||||
var anim;
|
||||
|
||||
for (i = 0; i < sort.length; i++) {
|
||||
num = sort[i];
|
||||
// if (num != i) {
|
||||
rowAnim = Raphael.animation({transform: ["t", startX, (startY + cellWidth * i)]}, 200, "<>");
|
||||
this.row[num].animate(rowAnim.delay(100 * i));
|
||||
// }
|
||||
}
|
||||
|
||||
var that = this;
|
||||
var moveEnd = function () {
|
||||
that.move = false;
|
||||
};
|
||||
|
||||
for (i = 0; i < sort.length; i++) {
|
||||
num = sort[i];
|
||||
// if (num != i) {
|
||||
//columnLineAnim = Raphael.animation({transform: ["t", (startX + cellWidth * i), startY]}, 1000, "<>");
|
||||
columnTextAnim = Raphael.animation({transform: ["t", (startX + cellWidth * i), startY, "r", 90]},
|
||||
200, "<>");
|
||||
//this.columnLine[num].animate(columnLineAnim.delay(500 * (i + sort.length + 1)));
|
||||
this.columnText[num].animate(columnTextAnim.delay(100 * (i + sort.length + 1)));
|
||||
|
||||
for (j = 0; j < sort.length; j++) {
|
||||
if (i === sort.length - 1 && j === sort.length - 1) {
|
||||
anim = Raphael.animation({'x': cellWidth * i}, 200, "<>", moveEnd);
|
||||
} else {
|
||||
anim = Raphael.animation({'x': cellWidth * i}, 200, "<>");
|
||||
}
|
||||
rects[j * sort.length + num].animate(anim.delay(100 * (i + sort.length + 1)));
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Matrix.prototype.render = function (options) {
|
||||
if (!this.move) {
|
||||
this.canvas.clear();
|
||||
this.setOptions(options);
|
||||
this.layout();
|
||||
this.generatePaths();
|
||||
}
|
||||
};
|
||||
|
||||
return Matrix;
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1160
lib/charts/tree.js
1160
lib/charts/tree.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user