UNPKG

16.9 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var tslib_1 = require("tslib");
4var util_1 = require("@antv/util");
5// 暂未发包
6// @ts-ignore
7var component_1 = require("@antv/component");
8var animate_1 = require("../../animate");
9var base_1 = tslib_1.__importDefault(require("../../base"));
10var graphics_1 = require("../../util/graphics");
11var constant_1 = require("../../constant");
12var constant_2 = require("../shape/constant");
13/**
14 * Element 图形元素。
15 * 定义:在 G2 中,我们会将数据通过图形语法映射成不同的图形,比如点图,数据集中的每条数据会对应一个点,柱状图每条数据对应一个柱子,线图则是一组数据对应一条折线,Element 即一条/一组数据对应的图形元素,它代表一条数据或者一个数据集,在图形层面,它可以是单个 Shape 也可以是多个 Shape,我们称之为图形元素。
16 */
17var Element = /** @class */ (function (_super) {
18 tslib_1.__extends(Element, _super);
19 function Element(cfg) {
20 var _this = _super.call(this, cfg) || this;
21 // 存储当前开启的状态
22 _this.states = [];
23 var shapeFactory = cfg.shapeFactory, container = cfg.container, offscreenGroup = cfg.offscreenGroup, _a = cfg.visible, visible = _a === void 0 ? true : _a;
24 _this.shapeFactory = shapeFactory;
25 _this.container = container;
26 _this.offscreenGroup = offscreenGroup;
27 _this.visible = visible;
28 return _this;
29 }
30 /**
31 * 绘制图形。
32 * @param model 绘制数据。
33 * @param isUpdate 可选,是否是更新发生后的绘制。
34 */
35 Element.prototype.draw = function (model, isUpdate) {
36 if (isUpdate === void 0) { isUpdate = false; }
37 this.model = model;
38 this.data = model.data; // 存储原始数据
39 this.shapeType = this.getShapeType(model);
40 // 绘制图形
41 this.drawShape(model, isUpdate);
42 if (this.visible === false) {
43 // 用户在初始化的时候声明 visible: false
44 this.changeVisible(false);
45 }
46 };
47 /**
48 * 更新图形。
49 * @param model 更新的绘制数据。
50 */
51 Element.prototype.update = function (model) {
52 var _a = this, shapeFactory = _a.shapeFactory, shape = _a.shape;
53 if (!shape) {
54 return;
55 }
56 // 更新数据
57 this.model = model;
58 this.data = model.data;
59 this.shapeType = this.getShapeType(model);
60 // step 1: 更新 shape 携带的信息
61 this.setShapeInfo(shape, model);
62 // step 2: 使用虚拟 Group 重新绘制 shape,然后更新当前 shape
63 var offscreenGroup = this.getOffscreenGroup();
64 var newShape = shapeFactory.drawShape(this.shapeType, model, offscreenGroup);
65 // @ts-ignore
66 newShape.cfg.data = this.data;
67 // @ts-ignore
68 newShape.cfg.origin = model;
69 // label 需要使用
70 newShape.cfg.element = this;
71 // step 3: 同步 shape 样式
72 this.syncShapeStyle(shape, newShape, this.getStates(), this.getAnimateCfg('update'));
73 };
74 /**
75 * 销毁 element 实例。
76 */
77 Element.prototype.destroy = function () {
78 var _a = this, shapeFactory = _a.shapeFactory, shape = _a.shape;
79 if (shape) {
80 var animateCfg = this.getAnimateCfg('leave');
81 if (animateCfg) {
82 // 指定了动画配置则执行销毁动画
83 animate_1.doAnimate(shape, animateCfg, {
84 coordinate: shapeFactory.coordinate,
85 toAttrs: tslib_1.__assign({}, shape.attr()),
86 });
87 }
88 else {
89 // 否则直接销毁
90 shape.remove(true);
91 }
92 }
93 // reset
94 this.states = [];
95 this.shapeFactory = undefined;
96 this.container = undefined;
97 this.shape = undefined;
98 this.animate = undefined;
99 this.geometry = undefined;
100 this.labelShape = undefined;
101 this.model = undefined;
102 this.data = undefined;
103 this.offscreenGroup = undefined;
104 this.statesStyle = undefined;
105 _super.prototype.destroy.call(this);
106 };
107 /**
108 * 显示或者隐藏 element。
109 * @param visible 是否可见。
110 */
111 Element.prototype.changeVisible = function (visible) {
112 _super.prototype.changeVisible.call(this, visible);
113 if (visible) {
114 if (this.shape) {
115 this.shape.show();
116 }
117 if (this.labelShape) {
118 this.labelShape.forEach(function (label) {
119 label.show();
120 });
121 }
122 }
123 else {
124 if (this.shape) {
125 this.shape.hide();
126 }
127 if (this.labelShape) {
128 this.labelShape.forEach(function (label) {
129 label.hide();
130 });
131 }
132 }
133 };
134 /**
135 * 设置 Element 的状态。
136 *
137 * 目前 Element 开放三种状态:
138 * 1. active
139 * 2. selected
140 * 3. inactive
141 *
142 * 这三种状态相互独立,可以进行叠加。
143 *
144 * 这三种状态的样式可在 [[Theme]] 主题中或者通过 `geometry.state()` 接口进行配置。
145 *
146 * ```ts
147 * // 激活 active 状态
148 * setState('active', true);
149 * ```
150 *
151 * @param stateName 状态名
152 * @param stateStatus 是否开启状态
153 */
154 Element.prototype.setState = function (stateName, stateStatus) {
155 var _a = this, states = _a.states, shapeFactory = _a.shapeFactory, model = _a.model, shape = _a.shape, shapeType = _a.shapeType;
156 var index = states.indexOf(stateName);
157 if (stateStatus) {
158 // 开启状态
159 if (index > -1) {
160 // 该状态已经开启,则返回
161 return;
162 }
163 states.push(stateName);
164 if (stateName === 'active' || stateName === 'selected') {
165 shape.toFront();
166 }
167 }
168 else {
169 if (index === -1) {
170 // 关闭状态,但是状态未设置过
171 return;
172 }
173 states.splice(index, 1);
174 if (stateName === 'active' || stateName === 'selected') {
175 shape.toBack();
176 }
177 }
178 // 使用虚拟 group 重新绘制 shape,然后对这个 shape 应用状态样式后,更新当前 shape。
179 var offscreenShape = shapeFactory.drawShape(shapeType, model, this.getOffscreenGroup());
180 if (states.length) {
181 // 应用当前状态
182 this.syncShapeStyle(shape, offscreenShape, states, null);
183 }
184 else {
185 // 如果没有状态,则需要恢复至原始状态
186 this.syncShapeStyle(shape, offscreenShape, ['reset'], null);
187 }
188 offscreenShape.remove(true); // 销毁,减少内存占用
189 var eventObject = {
190 state: stateName,
191 stateStatus: stateStatus,
192 element: this,
193 target: this.container,
194 };
195 this.container.emit('statechange', eventObject);
196 // @ts-ignore
197 component_1.propagationDelegate(this.shape, 'statechange', eventObject);
198 };
199 /**
200 * 清空状量态,恢复至初始状态。
201 */
202 Element.prototype.clearStates = function () {
203 var _this = this;
204 var states = this.states;
205 util_1.each(states, function (state) {
206 _this.setState(state, false);
207 });
208 this.states = [];
209 };
210 /**
211 * 查询当前 Element 上是否已设置 `stateName` 对应的状态。
212 * @param stateName 状态名称。
213 * @returns true 表示存在,false 表示不存在。
214 */
215 Element.prototype.hasState = function (stateName) {
216 return this.states.includes(stateName);
217 };
218 /**
219 * 获取当前 Element 上所有的状态。
220 * @returns 当前 Element 上所有的状态数组。
221 */
222 Element.prototype.getStates = function () {
223 return this.states;
224 };
225 /**
226 * 获取 Element 对应的原始数据。
227 * @returns 原始数据。
228 */
229 Element.prototype.getData = function () {
230 return this.data;
231 };
232 /**
233 * 获取 Element 对应的图形绘制数据。
234 * @returns 图形绘制数据。
235 */
236 Element.prototype.getModel = function () {
237 return this.model;
238 };
239 /**
240 * 返回 Element 元素整体的 bbox,包含文本及文本连线(有的话)。
241 * @returns 整体包围盒。
242 */
243 Element.prototype.getBBox = function () {
244 var _a = this, shape = _a.shape, labelShape = _a.labelShape;
245 var bbox = {
246 x: 0,
247 y: 0,
248 minX: 0,
249 minY: 0,
250 maxX: 0,
251 maxY: 0,
252 width: 0,
253 height: 0,
254 };
255 if (shape) {
256 bbox = shape.getCanvasBBox();
257 }
258 if (labelShape) {
259 labelShape.forEach(function (label) {
260 var labelBBox = label.getCanvasBBox();
261 bbox.x = Math.min(labelBBox.x, bbox.x);
262 bbox.y = Math.min(labelBBox.y, bbox.y);
263 bbox.minX = Math.min(labelBBox.minX, bbox.minX);
264 bbox.minY = Math.min(labelBBox.minY, bbox.minY);
265 bbox.maxX = Math.max(labelBBox.maxX, bbox.maxX);
266 bbox.maxY = Math.max(labelBBox.maxY, bbox.maxY);
267 });
268 }
269 bbox.width = bbox.maxX - bbox.minX;
270 bbox.height = bbox.maxY - bbox.minY;
271 return bbox;
272 };
273 Element.prototype.getStatesStyle = function () {
274 if (!this.statesStyle) {
275 var _a = this, shapeType = _a.shapeType, geometry = _a.geometry, shapeFactory = _a.shapeFactory;
276 var stateOption = geometry.stateOption;
277 var defaultShapeType = shapeFactory.defaultShapeType;
278 var stateTheme = shapeFactory.theme[shapeType] || shapeFactory.theme[defaultShapeType];
279 this.statesStyle = util_1.deepMix({}, stateTheme, stateOption);
280 }
281 return this.statesStyle;
282 };
283 // 从主题中获取对应状态量的样式
284 Element.prototype.getStateStyle = function (stateName, shapeKey) {
285 var statesStyle = this.getStatesStyle();
286 var stateCfg = util_1.get(statesStyle, [stateName, 'style'], {});
287 var shapeStyle = stateCfg[shapeKey] || stateCfg;
288 if (util_1.isFunction(shapeStyle)) {
289 return shapeStyle(this);
290 }
291 return shapeStyle;
292 };
293 // 获取动画配置
294 Element.prototype.getAnimateCfg = function (animateType) {
295 var _this = this;
296 var animate = this.animate;
297 if (animate) {
298 var cfg_1 = animate[animateType];
299 if (cfg_1) {
300 // 增加动画的回调函数,如果外部传入了,则先执行外部,然后发射 geometry 的 animate 事件
301 return tslib_1.__assign(tslib_1.__assign({}, cfg_1), { callback: function () {
302 var _a;
303 util_1.isFunction(cfg_1.callback) && cfg_1.callback();
304 (_a = _this.geometry) === null || _a === void 0 ? void 0 : _a.emit(constant_1.GEOMETRY_LIFE_CIRCLE.AFTER_DRAW_ANIMATE);
305 } });
306 }
307 return cfg_1;
308 }
309 return null;
310 };
311 // 绘制图形
312 Element.prototype.drawShape = function (model, isUpdate) {
313 var _a;
314 if (isUpdate === void 0) { isUpdate = false; }
315 var _b = this, shapeFactory = _b.shapeFactory, container = _b.container, shapeType = _b.shapeType;
316 // 自定义 shape 有可能返回空 shape
317 this.shape = shapeFactory.drawShape(shapeType, model, container);
318 if (this.shape) {
319 this.setShapeInfo(this.shape, model); // 存储绘图数据
320 // @ts-ignore
321 var name_1 = this.shape.cfg.name;
322 // 附加 element 的 name, name 现在支持数组了,很好用了
323 if (!name_1) {
324 // 这个地方如果用户添加了 name, 则附加 name ,否则就添加自己的 name
325 // @ts-ignore
326 this.shape.cfg.name = ['element', this.shapeFactory.geometryType];
327 }
328 else if (util_1.isString(name_1)) {
329 // @ts-ignore
330 this.shape.cfg.name = ['element', name_1];
331 }
332 // 执行入场动画
333 var animateType = isUpdate ? 'enter' : 'appear';
334 var animateCfg = this.getAnimateCfg(animateType);
335 if (animateCfg) {
336 // 开始执行动画的生命周期
337 (_a = this.geometry) === null || _a === void 0 ? void 0 : _a.emit(constant_1.GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE);
338 animate_1.doAnimate(this.shape, animateCfg, {
339 coordinate: shapeFactory.coordinate,
340 toAttrs: tslib_1.__assign({}, this.shape.attr()),
341 });
342 }
343 }
344 };
345 // 获取虚拟 Group
346 Element.prototype.getOffscreenGroup = function () {
347 if (!this.offscreenGroup) {
348 var GroupCtor = this.container.getGroupBase(); // 获取分组的构造函数
349 this.offscreenGroup = new GroupCtor({});
350 }
351 return this.offscreenGroup;
352 };
353 // 设置 shape 上需要携带的信息
354 Element.prototype.setShapeInfo = function (shape, data) {
355 var _this = this;
356 // @ts-ignore
357 shape.cfg.origin = data;
358 // @ts-ignore
359 shape.cfg.element = this;
360 if (shape.isGroup()) {
361 var children = shape.get('children');
362 children.forEach(function (child) {
363 _this.setShapeInfo(child, data);
364 });
365 }
366 };
367 // 更新当前 shape 的样式
368 Element.prototype.syncShapeStyle = function (sourceShape, targetShape, states, animateCfg, index) {
369 var _this = this;
370 var _a;
371 if (states === void 0) { states = []; }
372 if (index === void 0) { index = 0; }
373 if (!sourceShape || !targetShape) {
374 return;
375 }
376 // 所有的 shape 都需要同步 clip
377 var clip = sourceShape.get('clipShape');
378 var newClip = targetShape.get('clipShape');
379 this.syncShapeStyle(clip, newClip, states, animateCfg);
380 if (sourceShape.isGroup()) {
381 var children = sourceShape.get('children');
382 var newChildren = targetShape.get('children');
383 for (var i = 0; i < children.length; i++) {
384 this.syncShapeStyle(children[i], newChildren[i], states, animateCfg, index + i);
385 }
386 }
387 else {
388 if (!util_1.isEmpty(states) && !util_1.isEqual(states, ['reset'])) {
389 var name_2 = sourceShape.get('name');
390 if (util_1.isArray(name_2)) {
391 // 会附加 element 的 name
392 name_2 = name_2[1];
393 }
394 util_1.each(states, function (state) {
395 // background shape 不进行状态样式设置
396 if (targetShape.get('name') !== constant_2.BACKGROUND_SHAPE) {
397 var style = _this.getStateStyle(state, name_2 || index); // 如果用户没有设置 name,则默认根据索引值
398 targetShape.attr(style);
399 }
400 });
401 }
402 var newAttrs = graphics_1.getReplaceAttrs(sourceShape, targetShape);
403 if (this.animate) {
404 if (animateCfg) {
405 (_a = this.geometry) === null || _a === void 0 ? void 0 : _a.emit(constant_1.GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE);
406 // 需要进行动画
407 animate_1.doAnimate(sourceShape, animateCfg, {
408 coordinate: this.shapeFactory.coordinate,
409 toAttrs: newAttrs,
410 shapeModel: this.model,
411 });
412 }
413 else if (util_1.isEmpty(states)) {
414 sourceShape.stopAnimate();
415 sourceShape.animate(newAttrs, {
416 duration: 300,
417 });
418 }
419 else {
420 sourceShape.attr(newAttrs);
421 }
422 }
423 else {
424 sourceShape.attr(newAttrs);
425 }
426 }
427 };
428 Element.prototype.getShapeType = function (model) {
429 var shape = util_1.get(model, 'shape');
430 return util_1.isArray(shape) ? shape[0] : shape;
431 };
432 return Element;
433}(base_1.default));
434exports.default = Element;
435//# sourceMappingURL=index.js.map
\No newline at end of file