"use strict"; var Util = require('../util/common'); var _require = require('../graphic/'), Group = _require.Group; var DEFAULT_CFG = { anchorOffset: 5, // 锚点的偏移量 inflectionOffset: 15, // 拐点的偏移量 sidePadding: 20, // 文本距离画布四边的距离 lineHeight: 32, // 文本的行高 adjustOffset: 15, // 发生调整时的偏移量 skipOverlapLabels: false, // 是否不展示重叠的文本 triggerOn: 'touchstart', // 点击行为触发的时间类型 activeShape: false, // 当有图形被选中的时候,是否激活图形 activeStyle: { offset: 1, appendRadius: 8, fillOpacity: 0.5 }, label1OffsetY: -1, label2OffsetY: 1 }; function getEndPoint(center, angle, r) { return { x: center.x + r * Math.cos(angle), y: center.y + r * Math.sin(angle) }; } // 计算中间角度 function getMiddleAngle(startAngle, endAngle) { if (endAngle < startAngle) { endAngle += Math.PI * 2; } return (endAngle + startAngle) / 2; } // 判断两个矩形是否相交 function isOverlap(label1, label2) { var label1BBox = label1.getBBox(); var label2BBox = label2.getBBox(); return Math.max(label1BBox.minX, label2BBox.minX) <= Math.min(label1BBox.maxX, label2BBox.minX) && Math.max(label1BBox.minY, label2BBox.minY) <= Math.min(label1BBox.maxY, label2BBox.maxY); } var controller = /*#__PURE__*/function () { function controller(cfg) { var _this = this; this._handleEvent = function (ev) { var self = _this; var chart = self.chart, drawnLabels = self.drawnLabels, pieLabelCfg = self.pieLabelCfg; var onClick = pieLabelCfg.onClick, activeShape = pieLabelCfg.activeShape; var canvasEvent = Util.createEvent(ev, chart); var x = canvasEvent.x, y = canvasEvent.y; // 查找被点击的 label var clickedShape; for (var i = 0, len = drawnLabels.length; i < len; i++) { var shape = drawnLabels[i]; var bbox = shape.getBBox(); // 通过最小包围盒来判断击中情况 if (x >= bbox.minX && x <= bbox.maxX && y >= bbox.minY && y <= bbox.maxY) { clickedShape = shape; break; } } var pieData = chart.getSnapRecords({ x: x, y: y }); if (clickedShape) { canvasEvent.data = clickedShape.get('data'); } else if (pieData.length) { // 击中饼图扇形区域 canvasEvent.data = pieData[0]._origin; } onClick && onClick(canvasEvent); canvasEvent.data && activeShape && _this._activeShape(canvasEvent.data); }; Util.mix(this, cfg); var _chart = this.chart; this.canvasDom = _chart.get('canvas').get('el'); } var _proto = controller.prototype; _proto.renderLabels = function renderLabels() { var self = this; var chart = self.chart, pieLabelCfg = self.pieLabelCfg, labelGroup = self.labelGroup; var halves = [[], // left [] // right ]; // 存储左右 labels var geom = chart.get('geoms')[0]; var shapes = geom.get('container').get('children'); var anchorOffset = pieLabelCfg.anchorOffset, inflectionOffset = pieLabelCfg.inflectionOffset, label1 = pieLabelCfg.label1, label2 = pieLabelCfg.label2, lineHeight = pieLabelCfg.lineHeight, skipOverlapLabels = pieLabelCfg.skipOverlapLabels, label1OffsetY = pieLabelCfg.label1OffsetY, label2OffsetY = pieLabelCfg.label2OffsetY; var coord = chart.get('coord'); var center = coord.center, radius = coord.circleRadius; shapes.forEach(function (shape) { var _shape$_attrs$attrs = shape._attrs.attrs, startAngle = _shape$_attrs$attrs.startAngle, endAngle = _shape$_attrs$attrs.endAngle; var middleAngle = getMiddleAngle(startAngle, endAngle); var anchorPoint = getEndPoint(center, middleAngle, radius + anchorOffset); var inflectionPoint = getEndPoint(center, middleAngle, radius + inflectionOffset); var origin = shape.get('origin'); var _origin = origin._origin, color = origin.color; var label = { _anchor: anchorPoint, _inflection: inflectionPoint, _data: _origin, x: inflectionPoint.x, y: inflectionPoint.y, r: radius + inflectionOffset, fill: color }; var textGroup = new Group({ context: chart.get('canvas').get('context'), // 兼容 node、小程序环境 data: _origin // 存储原始数据 }); var textAttrs = { x: 0, y: 0, fontSize: 12, lineHeight: 12, fill: '#808080' }; if (Util.isFunction(label1)) { textGroup.addShape('Text', { attrs: Util.mix({ textBaseline: 'bottom' }, textAttrs, label1(_origin, color)), data: _origin, // 存储原始数据 offsetY: label1OffsetY }); } if (Util.isFunction(label2)) { textGroup.addShape('Text', { attrs: Util.mix({ textBaseline: 'top' }, textAttrs, label2(_origin, color)), data: _origin, // 存储原始数据 offsetY: label2OffsetY }); } label.textGroup = textGroup; // 判断文本的方向 if (anchorPoint.x < center.x) { label._side = 'left'; halves[0].push(label); } else { label._side = 'right'; halves[1].push(label); } }); var drawnLabels = []; if (skipOverlapLabels) { var lastLabel; // 存储上一个 label 对象,用于检测文本是否重叠 var labels = halves[1].concat(halves[0]); // 顺时针 for (var i = 0, len = labels.length; i < len; i++) { var label = labels[i]; var textGroup = self._drawLabel(label); if (lastLabel) { if (isOverlap(textGroup, lastLabel)) { // 重叠了就不绘制 continue; } } labelGroup.add(textGroup); self._drawLabelLine(label); lastLabel = textGroup; drawnLabels.push(textGroup); } } else { var height = chart.get('height'); var maxCountForOneSide = parseInt(height / lineHeight, 10); halves.forEach(function (half) { if (half.length > maxCountForOneSide) { half.splice(maxCountForOneSide, half.length - maxCountForOneSide); } half.sort(function (a, b) { return a.y - b.y; }); var labels = self._antiCollision(half); drawnLabels = drawnLabels.concat(labels); }); } this.drawnLabels = drawnLabels; }; _proto.bindEvents = function bindEvents() { var pieLabelCfg = this.pieLabelCfg; var triggerOn = pieLabelCfg.triggerOn || 'touchstart'; Util.addEventListener(this.canvasDom, triggerOn, this._handleEvent); }; _proto.unBindEvents = function unBindEvents() { var pieLabelCfg = this.pieLabelCfg; var triggerOn = pieLabelCfg.triggerOn || 'touchstart'; Util.removeEventListener(this.canvasDom, triggerOn, this._handleEvent); }; _proto.clear = function clear() { this.labelGroup && this.labelGroup.clear(); this.halo && this.halo.remove(true); this.lastSelectedData = null; this.drawnLabels = []; this.unBindEvents(); }; _proto._drawLabel = function _drawLabel(label) { var pieLabelCfg = this.pieLabelCfg, chart = this.chart; var canvasWidth = chart.get('width'); var sidePadding = pieLabelCfg.sidePadding; var y = label.y, textGroup = label.textGroup; var children = textGroup.get('children'); var textAttrs = { textAlign: label._side === 'left' ? 'left' : 'right', x: label._side === 'left' ? sidePadding : canvasWidth - sidePadding }; children.forEach(function (child) { child.attr(textAttrs); child.attr('y', y + child.get('offsetY')); }); return textGroup; }; _proto._drawLabelLine = function _drawLabelLine(label, maxLabelWidth) { var chart = this.chart, pieLabelCfg = this.pieLabelCfg, labelGroup = this.labelGroup; var canvasWidth = chart.get('width'); var sidePadding = pieLabelCfg.sidePadding, adjustOffset = pieLabelCfg.adjustOffset, lineStyle = pieLabelCfg.lineStyle, anchorStyle = pieLabelCfg.anchorStyle, skipOverlapLabels = pieLabelCfg.skipOverlapLabels; var _anchor = label._anchor, _inflection = label._inflection, fill = label.fill, y = label.y; var lastPoint = { x: label._side === 'left' ? sidePadding : canvasWidth - sidePadding, y: y }; var points = [_anchor, _inflection, lastPoint]; if (!skipOverlapLabels && _inflection.y !== y) { // 展示全部文本文本位置做过调整 if (_inflection.y < y) { // 文本被调整下去了,则添加拐点连接线 var point1 = _inflection; var point2 = { x: label._side === 'left' ? lastPoint.x + maxLabelWidth + adjustOffset : lastPoint.x - maxLabelWidth - adjustOffset, y: _inflection.y }; var point3 = { x: label._side === 'left' ? lastPoint.x + maxLabelWidth : lastPoint.x - maxLabelWidth, y: lastPoint.y }; points = [_anchor, point1, point2, point3, lastPoint]; if (label._side === 'right' && point2.x < point1.x || label._side === 'left' && point2.x > point1.x) { points = [_anchor, point3, lastPoint]; } } else { points = [_anchor, { x: _inflection.x, y: y }, lastPoint]; } } labelGroup.addShape('Polyline', { attrs: Util.mix({ points: points, lineWidth: 1, stroke: fill }, lineStyle) }); // 绘制锚点 labelGroup.addShape('Circle', { attrs: Util.mix({ x: _anchor.x, y: _anchor.y, r: 2, fill: fill }, anchorStyle) }); }; _proto._antiCollision = function _antiCollision(half) { var self = this; var chart = self.chart, pieLabelCfg = self.pieLabelCfg; var coord = chart.get('coord'); var canvasHeight = chart.get('height'); var center = coord.center, r = coord.circleRadius; var inflectionOffset = pieLabelCfg.inflectionOffset, lineHeight = pieLabelCfg.lineHeight; var startY = center.y - r - inflectionOffset - lineHeight; var overlapping = true; var totalH = canvasHeight; var i; var maxY = 0; var minY = Number.MIN_VALUE; var maxLabelWidth = 0; var boxes = half.map(function (label) { var labelY = label.y; if (labelY > maxY) { maxY = labelY; } if (labelY < minY) { minY = labelY; } var textGroup = label.textGroup; var labelWidth = textGroup.getBBox().width; if (labelWidth >= maxLabelWidth) { maxLabelWidth = labelWidth; } return { size: lineHeight, targets: [labelY - startY] }; }); if (maxY - startY > totalH) { totalH = maxY - startY; } var iteratorBoxed = function iteratorBoxed(boxes) { boxes.forEach(function (box) { var target = (Math.min.apply(minY, box.targets) + Math.max.apply(minY, box.targets)) / 2; box.pos = Math.min(Math.max(minY, target - box.size / 2), totalH - box.size); }); }; while (overlapping) { iteratorBoxed(boxes); // detect overlapping and join boxes overlapping = false; i = boxes.length; while (i--) { if (i > 0) { var previousBox = boxes[i - 1]; var box = boxes[i]; if (previousBox.pos + previousBox.size > box.pos) { // overlapping previousBox.size += box.size; previousBox.targets = previousBox.targets.concat(box.targets); // overflow, shift up if (previousBox.pos + previousBox.size > totalH) { previousBox.pos = totalH - previousBox.size; } boxes.splice(i, 1); // removing box overlapping = true; } } } } i = 0; boxes.forEach(function (b) { var posInCompositeBox = startY; // middle of the label b.targets.forEach(function () { half[i].y = b.pos + posInCompositeBox + lineHeight / 2; posInCompositeBox += lineHeight; i++; }); }); var drawnLabels = []; half.forEach(function (label) { var textGroup = self._drawLabel(label); var labelGroup = self.labelGroup; labelGroup.add(textGroup); self._drawLabelLine(label, maxLabelWidth); drawnLabels.push(textGroup); }); return drawnLabels; }; _proto._getSelectedShapeByData = function _getSelectedShapeByData(data) { var selectedShape = null; var chart = this.chart; var geom = chart.get('geoms')[0]; var container = geom.get('container'); var children = container.get('children'); Util.each(children, function (child) { if (child.get('isShape') && child.get('className') === geom.get('type')) { // get geometry's shape var shapeData = child.get('origin')._origin; if (Util.isObjectValueEqual(shapeData, data)) { selectedShape = child; return false; } } }); return selectedShape; }; _proto._activeShape = function _activeShape(data) { var chart = this.chart, lastSelectedData = this.lastSelectedData, pieLabelCfg = this.pieLabelCfg; if (data === lastSelectedData) { return; } this.lastSelectedData = data; var activeStyle = pieLabelCfg.activeStyle; var selectedShape = this._getSelectedShapeByData(data); var _selectedShape$_attrs = selectedShape._attrs.attrs, x = _selectedShape$_attrs.x, y = _selectedShape$_attrs.y, startAngle = _selectedShape$_attrs.startAngle, endAngle = _selectedShape$_attrs.endAngle, r = _selectedShape$_attrs.r, fill = _selectedShape$_attrs.fill; var frontPlot = chart.get('frontPlot'); this.halo && this.halo.remove(true); var halo = frontPlot.addShape('sector', { attrs: Util.mix({ x: x, y: y, r: r + activeStyle.offset + activeStyle.appendRadius, r0: r + activeStyle.offset, fill: fill, startAngle: startAngle, endAngle: endAngle }, activeStyle) }); this.halo = halo; chart.get('canvas').draw(); }; return controller; }(); module.exports = { init: function init(chart) { var frontPlot = chart.get('frontPlot'); var labelGroup = frontPlot.addGroup({ className: 'pie-label', zIndex: 0 }); var pieLabelController = new controller({ chart: chart, labelGroup: labelGroup }); chart.set('pieLabelController', pieLabelController); chart.pieLabel = function (cfg) { cfg = Util.deepMix({}, DEFAULT_CFG, cfg); pieLabelController.pieLabelCfg = cfg; return this; }; }, afterGeomDraw: function afterGeomDraw(chart) { var controller = chart.get('pieLabelController'); if (controller.pieLabelCfg) { // 用户配置了饼图文本 controller.renderLabels(); controller.bindEvents(); // 绑定事件 } }, clearInner: function clearInner(chart) { var controller = chart.get('pieLabelController'); if (controller.pieLabelCfg) { // 用户配置了饼图文本 controller.clear(); } } };