9c34bc18870f206be3c377d90cacdf7e0da060be.svn-base 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. /**
  2. * echarts图表类:chord diagram
  3. *
  4. * @author pissang (https://github.com/pissang/)
  5. *
  6. * TODO 非Ribbon Type 支持 undirected graph ?
  7. */
  8. define(function (require) {
  9. 'use strict';
  10. var ChartBase = require('./base');
  11. // 图形依赖
  12. var TextShape = require('zrender/shape/Text');
  13. var LineShape = require('zrender/shape/Line');
  14. var SectorShape = require('zrender/shape/Sector');
  15. var RibbonShape = require('../util/shape/Ribbon');
  16. var IconShape = require('../util/shape/Icon');
  17. var BezierCurveShape = require('zrender/shape/BezierCurve');
  18. var ecConfig = require('../config');
  19. // 和弦图默认参数
  20. ecConfig.chord = {
  21. zlevel: 0, // 一级层叠
  22. z: 2, // 二级层叠
  23. clickable: true,
  24. radius: ['65%', '75%'],
  25. center: ['50%', '50%'],
  26. padding: 2,
  27. sort: 'none', // can be 'none', 'ascending', 'descending'
  28. sortSub: 'none', // can be 'none', 'ascending', 'descending'
  29. startAngle: 90,
  30. clockWise: true,
  31. ribbonType: true,
  32. /***************** 下面的配置项在 ribbonType 为 false 时有效 */
  33. // 同force类似
  34. minRadius: 10,
  35. maxRadius: 20,
  36. symbol: 'circle',
  37. /***************** 上面的配置项在 ribbonType 为 false 时有效 */
  38. /***************** 下面的配置项在 ribbonType 为 true 时有效 */
  39. showScale: false,
  40. showScaleText: false,
  41. /***************** 上面的配置项在 ribbonType 为 true 时有效 */
  42. // 分类里如果有样式会覆盖节点默认样式
  43. // categories: [{
  44. // itemStyle
  45. // symbol
  46. // symbolSize
  47. // name
  48. // }],
  49. itemStyle: {
  50. normal: {
  51. borderWidth: 0,
  52. borderColor: '#000',
  53. label: {
  54. show: true,
  55. rotate: false,
  56. distance: 5
  57. // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
  58. },
  59. chordStyle: {
  60. /** ribbonType = false 时有效 */
  61. width: 1,
  62. color: 'black',
  63. /** ribbonType = true 时有效 */
  64. borderWidth: 1,
  65. borderColor: '#999',
  66. opacity: 0.5
  67. }
  68. },
  69. emphasis: {
  70. borderWidth: 0,
  71. borderColor: '#000',
  72. chordStyle: {
  73. /** ribbonType = false 时有效 */
  74. width: 1,
  75. color: 'black',
  76. /** ribbonType = true 时有效 */
  77. borderWidth: 1,
  78. borderColor: '#999'
  79. }
  80. }
  81. }
  82. /****** 使用 Data-matrix 表示数据 */
  83. // data: [],
  84. // Source data matrix
  85. /**
  86. * target
  87. * -1--2--3--4--5-
  88. * 1| x x x x x
  89. * 2| x x x x x
  90. * 3| x x x x x source
  91. * 4| x x x x x
  92. * 5| x x x x x
  93. *
  94. * Relation ship from source to target
  95. * https://github.com/mbostock/d3/wiki/Chord-Layout#wiki-chord
  96. *
  97. * Row based
  98. */
  99. // matrix: [],
  100. /****** 使用 node-links 表示数据 */
  101. // 参考 force
  102. // nodes: [],
  103. // links: []
  104. };
  105. var ecData = require('../util/ecData');
  106. var zrUtil = require('zrender/tool/util');
  107. var vec2 = require('zrender/tool/vector');
  108. var Graph = require('../data/Graph');
  109. var ChordLayout = require('../layout/Chord');
  110. function Chord(ecTheme, messageCenter, zr, option, myChart) {
  111. // 图表基类
  112. ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
  113. this.scaleLineLength = 4;
  114. this.scaleUnitAngle = 4;
  115. this.refresh(option);
  116. }
  117. Chord.prototype = {
  118. type: ecConfig.CHART_TYPE_CHORD,
  119. /**
  120. * 绘制图形
  121. */
  122. _init: function () {
  123. var series = this.series;
  124. this.selectedMap = {};
  125. var chordSeriesMap = {};
  126. var chordSeriesGroups = {};
  127. for (var i = 0, l = series.length; i < l; i++) {
  128. if (series[i].type === this.type) {
  129. var _isSelected = this.isSelected(series[i].name);
  130. // Filter by selected serie
  131. this.selectedMap[series[i].name] = _isSelected;
  132. if (_isSelected) {
  133. this.buildMark(i);
  134. }
  135. this.reformOption(series[i]);
  136. chordSeriesMap[series[i].name] = series[i];
  137. }
  138. }
  139. for (var i = 0, l = series.length; i < l; i++) {
  140. if (series[i].type === this.type) {
  141. if (series[i].insertToSerie) {
  142. var referenceSerie = chordSeriesMap[series[i].insertToSerie];
  143. series[i]._referenceSerie = referenceSerie;
  144. }
  145. else {
  146. chordSeriesGroups[series[i].name] = [series[i]];
  147. }
  148. }
  149. }
  150. for (var i = 0, l = series.length; i < l; i++) {
  151. if (series[i].type === this.type) {
  152. if (series[i].insertToSerie) {
  153. // insertToSerie 可能会存在链式的使用,找到最原始的系列,分到一个 Group 里
  154. var mainSerie = series[i]._referenceSerie;
  155. while (mainSerie && mainSerie._referenceSerie) {
  156. mainSerie = mainSerie._referenceSerie;
  157. }
  158. if (
  159. chordSeriesGroups[mainSerie.name]
  160. && this.selectedMap[series[i].name]
  161. ) {
  162. chordSeriesGroups[mainSerie.name].push(series[i]);
  163. }
  164. }
  165. }
  166. }
  167. for (var name in chordSeriesGroups) {
  168. this._buildChords(chordSeriesGroups[name]);
  169. }
  170. this.addShapeList();
  171. },
  172. _getNodeCategory: function (serie, group) {
  173. return serie.categories && serie.categories[group.category || 0];
  174. },
  175. _getNodeQueryTarget: function (serie, group) {
  176. var category = this._getNodeCategory(serie, group);
  177. return [group, category, serie];
  178. },
  179. _getEdgeQueryTarget: function (serie, edge, type) {
  180. type = type || 'normal';
  181. return [
  182. (edge.itemStyle && edge.itemStyle[type]),
  183. serie.itemStyle[type].chordStyle
  184. ];
  185. },
  186. _buildChords: function (series) {
  187. var graphs = [];
  188. var mainSerie = series[0];
  189. var nodeFilter = function (n) {
  190. return n.layout.size > 0;
  191. };
  192. var createEdgeFilter = function (graph) {
  193. return function (e) {
  194. return graph.getEdge(e.node2, e.node1);
  195. };
  196. };
  197. for (var i = 0; i < series.length; i++) {
  198. var serie = series[i];
  199. if (this.selectedMap[serie.name]) {
  200. var graph;
  201. // matrix 表示边
  202. if (serie.matrix) {
  203. graph = this._getSerieGraphFromDataMatrix(
  204. serie, mainSerie
  205. );
  206. }
  207. // links 表示边
  208. else if (serie.links) {
  209. graph = this._getSerieGraphFromNodeLinks(
  210. serie, mainSerie
  211. );
  212. }
  213. // 过滤输出为0的节点
  214. graph.filterNode(nodeFilter, this);
  215. if (serie.ribbonType) {
  216. graph.filterEdge(createEdgeFilter(graph));
  217. }
  218. graphs.push(graph);
  219. graph.__serie = serie;
  220. }
  221. }
  222. if (!graphs.length) {
  223. return;
  224. }
  225. var mainGraph = graphs[0];
  226. if (!mainSerie.ribbonType) {
  227. var minRadius = mainSerie.minRadius;
  228. var maxRadius = mainSerie.maxRadius;
  229. // Map size to [minRadius, maxRadius]
  230. var min = Infinity, max = -Infinity;
  231. mainGraph.eachNode(function (node) {
  232. max = Math.max(node.layout.size, max);
  233. min = Math.min(node.layout.size, min);
  234. });
  235. var multiplier = (maxRadius - minRadius) / (max - min);
  236. mainGraph.eachNode(function (node) {
  237. var queryTarget = this._getNodeQueryTarget(mainSerie, node);
  238. var symbolSize = this.query(queryTarget, 'symbolSize');
  239. if (max === min) {
  240. node.layout.size = symbolSize || min;
  241. }
  242. else {
  243. node.layout.size = symbolSize
  244. || (node.layout.size - min) * multiplier + minRadius;
  245. }
  246. }, this);
  247. }
  248. // Do layout
  249. var layout = new ChordLayout();
  250. layout.clockWise = mainSerie.clockWise;
  251. layout.startAngle = mainSerie.startAngle * Math.PI / 180;
  252. if (!layout.clockWise) {
  253. layout.startAngle = -layout.startAngle;
  254. }
  255. layout.padding = mainSerie.padding * Math.PI / 180;
  256. layout.sort = mainSerie.sort;
  257. layout.sortSub = mainSerie.sortSub;
  258. layout.directed = mainSerie.ribbonType;
  259. layout.run(graphs);
  260. var showLabel = this.query(
  261. mainSerie, 'itemStyle.normal.label.show'
  262. );
  263. if (mainSerie.ribbonType) {
  264. this._buildSectors(mainSerie, 0, mainGraph, mainSerie, graphs);
  265. if (showLabel) {
  266. this._buildLabels(mainSerie, 0, mainGraph, mainSerie, graphs);
  267. }
  268. for (var i = 0, j = 0; i < series.length; i++) {
  269. if (this.selectedMap[series[i].name]) {
  270. this._buildRibbons(series, i, graphs[j++], mainSerie);
  271. }
  272. }
  273. if (mainSerie.showScale) {
  274. this._buildScales(mainSerie, 0, mainGraph);
  275. }
  276. }
  277. else {
  278. this._buildNodeIcons(mainSerie, 0, mainGraph, mainSerie, graphs);
  279. if (showLabel) {
  280. this._buildLabels(mainSerie, 0, mainGraph, mainSerie, graphs);
  281. }
  282. for (var i = 0, j = 0; i < series.length; i++) {
  283. if (this.selectedMap[series[i].name]) {
  284. this._buildEdgeCurves(series, i, graphs[j++], mainSerie, mainGraph);
  285. }
  286. }
  287. }
  288. this._initHoverHandler(series, graphs);
  289. },
  290. _getSerieGraphFromDataMatrix: function (serie, mainSerie) {
  291. var nodesData = [];
  292. var count = 0;
  293. var matrix = [];
  294. // 复制一份新的matrix
  295. for (var i = 0; i < serie.matrix.length; i++) {
  296. matrix[i] = serie.matrix[i].slice();
  297. }
  298. var data = serie.data || serie.nodes;
  299. for (var i = 0; i < data.length; i++) {
  300. var node = {};
  301. var group = data[i];
  302. group.rawIndex = i;
  303. for (var key in group) {
  304. // name改为id
  305. if (key === 'name') {
  306. node['id'] = group['name'];
  307. }
  308. else {
  309. node[key] = group[key];
  310. }
  311. }
  312. // legends 选择优先级 category -> group
  313. var category = this._getNodeCategory(mainSerie, group);
  314. var name = category ? category.name : group.name;
  315. this.selectedMap[name] = this.isSelected(name);
  316. if (this.selectedMap[name]) {
  317. nodesData.push(node);
  318. count++;
  319. }
  320. else {
  321. // 过滤legend未选中的数据
  322. matrix.splice(count, 1);
  323. for (var j = 0; j < matrix.length; j++) {
  324. matrix[j].splice(count, 1);
  325. }
  326. }
  327. }
  328. var graph = Graph.fromMatrix(nodesData, matrix, true);
  329. // Prepare layout parameters
  330. graph.eachNode(function (n, idx) {
  331. n.layout = {
  332. size: n.data.outValue
  333. };
  334. n.rawIndex = n.data.rawIndex;
  335. });
  336. graph.eachEdge(function (e) {
  337. e.layout = {
  338. weight: e.data.weight
  339. };
  340. });
  341. return graph;
  342. },
  343. _getSerieGraphFromNodeLinks: function (serie, mainSerie) {
  344. var graph = new Graph(true);
  345. var nodes = serie.data || serie.nodes;
  346. for (var i = 0, len = nodes.length; i < len; i++) {
  347. var n = nodes[i];
  348. if (!n || n.ignore) {
  349. continue;
  350. }
  351. // legends 选择优先级 category -> group
  352. var category = this._getNodeCategory(mainSerie, n);
  353. var name = category ? category.name : n.name;
  354. this.selectedMap[name] = this.isSelected(name);
  355. if (this.selectedMap[name]) {
  356. var node = graph.addNode(n.name, n);
  357. node.rawIndex = i;
  358. }
  359. }
  360. for (var i = 0, len = serie.links.length; i < len; i++) {
  361. var e = serie.links[i];
  362. var n1 = e.source;
  363. var n2 = e.target;
  364. if (typeof(n1) === 'number') {
  365. n1 = nodes[n1];
  366. if (n1) {
  367. n1 = n1.name;
  368. }
  369. }
  370. if (typeof(n2) === 'number') {
  371. n2 = nodes[n2];
  372. if (n2) {
  373. n2 = n2.name;
  374. }
  375. }
  376. var edge = graph.addEdge(n1, n2, e);
  377. if (edge) {
  378. edge.rawIndex = i;
  379. }
  380. }
  381. graph.eachNode(function (n) {
  382. var value = n.data.value;
  383. if (value == null) { // value 是 null 或者 undefined
  384. value = 0;
  385. if (mainSerie.ribbonType) {
  386. // 默认使用所有出边值的和作为节点的大小, 不修改 data 里的数值
  387. for (var i = 0; i < n.outEdges.length; i++) {
  388. value += n.outEdges[i].data.weight || 0;
  389. }
  390. }
  391. else {
  392. // 默认使用所有边值的和作为节点的大小, 不修改 data 里的数值
  393. for (var i = 0; i < n.edges.length; i++) {
  394. value += n.edges[i].data.weight || 0;
  395. }
  396. }
  397. }
  398. n.layout = {
  399. size: value
  400. };
  401. });
  402. graph.eachEdge(function (e) {
  403. e.layout = {
  404. // 默认 weight 为1
  405. weight: e.data.weight == null ? 1 : e.data.weight
  406. };
  407. });
  408. return graph;
  409. },
  410. _initHoverHandler: function (series, graphs) {
  411. var mainSerie = series[0];
  412. var mainGraph = graphs[0];
  413. var self = this;
  414. mainGraph.eachNode(function (node) {
  415. node.shape.onmouseover = function () {
  416. mainGraph.eachNode(function (n) {
  417. n.shape.style.opacity = 0.1;
  418. if (n.labelShape) {
  419. n.labelShape.style.opacity = 0.1;
  420. n.labelShape.modSelf();
  421. }
  422. n.shape.modSelf();
  423. });
  424. for (var i = 0; i < graphs.length; i++) {
  425. for (var j = 0; j < graphs[i].edges.length; j++) {
  426. var e = graphs[i].edges[j];
  427. var queryTarget = self._getEdgeQueryTarget(
  428. graphs[i].__serie, e.data
  429. );
  430. e.shape.style.opacity = self.deepQuery(
  431. queryTarget, 'opacity'
  432. ) * 0.1;
  433. e.shape.modSelf();
  434. }
  435. }
  436. node.shape.style.opacity = 1;
  437. if (node.labelShape) {
  438. node.labelShape.style.opacity = 1;
  439. }
  440. for (var i = 0; i < graphs.length; i++) {
  441. var n = graphs[i].getNodeById(node.id);
  442. if (n) { // 节点有可能没数据被过滤掉了
  443. for (var j = 0; j < n.outEdges.length; j++) {
  444. var e = n.outEdges[j];
  445. var queryTarget = self._getEdgeQueryTarget(
  446. graphs[i].__serie, e.data
  447. );
  448. e.shape.style.opacity = self.deepQuery(
  449. queryTarget, 'opacity'
  450. );
  451. var other = graphs[0].getNodeById(e.node2.id);
  452. if (other) {
  453. if (other.shape) {
  454. other.shape.style.opacity = 1;
  455. }
  456. if (other.labelShape) {
  457. other.labelShape.style.opacity = 1;
  458. }
  459. }
  460. }
  461. }
  462. }
  463. self.zr.refreshNextFrame();
  464. };
  465. node.shape.onmouseout = function () {
  466. mainGraph.eachNode(function (n) {
  467. n.shape.style.opacity = 1;
  468. if (n.labelShape) {
  469. n.labelShape.style.opacity = 1;
  470. n.labelShape.modSelf();
  471. }
  472. n.shape.modSelf();
  473. });
  474. for (var i = 0; i < graphs.length; i++) {
  475. for (var j = 0; j < graphs[i].edges.length; j++) {
  476. var e = graphs[i].edges[j];
  477. var queryTarget = [e.data, mainSerie];
  478. e.shape.style.opacity = self.deepQuery(
  479. queryTarget, 'itemStyle.normal.chordStyle.opacity'
  480. );
  481. e.shape.modSelf();
  482. }
  483. }
  484. self.zr.refreshNextFrame();
  485. };
  486. });
  487. },
  488. _buildSectors: function (serie, serieIdx, graph, mainSerie) {
  489. var center = this.parseCenter(this.zr, mainSerie.center);
  490. var radius = this.parseRadius(this.zr, mainSerie.radius);
  491. var clockWise = mainSerie.clockWise;
  492. var sign = clockWise ? 1 : -1;
  493. graph.eachNode(function (node) {
  494. var category = this._getNodeCategory(mainSerie, node.data);
  495. // 默认使用 category 分类颜色
  496. var color = category ? this.getColor(category.name) : this.getColor(node.id);
  497. var startAngle = node.layout.startAngle / Math.PI * 180 * sign;
  498. var endAngle = node.layout.endAngle / Math.PI * 180 * sign;
  499. var sector = new SectorShape({
  500. zlevel: serie.zlevel,
  501. z : serie.z,
  502. style: {
  503. x: center[0],
  504. y: center[1],
  505. r0: radius[0],
  506. r: radius[1],
  507. startAngle: startAngle,
  508. endAngle: endAngle,
  509. brushType: 'fill',
  510. opacity: 1,
  511. color: color,
  512. clockWise: clockWise
  513. },
  514. clickable: mainSerie.clickable,
  515. highlightStyle: {
  516. brushType: 'fill'
  517. }
  518. });
  519. sector.style.lineWidth = this.deepQuery(
  520. [node.data, mainSerie],
  521. 'itemStyle.normal.borderWidth'
  522. );
  523. sector.highlightStyle.lineWidth = this.deepQuery(
  524. [node.data, mainSerie],
  525. 'itemStyle.emphasis.borderWidth'
  526. );
  527. sector.style.strokeColor = this.deepQuery(
  528. [node.data, mainSerie],
  529. 'itemStyle.normal.borderColor'
  530. );
  531. sector.highlightStyle.strokeColor = this.deepQuery(
  532. [node.data, mainSerie],
  533. 'itemStyle.emphasis.borderColor'
  534. );
  535. if (sector.style.lineWidth > 0) {
  536. sector.style.brushType = 'both';
  537. }
  538. if (sector.highlightStyle.lineWidth > 0) {
  539. sector.highlightStyle.brushType = 'both';
  540. }
  541. ecData.pack(
  542. sector,
  543. serie,
  544. serieIdx,
  545. node.data,
  546. node.rawIndex,
  547. node.id,
  548. // special
  549. node.category
  550. );
  551. this.shapeList.push(sector);
  552. node.shape = sector;
  553. }, this);
  554. },
  555. _buildNodeIcons: function (serie, serieIdx, graph, mainSerie) {
  556. var center = this.parseCenter(this.zr, mainSerie.center);
  557. var radius = this.parseRadius(this.zr, mainSerie.radius);
  558. // PENDING
  559. var r = radius[1];
  560. graph.eachNode(function (node) {
  561. var startAngle = node.layout.startAngle;
  562. var endAngle = node.layout.endAngle;
  563. var angle = (startAngle + endAngle) / 2;
  564. var x = r * Math.cos(angle);
  565. var y = r * Math.sin(angle);
  566. var queryTarget = this._getNodeQueryTarget(mainSerie, node.data);
  567. var category = this._getNodeCategory(mainSerie, node.data);
  568. var color = this.deepQuery(queryTarget, 'itemStyle.normal.color');
  569. if (!color) {
  570. color = category ? this.getColor(category.name) : this.getColor(node.id);
  571. }
  572. var iconShape = new IconShape({
  573. zlevel: serie.zlevel,
  574. z: serie.z + 1,
  575. style: {
  576. x: - node.layout.size,
  577. y: - node.layout.size,
  578. width: node.layout.size * 2,
  579. height: node.layout.size * 2,
  580. iconType: this.deepQuery(queryTarget, 'symbol'),
  581. color: color,
  582. brushType: 'both',
  583. lineWidth: this.deepQuery(queryTarget, 'itemStyle.normal.borderWidth'),
  584. strokeColor: this.deepQuery(queryTarget, 'itemStyle.normal.borderColor')
  585. },
  586. highlightStyle: {
  587. color: this.deepQuery(queryTarget, 'itemStyle.emphasis.color'),
  588. lineWidth: this.deepQuery(queryTarget, 'itemStyle.emphasis.borderWidth'),
  589. strokeColor: this.deepQuery(queryTarget, 'itemStyle.emphasis.borderColor')
  590. },
  591. clickable: mainSerie.clickable,
  592. position: [x + center[0], y + center[1]]
  593. });
  594. ecData.pack(
  595. iconShape,
  596. serie,
  597. serieIdx,
  598. node.data,
  599. node.rawIndex,
  600. node.id,
  601. // special
  602. node.category
  603. );
  604. this.shapeList.push(iconShape);
  605. node.shape = iconShape;
  606. }, this);
  607. },
  608. _buildLabels: function (serie, serieIdx, graph, mainSerie) {
  609. // var labelColor = this.query(
  610. // mainSerie, 'itemStyle.normal.label.color'
  611. // );
  612. var rotateLabel = this.query(
  613. mainSerie, 'itemStyle.normal.label.rotate'
  614. );
  615. var labelDistance = this.query(
  616. mainSerie, 'itemStyle.normal.label.distance'
  617. );
  618. var center = this.parseCenter(this.zr, mainSerie.center);
  619. var radius = this.parseRadius(this.zr, mainSerie.radius);
  620. var clockWise = mainSerie.clockWise;
  621. var sign = clockWise ? 1 : -1;
  622. graph.eachNode(function (node) {
  623. var startAngle = node.layout.startAngle / Math.PI * 180 * sign;
  624. var endAngle = node.layout.endAngle / Math.PI * 180 * sign;
  625. var angle = (startAngle * -sign + endAngle * -sign) / 2;
  626. angle %= 360;
  627. if (angle < 0) { // Constrain to [0,360]
  628. angle += 360;
  629. }
  630. var isRightSide = angle <= 90
  631. || angle >= 270;
  632. angle = angle * Math.PI / 180;
  633. var v = [Math.cos(angle), -Math.sin(angle)];
  634. var distance = 0;
  635. if (mainSerie.ribbonType) {
  636. distance = mainSerie.showScaleText ? 35 + labelDistance : labelDistance;
  637. }
  638. else {
  639. distance = labelDistance + node.layout.size;
  640. }
  641. var start = vec2.scale([], v, radius[1] + distance);
  642. vec2.add(start, start, center);
  643. var labelShape = {
  644. zlevel: serie.zlevel,
  645. z: serie.z + 1,
  646. hoverable: false,
  647. style: {
  648. text: node.data.label == null ? node.id : node.data.label,
  649. textAlign: isRightSide ? 'left' : 'right'
  650. }
  651. };
  652. if (rotateLabel) {
  653. labelShape.rotation = isRightSide ? angle : Math.PI + angle;
  654. if (isRightSide) {
  655. labelShape.style.x = radius[1] + distance;
  656. }
  657. else {
  658. labelShape.style.x = -radius[1] - distance;
  659. }
  660. labelShape.style.y = 0;
  661. labelShape.position = center.slice();
  662. }
  663. else {
  664. labelShape.style.x = start[0];
  665. labelShape.style.y = start[1];
  666. }
  667. // zrender/Text并没有textColor属性,ctx fillStyle使用的是color
  668. labelShape.style.color = this.deepQuery(
  669. [node.data, mainSerie],
  670. 'itemStyle.normal.label.textStyle.color'
  671. ) || '#000000';
  672. labelShape.style.textFont = this.getFont(this.deepQuery(
  673. [node.data, mainSerie],
  674. 'itemStyle.normal.label.textStyle'
  675. ));
  676. labelShape = new TextShape(labelShape);
  677. this.shapeList.push(labelShape);
  678. node.labelShape = labelShape;
  679. }, this);
  680. },
  681. _buildRibbons : function (series, serieIdx, graph, mainSerie) {
  682. var serie = series[serieIdx];
  683. var center = this.parseCenter(this.zr, mainSerie.center);
  684. var radius = this.parseRadius(this.zr, mainSerie.radius);
  685. // graph.edges.length = 1;
  686. graph.eachEdge(function (edge, idx) {
  687. var color;
  688. // 反向边
  689. var other = graph.getEdge(edge.node2, edge.node1);
  690. if (!other // 只有单边
  691. || edge.shape // 已经创建过Ribbon
  692. ) {
  693. return;
  694. }
  695. if (other.shape) { // 已经创建过Ribbon
  696. edge.shape = other.shape;
  697. return;
  698. }
  699. var s0 = edge.layout.startAngle / Math.PI * 180;
  700. var s1 = edge.layout.endAngle / Math.PI * 180;
  701. var t0 = other.layout.startAngle / Math.PI * 180;
  702. var t1 = other.layout.endAngle / Math.PI * 180;
  703. if (series.length === 1) {
  704. // 取小端的颜色
  705. if (edge.layout.weight <= other.layout.weight) {
  706. color = this.getColor(edge.node1.id);
  707. }
  708. else {
  709. color = this.getColor(edge.node2.id);
  710. }
  711. } else {
  712. // 使用系列颜色
  713. color = this.getColor(serie.name);
  714. }
  715. var queryTarget = this._getEdgeQueryTarget(serie, edge.data);
  716. var queryTargetEmphasis = this._getEdgeQueryTarget(
  717. serie, edge.data, 'emphasis'
  718. );
  719. var ribbon = new RibbonShape({
  720. zlevel: serie.zlevel,
  721. z: serie.z,
  722. style: {
  723. x: center[0],
  724. y: center[1],
  725. r: radius[0],
  726. source0: s0,
  727. source1: s1,
  728. target0: t0,
  729. target1: t1,
  730. brushType: 'both',
  731. opacity: this.deepQuery(
  732. queryTarget, 'opacity'
  733. ),
  734. color: color,
  735. lineWidth: this.deepQuery(queryTarget, 'borderWidth'),
  736. strokeColor: this.deepQuery(queryTarget, 'borderColor'),
  737. clockWise: mainSerie.clockWise
  738. },
  739. clickable: mainSerie.clickable,
  740. highlightStyle: {
  741. brushType: 'both',
  742. opacity: this.deepQuery(
  743. queryTargetEmphasis, 'opacity'
  744. ),
  745. lineWidth: this.deepQuery(queryTargetEmphasis, 'borderWidth'),
  746. strokeColor: this.deepQuery(queryTargetEmphasis, 'borderColor')
  747. }
  748. });
  749. var node1, node2;
  750. // 从大端到小端
  751. if (edge.layout.weight <= other.layout.weight) {
  752. node1 = other.node1;
  753. node2 = other.node2;
  754. } else {
  755. node1 = edge.node1;
  756. node2 = edge.node2;
  757. }
  758. ecData.pack(
  759. ribbon,
  760. serie,
  761. serieIdx,
  762. edge.data,
  763. edge.rawIndex == null ? idx : edge.rawIndex,
  764. edge.data.name || (node1.id + '-' + node2.id),
  765. // special
  766. node1.id,
  767. // special2
  768. node2.id
  769. );
  770. this.shapeList.push(ribbon);
  771. edge.shape = ribbon;
  772. }, this);
  773. },
  774. _buildEdgeCurves: function (series, serieIdx, graph, mainSerie, mainGraph) {
  775. var serie = series[serieIdx];
  776. var center = this.parseCenter(this.zr, mainSerie.center);
  777. graph.eachEdge(function (e, idx) {
  778. var node1 = mainGraph.getNodeById(e.node1.id);
  779. var node2 = mainGraph.getNodeById(e.node2.id);
  780. var shape1 = node1.shape;
  781. var shape2 = node2.shape;
  782. var queryTarget = this._getEdgeQueryTarget(serie, e.data);
  783. var queryTargetEmphasis = this._getEdgeQueryTarget(
  784. serie, e.data, 'emphasis'
  785. );
  786. var curveShape = new BezierCurveShape({
  787. zlevel: serie.zlevel,
  788. z: serie.z,
  789. style: {
  790. xStart: shape1.position[0],
  791. yStart: shape1.position[1],
  792. xEnd: shape2.position[0],
  793. yEnd: shape2.position[1],
  794. cpX1: center[0],
  795. cpY1: center[1],
  796. lineWidth: this.deepQuery(
  797. queryTarget, 'width'
  798. ),
  799. strokeColor: this.deepQuery(
  800. queryTarget, 'color'
  801. ),
  802. opacity: this.deepQuery(
  803. queryTarget, 'opacity'
  804. )
  805. },
  806. highlightStyle: {
  807. lineWidth: this.deepQuery(
  808. queryTargetEmphasis, 'width'
  809. ),
  810. strokeColor: this.deepQuery(
  811. queryTargetEmphasis, 'color'
  812. ),
  813. opacity: this.deepQuery(
  814. queryTargetEmphasis, 'opacity'
  815. )
  816. }
  817. });
  818. ecData.pack(
  819. curveShape,
  820. serie,
  821. serieIdx,
  822. e.data,
  823. e.rawIndex == null ? idx : e.rawIndex,
  824. e.data.name || (e.node1.id + '-' + e.node2.id),
  825. // special
  826. e.node1.id,
  827. // special2
  828. e.node2.id
  829. );
  830. this.shapeList.push(curveShape);
  831. e.shape = curveShape;
  832. }, this);
  833. },
  834. _buildScales: function (serie, serieIdx, graph) {
  835. var clockWise = serie.clockWise;
  836. var center = this.parseCenter(this.zr, serie.center);
  837. var radius = this.parseRadius(this.zr, serie.radius);
  838. var sign = clockWise ? 1 : -1;
  839. var sumValue = 0;
  840. var maxValue = -Infinity;
  841. var unitPostfix;
  842. var unitScale;
  843. if (serie.showScaleText) {
  844. graph.eachNode(function (node) {
  845. var val = node.data.value;
  846. if (val > maxValue) {
  847. maxValue = val;
  848. }
  849. sumValue += val;
  850. });
  851. if (maxValue > 1e10) {
  852. unitPostfix = 'b';
  853. unitScale = 1e-9;
  854. }
  855. else if (maxValue > 1e7) {
  856. unitPostfix = 'm';
  857. unitScale = 1e-6;
  858. }
  859. else if (maxValue > 1e4) {
  860. unitPostfix = 'k';
  861. unitScale = 1e-3;
  862. }
  863. else {
  864. unitPostfix = '';
  865. unitScale = 1;
  866. }
  867. }
  868. var unitValue = sumValue / (360 - serie.padding);
  869. graph.eachNode(function (node) {
  870. var startAngle = node.layout.startAngle / Math.PI * 180;
  871. var endAngle = node.layout.endAngle / Math.PI * 180;
  872. var scaleAngle = startAngle;
  873. while (true) {
  874. if ((clockWise && scaleAngle > endAngle)
  875. || (!clockWise && scaleAngle < endAngle)
  876. ) {
  877. break;
  878. }
  879. var theta = scaleAngle / 180 * Math.PI;
  880. var v = [Math.cos(theta), Math.sin(theta)];
  881. var start = vec2.scale([], v, radius[1] + 1);
  882. vec2.add(start, start, center);
  883. var end = vec2.scale([], v, radius[1] + this.scaleLineLength);
  884. vec2.add(end, end, center);
  885. var scaleShape = new LineShape({
  886. zlevel: serie.zlevel,
  887. z: serie.z - 1,
  888. hoverable: false,
  889. style: {
  890. xStart: start[0],
  891. yStart: start[1],
  892. xEnd: end[0],
  893. yEnd: end[1],
  894. lineCap: 'round',
  895. brushType: 'stroke',
  896. strokeColor: '#666',
  897. lineWidth: 1
  898. }
  899. });
  900. this.shapeList.push(scaleShape);
  901. scaleAngle += sign * this.scaleUnitAngle;
  902. }
  903. if (!serie.showScaleText) {
  904. return;
  905. }
  906. var scaleTextAngle = startAngle;
  907. var step = unitValue * 5 * this.scaleUnitAngle;
  908. var scaleValue = 0;
  909. while (true) {
  910. if ((clockWise && scaleTextAngle > endAngle)
  911. || (!clockWise && scaleTextAngle < endAngle)
  912. ) {
  913. break;
  914. }
  915. var theta = scaleTextAngle;
  916. theta = theta % 360;
  917. if (theta < 0) {
  918. theta += 360;
  919. }
  920. var isRightSide = theta <= 90
  921. || theta >= 270;
  922. var textShape = new TextShape({
  923. zlevel: serie.zlevel,
  924. z: serie.z - 1,
  925. hoverable: false,
  926. style: {
  927. x: isRightSide
  928. ? radius[1] + this.scaleLineLength + 4
  929. : -radius[1] - this.scaleLineLength - 4,
  930. y: 0,
  931. text: Math.round(scaleValue * 10) / 10
  932. + unitPostfix,
  933. textAlign: isRightSide ? 'left' : 'right'
  934. },
  935. position: center.slice(),
  936. rotation: isRightSide
  937. ? [-theta / 180 * Math.PI, 0, 0]
  938. : [
  939. -(theta + 180) / 180 * Math.PI,
  940. 0, 0
  941. ]
  942. });
  943. this.shapeList.push(textShape);
  944. scaleValue += step * unitScale;
  945. scaleTextAngle += sign * this.scaleUnitAngle * 5;
  946. }
  947. }, this);
  948. },
  949. refresh : function (newOption) {
  950. if (newOption) {
  951. this.option = newOption;
  952. this.series = newOption.series;
  953. }
  954. this.legend = this.component.legend;
  955. if (this.legend) {
  956. this.getColor = function(param) {
  957. return this.legend.getColor(param);
  958. };
  959. this.isSelected = function(param) {
  960. return this.legend.isSelected(param);
  961. };
  962. }
  963. else {
  964. var colorMap = {};
  965. var count = 0;
  966. this.getColor = function (key) {
  967. if (colorMap[key]) {
  968. return colorMap[key];
  969. }
  970. if (!colorMap[key]) {
  971. colorMap[key] = this.zr.getColor(count++);
  972. }
  973. return colorMap[key];
  974. };
  975. this.isSelected = function () {
  976. return true;
  977. };
  978. }
  979. this.backupShapeList();
  980. this._init();
  981. },
  982. reformOption : function (opt) {
  983. var _merge = zrUtil.merge;
  984. opt = _merge(
  985. _merge(
  986. opt || {},
  987. this.ecTheme.chord
  988. ),
  989. ecConfig.chord
  990. );
  991. opt.itemStyle.normal.label.textStyle = this.getTextStyle(
  992. opt.itemStyle.normal.label.textStyle
  993. );
  994. this.z = opt.z;
  995. this.zlevel = opt.zlevel;
  996. }
  997. };
  998. zrUtil.inherits(Chord, ChartBase);
  999. // 图表注册
  1000. require('../chart').define('chord', Chord);
  1001. return Chord;
  1002. });