573 lines
14 KiB
JavaScript
573 lines
14 KiB
JavaScript
![]() |
function _createSuper(Derived) { return function () { var Super = _getPrototypeOf(Derived), result; if (_isNativeReflectConstruct()) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
|||
|
|
|||
|
function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
|
|||
|
|
|||
|
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
|||
|
|
|||
|
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
|
|||
|
|
|||
|
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
|||
|
|
|||
|
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
|
|||
|
|
|||
|
var Util = require('../util');
|
|||
|
|
|||
|
var DomUtil = Util.DomUtil;
|
|||
|
|
|||
|
var Component = require('../component');
|
|||
|
|
|||
|
var positionAdjust = require('./utils/position-adjust');
|
|||
|
|
|||
|
var spirialAdjust = require('./utils/spiral-adjust');
|
|||
|
|
|||
|
var bboxAdjust = require('./utils/bbox-adjust');
|
|||
|
|
|||
|
var LAYOUTS = {
|
|||
|
scatter: positionAdjust,
|
|||
|
map: spirialAdjust,
|
|||
|
treemap: bboxAdjust
|
|||
|
};
|
|||
|
|
|||
|
var Label = /*#__PURE__*/function (_Component) {
|
|||
|
_inheritsLoose(Label, _Component);
|
|||
|
|
|||
|
var _super = _createSuper(Label);
|
|||
|
|
|||
|
function Label() {
|
|||
|
return _Component.apply(this, arguments) || this;
|
|||
|
}
|
|||
|
|
|||
|
var _proto = Label.prototype;
|
|||
|
|
|||
|
_proto.getDefaultCfg = function getDefaultCfg() {
|
|||
|
var cfg = _Component.prototype.getDefaultCfg.call(this);
|
|||
|
|
|||
|
return Util.mix({}, cfg, {
|
|||
|
name: 'label',
|
|||
|
|
|||
|
/**
|
|||
|
* label类型
|
|||
|
* @type {(String)}
|
|||
|
*/
|
|||
|
type: 'default',
|
|||
|
|
|||
|
/**
|
|||
|
* 默认文本样式
|
|||
|
* @type {Array}
|
|||
|
*/
|
|||
|
textStyle: null,
|
|||
|
|
|||
|
/**
|
|||
|
* 文本显示格式化回调函数
|
|||
|
* @type {Function}
|
|||
|
*/
|
|||
|
formatter: null,
|
|||
|
|
|||
|
/**
|
|||
|
* 显示的文本集合
|
|||
|
* @type {Array}
|
|||
|
*/
|
|||
|
items: null,
|
|||
|
|
|||
|
/**
|
|||
|
* 是否使用html渲染label
|
|||
|
* @type {String}
|
|||
|
*/
|
|||
|
useHtml: false,
|
|||
|
|
|||
|
/**
|
|||
|
* html 渲染时用的容器的模板,必须存在 class = "g-labels"
|
|||
|
* @type {String}
|
|||
|
*/
|
|||
|
containerTpl: '<div class="g-labels" style="position:absolute;top:0;left:0;"></div>',
|
|||
|
|
|||
|
/**
|
|||
|
* html 渲染时单个 label 的模板,必须存在 class = "g-label"
|
|||
|
* @type {String}
|
|||
|
*/
|
|||
|
itemTpl: '<div class="g-label" style="position:absolute;">{text}</div>',
|
|||
|
|
|||
|
/**
|
|||
|
* label牵引线定义
|
|||
|
* @type {String || Object}
|
|||
|
*/
|
|||
|
labelLine: false,
|
|||
|
|
|||
|
/**
|
|||
|
* label牵引线容器
|
|||
|
* @type Object
|
|||
|
*/
|
|||
|
lineGroup: null,
|
|||
|
|
|||
|
/**
|
|||
|
* 需添加label的shape
|
|||
|
* @type Object
|
|||
|
*/
|
|||
|
shapes: null,
|
|||
|
|
|||
|
/**
|
|||
|
* 默认为true。为false时指定直接用items渲染文本,不进行config
|
|||
|
* @type Object
|
|||
|
*/
|
|||
|
config: true,
|
|||
|
|
|||
|
/**
|
|||
|
* 是否进行拾取
|
|||
|
* @type Object
|
|||
|
*/
|
|||
|
capture: true
|
|||
|
});
|
|||
|
}
|
|||
|
/*
|
|||
|
* 清空label容器
|
|||
|
*/
|
|||
|
;
|
|||
|
|
|||
|
_proto.clear = function clear() {
|
|||
|
var group = this.get('group');
|
|||
|
var container = this.get('container');
|
|||
|
|
|||
|
if (group && !group.get('destroyed')) {
|
|||
|
group.clear();
|
|||
|
}
|
|||
|
|
|||
|
if (container) {
|
|||
|
container.innerHTML = '';
|
|||
|
}
|
|||
|
|
|||
|
_Component.prototype.clear.call(this);
|
|||
|
}
|
|||
|
/**
|
|||
|
* 销毁group
|
|||
|
*/
|
|||
|
;
|
|||
|
|
|||
|
_proto.destroy = function destroy() {
|
|||
|
var group = this.get('group');
|
|||
|
var container = this.get('container');
|
|||
|
|
|||
|
if (!group.destroy) {
|
|||
|
group.destroy();
|
|||
|
}
|
|||
|
|
|||
|
if (container) {
|
|||
|
container.parentNode && container.parentNode.removeChild(container);
|
|||
|
}
|
|||
|
|
|||
|
_Component.prototype.destroy.call(this); // 要最后调用 super.destroy 否则 get 属性会无效
|
|||
|
|
|||
|
}
|
|||
|
/**
|
|||
|
* label绘制全过程
|
|||
|
*/
|
|||
|
;
|
|||
|
|
|||
|
_proto.render = function render() {
|
|||
|
this.clear();
|
|||
|
|
|||
|
this._init();
|
|||
|
|
|||
|
this.beforeDraw();
|
|||
|
this.draw();
|
|||
|
this.afterDraw();
|
|||
|
};
|
|||
|
|
|||
|
_proto._dryDraw = function _dryDraw() {
|
|||
|
var self = this;
|
|||
|
var items = self.get('items');
|
|||
|
var children = self.getLabels();
|
|||
|
var count = children.length;
|
|||
|
Util.each(items, function (item, index) {
|
|||
|
if (index < count) {
|
|||
|
var label = children[index];
|
|||
|
self.changeLabel(label, item);
|
|||
|
} else {
|
|||
|
var labelShape = self._addLabel(item, index);
|
|||
|
|
|||
|
if (labelShape) {
|
|||
|
labelShape._id = item._id;
|
|||
|
labelShape.set('coord', item.coord);
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
for (var i = count - 1; i >= items.length; i--) {
|
|||
|
children[i].remove();
|
|||
|
}
|
|||
|
|
|||
|
self._adjustLabels();
|
|||
|
|
|||
|
if (self.get('labelLine') || !self.get('config')) {
|
|||
|
self.drawLines();
|
|||
|
}
|
|||
|
}
|
|||
|
/**
|
|||
|
* 更新label
|
|||
|
* 1. 将items与group中的children对比,更新/新增/删除labels
|
|||
|
* 2. labels布局优化
|
|||
|
* 3. 画label连接线
|
|||
|
* 4. 绘制到画布
|
|||
|
*/
|
|||
|
;
|
|||
|
|
|||
|
_proto.draw = function draw() {
|
|||
|
this._dryDraw();
|
|||
|
|
|||
|
this.get('canvas').draw();
|
|||
|
}
|
|||
|
/*
|
|||
|
* 更新label
|
|||
|
* oldLabel shape或label dom
|
|||
|
* newLabel label data
|
|||
|
* index items中的下标
|
|||
|
*/
|
|||
|
;
|
|||
|
|
|||
|
_proto.changeLabel = function changeLabel(oldLabel, newLabel) {
|
|||
|
if (!oldLabel) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (oldLabel.tagName) {
|
|||
|
var node = this._createDom(newLabel);
|
|||
|
|
|||
|
oldLabel.innerHTML = node.innerHTML;
|
|||
|
|
|||
|
this._setCustomPosition(newLabel, oldLabel);
|
|||
|
} else {
|
|||
|
oldLabel._id = newLabel._id;
|
|||
|
oldLabel.attr('text', newLabel.text);
|
|||
|
|
|||
|
if (oldLabel.attr('x') !== newLabel.x || oldLabel.attr('y') !== newLabel.y) {
|
|||
|
oldLabel.resetMatrix();
|
|||
|
|
|||
|
if (newLabel.textStyle.rotate) {
|
|||
|
oldLabel.rotateAtStart(newLabel.textStyle.rotate);
|
|||
|
delete newLabel.textStyle.rotate;
|
|||
|
}
|
|||
|
|
|||
|
oldLabel.attr(newLabel);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
/**
|
|||
|
* 显示label
|
|||
|
*/
|
|||
|
;
|
|||
|
|
|||
|
_proto.show = function show() {
|
|||
|
var group = this.get('group');
|
|||
|
var container = this.get('container');
|
|||
|
|
|||
|
if (group) {
|
|||
|
group.show();
|
|||
|
}
|
|||
|
|
|||
|
if (container) {
|
|||
|
container.style.opacity = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
/**
|
|||
|
* 隐藏label
|
|||
|
*/
|
|||
|
;
|
|||
|
|
|||
|
_proto.hide = function hide() {
|
|||
|
var group = this.get('group');
|
|||
|
var container = this.get('container');
|
|||
|
|
|||
|
if (group) {
|
|||
|
group.hide();
|
|||
|
}
|
|||
|
|
|||
|
if (container) {
|
|||
|
container.style.opacity = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
/**
|
|||
|
* 画label连接线
|
|||
|
*/
|
|||
|
;
|
|||
|
|
|||
|
_proto.drawLines = function drawLines() {
|
|||
|
var self = this;
|
|||
|
var lineStyle = self.get('labelLine');
|
|||
|
|
|||
|
if (typeof lineStyle === 'boolean') {
|
|||
|
self.set('labelLine', {});
|
|||
|
}
|
|||
|
|
|||
|
var lineGroup = self.get('lineGroup');
|
|||
|
|
|||
|
if (!lineGroup || lineGroup.get('destroyed')) {
|
|||
|
lineGroup = self.get('group').addGroup({
|
|||
|
elCls: 'x-line-group'
|
|||
|
});
|
|||
|
self.set('lineGroup', lineGroup);
|
|||
|
} else {
|
|||
|
lineGroup.clear();
|
|||
|
}
|
|||
|
|
|||
|
Util.each(self.get('items'), function (label) {
|
|||
|
self.lineToLabel(label, lineGroup);
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
_proto.lineToLabel = function lineToLabel(label, lineGroup) {
|
|||
|
var self = this;
|
|||
|
|
|||
|
if (!self.get('config') && !label.labelLine) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var lineStyle = label.labelLine || self.get('labelLine');
|
|||
|
var capture = typeof label.capture === 'undefined' ? self.get('capture') : label.capture;
|
|||
|
var path = lineStyle.path;
|
|||
|
|
|||
|
if (path && Util.isFunction(lineStyle.path)) {
|
|||
|
path = lineStyle.path(label);
|
|||
|
}
|
|||
|
|
|||
|
if (!path) {
|
|||
|
var start = label.start || {
|
|||
|
x: label.x - label._offset.x,
|
|||
|
y: label.y - label._offset.y
|
|||
|
};
|
|||
|
path = [['M', start.x, start.y], ['L', label.x, label.y]];
|
|||
|
}
|
|||
|
|
|||
|
var stroke = label.color;
|
|||
|
|
|||
|
if (!stroke) {
|
|||
|
if (label.textStyle && label.textStyle.fill) {
|
|||
|
stroke = label.textStyle.fill;
|
|||
|
} else {
|
|||
|
stroke = '#000';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var lineShape = lineGroup.addShape('path', {
|
|||
|
attrs: Util.mix({
|
|||
|
path: path,
|
|||
|
fill: null,
|
|||
|
stroke: stroke
|
|||
|
}, lineStyle),
|
|||
|
capture: capture
|
|||
|
}); // label 对应线的动画关闭
|
|||
|
|
|||
|
lineShape.name = self.get('name'); // generate labelLine id according to label id
|
|||
|
|
|||
|
lineShape._id = label._id && label._id.replace('glabel', 'glabelline');
|
|||
|
lineShape.set('coord', self.get('coord'));
|
|||
|
} // 根据type对label布局
|
|||
|
;
|
|||
|
|
|||
|
_proto._adjustLabels = function _adjustLabels() {
|
|||
|
var self = this;
|
|||
|
var type = self.get('type');
|
|||
|
var labels = self.getLabels();
|
|||
|
var shapes = self.get('shapes');
|
|||
|
var layout = LAYOUTS[type];
|
|||
|
|
|||
|
if (type === 'default' || !layout) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
layout(labels, shapes);
|
|||
|
}
|
|||
|
/**
|
|||
|
* 获取当前所有label实例
|
|||
|
* @return {Array} 当前label实例
|
|||
|
*/
|
|||
|
;
|
|||
|
|
|||
|
_proto.getLabels = function getLabels() {
|
|||
|
var container = this.get('container');
|
|||
|
|
|||
|
if (container) {
|
|||
|
return Util.toArray(container.childNodes);
|
|||
|
}
|
|||
|
|
|||
|
return this.get('group').get('children');
|
|||
|
} // 先计算label的所有配置项,然后生成label实例
|
|||
|
;
|
|||
|
|
|||
|
_proto._addLabel = function _addLabel(item, index) {
|
|||
|
var cfg = item;
|
|||
|
|
|||
|
if (this.get('config')) {
|
|||
|
cfg = this._getLabelCfg(item, index);
|
|||
|
}
|
|||
|
|
|||
|
return this._createText(cfg);
|
|||
|
};
|
|||
|
|
|||
|
_proto._getLabelCfg = function _getLabelCfg(item, index) {
|
|||
|
var textStyle = this.get('textStyle') || {};
|
|||
|
var formatter = this.get('formatter');
|
|||
|
var htmlTemplate = this.get('htmlTemplate');
|
|||
|
|
|||
|
if (!Util.isObject(item)) {
|
|||
|
var tmp = item;
|
|||
|
item = {};
|
|||
|
item.text = tmp;
|
|||
|
}
|
|||
|
|
|||
|
if (Util.isFunction(textStyle)) {
|
|||
|
textStyle = textStyle(item.text, item, index);
|
|||
|
}
|
|||
|
|
|||
|
if (formatter) {
|
|||
|
item.text = formatter(item.text, item, index);
|
|||
|
}
|
|||
|
|
|||
|
if (htmlTemplate) {
|
|||
|
item.useHtml = true;
|
|||
|
|
|||
|
if (Util.isFunction(htmlTemplate)) {
|
|||
|
item.text = htmlTemplate(item.text, item, index);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (Util.isNil(item.text)) {
|
|||
|
item.text = '';
|
|||
|
}
|
|||
|
|
|||
|
item.text = item.text + ''; // ? 为什么转换为字符串
|
|||
|
|
|||
|
var cfg = Util.mix({}, item, {
|
|||
|
textStyle: textStyle
|
|||
|
}, {
|
|||
|
x: item.x || 0,
|
|||
|
y: item.y || 0
|
|||
|
});
|
|||
|
return cfg;
|
|||
|
}
|
|||
|
/**
|
|||
|
* label初始化,主要针对html容器
|
|||
|
*/
|
|||
|
;
|
|||
|
|
|||
|
_proto._init = function _init() {
|
|||
|
if (!this.get('group')) {
|
|||
|
var group = this.get('canvas').addGroup({
|
|||
|
id: 'label-group'
|
|||
|
});
|
|||
|
this.set('group', group);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
_proto.initHtmlContainer = function initHtmlContainer() {
|
|||
|
var container = this.get('container');
|
|||
|
|
|||
|
if (!container) {
|
|||
|
var containerTpl = this.get('containerTpl');
|
|||
|
var wrapper = this.get('canvas').get('el').parentNode;
|
|||
|
container = DomUtil.createDom(containerTpl);
|
|||
|
wrapper.style.position = 'relative';
|
|||
|
wrapper.appendChild(container);
|
|||
|
this.set('container', container);
|
|||
|
} else if (Util.isString(container)) {
|
|||
|
container = document.getElementById(container);
|
|||
|
|
|||
|
if (container) {
|
|||
|
this.set('container', container);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return container;
|
|||
|
} // 分html dom和G shape两种情况生成label实例
|
|||
|
;
|
|||
|
|
|||
|
_proto._createText = function _createText(config) {
|
|||
|
// @2018-11-29 by blue.lb 这里由于使用delete导致之后的配置无法获取到point和rotate,出现问题,深拷贝一次比较好
|
|||
|
var cfg = Util.deepMix({}, config);
|
|||
|
var container = this.get('container');
|
|||
|
var capture = typeof cfg.capture === 'undefined' ? this.get('capture') : cfg.capture;
|
|||
|
var labelShape;
|
|||
|
|
|||
|
if (cfg.useHtml || cfg.htmlTemplate) {
|
|||
|
if (!container) {
|
|||
|
container = this.initHtmlContainer();
|
|||
|
}
|
|||
|
|
|||
|
var node = this._createDom(cfg);
|
|||
|
|
|||
|
container.appendChild(node);
|
|||
|
|
|||
|
this._setCustomPosition(cfg, node);
|
|||
|
} else {
|
|||
|
var name = this.get('name');
|
|||
|
var origin = cfg.point;
|
|||
|
var group = this.get('group');
|
|||
|
delete cfg.point; // 临时解决,否则影响动画
|
|||
|
|
|||
|
var rotate = cfg.rotate; // textStyle中的rotate虽然可以正常画出,但是在做动画的时候可能会导致动画异常。移出,在定义好shape后通过transform实现效果。
|
|||
|
|
|||
|
if (cfg.textStyle) {
|
|||
|
if (cfg.textStyle.rotate) {
|
|||
|
rotate = cfg.textStyle.rotate;
|
|||
|
delete cfg.textStyle.rotate;
|
|||
|
}
|
|||
|
|
|||
|
cfg = Util.mix({
|
|||
|
x: cfg.x,
|
|||
|
y: cfg.y,
|
|||
|
textAlign: cfg.textAlign,
|
|||
|
text: cfg.text
|
|||
|
}, cfg.textStyle);
|
|||
|
}
|
|||
|
|
|||
|
labelShape = group.addShape('text', {
|
|||
|
attrs: cfg,
|
|||
|
capture: capture
|
|||
|
});
|
|||
|
|
|||
|
if (rotate) {
|
|||
|
// rotate是用角度定义的,转换为弧度
|
|||
|
if (Math.abs(rotate) > Math.PI * 2) {
|
|||
|
rotate = rotate / 180 * Math.PI;
|
|||
|
}
|
|||
|
|
|||
|
labelShape.transform([['t', -cfg.x, -cfg.y], ['r', rotate], ['t', cfg.x, cfg.y]]);
|
|||
|
}
|
|||
|
|
|||
|
labelShape.setSilent('origin', origin || cfg);
|
|||
|
labelShape.name = name; // 用于事件标注
|
|||
|
|
|||
|
this.get('appendInfo') && labelShape.setSilent('appendInfo', this.get('appendInfo'));
|
|||
|
return labelShape;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
_proto._createDom = function _createDom(cfg) {
|
|||
|
var itemTpl = this.get('itemTpl');
|
|||
|
var str = Util.substitute(itemTpl, {
|
|||
|
text: cfg.text
|
|||
|
});
|
|||
|
return DomUtil.createDom(str);
|
|||
|
} // 根据文本对齐方式确定dom位置
|
|||
|
;
|
|||
|
|
|||
|
_proto._setCustomPosition = function _setCustomPosition(cfg, htmlDom) {
|
|||
|
var textAlign = cfg.textAlign || 'left';
|
|||
|
var top = cfg.y;
|
|||
|
var left = cfg.x;
|
|||
|
var width = DomUtil.getOuterWidth(htmlDom);
|
|||
|
var height = DomUtil.getOuterHeight(htmlDom);
|
|||
|
top = top - height / 2;
|
|||
|
|
|||
|
if (textAlign === 'center') {
|
|||
|
left = left - width / 2;
|
|||
|
} else if (textAlign === 'right') {
|
|||
|
left = left - width;
|
|||
|
}
|
|||
|
|
|||
|
htmlDom.style.top = parseInt(top, 10) + 'px';
|
|||
|
htmlDom.style.left = parseInt(left, 10) + 'px';
|
|||
|
};
|
|||
|
|
|||
|
return Label;
|
|||
|
}(Component);
|
|||
|
|
|||
|
module.exports = Label;
|