768fb9fcef69a172a2f4d9b60a05d21555306d66.svn-base 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. /**
  2. * echarts图表类:力导向图
  3. *
  4. * @author pissang (https://github.com/pissang/)
  5. *
  6. */
  7. define(function (require) {
  8. 'use strict';
  9. var ChartBase = require('./base');
  10. var Graph = require('../data/Graph');
  11. var ForceLayout = require('../layout/Force');
  12. // 图形依赖
  13. var LineShape = require('zrender/shape/Line');
  14. var BezierCurveShape = require('zrender/shape/BezierCurve');
  15. var ImageShape = require('zrender/shape/Image');
  16. var IconShape = require('../util/shape/Icon');
  17. var ecConfig = require('../config');
  18. // 力导向布局图默认参数
  19. ecConfig.force = {
  20. zlevel: 1, // 一级层叠
  21. z: 2, // 二级层叠
  22. // 布局中心
  23. center: ['50%', '50%'],
  24. // 布局大小
  25. size: '100%',
  26. // 防止节点和节点,节点和边之间的重叠
  27. preventOverlap: false,
  28. // 布局冷却因子,值越小结束时间越短,值越大时间越长但是结果也越收敛
  29. coolDown: 0.99,
  30. // 数据映射到圆的半径的最小值和最大值
  31. minRadius: 10,
  32. maxRadius: 20,
  33. // 是否根据屏幕比例拉伸
  34. ratioScaling: false,
  35. // 在 500+ 顶点的图上建议设置 large 为 true, 会使用 Barnes-Hut simulation
  36. // 同时开启 useWorker 并且把 steps 值调大
  37. // 关于Barnes-Hut simulation: http://en.wikipedia.org/wiki/Barnes–Hut_simulation
  38. large: false,
  39. // 是否在浏览器支持 worker 的时候使用 web worker
  40. useWorker: false,
  41. // 每一帧 force 迭代的次数,仅在启用webworker的情况下有用
  42. steps: 1,
  43. // 布局缩放因子,并不完全精确, 效果跟布局大小类似
  44. scaling: 1.0,
  45. // 向心力因子,越大向心力越大( 所有顶点会往 center 的位置收拢 )
  46. gravity: 1,
  47. symbol: 'circle',
  48. // symbolSize 为 0 的话使用映射到minRadius-maxRadius后的值
  49. symbolSize: 0,
  50. linkSymbol: null,
  51. linkSymbolSize: [10, 15],
  52. draggable: true,
  53. clickable: true,
  54. roam: false,
  55. // 分类里如果有样式会覆盖节点默认样式
  56. // categories: [{
  57. // itemStyle
  58. // symbol
  59. // symbolSize
  60. // name
  61. // }],
  62. itemStyle: {
  63. normal: {
  64. // color: 各异,
  65. label: {
  66. show: false,
  67. position: 'inside'
  68. // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
  69. },
  70. nodeStyle: {
  71. brushType : 'both',
  72. borderColor : '#5182ab',
  73. borderWidth: 1
  74. },
  75. linkStyle: {
  76. color: '#5182ab',
  77. width: 1,
  78. type: 'line'
  79. }
  80. },
  81. emphasis: {
  82. // color: 各异,
  83. label: {
  84. show: false
  85. // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
  86. },
  87. nodeStyle: {},
  88. linkStyle: {
  89. opacity: 0
  90. }
  91. }
  92. }
  93. // nodes: [{
  94. // name: 'xxx',
  95. // value: 1,
  96. // itemStyle: {},
  97. // initial: [0, 0],
  98. // fixX: false,
  99. // fixY: false,
  100. // ignore: false,
  101. // symbol: 'circle',
  102. // symbolSize: 0
  103. // }]
  104. // links: [{
  105. // source: 1,
  106. // target: 2,
  107. // weight: 1,
  108. // itemStyle: {}
  109. // }, {
  110. // source: 'xxx',
  111. // target: 'ooo'
  112. // }]
  113. };
  114. var ecData = require('../util/ecData');
  115. var zrUtil = require('zrender/tool/util');
  116. var zrConfig = require('zrender/config');
  117. var vec2 = require('zrender/tool/vector');
  118. /**
  119. * 构造函数
  120. * @param {Object} messageCenter echart消息中心
  121. * @param {ZRender} zr zrender实例
  122. * @param {Object} series 数据
  123. * @param {Object} component 组件
  124. */
  125. function Force(ecTheme, messageCenter, zr, option, myChart) {
  126. var self = this;
  127. // 图表基类
  128. ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
  129. // 保存节点的位置,改变数据时能够有更好的动画效果
  130. this.__nodePositionMap = {};
  131. this._graph = new Graph(true);
  132. this._layout = new ForceLayout();
  133. this._layout.onupdate = function() {
  134. self._step();
  135. };
  136. this._steps = 1;
  137. // 关闭可拖拽属性
  138. this.ondragstart = function() {
  139. ondragstart.apply(self, arguments);
  140. };
  141. this.ondragend = function() {
  142. ondragend.apply(self, arguments);
  143. };
  144. this.ondrop = function() {};
  145. this.shapeHandler.ondragstart = function() {
  146. self.isDragstart = true;
  147. };
  148. this.onmousemove = function() {
  149. onmousemove.apply(self, arguments);
  150. };
  151. this.refresh(option);
  152. }
  153. /**
  154. * 绘制图形
  155. */
  156. Force.prototype = {
  157. constructor: Force,
  158. type : ecConfig.CHART_TYPE_FORCE,
  159. _init: function() {
  160. this.selectedMap = {};
  161. var legend = this.component.legend;
  162. var series = this.series;
  163. var serieName;
  164. this.clear();
  165. for (var i = 0, l = series.length; i < l; i++) {
  166. var serie = series[i];
  167. if (serie.type === ecConfig.CHART_TYPE_FORCE) {
  168. series[i] = this.reformOption(series[i]);
  169. serieName = series[i].name || '';
  170. // 系列图例开关
  171. this.selectedMap[serieName] =
  172. legend ? legend.isSelected(serieName) : true;
  173. if (!this.selectedMap[serieName]) {
  174. continue;
  175. }
  176. this.buildMark(i);
  177. // TODO 多个 force
  178. this._initSerie(serie, i);
  179. break;
  180. }
  181. }
  182. this.animationEffect();
  183. },
  184. _getNodeCategory: function (serie, node) {
  185. return serie.categories && serie.categories[node.category || 0];
  186. },
  187. _getNodeQueryTarget: function (serie, node, type) {
  188. type = type || 'normal';
  189. var category = this._getNodeCategory(serie, node) || {};
  190. return [
  191. // Node
  192. node.itemStyle && node.itemStyle[type],
  193. // Category
  194. category && category.itemStyle && category.itemStyle[type],
  195. // Serie
  196. serie.itemStyle[type].nodeStyle
  197. ];
  198. },
  199. _getEdgeQueryTarget: function (serie, edge, type) {
  200. type = type || 'normal';
  201. return [
  202. (edge.itemStyle && edge.itemStyle[type]),
  203. serie.itemStyle[type].linkStyle
  204. ];
  205. },
  206. _initSerie: function(serie, serieIdx) {
  207. this._temperature = 1;
  208. // matrix 表示边
  209. if (serie.matrix) {
  210. this._graph = this._getSerieGraphFromDataMatrix(serie);
  211. }
  212. // links 表示边
  213. else if (serie.links) {
  214. this._graph = this._getSerieGraphFromNodeLinks(serie);
  215. }
  216. this._buildLinkShapes(serie, serieIdx);
  217. this._buildNodeShapes(serie, serieIdx);
  218. var panable = serie.roam === true || serie.roam === 'move';
  219. var zoomable = serie.roam === true || serie.roam === 'scale';
  220. // Enable pan and zooom
  221. this.zr.modLayer(this.getZlevelBase(), {
  222. panable: panable,
  223. zoomable: zoomable
  224. });
  225. if (
  226. this.query('markPoint.effect.show')
  227. || this.query('markLine.effect.show')
  228. ) {
  229. this.zr.modLayer(ecConfig.EFFECT_ZLEVEL, {
  230. panable: panable,
  231. zoomable: zoomable
  232. });
  233. }
  234. this._initLayout(serie);
  235. this._step();
  236. },
  237. _getSerieGraphFromDataMatrix: function (serie) {
  238. var nodesData = [];
  239. var count = 0;
  240. var matrix = [];
  241. // 复制一份新的matrix
  242. for (var i = 0; i < serie.matrix.length; i++) {
  243. matrix[i] = serie.matrix[i].slice();
  244. }
  245. var data = serie.data || serie.nodes;
  246. for (var i = 0; i < data.length; i++) {
  247. var node = {};
  248. var group = data[i];
  249. for (var key in group) {
  250. // name改为id
  251. if (key === 'name') {
  252. node['id'] = group['name'];
  253. }
  254. else {
  255. node[key] = group[key];
  256. }
  257. }
  258. // legends 选择优先级 category -> group
  259. var category = this._getNodeCategory(serie, group);
  260. var name = category ? category.name : group.name;
  261. this.selectedMap[name] = this.isSelected(name);
  262. if (this.selectedMap[name]) {
  263. nodesData.push(node);
  264. count++;
  265. }
  266. else {
  267. // 过滤legend未选中的数据
  268. matrix.splice(count, 1);
  269. for (var j = 0; j < matrix.length; j++) {
  270. matrix[j].splice(count, 1);
  271. }
  272. }
  273. }
  274. var graph = Graph.fromMatrix(nodesData, matrix, true);
  275. // Prepare layout parameters
  276. graph.eachNode(function (n, idx) {
  277. n.layout = {
  278. size: n.data.value,
  279. mass: 0
  280. };
  281. n.rawIndex = idx;
  282. });
  283. graph.eachEdge(function (e) {
  284. e.layout = {
  285. weight: e.data.weight
  286. };
  287. });
  288. return graph;
  289. },
  290. _getSerieGraphFromNodeLinks: function (serie) {
  291. var graph = new Graph(true);
  292. var nodes = serie.data || serie.nodes;
  293. for (var i = 0, len = nodes.length; i < len; i++) {
  294. var n = nodes[i];
  295. if (!n || n.ignore) {
  296. continue;
  297. }
  298. // legends 选择优先级 category -> group
  299. var category = this._getNodeCategory(serie, n);
  300. var name = category ? category.name : n.name;
  301. this.selectedMap[name] = this.isSelected(name);
  302. if (this.selectedMap[name]) {
  303. var node = graph.addNode(n.name, n);
  304. node.rawIndex = i;
  305. }
  306. }
  307. for (var i = 0, len = serie.links.length; i < len; i++) {
  308. var e = serie.links[i];
  309. var n1 = e.source;
  310. var n2 = e.target;
  311. if (typeof(n1) === 'number') {
  312. n1 = nodes[n1];
  313. if (n1) {
  314. n1 = n1.name;
  315. }
  316. }
  317. if (typeof(n2) === 'number') {
  318. n2 = nodes[n2];
  319. if (n2) {
  320. n2 = n2.name;
  321. }
  322. }
  323. var edge = graph.addEdge(n1, n2, e);
  324. if (edge) {
  325. edge.rawIndex = i;
  326. }
  327. }
  328. graph.eachNode(function (n) {
  329. var value = n.data.value;
  330. if (value == null) { // value 是 null 或者 undefined
  331. value = 0;
  332. // 默认使用所有边值的和作为节点的大小, 不修改 data 里的数值
  333. for (var i = 0; i < n.edges.length; i++) {
  334. value += n.edges[i].data.weight || 0;
  335. }
  336. }
  337. n.layout = {
  338. size: value,
  339. mass: 0
  340. };
  341. });
  342. graph.eachEdge(function (e) {
  343. e.layout = {
  344. // 默认 weight 为1
  345. weight: e.data.weight == null ? 1 : e.data.weight
  346. };
  347. });
  348. return graph;
  349. },
  350. _initLayout: function(serie) {
  351. var graph = this._graph;
  352. var len = graph.nodes.length;
  353. var minRadius = this.query(serie, 'minRadius');
  354. var maxRadius = this.query(serie, 'maxRadius');
  355. this._steps = serie.steps || 1;
  356. var layout = this._layout;
  357. layout.center = this.parseCenter(this.zr, serie.center);
  358. layout.width = this.parsePercent(serie.size, this.zr.getWidth());
  359. layout.height = this.parsePercent(serie.size, this.zr.getHeight());
  360. layout.large = serie.large;
  361. layout.scaling = serie.scaling;
  362. layout.ratioScaling = serie.ratioScaling;
  363. layout.gravity = serie.gravity;
  364. layout.temperature = 1;
  365. layout.coolDown = serie.coolDown;
  366. layout.preventNodeEdgeOverlap = serie.preventOverlap;
  367. layout.preventNodeOverlap = serie.preventOverlap;
  368. // 将值映射到minRadius-maxRadius的范围上
  369. var min = Infinity; var max = -Infinity;
  370. for (var i = 0; i < len; i++) {
  371. var gNode = graph.nodes[i];
  372. max = Math.max(gNode.layout.size, max);
  373. min = Math.min(gNode.layout.size, min);
  374. }
  375. var divider = max - min;
  376. for (var i = 0; i < len; i++) {
  377. var gNode = graph.nodes[i];
  378. if (divider > 0) {
  379. gNode.layout.size =
  380. (gNode.layout.size - min) * (maxRadius - minRadius) / divider
  381. + minRadius;
  382. // 节点质量是归一的
  383. gNode.layout.mass = gNode.layout.size / maxRadius;
  384. } else {
  385. gNode.layout.size = (maxRadius - minRadius) / 2;
  386. gNode.layout.mass = 0.5;
  387. }
  388. }
  389. for (var i = 0; i < len; i++) {
  390. // var initPos;
  391. var gNode = graph.nodes[i];
  392. if (typeof(this.__nodePositionMap[gNode.id]) !== 'undefined') {
  393. gNode.layout.position = vec2.create();
  394. vec2.copy(gNode.layout.position, this.__nodePositionMap[gNode.id]);
  395. }
  396. else if (typeof(gNode.data.initial) !== 'undefined') {
  397. gNode.layout.position = vec2.create();
  398. vec2.copy(gNode.layout.position, gNode.data.initial);
  399. }
  400. else {
  401. var center = this._layout.center;
  402. var size = Math.min(this._layout.width, this._layout.height);
  403. gNode.layout.position = _randomInSquare(
  404. center[0], center[1], size * 0.8
  405. );
  406. }
  407. var style = gNode.shape.style;
  408. var radius = gNode.layout.size;
  409. style.width = style.width || (radius * 2);
  410. style.height = style.height || (radius * 2);
  411. style.x = -style.width / 2;
  412. style.y = -style.height / 2;
  413. vec2.copy(gNode.shape.position, gNode.layout.position);
  414. }
  415. // 边
  416. len = graph.edges.length;
  417. max = -Infinity;
  418. for (var i = 0; i < len; i++) {
  419. var e = graph.edges[i];
  420. if (e.layout.weight > max) {
  421. max = e.layout.weight;
  422. }
  423. }
  424. // 权重归一
  425. for (var i = 0; i < len; i++) {
  426. var e = graph.edges[i];
  427. e.layout.weight /= max;
  428. }
  429. this._layout.init(graph, serie.useWorker);
  430. },
  431. _buildNodeShapes: function(serie, serieIdx) {
  432. var graph = this._graph;
  433. var categories = this.query(serie, 'categories');
  434. graph.eachNode(function (node) {
  435. var category = this._getNodeCategory(serie, node.data);
  436. var queryTarget = [node.data, category, serie];
  437. var styleQueryTarget = this._getNodeQueryTarget(serie, node.data);
  438. var emphasisStyleQueryTarget = this._getNodeQueryTarget(
  439. serie, node.data, 'emphasis'
  440. );
  441. var shape = new IconShape({
  442. style: {
  443. x: 0,
  444. y: 0,
  445. color: this.deepQuery(styleQueryTarget, 'color'),
  446. brushType: 'both',
  447. // 兼容原有写法
  448. strokeColor: this.deepQuery(styleQueryTarget, 'strokeColor')
  449. || this.deepQuery(styleQueryTarget, 'borderColor'),
  450. lineWidth: this.deepQuery(styleQueryTarget, 'lineWidth')
  451. || this.deepQuery(styleQueryTarget, 'borderWidth')
  452. },
  453. highlightStyle: {
  454. color: this.deepQuery(emphasisStyleQueryTarget, 'color'),
  455. // 兼容原有写法
  456. strokeColor: this.deepQuery(emphasisStyleQueryTarget, 'strokeColor')
  457. || this.deepQuery(emphasisStyleQueryTarget, 'borderColor'),
  458. lineWidth: this.deepQuery(emphasisStyleQueryTarget, 'lineWidth')
  459. || this.deepQuery(emphasisStyleQueryTarget, 'borderWidth')
  460. },
  461. clickable: serie.clickable,
  462. zlevel: this.getZlevelBase(),
  463. z: this.getZBase()
  464. });
  465. if (!shape.style.color) {
  466. shape.style.color = category
  467. ? this.getColor(category.name) : this.getColor(node.id);
  468. }
  469. shape.style.iconType = this.deepQuery(queryTarget, 'symbol');
  470. var symbolSize = this.deepQuery(queryTarget, 'symbolSize') || 0;
  471. if (typeof symbolSize === 'number') {
  472. symbolSize = [symbolSize, symbolSize];
  473. }
  474. // 强制设定节点大小,否则默认映射到 minRadius 到 maxRadius 后的值
  475. shape.style.width = symbolSize[0] * 2;
  476. shape.style.height = symbolSize[1] * 2;
  477. if (shape.style.iconType.match('image')) {
  478. shape.style.image = shape.style.iconType.replace(
  479. new RegExp('^image:\\/\\/'), ''
  480. );
  481. shape = new ImageShape({
  482. style: shape.style,
  483. highlightStyle: shape.highlightStyle,
  484. clickable: shape.clickable,
  485. zlevel: this.getZlevelBase(),
  486. z: this.getZBase()
  487. });
  488. }
  489. // 节点标签样式
  490. if (this.deepQuery(queryTarget, 'itemStyle.normal.label.show')) {
  491. shape.style.text = node.data.label == null ? node.id : node.data.label;
  492. shape.style.textPosition = this.deepQuery(
  493. queryTarget, 'itemStyle.normal.label.position'
  494. ) ;
  495. shape.style.textColor = this.deepQuery(
  496. queryTarget, 'itemStyle.normal.label.textStyle.color'
  497. );
  498. shape.style.textFont = this.getFont(this.deepQuery(
  499. queryTarget, 'itemStyle.normal.label.textStyle'
  500. ) || {});
  501. }
  502. if (this.deepQuery(queryTarget, 'itemStyle.emphasis.label.show')) {
  503. shape.highlightStyle.textPosition = this.deepQuery(
  504. queryTarget, 'itemStyle.emphasis.label.position'
  505. );
  506. shape.highlightStyle.textColor = this.deepQuery(
  507. queryTarget, 'itemStyle.emphasis.label.textStyle.color'
  508. );
  509. shape.highlightStyle.textFont = this.getFont(this.deepQuery(
  510. queryTarget, 'itemStyle.emphasis.label.textStyle'
  511. ) || {});
  512. }
  513. // 拖拽特性
  514. if (this.deepQuery(queryTarget, 'draggable')) {
  515. this.setCalculable(shape);
  516. shape.dragEnableTime = 0;
  517. shape.draggable = true;
  518. shape.ondragstart = this.shapeHandler.ondragstart;
  519. shape.ondragover = null;
  520. }
  521. var categoryName = '';
  522. if (typeof(node.category) !== 'undefined') {
  523. var category = categories[node.category];
  524. categoryName = (category && category.name) || '';
  525. }
  526. // !!Pack data before addShape
  527. ecData.pack(
  528. shape,
  529. serie,
  530. serieIdx,
  531. // data
  532. node.data,
  533. // data index
  534. node.rawIndex,
  535. // name
  536. node.data.name || '',
  537. // category
  538. // special
  539. node.category
  540. );
  541. this.shapeList.push(shape);
  542. this.zr.addShape(shape);
  543. node.shape = shape;
  544. }, this);
  545. },
  546. _buildLinkShapes: function(serie, serieIdx) {
  547. var graph = this._graph;
  548. var len = graph.edges.length;
  549. for (var i = 0; i < len; i++) {
  550. var gEdge = graph.edges[i];
  551. var link = gEdge.data;
  552. var source = gEdge.node1;
  553. var target = gEdge.node2;
  554. var otherEdge = graph.getEdge(target, source);
  555. var queryTarget = this._getEdgeQueryTarget(serie, link);
  556. var linkType = this.deepQuery(queryTarget, 'type');
  557. // TODO 暂时只有线段支持箭头
  558. if (serie.linkSymbol && serie.linkSymbol !== 'none') {
  559. linkType = 'line';
  560. }
  561. var LinkShapeCtor = linkType === 'line' ? LineShape : BezierCurveShape;
  562. var linkShape = new LinkShapeCtor({
  563. style : {
  564. xStart : 0,
  565. yStart : 0,
  566. xEnd : 0,
  567. yEnd : 0
  568. },
  569. clickable: this.query(serie, 'clickable'),
  570. highlightStyle : {},
  571. zlevel: this.getZlevelBase(),
  572. z: this.getZBase()
  573. });
  574. if (otherEdge && otherEdge.shape) {
  575. // 偏移一定位置放置双向边重叠
  576. linkShape.style.offset = 4;
  577. otherEdge.shape.style.offset = 4;
  578. }
  579. zrUtil.merge(
  580. linkShape.style,
  581. this.query(serie, 'itemStyle.normal.linkStyle'),
  582. true
  583. );
  584. zrUtil.merge(
  585. linkShape.highlightStyle,
  586. this.query(serie, 'itemStyle.emphasis.linkStyle'),
  587. true
  588. );
  589. if (typeof(link.itemStyle) !== 'undefined') {
  590. if(link.itemStyle.normal){
  591. zrUtil.merge(linkShape.style, link.itemStyle.normal, true);
  592. }
  593. if(link.itemStyle.emphasis){
  594. zrUtil.merge(
  595. linkShape.highlightStyle,
  596. link.itemStyle.emphasis,
  597. true
  598. );
  599. }
  600. }
  601. // 兼容原有写法
  602. linkShape.style.lineWidth
  603. = linkShape.style.lineWidth || linkShape.style.width;
  604. linkShape.style.strokeColor
  605. = linkShape.style.strokeColor || linkShape.style.color;
  606. linkShape.highlightStyle.lineWidth
  607. = linkShape.highlightStyle.lineWidth || linkShape.highlightStyle.width;
  608. linkShape.highlightStyle.strokeColor
  609. = linkShape.highlightStyle.strokeColor || linkShape.highlightStyle.color;
  610. ecData.pack(
  611. linkShape,
  612. // serie
  613. serie,
  614. // serie index
  615. serieIdx,
  616. // link data
  617. gEdge.data,
  618. // link data index
  619. gEdge.rawIndex == null ? i : gEdge.rawIndex,
  620. // source name - target name
  621. gEdge.data.name || (source.id + ' - ' + target.id),
  622. // link source id
  623. // special
  624. source.id,
  625. // link target id
  626. // special2
  627. target.id
  628. );
  629. this.shapeList.push(linkShape);
  630. this.zr.addShape(linkShape);
  631. gEdge.shape = linkShape;
  632. // Arrow shape
  633. if (serie.linkSymbol && serie.linkSymbol !== 'none') {
  634. var symbolShape = new IconShape({
  635. style: {
  636. x: -5,
  637. y: 0,
  638. width: serie.linkSymbolSize[0],
  639. height: serie.linkSymbolSize[1],
  640. iconType: serie.linkSymbol,
  641. brushType: 'fill',
  642. // Use same style with link shape
  643. color: linkShape.style.strokeColor
  644. },
  645. highlightStyle: {
  646. brushType: 'fill'
  647. },
  648. position: [0, 0],
  649. rotation: 0,
  650. zlevel: this.getZlevelBase(),
  651. z: this.getZBase()
  652. });
  653. linkShape._symbolShape = symbolShape;
  654. this.shapeList.push(symbolShape);
  655. this.zr.addShape(symbolShape);
  656. }
  657. }
  658. },
  659. _updateLinkShapes: function() {
  660. var v = vec2.create();
  661. var n = vec2.create();
  662. var p1 = vec2.create();
  663. var p2 = vec2.create();
  664. var edges = this._graph.edges;
  665. for (var i = 0, len = edges.length; i < len; i++) {
  666. var edge = edges[i];
  667. var sourceShape = edge.node1.shape;
  668. var targetShape = edge.node2.shape;
  669. vec2.copy(p1, sourceShape.position);
  670. vec2.copy(p2, targetShape.position);
  671. var edgeShapeStyle = edge.shape.style;
  672. vec2.sub(v, p1, p2);
  673. vec2.normalize(v, v);
  674. if (edgeShapeStyle.offset) {
  675. n[0] = v[1];
  676. n[1] = - v[0];
  677. vec2.scaleAndAdd(p1, p1, n, edgeShapeStyle.offset);
  678. vec2.scaleAndAdd(p2, p2, n, edgeShapeStyle.offset);
  679. }
  680. else if (edge.shape.type === 'bezier-curve') {
  681. edgeShapeStyle.cpX1 = (p1[0] + p2[0]) / 2 - (p2[1] - p1[1]) / 4;
  682. edgeShapeStyle.cpY1 = (p1[1] + p2[1]) / 2 - (p1[0] - p2[0]) / 4;
  683. }
  684. edgeShapeStyle.xStart = p1[0];
  685. edgeShapeStyle.yStart = p1[1];
  686. edgeShapeStyle.xEnd = p2[0];
  687. edgeShapeStyle.yEnd = p2[1];
  688. edge.shape.modSelf();
  689. if (edge.shape._symbolShape) {
  690. var symbolShape = edge.shape._symbolShape;
  691. vec2.copy(symbolShape.position, p2);
  692. vec2.scaleAndAdd(
  693. symbolShape.position, symbolShape.position,
  694. v, targetShape.style.width / 2 + 2
  695. );
  696. var angle = Math.atan2(v[1], v[0]);
  697. symbolShape.rotation = Math.PI / 2 - angle;
  698. symbolShape.modSelf();
  699. }
  700. }
  701. },
  702. _syncNodePositions: function() {
  703. var graph = this._graph;
  704. for (var i = 0; i < graph.nodes.length; i++) {
  705. var gNode = graph.nodes[i];
  706. var position = gNode.layout.position;
  707. var node = gNode.data;
  708. var shape = gNode.shape;
  709. var fixX = shape.fixed || node.fixX;
  710. var fixY = shape.fixed || node.fixY;
  711. if (fixX === true) {
  712. fixX = 1;
  713. } else if (isNaN(fixX)) {
  714. fixX = 0;
  715. }
  716. if (fixY === true) {
  717. fixY = 1;
  718. } else if (isNaN(fixY)) {
  719. fixY = 0;
  720. }
  721. shape.position[0] += (position[0] - shape.position[0]) * (1 - fixX);
  722. shape.position[1] += (position[1] - shape.position[1]) * (1 - fixY);
  723. vec2.copy(position, shape.position);
  724. var nodeName = node.name;
  725. if (nodeName) {
  726. var gPos = this.__nodePositionMap[nodeName];
  727. if (!gPos) {
  728. gPos = this.__nodePositionMap[nodeName] = vec2.create();
  729. }
  730. vec2.copy(gPos, position);
  731. }
  732. shape.modSelf();
  733. }
  734. },
  735. _step: function(e) {
  736. this._syncNodePositions();
  737. this._updateLinkShapes();
  738. this.zr.refreshNextFrame();
  739. if (this._layout.temperature > 0.01) {
  740. this._layout.step(this._steps);
  741. } else {
  742. this.messageCenter.dispatch(
  743. ecConfig.EVENT.FORCE_LAYOUT_END,
  744. {},
  745. {},
  746. this.myChart
  747. );
  748. }
  749. },
  750. refresh: function(newOption) {
  751. if (newOption) {
  752. this.option = newOption;
  753. this.series = this.option.series;
  754. }
  755. this.legend = this.component.legend;
  756. if (this.legend) {
  757. this.getColor = function(param) {
  758. return this.legend.getColor(param);
  759. };
  760. this.isSelected = function(param) {
  761. return this.legend.isSelected(param);
  762. };
  763. }
  764. else {
  765. var colorMap = {};
  766. var count = 0;
  767. this.getColor = function (key) {
  768. if (colorMap[key]) {
  769. return colorMap[key];
  770. }
  771. if (!colorMap[key]) {
  772. colorMap[key] = this.zr.getColor(count++);
  773. }
  774. return colorMap[key];
  775. };
  776. this.isSelected = function () {
  777. return true;
  778. };
  779. }
  780. this._init();
  781. },
  782. dispose: function(){
  783. this.clear();
  784. this.shapeList = null;
  785. this.effectList = null;
  786. this._layout.dispose();
  787. this._layout = null;
  788. this.__nodePositionMap = {};
  789. },
  790. getPosition: function () {
  791. var position = [];
  792. this._graph.eachNode(function (n) {
  793. if (n.layout) {
  794. position.push({
  795. name: n.data.name,
  796. position: Array.prototype.slice.call(n.layout.position)
  797. });
  798. }
  799. });
  800. return position;
  801. }
  802. };
  803. /**
  804. * 拖拽开始
  805. */
  806. function ondragstart(param) {
  807. if (!this.isDragstart || !param.target) {
  808. // 没有在当前实例上发生拖拽行为则直接返回
  809. return;
  810. }
  811. var shape = param.target;
  812. shape.fixed = true;
  813. // 处理完拖拽事件后复位
  814. this.isDragstart = false;
  815. this.zr.on(zrConfig.EVENT.MOUSEMOVE, this.onmousemove);
  816. }
  817. function onmousemove() {
  818. this._layout.temperature = 0.8;
  819. this._step();
  820. }
  821. /**
  822. * 数据项被拖拽出去,重载基类方法
  823. */
  824. function ondragend(param, status) {
  825. if (!this.isDragend || !param.target) {
  826. // 没有在当前实例上发生拖拽行为则直接返回
  827. return;
  828. }
  829. var shape = param.target;
  830. shape.fixed = false;
  831. // 别status = {}赋值啊!!
  832. status.dragIn = true;
  833. //你自己refresh的话把他设为false,设true就会重新调refresh接口
  834. status.needRefresh = false;
  835. // 处理完拖拽事件后复位
  836. this.isDragend = false;
  837. this.zr.un(zrConfig.EVENT.MOUSEMOVE, this.onmousemove);
  838. }
  839. function _randomInSquare(x, y, size) {
  840. var v = vec2.create();
  841. v[0] = (Math.random() - 0.5) * size + x;
  842. v[1] = (Math.random() - 0.5) * size + y;
  843. return v;
  844. }
  845. zrUtil.inherits(Force, ChartBase);
  846. // 图表注册
  847. require('../chart').define('force', Force);
  848. return Force;
  849. });