import { __assign, __extends } from "tslib"; import { deepMix, each, get, isArray, isFunction, isString } from '@antv/util'; import { doAnimate } from '../../animate'; import Base from '../../base'; import { getReplaceAttrs } from '../../util/graphics'; import { propagationDelegate } from '@antv/component/lib/util/event'; /** * Element 图形元素。 * 定义:在 G2 中,我们会将数据通过图形语法映射成不同的图形,比如点图,数据集中的每条数据会对应一个点,柱状图每条数据对应一个柱子,线图则是一组数据对应一条折线,Element 即一条/一组数据对应的图形元素,它代表一条数据或者一个数据集,在图形层面,它可以是单个 Shape 也可以是多个 Shape,我们称之为图形元素。 */ var Element = /** @class */ (function (_super) { __extends(Element, _super); function Element(cfg) { var _this = _super.call(this, cfg) || this; // 存储当前开启的状态 _this.states = []; var shapeFactory = cfg.shapeFactory, container = cfg.container, offscreenGroup = cfg.offscreenGroup, _a = cfg.visible, visible = _a === void 0 ? true : _a; _this.shapeFactory = shapeFactory; _this.container = container; _this.offscreenGroup = offscreenGroup; _this.visible = visible; return _this; } /** * 绘制图形。 * @param model 绘制数据。 * @param isUpdate 可选,是否是更新发生后的绘制。 */ Element.prototype.draw = function (model, isUpdate) { if (isUpdate === void 0) { isUpdate = false; } this.model = model; this.data = model.data; // 存储原始数据 this.shapeType = this.getShapeType(model); // 绘制图形 this.drawShape(model, isUpdate); if (this.visible === false) { // 用户在初始化的时候声明 visible: false this.changeVisible(false); } }; /** * 更新图形。 * @param model 更新的绘制数据。 */ Element.prototype.update = function (model) { var _a = this, shapeFactory = _a.shapeFactory, shape = _a.shape; if (!shape) { return; } // 更新数据 this.model = model; this.data = model.data; this.shapeType = this.getShapeType(model); // step 1: 更新 shape 携带的信息 this.setShapeInfo(shape, model); // step 2: 使用虚拟 Group 重新绘制 shape,然后更新当前 shape var offscreenGroup = this.getOffscreenGroup(); var newShape = shapeFactory.drawShape(this.shapeType, model, offscreenGroup); // @ts-ignore newShape.cfg.data = this.data; // @ts-ignore newShape.cfg.origin = model; // step 3: 同步 shape 样式 this.syncShapeStyle(shape, newShape, '', this.getAnimateCfg('update')); }; /** * 销毁 element 实例。 */ Element.prototype.destroy = function () { var _a = this, shapeFactory = _a.shapeFactory, shape = _a.shape; if (shape) { var animateCfg = this.getAnimateCfg('leave'); if (animateCfg) { // 指定了动画配置则执行销毁动画 doAnimate(shape, animateCfg, { coordinate: shapeFactory.coordinate, toAttrs: __assign({}, shape.attr()), }); } else { // 否则直接销毁 shape.remove(true); } } // reset this.states = []; this.shapeFactory = undefined; this.container = undefined; this.shape = undefined; this.animate = undefined; this.geometry = undefined; this.labelShape = undefined; this.model = undefined; this.data = undefined; this.offscreenGroup = undefined; this.statesStyle = undefined; _super.prototype.destroy.call(this); }; /** * 显示或者隐藏 element。 * @param visible 是否可见。 */ Element.prototype.changeVisible = function (visible) { _super.prototype.changeVisible.call(this, visible); if (visible) { if (this.shape) { this.shape.show(); } if (this.labelShape) { this.labelShape.forEach(function (label) { label.show(); }); } } else { if (this.shape) { this.shape.hide(); } if (this.labelShape) { this.labelShape.forEach(function (label) { label.hide(); }); } } }; /** * 设置 Element 的状态。 * * 目前 Element 开放三种状态: * 1. active * 2. selected * 3. inactive * * 这三种状态相互独立,可以进行叠加。 * * 这三种状态的样式可在 [[Theme]] 主题中或者通过 `geometry.state()` 接口进行配置。 * * ```ts * // 激活 active 状态 * setState('active', true); * ``` * * @param stateName 状态名 * @param stateStatus 是否开启状态 */ Element.prototype.setState = function (stateName, stateStatus) { var _this = this; var _a = this, states = _a.states, shapeFactory = _a.shapeFactory, model = _a.model, shape = _a.shape, shapeType = _a.shapeType; var index = states.indexOf(stateName); if (stateStatus) { // 开启状态 if (index > -1) { // 该状态已经开启,则返回 return; } states.push(stateName); if (stateName === 'active' || stateName === 'selected') { shape.toFront(); } } else { if (index === -1) { // 关闭状态,但是状态未设置过 return; } states.splice(index, 1); if (stateName === 'active' || stateName === 'selected') { shape.toBack(); } } // 使用虚拟 group 重新绘制 shape,然后对这个 shape 应用状态样式后,更新当前 shape。 var offscreenShape = shapeFactory.drawShape(shapeType, model, this.getOffscreenGroup()); if (states.length) { // 应用当前状态 states.forEach(function (state) { _this.syncShapeStyle(shape, offscreenShape, state, null); }); } else { // 如果没有状态,则需要恢复至原始状态 this.syncShapeStyle(shape, offscreenShape, 'reset', null); } offscreenShape.remove(true); // 销毁,减少内存占用 var eventObject = { state: stateName, stateStatus: stateStatus, element: this, target: this.container, }; this.container.emit('statechange', eventObject); //@ts-ignore propagationDelegate(this.shape, 'statechange', eventObject); }; /** * 清空状量态,恢复至初始状态。 */ Element.prototype.clearStates = function () { var _this = this; var states = this.states; each(states, function (state) { _this.setState(state, false); }); this.states = []; }; /** * 查询当前 Element 上是否已设置 `stateName` 对应的状态。 * @param stateName 状态名称。 * @returns true 表示存在,false 表示不存在。 */ Element.prototype.hasState = function (stateName) { return this.states.includes(stateName); }; /** * 获取当前 Element 上所有的状态。 * @returns 当前 Element 上所有的状态数组。 */ Element.prototype.getStates = function () { return this.states; }; /** * 获取 Element 对应的原始数据。 * @returns 原始数据。 */ Element.prototype.getData = function () { return this.data; }; /** * 获取 Element 对应的图形绘制数据。 * @returns 图形绘制数据。 */ Element.prototype.getModel = function () { return this.model; }; /** * 返回 Element 元素整体的 bbox,包含文本及文本连线(有的话)。 * @returns 整体包围盒。 */ Element.prototype.getBBox = function () { var _a = this, shape = _a.shape, labelShape = _a.labelShape; var bbox = { x: 0, y: 0, minX: 0, minY: 0, maxX: 0, maxY: 0, width: 0, height: 0, }; if (shape) { bbox = shape.getCanvasBBox(); } if (labelShape) { labelShape.forEach(function (label) { var labelBBox = label.getCanvasBBox(); bbox.x = Math.min(labelBBox.x, bbox.x); bbox.y = Math.min(labelBBox.y, bbox.y); bbox.minX = Math.min(labelBBox.minX, bbox.minX); bbox.minY = Math.min(labelBBox.minY, bbox.minY); bbox.maxX = Math.max(labelBBox.maxX, bbox.maxX); bbox.maxY = Math.max(labelBBox.maxY, bbox.maxY); }); } bbox.width = bbox.maxX - bbox.minX; bbox.height = bbox.maxY - bbox.minY; return bbox; }; Element.prototype.getStatesStyle = function () { if (!this.statesStyle) { var _a = this, shapeType = _a.shapeType, geometry = _a.geometry, shapeFactory = _a.shapeFactory; var stateOption = geometry.stateOption; var defaultShapeType = shapeFactory.defaultShapeType; var stateTheme = shapeFactory.theme[shapeType] || shapeFactory.theme[defaultShapeType]; this.statesStyle = deepMix({}, stateTheme, stateOption); } return this.statesStyle; }; // 从主题中获取对应状态量的样式 Element.prototype.getStateStyle = function (stateName, shapeKey) { var statesStyle = this.getStatesStyle(); var stateCfg = get(statesStyle, [stateName, 'style'], {}); var shapeStyle = stateCfg[shapeKey] || stateCfg; if (isFunction(shapeStyle)) { return shapeStyle(this); } return shapeStyle; }; // 获取动画配置 Element.prototype.getAnimateCfg = function (animateType) { var animate = this.animate; if (animate) { return animate[animateType]; } return null; }; // 绘制图形 Element.prototype.drawShape = function (model, isUpdate) { if (isUpdate === void 0) { isUpdate = false; } var _a = this, shapeFactory = _a.shapeFactory, container = _a.container, shapeType = _a.shapeType; // 自定义 shape 有可能返回空 shape this.shape = shapeFactory.drawShape(shapeType, model, container); if (this.shape) { this.setShapeInfo(this.shape, model); // 存储绘图数据 // @ts-ignore var name_1 = this.shape.cfg.name; // 附加 element 的 name, name 现在支持数组了,很好用了 if (!name_1) { // 这个地方如果用户添加了 name, 则附加 name ,否则就添加自己的 name // @ts-ignore this.shape.cfg.name = ['element', this.shapeFactory.geometryType]; } else if (isString(name_1)) { // @ts-ignore this.shape.cfg.name = ['element', name_1]; } // 执行入场动画 var animateType = isUpdate ? 'enter' : 'appear'; var animateCfg = this.getAnimateCfg(animateType); if (animateCfg) { doAnimate(this.shape, animateCfg, { coordinate: shapeFactory.coordinate, toAttrs: __assign({}, this.shape.attr()), }); } } }; // 获取虚拟 Group Element.prototype.getOffscreenGroup = function () { if (!this.offscreenGroup) { var GroupCtor = this.container.getGroupBase(); // 获取分组的构造函数 this.offscreenGroup = new GroupCtor({}); } return this.offscreenGroup; }; // 设置 shape 上需要携带的信息 Element.prototype.setShapeInfo = function (shape, data) { var _this = this; // @ts-ignore shape.cfg.origin = data; // @ts-ignore shape.cfg.element = this; if (shape.isGroup()) { var children = shape.get('children'); children.forEach(function (child) { _this.setShapeInfo(child, data); }); } }; // 更新当前 shape 的样式 Element.prototype.syncShapeStyle = function (sourceShape, targetShape, state, animateCfg, index) { if (state === void 0) { state = ''; } if (index === void 0) { index = 0; } if (sourceShape.isGroup()) { var children = sourceShape.get('children'); var newChildren = targetShape.get('children'); for (var i = 0; i < children.length; i++) { this.syncShapeStyle(children[i], newChildren[i], state, animateCfg, index + i); } } else { if (state && state !== 'reset') { var name_2 = sourceShape.get('name'); if (isArray(name_2)) { // 会附加 element 的 name name_2 = name_2[1]; } var style = this.getStateStyle(state, name_2 || index); // 如果用户没有设置 name,则默认根据索引值 targetShape.attr(style); } var newAttrs = getReplaceAttrs(sourceShape, targetShape); if (this.animate) { if (animateCfg) { // 需要进行动画 doAnimate(sourceShape, animateCfg, { coordinate: this.shapeFactory.coordinate, toAttrs: newAttrs, shapeModel: this.model, }); } else if (state) { sourceShape.stopAnimate(); sourceShape.animate(newAttrs, { duration: 300, }); } else { sourceShape.attr(newAttrs); } } else { sourceShape.attr(newAttrs); } } }; Element.prototype.getShapeType = function (model) { var shape = get(model, 'shape'); return isArray(shape) ? shape[0] : shape; }; return Element; }(Base)); export default Element; //# sourceMappingURL=index.js.map