pie.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. define('echarts/chart/pie', [
  2. 'require',
  3. './base',
  4. 'zrender/shape/Text',
  5. 'zrender/shape/Ring',
  6. 'zrender/shape/Circle',
  7. 'zrender/shape/Sector',
  8. 'zrender/shape/Polyline',
  9. '../config',
  10. '../util/ecData',
  11. 'zrender/tool/util',
  12. 'zrender/tool/math',
  13. 'zrender/tool/color',
  14. '../chart'
  15. ], function (require) {
  16. var ChartBase = require('./base');
  17. var TextShape = require('zrender/shape/Text');
  18. var RingShape = require('zrender/shape/Ring');
  19. var CircleShape = require('zrender/shape/Circle');
  20. var SectorShape = require('zrender/shape/Sector');
  21. var PolylineShape = require('zrender/shape/Polyline');
  22. var ecConfig = require('../config');
  23. ecConfig.pie = {
  24. zlevel: 0,
  25. z: 2,
  26. clickable: true,
  27. legendHoverLink: true,
  28. center: [
  29. '50%',
  30. '50%'
  31. ],
  32. radius: [
  33. 0,
  34. '75%'
  35. ],
  36. clockWise: true,
  37. startAngle: 90,
  38. minAngle: 0,
  39. selectedOffset: 10,
  40. itemStyle: {
  41. normal: {
  42. borderColor: 'rgba(0,0,0,0)',
  43. borderWidth: 1,
  44. label: {
  45. show: true,
  46. position: 'outer'
  47. },
  48. labelLine: {
  49. show: true,
  50. length: 20,
  51. lineStyle: {
  52. width: 1,
  53. type: 'solid'
  54. }
  55. }
  56. },
  57. emphasis: {
  58. borderColor: 'rgba(0,0,0,0)',
  59. borderWidth: 1,
  60. label: { show: false },
  61. labelLine: {
  62. show: false,
  63. length: 20,
  64. lineStyle: {
  65. width: 1,
  66. type: 'solid'
  67. }
  68. }
  69. }
  70. }
  71. };
  72. var ecData = require('../util/ecData');
  73. var zrUtil = require('zrender/tool/util');
  74. var zrMath = require('zrender/tool/math');
  75. var zrColor = require('zrender/tool/color');
  76. function Pie(ecTheme, messageCenter, zr, option, myChart) {
  77. ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
  78. var self = this;
  79. self.shapeHandler.onmouseover = function (param) {
  80. var shape = param.target;
  81. var seriesIndex = ecData.get(shape, 'seriesIndex');
  82. var dataIndex = ecData.get(shape, 'dataIndex');
  83. var percent = ecData.get(shape, 'special');
  84. var center = [
  85. shape.style.x,
  86. shape.style.y
  87. ];
  88. var startAngle = shape.style.startAngle;
  89. var endAngle = shape.style.endAngle;
  90. var midAngle = ((endAngle + startAngle) / 2 + 360) % 360;
  91. var defaultColor = shape.highlightStyle.color;
  92. var label = self.getLabel(seriesIndex, dataIndex, percent, center, midAngle, defaultColor, true);
  93. if (label) {
  94. self.zr.addHoverShape(label);
  95. }
  96. var labelLine = self.getLabelLine(seriesIndex, dataIndex, center, shape.style.r0, shape.style.r, midAngle, defaultColor, true);
  97. if (labelLine) {
  98. self.zr.addHoverShape(labelLine);
  99. }
  100. };
  101. this.refresh(option);
  102. }
  103. Pie.prototype = {
  104. type: ecConfig.CHART_TYPE_PIE,
  105. _buildShape: function () {
  106. var series = this.series;
  107. var legend = this.component.legend;
  108. this.selectedMap = {};
  109. this._selected = {};
  110. var center;
  111. var radius;
  112. var pieCase;
  113. this._selectedMode = false;
  114. var serieName;
  115. for (var i = 0, l = series.length; i < l; i++) {
  116. if (series[i].type === ecConfig.CHART_TYPE_PIE) {
  117. series[i] = this.reformOption(series[i]);
  118. this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
  119. serieName = series[i].name || '';
  120. this.selectedMap[serieName] = legend ? legend.isSelected(serieName) : true;
  121. if (!this.selectedMap[serieName]) {
  122. continue;
  123. }
  124. center = this.parseCenter(this.zr, series[i].center);
  125. radius = this.parseRadius(this.zr, series[i].radius);
  126. this._selectedMode = this._selectedMode || series[i].selectedMode;
  127. this._selected[i] = [];
  128. if (this.deepQuery([
  129. series[i],
  130. this.option
  131. ], 'calculable')) {
  132. pieCase = {
  133. zlevel: this.getZlevelBase(),
  134. z: this.getZBase(),
  135. hoverable: false,
  136. style: {
  137. x: center[0],
  138. y: center[1],
  139. r0: radius[0] <= 10 ? 0 : radius[0] - 10,
  140. r: radius[1] + 10,
  141. brushType: 'stroke',
  142. lineWidth: 1,
  143. strokeColor: series[i].calculableHolderColor || this.ecTheme.calculableHolderColor || ecConfig.calculableHolderColor
  144. }
  145. };
  146. ecData.pack(pieCase, series[i], i, undefined, -1);
  147. this.setCalculable(pieCase);
  148. pieCase = radius[0] <= 10 ? new CircleShape(pieCase) : new RingShape(pieCase);
  149. this.shapeList.push(pieCase);
  150. }
  151. this._buildSinglePie(i);
  152. this.buildMark(i);
  153. }
  154. }
  155. this.addShapeList();
  156. },
  157. _buildSinglePie: function (seriesIndex) {
  158. var series = this.series;
  159. var serie = series[seriesIndex];
  160. var data = serie.data;
  161. var legend = this.component.legend;
  162. var itemName;
  163. var totalSelected = 0;
  164. var totalSelectedValue0 = 0;
  165. var totalValue = 0;
  166. var maxValue = Number.NEGATIVE_INFINITY;
  167. var singleShapeList = [];
  168. for (var i = 0, l = data.length; i < l; i++) {
  169. itemName = data[i].name;
  170. this.selectedMap[itemName] = legend ? legend.isSelected(itemName) : true;
  171. if (this.selectedMap[itemName] && !isNaN(data[i].value)) {
  172. if (+data[i].value !== 0) {
  173. totalSelected++;
  174. } else {
  175. totalSelectedValue0++;
  176. }
  177. totalValue += +data[i].value;
  178. maxValue = Math.max(maxValue, +data[i].value);
  179. }
  180. }
  181. if (totalValue === 0) {
  182. return;
  183. }
  184. var percent = 100;
  185. var clockWise = serie.clockWise;
  186. var startAngle = (serie.startAngle.toFixed(2) - 0 + 360) % 360;
  187. var endAngle;
  188. var minAngle = serie.minAngle || 0.01;
  189. var totalAngle = 360 - minAngle * totalSelected - 0.01 * totalSelectedValue0;
  190. var defaultColor;
  191. var roseType = serie.roseType;
  192. var center;
  193. var radius;
  194. var r0;
  195. var r1;
  196. for (var i = 0, l = data.length; i < l; i++) {
  197. itemName = data[i].name;
  198. if (!this.selectedMap[itemName] || isNaN(data[i].value)) {
  199. continue;
  200. }
  201. defaultColor = legend ? legend.getColor(itemName) : this.zr.getColor(i);
  202. percent = data[i].value / totalValue;
  203. if (roseType != 'area') {
  204. endAngle = clockWise ? startAngle - percent * totalAngle - (percent !== 0 ? minAngle : 0.01) : percent * totalAngle + startAngle + (percent !== 0 ? minAngle : 0.01);
  205. } else {
  206. endAngle = clockWise ? startAngle - 360 / l : 360 / l + startAngle;
  207. }
  208. endAngle = endAngle.toFixed(2) - 0;
  209. percent = (percent * 100).toFixed(2);
  210. center = this.parseCenter(this.zr, serie.center);
  211. radius = this.parseRadius(this.zr, serie.radius);
  212. r0 = +radius[0];
  213. r1 = +radius[1];
  214. if (roseType === 'radius') {
  215. r1 = data[i].value / maxValue * (r1 - r0) * 0.8 + (r1 - r0) * 0.2 + r0;
  216. } else if (roseType === 'area') {
  217. r1 = Math.sqrt(data[i].value / maxValue) * (r1 - r0) + r0;
  218. }
  219. if (clockWise) {
  220. var temp;
  221. temp = startAngle;
  222. startAngle = endAngle;
  223. endAngle = temp;
  224. }
  225. this._buildItem(singleShapeList, seriesIndex, i, percent, data[i].selected, center, r0, r1, startAngle, endAngle, defaultColor);
  226. if (!clockWise) {
  227. startAngle = endAngle;
  228. }
  229. }
  230. this._autoLabelLayout(singleShapeList, center, r1);
  231. for (var i = 0, l = singleShapeList.length; i < l; i++) {
  232. this.shapeList.push(singleShapeList[i]);
  233. }
  234. singleShapeList = null;
  235. },
  236. _buildItem: function (singleShapeList, seriesIndex, dataIndex, percent, isSelected, center, r0, r1, startAngle, endAngle, defaultColor) {
  237. var series = this.series;
  238. var midAngle = ((endAngle + startAngle) / 2 + 360) % 360;
  239. var sector = this.getSector(seriesIndex, dataIndex, percent, isSelected, center, r0, r1, startAngle, endAngle, defaultColor);
  240. ecData.pack(sector, series[seriesIndex], seriesIndex, series[seriesIndex].data[dataIndex], dataIndex, series[seriesIndex].data[dataIndex].name, percent);
  241. singleShapeList.push(sector);
  242. var label = this.getLabel(seriesIndex, dataIndex, percent, center, midAngle, defaultColor, false);
  243. var labelLine = this.getLabelLine(seriesIndex, dataIndex, center, r0, r1, midAngle, defaultColor, false);
  244. if (labelLine) {
  245. ecData.pack(labelLine, series[seriesIndex], seriesIndex, series[seriesIndex].data[dataIndex], dataIndex, series[seriesIndex].data[dataIndex].name, percent);
  246. singleShapeList.push(labelLine);
  247. }
  248. if (label) {
  249. ecData.pack(label, series[seriesIndex], seriesIndex, series[seriesIndex].data[dataIndex], dataIndex, series[seriesIndex].data[dataIndex].name, percent);
  250. label._labelLine = labelLine;
  251. singleShapeList.push(label);
  252. }
  253. },
  254. getSector: function (seriesIndex, dataIndex, percent, isSelected, center, r0, r1, startAngle, endAngle, defaultColor) {
  255. var series = this.series;
  256. var serie = series[seriesIndex];
  257. var data = serie.data[dataIndex];
  258. var queryTarget = [
  259. data,
  260. serie
  261. ];
  262. var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
  263. var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis') || {};
  264. var normalColor = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data) || defaultColor;
  265. var emphasisColor = this.getItemStyleColor(emphasis.color, seriesIndex, dataIndex, data) || (typeof normalColor === 'string' ? zrColor.lift(normalColor, -0.2) : normalColor);
  266. var sector = {
  267. zlevel: this.getZlevelBase(),
  268. z: this.getZBase(),
  269. clickable: this.deepQuery(queryTarget, 'clickable'),
  270. style: {
  271. x: center[0],
  272. y: center[1],
  273. r0: r0,
  274. r: r1,
  275. startAngle: startAngle,
  276. endAngle: endAngle,
  277. brushType: 'both',
  278. color: normalColor,
  279. lineWidth: normal.borderWidth,
  280. strokeColor: normal.borderColor,
  281. lineJoin: 'round'
  282. },
  283. highlightStyle: {
  284. color: emphasisColor,
  285. lineWidth: emphasis.borderWidth,
  286. strokeColor: emphasis.borderColor,
  287. lineJoin: 'round'
  288. },
  289. _seriesIndex: seriesIndex,
  290. _dataIndex: dataIndex
  291. };
  292. if (isSelected) {
  293. var midAngle = ((sector.style.startAngle + sector.style.endAngle) / 2).toFixed(2) - 0;
  294. sector.style._hasSelected = true;
  295. sector.style._x = sector.style.x;
  296. sector.style._y = sector.style.y;
  297. var offset = this.query(serie, 'selectedOffset');
  298. sector.style.x += zrMath.cos(midAngle, true) * offset;
  299. sector.style.y -= zrMath.sin(midAngle, true) * offset;
  300. this._selected[seriesIndex][dataIndex] = true;
  301. } else {
  302. this._selected[seriesIndex][dataIndex] = false;
  303. }
  304. if (this._selectedMode) {
  305. sector.onclick = this.shapeHandler.onclick;
  306. }
  307. if (this.deepQuery([
  308. data,
  309. serie,
  310. this.option
  311. ], 'calculable')) {
  312. this.setCalculable(sector);
  313. sector.draggable = true;
  314. }
  315. if (this._needLabel(serie, data, true) || this._needLabelLine(serie, data, true)) {
  316. sector.onmouseover = this.shapeHandler.onmouseover;
  317. }
  318. sector = new SectorShape(sector);
  319. return sector;
  320. },
  321. getLabel: function (seriesIndex, dataIndex, percent, center, midAngle, defaultColor, isEmphasis) {
  322. var series = this.series;
  323. var serie = series[seriesIndex];
  324. var data = serie.data[dataIndex];
  325. if (!this._needLabel(serie, data, isEmphasis)) {
  326. return;
  327. }
  328. var status = isEmphasis ? 'emphasis' : 'normal';
  329. var itemStyle = zrUtil.merge(zrUtil.clone(data.itemStyle) || {}, serie.itemStyle);
  330. var labelControl = itemStyle[status].label;
  331. var textStyle = labelControl.textStyle || {};
  332. var centerX = center[0];
  333. var centerY = center[1];
  334. var x;
  335. var y;
  336. var radius = this.parseRadius(this.zr, serie.radius);
  337. var textAlign;
  338. var textBaseline = 'middle';
  339. labelControl.position = labelControl.position || itemStyle.normal.label.position;
  340. if (labelControl.position === 'center') {
  341. x = centerX;
  342. y = centerY;
  343. textAlign = 'center';
  344. } else if (labelControl.position === 'inner' || labelControl.position === 'inside') {
  345. radius = (radius[0] + radius[1]) * (labelControl.distance || 0.5);
  346. x = Math.round(centerX + radius * zrMath.cos(midAngle, true));
  347. y = Math.round(centerY - radius * zrMath.sin(midAngle, true));
  348. defaultColor = '#fff';
  349. textAlign = 'center';
  350. } else {
  351. radius = radius[1] - -itemStyle[status].labelLine.length;
  352. x = Math.round(centerX + radius * zrMath.cos(midAngle, true));
  353. y = Math.round(centerY - radius * zrMath.sin(midAngle, true));
  354. textAlign = midAngle >= 90 && midAngle <= 270 ? 'right' : 'left';
  355. }
  356. if (labelControl.position != 'center' && labelControl.position != 'inner' && labelControl.position != 'inside') {
  357. x += textAlign === 'left' ? 20 : -20;
  358. }
  359. data.__labelX = x - (textAlign === 'left' ? 5 : -5);
  360. data.__labelY = y;
  361. var ts = new TextShape({
  362. zlevel: this.getZlevelBase(),
  363. z: this.getZBase() + 1,
  364. hoverable: false,
  365. style: {
  366. x: x,
  367. y: y,
  368. color: textStyle.color || defaultColor,
  369. text: this.getLabelText(seriesIndex, dataIndex, percent, status),
  370. textAlign: textStyle.align || textAlign,
  371. textBaseline: textStyle.baseline || textBaseline,
  372. textFont: this.getFont(textStyle)
  373. },
  374. highlightStyle: { brushType: 'fill' }
  375. });
  376. ts._radius = radius;
  377. ts._labelPosition = labelControl.position || 'outer';
  378. ts._rect = ts.getRect(ts.style);
  379. ts._seriesIndex = seriesIndex;
  380. ts._dataIndex = dataIndex;
  381. return ts;
  382. },
  383. getLabelText: function (seriesIndex, dataIndex, percent, status) {
  384. var series = this.series;
  385. var serie = series[seriesIndex];
  386. var data = serie.data[dataIndex];
  387. var formatter = this.deepQuery([
  388. data,
  389. serie
  390. ], 'itemStyle.' + status + '.label.formatter');
  391. if (formatter) {
  392. if (typeof formatter === 'function') {
  393. return formatter.call(this.myChart, {
  394. seriesIndex: seriesIndex,
  395. seriesName: serie.name || '',
  396. series: serie,
  397. dataIndex: dataIndex,
  398. data: data,
  399. name: data.name,
  400. value: data.value,
  401. percent: percent
  402. });
  403. } else if (typeof formatter === 'string') {
  404. formatter = formatter.replace('{a}', '{a0}').replace('{b}', '{b0}').replace('{c}', '{c0}').replace('{d}', '{d0}');
  405. formatter = formatter.replace('{a0}', serie.name).replace('{b0}', data.name).replace('{c0}', data.value).replace('{d0}', percent);
  406. return formatter;
  407. }
  408. } else {
  409. return data.name;
  410. }
  411. },
  412. getLabelLine: function (seriesIndex, dataIndex, center, r0, r1, midAngle, defaultColor, isEmphasis) {
  413. var series = this.series;
  414. var serie = series[seriesIndex];
  415. var data = serie.data[dataIndex];
  416. if (this._needLabelLine(serie, data, isEmphasis)) {
  417. var status = isEmphasis ? 'emphasis' : 'normal';
  418. var itemStyle = zrUtil.merge(zrUtil.clone(data.itemStyle) || {}, serie.itemStyle);
  419. var labelLineControl = itemStyle[status].labelLine;
  420. var lineStyle = labelLineControl.lineStyle || {};
  421. var centerX = center[0];
  422. var centerY = center[1];
  423. var minRadius = r1;
  424. var maxRadius = this.parseRadius(this.zr, serie.radius)[1] - -labelLineControl.length;
  425. var cosValue = zrMath.cos(midAngle, true);
  426. var sinValue = zrMath.sin(midAngle, true);
  427. return new PolylineShape({
  428. zlevel: this.getZlevelBase(),
  429. z: this.getZBase() + 1,
  430. hoverable: false,
  431. style: {
  432. pointList: [
  433. [
  434. centerX + minRadius * cosValue,
  435. centerY - minRadius * sinValue
  436. ],
  437. [
  438. centerX + maxRadius * cosValue,
  439. centerY - maxRadius * sinValue
  440. ],
  441. [
  442. data.__labelX,
  443. data.__labelY
  444. ]
  445. ],
  446. strokeColor: lineStyle.color || defaultColor,
  447. lineType: lineStyle.type,
  448. lineWidth: lineStyle.width
  449. },
  450. _seriesIndex: seriesIndex,
  451. _dataIndex: dataIndex
  452. });
  453. } else {
  454. return;
  455. }
  456. },
  457. _needLabel: function (serie, data, isEmphasis) {
  458. return this.deepQuery([
  459. data,
  460. serie
  461. ], 'itemStyle.' + (isEmphasis ? 'emphasis' : 'normal') + '.label.show');
  462. },
  463. _needLabelLine: function (serie, data, isEmphasis) {
  464. return this.deepQuery([
  465. data,
  466. serie
  467. ], 'itemStyle.' + (isEmphasis ? 'emphasis' : 'normal') + '.labelLine.show');
  468. },
  469. _autoLabelLayout: function (sList, center, r) {
  470. var leftList = [];
  471. var rightList = [];
  472. for (var i = 0, l = sList.length; i < l; i++) {
  473. if (sList[i]._labelPosition === 'outer' || sList[i]._labelPosition === 'outside') {
  474. sList[i]._rect._y = sList[i]._rect.y;
  475. if (sList[i]._rect.x < center[0]) {
  476. leftList.push(sList[i]);
  477. } else {
  478. rightList.push(sList[i]);
  479. }
  480. }
  481. }
  482. this._layoutCalculate(leftList, center, r, -1);
  483. this._layoutCalculate(rightList, center, r, 1);
  484. },
  485. _layoutCalculate: function (tList, center, r, direction) {
  486. tList.sort(function (a, b) {
  487. return a._rect.y - b._rect.y;
  488. });
  489. function _changeDown(start, end, delta, direction) {
  490. for (var j = start; j < end; j++) {
  491. tList[j]._rect.y += delta;
  492. tList[j].style.y += delta;
  493. if (tList[j]._labelLine) {
  494. tList[j]._labelLine.style.pointList[1][1] += delta;
  495. tList[j]._labelLine.style.pointList[2][1] += delta;
  496. }
  497. if (j > start && j + 1 < end && tList[j + 1]._rect.y > tList[j]._rect.y + tList[j]._rect.height) {
  498. _changeUp(j, delta / 2);
  499. return;
  500. }
  501. }
  502. _changeUp(end - 1, delta / 2);
  503. }
  504. function _changeUp(end, delta) {
  505. for (var j = end; j >= 0; j--) {
  506. tList[j]._rect.y -= delta;
  507. tList[j].style.y -= delta;
  508. if (tList[j]._labelLine) {
  509. tList[j]._labelLine.style.pointList[1][1] -= delta;
  510. tList[j]._labelLine.style.pointList[2][1] -= delta;
  511. }
  512. if (j > 0 && tList[j]._rect.y > tList[j - 1]._rect.y + tList[j - 1]._rect.height) {
  513. break;
  514. }
  515. }
  516. }
  517. function _changeX(sList, isDownList, center, r, direction) {
  518. var x = center[0];
  519. var y = center[1];
  520. var deltaX;
  521. var deltaY;
  522. var length;
  523. var lastDeltaX = direction > 0 ? isDownList ? Number.MAX_VALUE : 0 : isDownList ? Number.MAX_VALUE : 0;
  524. for (var i = 0, l = sList.length; i < l; i++) {
  525. deltaY = Math.abs(sList[i]._rect.y - y);
  526. length = sList[i]._radius - r;
  527. deltaX = deltaY < r + length ? Math.sqrt((r + length + 20) * (r + length + 20) - Math.pow(sList[i]._rect.y - y, 2)) : Math.abs(sList[i]._rect.x + (direction > 0 ? 0 : sList[i]._rect.width) - x);
  528. if (isDownList && deltaX >= lastDeltaX) {
  529. deltaX = lastDeltaX - 10;
  530. }
  531. if (!isDownList && deltaX <= lastDeltaX) {
  532. deltaX = lastDeltaX + 10;
  533. }
  534. sList[i]._rect.x = sList[i].style.x = x + deltaX * direction;
  535. if (sList[i]._labelLine) {
  536. sList[i]._labelLine.style.pointList[2][0] = x + (deltaX - 5) * direction;
  537. sList[i]._labelLine.style.pointList[1][0] = x + (deltaX - 20) * direction;
  538. }
  539. lastDeltaX = deltaX;
  540. }
  541. }
  542. var lastY = 0;
  543. var delta;
  544. var len = tList.length;
  545. var upList = [];
  546. var downList = [];
  547. for (var i = 0; i < len; i++) {
  548. delta = tList[i]._rect.y - lastY;
  549. if (delta < 0) {
  550. _changeDown(i, len, -delta, direction);
  551. }
  552. lastY = tList[i]._rect.y + tList[i]._rect.height;
  553. }
  554. if (this.zr.getHeight() - lastY < 0) {
  555. _changeUp(len - 1, lastY - this.zr.getHeight());
  556. }
  557. for (var i = 0; i < len; i++) {
  558. if (tList[i]._rect.y >= center[1]) {
  559. downList.push(tList[i]);
  560. } else {
  561. upList.push(tList[i]);
  562. }
  563. }
  564. _changeX(downList, true, center, r, direction);
  565. _changeX(upList, false, center, r, direction);
  566. },
  567. reformOption: function (opt) {
  568. var _merge = zrUtil.merge;
  569. opt = _merge(_merge(opt || {}, zrUtil.clone(this.ecTheme.pie || {})), zrUtil.clone(ecConfig.pie));
  570. opt.itemStyle.normal.label.textStyle = this.getTextStyle(opt.itemStyle.normal.label.textStyle);
  571. opt.itemStyle.emphasis.label.textStyle = this.getTextStyle(opt.itemStyle.emphasis.label.textStyle);
  572. this.z = opt.z;
  573. this.zlevel = opt.zlevel;
  574. return opt;
  575. },
  576. refresh: function (newOption) {
  577. if (newOption) {
  578. this.option = newOption;
  579. this.series = newOption.series;
  580. }
  581. this.backupShapeList();
  582. this._buildShape();
  583. },
  584. addDataAnimation: function (params, done) {
  585. var series = this.series;
  586. var aniMap = {};
  587. for (var i = 0, l = params.length; i < l; i++) {
  588. aniMap[params[i][0]] = params[i];
  589. }
  590. var aniCount = 0;
  591. function animationDone() {
  592. aniCount--;
  593. if (aniCount === 0) {
  594. done && done();
  595. }
  596. }
  597. var sectorMap = {};
  598. var textMap = {};
  599. var lineMap = {};
  600. var backupShapeList = this.shapeList;
  601. this.shapeList = [];
  602. var seriesIndex;
  603. var isHead;
  604. var dataGrow;
  605. var deltaIdxMap = {};
  606. for (var i = 0, l = params.length; i < l; i++) {
  607. seriesIndex = params[i][0];
  608. isHead = params[i][2];
  609. dataGrow = params[i][3];
  610. if (series[seriesIndex] && series[seriesIndex].type === ecConfig.CHART_TYPE_PIE) {
  611. if (isHead) {
  612. if (!dataGrow) {
  613. sectorMap[seriesIndex + '_' + series[seriesIndex].data.length] = 'delete';
  614. }
  615. deltaIdxMap[seriesIndex] = 1;
  616. } else {
  617. if (!dataGrow) {
  618. sectorMap[seriesIndex + '_-1'] = 'delete';
  619. deltaIdxMap[seriesIndex] = -1;
  620. } else {
  621. deltaIdxMap[seriesIndex] = 0;
  622. }
  623. }
  624. this._buildSinglePie(seriesIndex);
  625. }
  626. }
  627. var dataIndex;
  628. var key;
  629. for (var i = 0, l = this.shapeList.length; i < l; i++) {
  630. seriesIndex = this.shapeList[i]._seriesIndex;
  631. dataIndex = this.shapeList[i]._dataIndex;
  632. key = seriesIndex + '_' + dataIndex;
  633. switch (this.shapeList[i].type) {
  634. case 'sector':
  635. sectorMap[key] = this.shapeList[i];
  636. break;
  637. case 'text':
  638. textMap[key] = this.shapeList[i];
  639. break;
  640. case 'polyline':
  641. lineMap[key] = this.shapeList[i];
  642. break;
  643. }
  644. }
  645. this.shapeList = [];
  646. var targeSector;
  647. for (var i = 0, l = backupShapeList.length; i < l; i++) {
  648. seriesIndex = backupShapeList[i]._seriesIndex;
  649. if (aniMap[seriesIndex]) {
  650. dataIndex = backupShapeList[i]._dataIndex + deltaIdxMap[seriesIndex];
  651. key = seriesIndex + '_' + dataIndex;
  652. targeSector = sectorMap[key];
  653. if (!targeSector) {
  654. continue;
  655. }
  656. if (backupShapeList[i].type === 'sector') {
  657. if (targeSector != 'delete') {
  658. aniCount++;
  659. this.zr.animate(backupShapeList[i].id, 'style').when(400, {
  660. startAngle: targeSector.style.startAngle,
  661. endAngle: targeSector.style.endAngle
  662. }).done(animationDone).start();
  663. } else {
  664. aniCount++;
  665. this.zr.animate(backupShapeList[i].id, 'style').when(400, deltaIdxMap[seriesIndex] < 0 ? { startAngle: backupShapeList[i].style.startAngle } : { endAngle: backupShapeList[i].style.endAngle }).done(animationDone).start();
  666. }
  667. } else if (backupShapeList[i].type === 'text' || backupShapeList[i].type === 'polyline') {
  668. if (targeSector === 'delete') {
  669. this.zr.delShape(backupShapeList[i].id);
  670. } else {
  671. switch (backupShapeList[i].type) {
  672. case 'text':
  673. aniCount++;
  674. targeSector = textMap[key];
  675. this.zr.animate(backupShapeList[i].id, 'style').when(400, {
  676. x: targeSector.style.x,
  677. y: targeSector.style.y
  678. }).done(animationDone).start();
  679. break;
  680. case 'polyline':
  681. aniCount++;
  682. targeSector = lineMap[key];
  683. this.zr.animate(backupShapeList[i].id, 'style').when(400, { pointList: targeSector.style.pointList }).done(animationDone).start();
  684. break;
  685. }
  686. }
  687. }
  688. }
  689. }
  690. this.shapeList = backupShapeList;
  691. if (!aniCount) {
  692. animationDone();
  693. }
  694. },
  695. onclick: function (param) {
  696. var series = this.series;
  697. if (!this.isClick || !param.target) {
  698. return;
  699. }
  700. this.isClick = false;
  701. var offset;
  702. var target = param.target;
  703. var style = target.style;
  704. var seriesIndex = ecData.get(target, 'seriesIndex');
  705. var dataIndex = ecData.get(target, 'dataIndex');
  706. for (var i = 0, len = this.shapeList.length; i < len; i++) {
  707. if (this.shapeList[i].id === target.id) {
  708. seriesIndex = ecData.get(target, 'seriesIndex');
  709. dataIndex = ecData.get(target, 'dataIndex');
  710. if (!style._hasSelected) {
  711. var midAngle = ((style.startAngle + style.endAngle) / 2).toFixed(2) - 0;
  712. target.style._hasSelected = true;
  713. this._selected[seriesIndex][dataIndex] = true;
  714. target.style._x = target.style.x;
  715. target.style._y = target.style.y;
  716. offset = this.query(series[seriesIndex], 'selectedOffset');
  717. target.style.x += zrMath.cos(midAngle, true) * offset;
  718. target.style.y -= zrMath.sin(midAngle, true) * offset;
  719. } else {
  720. target.style.x = target.style._x;
  721. target.style.y = target.style._y;
  722. target.style._hasSelected = false;
  723. this._selected[seriesIndex][dataIndex] = false;
  724. }
  725. this.zr.modShape(target.id, target);
  726. } else if (this.shapeList[i].style._hasSelected && this._selectedMode === 'single') {
  727. seriesIndex = ecData.get(this.shapeList[i], 'seriesIndex');
  728. dataIndex = ecData.get(this.shapeList[i], 'dataIndex');
  729. this.shapeList[i].style.x = this.shapeList[i].style._x;
  730. this.shapeList[i].style.y = this.shapeList[i].style._y;
  731. this.shapeList[i].style._hasSelected = false;
  732. this._selected[seriesIndex][dataIndex] = false;
  733. this.zr.modShape(this.shapeList[i].id, this.shapeList[i]);
  734. }
  735. }
  736. this.messageCenter.dispatch(ecConfig.EVENT.PIE_SELECTED, param.event, {
  737. selected: this._selected,
  738. target: ecData.get(target, 'name')
  739. }, this.myChart);
  740. this.zr.refreshNextFrame();
  741. }
  742. };
  743. zrUtil.inherits(Pie, ChartBase);
  744. require('../chart').define('pie', Pie);
  745. return Pie;
  746. });