pie-label.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. "use strict";
  2. exports.__esModule = true;
  3. exports.init = init;
  4. exports.afterGeomDraw = afterGeomDraw;
  5. exports.clearInner = clearInner;
  6. exports["default"] = void 0;
  7. var _common = require("../util/common");
  8. var _graphic = require("../graphic/");
  9. function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
  10. var DEFAULT_CFG = {
  11. anchorOffset: 5,
  12. // 锚点的偏移量
  13. inflectionOffset: 15,
  14. // 拐点的偏移量
  15. sidePadding: 20,
  16. // 文本距离画布四边的距离
  17. lineHeight: 32,
  18. // 文本的行高
  19. adjustOffset: 15,
  20. // 发生调整时的偏移量
  21. skipOverlapLabels: false,
  22. // 是否不展示重叠的文本
  23. triggerOn: 'touchstart',
  24. // 点击行为触发的时间类型
  25. activeShape: false,
  26. // 当有图形被选中的时候,是否激活图形
  27. activeStyle: {
  28. offset: 1,
  29. appendRadius: 8,
  30. fillOpacity: 0.5
  31. },
  32. label1OffsetY: -1,
  33. label2OffsetY: 1
  34. };
  35. function getEndPoint(center, angle, r) {
  36. return {
  37. x: center.x + r * Math.cos(angle),
  38. y: center.y + r * Math.sin(angle)
  39. };
  40. } // 计算中间角度
  41. function getMiddleAngle(startAngle, endAngle) {
  42. if (endAngle < startAngle) {
  43. endAngle += Math.PI * 2;
  44. }
  45. return (endAngle + startAngle) / 2;
  46. } // 判断两个矩形是否相交
  47. function isOverlap(label1, label2) {
  48. var label1BBox = label1.getBBox();
  49. var label2BBox = label2.getBBox();
  50. return Math.max(label1BBox.minX, label2BBox.minX) <= Math.min(label1BBox.maxX, label2BBox.maxX) && Math.max(label1BBox.minY, label2BBox.minY) <= Math.min(label1BBox.maxY, label2BBox.maxY);
  51. }
  52. var controller = /*#__PURE__*/function () {
  53. function controller(cfg) {
  54. var _this = this;
  55. _defineProperty(this, "_handleEvent", function (ev) {
  56. var self = _this;
  57. var chart = self.chart,
  58. drawnLabels = self.drawnLabels,
  59. pieLabelCfg = self.pieLabelCfg;
  60. var onClick = pieLabelCfg.onClick,
  61. activeShape = pieLabelCfg.activeShape;
  62. var canvasEvent = (0, _common.createEvent)(ev, chart);
  63. var x = canvasEvent.x,
  64. y = canvasEvent.y; // 查找被点击的 label
  65. var clickedShape;
  66. for (var i = 0, len = drawnLabels.length; i < len; i++) {
  67. var shape = drawnLabels[i];
  68. var bbox = shape.getBBox(); // 通过最小包围盒来判断击中情况
  69. if (x >= bbox.minX && x <= bbox.maxX && y >= bbox.minY && y <= bbox.maxY) {
  70. clickedShape = shape;
  71. break;
  72. }
  73. }
  74. var pieData = chart.getSnapRecords({
  75. x: x,
  76. y: y
  77. });
  78. if (clickedShape) {
  79. canvasEvent.data = clickedShape.get('data');
  80. } else if (pieData.length) {
  81. // 击中饼图扇形区域
  82. canvasEvent.data = pieData[0]._origin;
  83. }
  84. onClick && onClick(canvasEvent);
  85. canvasEvent.data && activeShape && _this._activeShape(canvasEvent.data);
  86. });
  87. (0, _common.mix)(this, cfg);
  88. var _chart = this.chart;
  89. this.canvasDom = _chart.get('canvas').get('el');
  90. }
  91. var _proto = controller.prototype;
  92. _proto.renderLabels = function renderLabels() {
  93. var self = this;
  94. var chart = self.chart,
  95. pieLabelCfg = self.pieLabelCfg,
  96. labelGroup = self.labelGroup;
  97. var halves = [[], // left
  98. [] // right
  99. ]; // 存储左右 labels
  100. var geom = chart.get('geoms')[0];
  101. var shapes = geom.get('container').get('children');
  102. var anchorOffset = pieLabelCfg.anchorOffset,
  103. inflectionOffset = pieLabelCfg.inflectionOffset,
  104. label1 = pieLabelCfg.label1,
  105. label2 = pieLabelCfg.label2,
  106. lineHeight = pieLabelCfg.lineHeight,
  107. skipOverlapLabels = pieLabelCfg.skipOverlapLabels,
  108. label1OffsetY = pieLabelCfg.label1OffsetY,
  109. label2OffsetY = pieLabelCfg.label2OffsetY;
  110. var coord = chart.get('coord');
  111. var center = coord.center,
  112. radius = coord.circleRadius;
  113. shapes.forEach(function (shape) {
  114. var _shape$_attrs$attrs = shape._attrs.attrs,
  115. startAngle = _shape$_attrs$attrs.startAngle,
  116. endAngle = _shape$_attrs$attrs.endAngle;
  117. var middleAngle = getMiddleAngle(startAngle, endAngle);
  118. var anchorPoint = getEndPoint(center, middleAngle, radius + anchorOffset);
  119. var inflectionPoint = getEndPoint(center, middleAngle, radius + inflectionOffset);
  120. var origin = shape.get('origin');
  121. var _origin = origin._origin,
  122. color = origin.color;
  123. var label = {
  124. _anchor: anchorPoint,
  125. _inflection: inflectionPoint,
  126. _data: _origin,
  127. x: inflectionPoint.x,
  128. y: inflectionPoint.y,
  129. r: radius + inflectionOffset,
  130. fill: color
  131. };
  132. var textGroup = new _graphic.Group({
  133. context: chart.get('canvas').get('context'),
  134. // 兼容 node、小程序环境
  135. data: _origin // 存储原始数据
  136. });
  137. var textAttrs = {
  138. x: 0,
  139. y: 0,
  140. fontSize: 12,
  141. lineHeight: 12,
  142. fill: '#808080'
  143. };
  144. if ((0, _common.isFunction)(label1)) {
  145. textGroup.addShape('Text', {
  146. attrs: (0, _common.mix)({
  147. textBaseline: 'bottom'
  148. }, textAttrs, label1(_origin, color)),
  149. data: _origin,
  150. // 存储原始数据
  151. offsetY: label1OffsetY
  152. });
  153. }
  154. if ((0, _common.isFunction)(label2)) {
  155. textGroup.addShape('Text', {
  156. attrs: (0, _common.mix)({
  157. textBaseline: 'top'
  158. }, textAttrs, label2(_origin, color)),
  159. data: _origin,
  160. // 存储原始数据
  161. offsetY: label2OffsetY
  162. });
  163. }
  164. label.textGroup = textGroup; // 判断文本的方向
  165. if (anchorPoint.x < center.x) {
  166. label._side = 'left';
  167. halves[0].push(label);
  168. } else {
  169. label._side = 'right';
  170. halves[1].push(label);
  171. }
  172. });
  173. var drawnLabels = [];
  174. if (skipOverlapLabels) {
  175. var lastLabel; // 存储上一个 label 对象,用于检测文本是否重叠
  176. var labels = halves[1].concat(halves[0]); // 顺时针
  177. for (var i = 0, len = labels.length; i < len; i++) {
  178. var label = labels[i];
  179. var textGroup = self._drawLabel(label);
  180. if (lastLabel) {
  181. if (isOverlap(textGroup, lastLabel)) {
  182. // 重叠了就不绘制
  183. continue;
  184. }
  185. }
  186. labelGroup.add(textGroup);
  187. self._drawLabelLine(label);
  188. lastLabel = textGroup;
  189. drawnLabels.push(textGroup);
  190. }
  191. } else {
  192. var height = chart.get('height');
  193. var maxCountForOneSide = parseInt(height / lineHeight, 10);
  194. halves.forEach(function (half) {
  195. if (half.length > maxCountForOneSide) {
  196. half.splice(maxCountForOneSide, half.length - maxCountForOneSide);
  197. }
  198. half.sort(function (a, b) {
  199. return a.y - b.y;
  200. });
  201. var labels = self._antiCollision(half);
  202. drawnLabels = drawnLabels.concat(labels);
  203. });
  204. }
  205. this.drawnLabels = drawnLabels;
  206. };
  207. _proto.bindEvents = function bindEvents() {
  208. var pieLabelCfg = this.pieLabelCfg;
  209. var triggerOn = pieLabelCfg.triggerOn || 'touchstart';
  210. (0, _common.addEventListener)(this.canvasDom, triggerOn, this._handleEvent);
  211. };
  212. _proto.unBindEvents = function unBindEvents() {
  213. var pieLabelCfg = this.pieLabelCfg;
  214. var triggerOn = pieLabelCfg.triggerOn || 'touchstart';
  215. (0, _common.removeEventListener)(this.canvasDom, triggerOn, this._handleEvent);
  216. };
  217. _proto.clear = function clear() {
  218. this.labelGroup && this.labelGroup.clear();
  219. this.halo && this.halo.remove(true);
  220. this.lastSelectedData = null;
  221. this.drawnLabels = [];
  222. this.unBindEvents();
  223. };
  224. _proto._drawLabel = function _drawLabel(label) {
  225. var pieLabelCfg = this.pieLabelCfg,
  226. chart = this.chart;
  227. var canvasWidth = chart.get('width');
  228. var sidePadding = pieLabelCfg.sidePadding;
  229. var y = label.y,
  230. textGroup = label.textGroup;
  231. var children = textGroup.get('children');
  232. var textAttrs = {
  233. textAlign: label._side === 'left' ? 'left' : 'right',
  234. x: label._side === 'left' ? sidePadding : canvasWidth - sidePadding
  235. };
  236. children.forEach(function (child) {
  237. child.attr(textAttrs);
  238. child.attr('y', y + child.get('offsetY'));
  239. });
  240. return textGroup;
  241. };
  242. _proto._drawLabelLine = function _drawLabelLine(label, maxLabelWidth) {
  243. var chart = this.chart,
  244. pieLabelCfg = this.pieLabelCfg,
  245. labelGroup = this.labelGroup;
  246. var canvasWidth = chart.get('width');
  247. var sidePadding = pieLabelCfg.sidePadding,
  248. adjustOffset = pieLabelCfg.adjustOffset,
  249. lineStyle = pieLabelCfg.lineStyle,
  250. anchorStyle = pieLabelCfg.anchorStyle,
  251. skipOverlapLabels = pieLabelCfg.skipOverlapLabels;
  252. var _anchor = label._anchor,
  253. _inflection = label._inflection,
  254. fill = label.fill,
  255. y = label.y;
  256. var lastPoint = {
  257. x: label._side === 'left' ? sidePadding : canvasWidth - sidePadding,
  258. y: y
  259. };
  260. var points = [_anchor, _inflection, lastPoint];
  261. if (!skipOverlapLabels && _inflection.y !== y) {
  262. // 展示全部文本文本位置做过调整
  263. if (_inflection.y < y) {
  264. // 文本被调整下去了,则添加拐点连接线
  265. var point1 = _inflection;
  266. var point2 = {
  267. x: label._side === 'left' ? lastPoint.x + maxLabelWidth + adjustOffset : lastPoint.x - maxLabelWidth - adjustOffset,
  268. y: _inflection.y
  269. };
  270. var point3 = {
  271. x: label._side === 'left' ? lastPoint.x + maxLabelWidth : lastPoint.x - maxLabelWidth,
  272. y: lastPoint.y
  273. };
  274. points = [_anchor, point1, point2, point3, lastPoint];
  275. if (label._side === 'right' && point2.x < point1.x || label._side === 'left' && point2.x > point1.x) {
  276. points = [_anchor, point3, lastPoint];
  277. }
  278. } else {
  279. points = [_anchor, {
  280. x: _inflection.x,
  281. y: y
  282. }, lastPoint];
  283. }
  284. }
  285. labelGroup.addShape('Polyline', {
  286. attrs: (0, _common.mix)({
  287. points: points,
  288. lineWidth: 1,
  289. stroke: fill
  290. }, lineStyle)
  291. }); // 绘制锚点
  292. labelGroup.addShape('Circle', {
  293. attrs: (0, _common.mix)({
  294. x: _anchor.x,
  295. y: _anchor.y,
  296. r: 2,
  297. fill: fill
  298. }, anchorStyle)
  299. });
  300. };
  301. _proto._antiCollision = function _antiCollision(half) {
  302. var self = this;
  303. var chart = self.chart,
  304. pieLabelCfg = self.pieLabelCfg;
  305. var coord = chart.get('coord');
  306. var canvasHeight = chart.get('height');
  307. var center = coord.center,
  308. r = coord.circleRadius;
  309. var inflectionOffset = pieLabelCfg.inflectionOffset,
  310. lineHeight = pieLabelCfg.lineHeight;
  311. var startY = center.y - r - inflectionOffset - lineHeight;
  312. var overlapping = true;
  313. var totalH = canvasHeight;
  314. var i;
  315. var maxY = 0;
  316. var minY = Number.MIN_VALUE;
  317. var maxLabelWidth = 0;
  318. var boxes = half.map(function (label) {
  319. var labelY = label.y;
  320. if (labelY > maxY) {
  321. maxY = labelY;
  322. }
  323. if (labelY < minY) {
  324. minY = labelY;
  325. }
  326. var textGroup = label.textGroup;
  327. var labelWidth = textGroup.getBBox().width;
  328. if (labelWidth >= maxLabelWidth) {
  329. maxLabelWidth = labelWidth;
  330. }
  331. return {
  332. size: lineHeight,
  333. targets: [labelY - startY]
  334. };
  335. });
  336. if (maxY - startY > totalH) {
  337. totalH = maxY - startY;
  338. }
  339. var iteratorBoxed = function iteratorBoxed(boxes) {
  340. boxes.forEach(function (box) {
  341. var target = (Math.min.apply(minY, box.targets) + Math.max.apply(minY, box.targets)) / 2;
  342. box.pos = Math.min(Math.max(minY, target - box.size / 2), totalH - box.size);
  343. });
  344. };
  345. while (overlapping) {
  346. iteratorBoxed(boxes); // detect overlapping and join boxes
  347. overlapping = false;
  348. i = boxes.length;
  349. while (i--) {
  350. if (i > 0) {
  351. var previousBox = boxes[i - 1];
  352. var box = boxes[i];
  353. if (previousBox.pos + previousBox.size > box.pos) {
  354. // overlapping
  355. previousBox.size += box.size;
  356. previousBox.targets = previousBox.targets.concat(box.targets); // overflow, shift up
  357. if (previousBox.pos + previousBox.size > totalH) {
  358. previousBox.pos = totalH - previousBox.size;
  359. }
  360. boxes.splice(i, 1); // removing box
  361. overlapping = true;
  362. }
  363. }
  364. }
  365. }
  366. i = 0;
  367. boxes.forEach(function (b) {
  368. var posInCompositeBox = startY; // middle of the label
  369. b.targets.forEach(function () {
  370. half[i].y = b.pos + posInCompositeBox + lineHeight / 2;
  371. posInCompositeBox += lineHeight;
  372. i++;
  373. });
  374. });
  375. var drawnLabels = [];
  376. half.forEach(function (label) {
  377. var textGroup = self._drawLabel(label);
  378. var labelGroup = self.labelGroup;
  379. labelGroup.add(textGroup);
  380. self._drawLabelLine(label, maxLabelWidth);
  381. drawnLabels.push(textGroup);
  382. });
  383. return drawnLabels;
  384. };
  385. _proto._getSelectedShapeByData = function _getSelectedShapeByData(data) {
  386. var selectedShape = null;
  387. var chart = this.chart;
  388. var geom = chart.get('geoms')[0];
  389. var container = geom.get('container');
  390. var children = container.get('children');
  391. (0, _common.each)(children, function (child) {
  392. if (child.get('isShape') && child.get('className') === geom.get('type')) {
  393. // get geometry's shape
  394. var shapeData = child.get('origin')._origin;
  395. if ((0, _common.isObjectValueEqual)(shapeData, data)) {
  396. selectedShape = child;
  397. return false;
  398. }
  399. }
  400. });
  401. return selectedShape;
  402. };
  403. _proto._activeShape = function _activeShape(data) {
  404. var chart = this.chart,
  405. lastSelectedData = this.lastSelectedData,
  406. pieLabelCfg = this.pieLabelCfg;
  407. if (data === lastSelectedData) {
  408. return;
  409. }
  410. this.lastSelectedData = data;
  411. var activeStyle = pieLabelCfg.activeStyle;
  412. var selectedShape = this._getSelectedShapeByData(data);
  413. var _selectedShape$_attrs = selectedShape._attrs.attrs,
  414. x = _selectedShape$_attrs.x,
  415. y = _selectedShape$_attrs.y,
  416. startAngle = _selectedShape$_attrs.startAngle,
  417. endAngle = _selectedShape$_attrs.endAngle,
  418. r = _selectedShape$_attrs.r,
  419. fill = _selectedShape$_attrs.fill;
  420. var frontPlot = chart.get('frontPlot');
  421. this.halo && this.halo.remove(true);
  422. var halo = frontPlot.addShape('sector', {
  423. attrs: (0, _common.mix)({
  424. x: x,
  425. y: y,
  426. r: r + activeStyle.offset + activeStyle.appendRadius,
  427. r0: r + activeStyle.offset,
  428. fill: fill,
  429. startAngle: startAngle,
  430. endAngle: endAngle
  431. }, activeStyle)
  432. });
  433. this.halo = halo;
  434. chart.get('canvas').draw();
  435. };
  436. return controller;
  437. }();
  438. function init(chart) {
  439. var frontPlot = chart.get('frontPlot');
  440. var labelGroup = frontPlot.addGroup({
  441. className: 'pie-label',
  442. zIndex: 0
  443. });
  444. var pieLabelController = new controller({
  445. chart: chart,
  446. labelGroup: labelGroup
  447. });
  448. chart.set('pieLabelController', pieLabelController);
  449. chart.pieLabel = function (cfg) {
  450. cfg = (0, _common.deepMix)({}, DEFAULT_CFG, cfg);
  451. pieLabelController.pieLabelCfg = cfg;
  452. return this;
  453. };
  454. }
  455. function afterGeomDraw(chart) {
  456. var controller = chart.get('pieLabelController');
  457. if (controller.pieLabelCfg) {
  458. // 用户配置了饼图文本
  459. controller.renderLabels();
  460. controller.bindEvents(); // 绑定事件
  461. }
  462. }
  463. function clearInner(chart) {
  464. var controller = chart.get('pieLabelController');
  465. if (controller.pieLabelCfg) {
  466. // 用户配置了饼图文本
  467. controller.clear();
  468. }
  469. }
  470. var _default = {
  471. init: init,
  472. afterGeomDraw: afterGeomDraw,
  473. clearInner: clearInner
  474. };
  475. exports["default"] = _default;