var _require = require('../../renderer'), Group = _require.Group; var _require2 = require('@antv/component/lib'), Label = _require2.Label; // const visualCenter = require('@antv/component/lib/label/utils/visual-center'); var Global = require('../../global'); var Util = require('../../util'); var IGNORE_ARR = ['line', 'point', 'path']; var ORIGIN = '_origin'; function avg(arr) { var sum = 0; Util.each(arr, function (value) { sum += value; }); return sum / arr.length; } // 计算多边形重心: https://en.wikipedia.org/wiki/Centroid#Of_a_polygon function getCentroid(xs, ys) { if (Util.isNumber(xs) && Util.isNumber(ys)) { return [xs, ys]; } var i = -1, x = 0, y = 0; var former, current = xs.length - 1; var diff, k = 0; while (++i < xs.length) { former = current; current = i; k += diff = xs[former] * ys[current] - xs[current] * ys[former]; x += (xs[former] + xs[current]) * diff; y += (ys[former] + ys[current]) * diff; } k *= 3; return [x / k, y / k]; } var GeomLabels = function GeomLabels(cfg) { GeomLabels.superclass.constructor.call(this, cfg); }; Util.extend(GeomLabels, Group); Util.augment(GeomLabels, { getDefaultCfg: function getDefaultCfg() { return { label: Global.label, /** * 用户传入的文本配置信息 * @type {Object} */ labelCfg: null, /** * 所在的坐标系 * @type {Object} */ coord: null, /** * 图表的类型 * @type {String} */ geomType: null, zIndex: 6 }; }, _renderUI: function _renderUI() { GeomLabels.superclass._renderUI.call(this); this.initLabelsCfg(); var labelsGroup = this.addGroup(); var lineGroup = this.addGroup({ elCls: 'x-line-group' }); var labelRenderer = this.get('labelRenderer'); this.set('labelsGroup', labelsGroup); this.set('lineGroup', lineGroup); this.get('labelRenderer').set('group', labelsGroup); labelRenderer.set('group', labelsGroup); labelRenderer.set('lineGroup', lineGroup); }, // 初始化labels的配置项 initLabelsCfg: function initLabelsCfg() { var self = this; var labelRenderer = new Label(); var labels = self.getDefaultLabelCfg(); var labelCfg = self.get('labelCfg'); // Util.merge(labels, labelCfg.cfg); Util.deepMix(labels, labelCfg.globalCfg || labelCfg.cfg); labelRenderer.set('config', false); if (labels.labelLine) { labelRenderer.set('labelLine', labels.labelLine); } labelRenderer.set('coord', self.get('coord')); this.set('labelRenderer', labelRenderer); self.set('label', labels); }, /** * @protected * 默认的文本样式 * @return {Object} default label config */ getDefaultLabelCfg: function getDefaultLabelCfg() { var self = this; var labelCfg = self.get('labelCfg').cfg || self.get('labelCfg').globalCfg; var geomType = self.get('geomType'); var viewTheme = self.get('viewTheme') || Global; if (geomType === 'polygon' || labelCfg && labelCfg.offset < 0 && Util.indexOf(IGNORE_ARR, geomType) === -1) { return Util.deepMix({}, self.get('label'), viewTheme.innerLabels, labelCfg); } return Util.deepMix({}, self.get('label'), viewTheme.label, labelCfg); }, /** * @protected * 获取labels * @param {Array} points points * @param {Array} shapes shapes * @return {Array} label items */ getLabelsItems: function getLabelsItems(points, shapes) { var self = this; var items = []; var geom = self.get('geom'); var coord = self.get('coord'); self._getLabelCfgs(points, shapes); var labelCfg = self.get('labelItemCfgs'); // 获取label相关的x,y的值,获取具体的x,y,防止存在数组 Util.each(points, function (point, i) { var origin = point[ORIGIN]; var label = labelCfg[i]; if (!label) { items.push(null); return; } if (!Util.isArray(label.text)) { label.text = [label.text]; } var total = label.text.length; Util.each(label.text, function (sub, subIndex) { if (Util.isNil(sub) || sub === '') { items.push(null); return; } var obj = self.getLabelPoint(label, point, subIndex); obj = Util.mix({}, label, obj); if (!obj.textAlign) { obj.textAlign = self.getLabelAlign(obj, subIndex, total); } if (geom) { obj._id = geom._getShapeId(origin) + '-glabel-' + subIndex + '-' + obj.text; } obj.coord = coord; items.push(obj); }); }); return items; }, /* /!* * @protected * 如果发生冲突则会调整文本的位置 * @param {Array} items 文本的集合 * @param {Array} shapes 关联形状 * @return {Array} adjusted items *!/ adjustItems(items, shapes) { // 多边形shape的label位于其可视中心 if (this.get('geomType') === 'polygon') { let index, shape, path, center, points; Util.each(items, (item, i) => { if (!item) return; shape = shapes[ i ]; path = shape.attr('path'); points = [[]]; index = 0; path.forEach((segment, i) => { if (segment[ 0 ] === 'z' || segment[ 0 ] === 'Z' && i !== path.length - 1) { points.push([]); index += 1; } if (segment.length === 3) { points[ index ].push([ segment[ 1 ], segment[ 2 ] ]); } }); center = visualCenter(points); item.x = center.x; item.y = center.y; }); } return items; } */ adjustItems: function adjustItems(items) { Util.each(items, function (item) { if (!item) { return; } if (item.offsetX) { item.x += item.offsetX; } if (item.offsetY) { item.y += item.offsetY; } }); return items; }, /** * drawing lines to labels * @param {Array} items labels * @param {Object} labelLine configuration for label lines */ drawLines: function drawLines(items) { var self = this; Util.each(items, function (point) { if (!point) { return; } if (point.offset > 0) { self.lineToLabel(point); } }); }, // 定义连接线 lineToLabel: function lineToLabel() {}, /** * @protected * 获取文本的位置信息 * @param {Array} labelCfg labels * @param {Object} point point * @param {Number} index index * @return {Object} point */ getLabelPoint: function getLabelPoint(labelCfg, point, index) { var self = this; var coord = self.get('coord'); var total = labelCfg.text.length; function getDimValue(value, idx) { if (Util.isArray(value)) { if (labelCfg.text.length === 1) { // 如果仅一个label,多个y,取最后一个y if (value.length <= 2) { value = value[value.length - 1]; // value = value[0]; } else { value = avg(value); } } else { value = value[idx]; } } return value; } var label = { text: labelCfg.text[index] }; // 多边形场景,多用于地图 if (point && this.get('geomType') === 'polygon') { var centroid = getCentroid(point.x, point.y); // 多边形的场景也有 x 和 y 只是数字的情况,譬如当 x 和 y 都是分类字段的时候 @see #1184 label.x = centroid[0]; label.y = centroid[1]; } else { label.x = getDimValue(point.x, index); label.y = getDimValue(point.y, index); } // get nearest point of the shape as the label line start point if (point && point.nextPoints && (point.shape === 'funnel' || point.shape === 'pyramid')) { var maxX = -Infinity; point.nextPoints.forEach(function (p) { p = coord.convert(p); if (p.x > maxX) { maxX = p.x; } }); label.x = (label.x + maxX) / 2; } // sharp edge of the pyramid if (point.shape === 'pyramid' && !point.nextPoints && point.points) { point.points.forEach(function (p) { p = coord.convert(p); if (Util.isArray(p.x) && !point.x.includes(p.x) || Util.isNumber(p.x) && point.x !== p.x) { label.x = (label.x + p.x) / 2; } }); } if (labelCfg.position) { self.setLabelPosition(label, point, index, labelCfg.position); } var offsetPoint = self.getLabelOffset(labelCfg, index, total); if (labelCfg.offsetX) { offsetPoint.x += labelCfg.offsetX; } if (labelCfg.offsetY) { offsetPoint.y += labelCfg.offsetY; } self.transLabelPoint(label); label.start = { x: label.x, y: label.y }; label.x += offsetPoint.x; label.y += offsetPoint.y; label.color = point.color; return label; }, setLabelPosition: function setLabelPosition() {}, transLabelPoint: function transLabelPoint(point) { var self = this; var coord = self.get('coord'); var tmpPoint = coord.applyMatrix(point.x, point.y, 1); point.x = tmpPoint[0]; point.y = tmpPoint[1]; }, getOffsetVector: function getOffsetVector(point) { var self = this; var offset = point.offset || 0; var coord = self.get('coord'); var vector; if (coord.isTransposed) { // 如果x,y翻转,则偏移x vector = coord.applyMatrix(offset, 0); } else { // 否则,偏转y vector = coord.applyMatrix(0, offset); } return vector; }, // 获取默认的偏移量 getDefaultOffset: function getDefaultOffset(point) { var self = this; var offset = 0; var coord = self.get('coord'); var vector = self.getOffsetVector(point); if (coord.isTransposed) { // 如果x,y翻转,则偏移x offset = vector[0]; } else { // 否则,偏转y offset = vector[1]; } var yScale = this.get('yScale'); if (yScale && point.point) { // 仅考虑 y 单值的情况,多值的情况在这里不考虑 var yValue = point.point[yScale.field]; if (yValue < 0) { offset = offset * -1; // 如果 y 值是负值,则反向 } } return offset; }, // 获取文本的偏移位置,x,y getLabelOffset: function getLabelOffset(point, index, total) { var self = this; var offset = self.getDefaultOffset(point); var coord = self.get('coord'); var transposed = coord.isTransposed; var yField = transposed ? 'x' : 'y'; var factor = transposed ? 1 : -1; // y 方向上越大,像素的坐标越小,所以transposed时将系数变成 var offsetPoint = { x: 0, y: 0 }; if (index > 0 || total === 1) { // 判断是否小于0 offsetPoint[yField] = offset * factor; } else { offsetPoint[yField] = offset * factor * -1; } return offsetPoint; }, getLabelAlign: function getLabelAlign(point, index, total) { var self = this; var align = 'center'; var coord = self.get('coord'); if (coord.isTransposed) { var offset = self.getDefaultOffset(point); // var vector = coord.applyMatrix(offset,0); if (offset < 0) { align = 'right'; } else if (offset === 0) { align = 'center'; } else { align = 'left'; } if (total > 1 && index === 0) { if (align === 'right') { align = 'left'; } else if (align === 'left') { align = 'right'; } } } return align; }, _getLabelValue: function _getLabelValue(origin, scales) { if (!Util.isArray(scales)) { scales = [scales]; } var text = []; Util.each(scales, function (scale) { var value = origin[scale.field]; if (Util.isArray(value)) { var tmp = []; Util.each(value, function (subVal) { tmp.push(scale.getText(subVal)); }); value = tmp; } else { value = scale.getText(value); } if (Util.isNil(value) || value === '') { text.push(null); } text.push(value); }); return text; }, // 获取每个label的配置 _getLabelCfgs: function _getLabelCfgs(points) { var self = this; var labelCfg = this.get('labelCfg'); var scales = labelCfg.scales; var defaultCfg = this.get('label'); var viewTheme = self.get('viewTheme') || Global; var cfgs = []; if (labelCfg.globalCfg && labelCfg.globalCfg.type) { self.set('type', labelCfg.globalCfg.type); } Util.each(points, function (point, i) { var cfg = {}; var origin = point[ORIGIN]; var originText = self._getLabelValue(origin, scales); if (labelCfg.callback) { // callback中应使用原始数据,而不是数据字符串 var originValues = scales.map(function (scale) { return origin[scale.field]; }); // 将point信息以及index信息也返回,方便能够根据point以及index,返回不同的配置 cfg = labelCfg.callback.apply(null, [].concat(originValues, [point, i])); } if (!cfg && cfg !== 0) { cfgs.push(null); return; } if (Util.isString(cfg) || Util.isNumber(cfg)) { cfg = { text: cfg }; } else { cfg.text = cfg.content || originText[0]; delete cfg.content; } cfg = Util.mix({}, defaultCfg, labelCfg.globalCfg || {}, cfg); // 兼容旧的源数据写在item.point中 point.point = origin; cfg.point = origin; if (cfg.htmlTemplate) { cfg.useHtml = true; cfg.text = cfg.htmlTemplate.call(null, cfg.text, point, i); delete cfg.htmlTemplate; } if (cfg.formatter) { cfg.text = cfg.formatter.call(null, cfg.text, point, i); delete cfg.formatter; } if (cfg.label) { // 兼容有些直接写在labelCfg.label的配置 var label = cfg.label; delete cfg.label; cfg = Util.mix(cfg, label); } if (cfg.textStyle) { // 兼容旧写法,globalCfg的offset优先级高 delete cfg.textStyle.offset; var textStyle = cfg.textStyle; if (Util.isFunction(textStyle)) { cfg.textStyle = textStyle.call(null, cfg.text, point, i); } } if (cfg.labelLine) { cfg.labelLine = Util.mix({}, defaultCfg.labelLine, cfg.labelLine); } // 因为 defaultCfg.textStyle 有可能是函数,所以这里可能没有把主题的 label 样式合进来 cfg.textStyle = Util.mix({}, defaultCfg.textStyle, viewTheme.label.textStyle, cfg.textStyle); delete cfg.items; cfgs.push(cfg); }); this.set('labelItemCfgs', cfgs); }, showLabels: function showLabels(points, shapes) { var self = this; var labelRenderer = self.get('labelRenderer'); var items = self.getLabelsItems(points, shapes); shapes = [].concat(shapes); var type = self.get('type'); items = self.adjustItems(items, shapes); self.drawLines(items); labelRenderer.set('items', items.filter(function (item, i) { if (!item) { shapes.splice(i, 1); return false; } return true; })); if (type) { labelRenderer.set('shapes', shapes); labelRenderer.set('type', type); labelRenderer.set('points', points); } labelRenderer.set('canvas', this.get('canvas')); labelRenderer.draw(); }, destroy: function destroy() { this.get('labelRenderer').destroy(); // 清理文本 GeomLabels.superclass.destroy.call(this); } }); // Util.assign(GeomLabels.prototype, Labels.LabelslabelRenderer); module.exports = GeomLabels;