UNPKG

59 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.registerGeometry = exports.View = void 0;
4var tslib_1 = require("tslib");
5var util_1 = require("@antv/util");
6var constant_1 = require("../constant");
7var base_1 = tslib_1.__importDefault(require("../base"));
8var facet_1 = require("../facet");
9var interaction_1 = require("../interaction");
10var theme_1 = require("../theme");
11var bbox_1 = require("../util/bbox");
12var coordinate_1 = require("../util/coordinate");
13var helper_1 = require("../util/helper");
14var tooltip_1 = require("../util/tooltip");
15var padding_1 = require("../util/padding");
16var scale_1 = require("../util/scale");
17var util_2 = require("../theme/util");
18var controller_1 = require("./controller");
19var coordinate_2 = tslib_1.__importDefault(require("./controller/coordinate"));
20var event_1 = tslib_1.__importDefault(require("./event"));
21var layout_1 = tslib_1.__importDefault(require("./layout"));
22var scale_pool_1 = require("./util/scale-pool");
23var padding_cal_1 = require("./layout/padding-cal");
24var auto_1 = require("./layout/auto");
25var sync_view_padding_1 = require("./util/sync-view-padding");
26/**
27 * G2 视图 View 类
28 */
29var View = /** @class */ (function (_super) {
30 tslib_1.__extends(View, _super);
31 function View(props) {
32 var _this = _super.call(this, { visible: props.visible }) || this;
33 /** 所有的子 view。 */
34 _this.views = [];
35 /** 所有的 geometry 实例。 */
36 _this.geometries = [];
37 /** 所有的组件 controllers。 */
38 _this.controllers = [];
39 /** 所有的 Interaction 实例。 */
40 _this.interactions = {};
41 /** 是否对超出坐标系范围的 Geometry 进行剪切 */
42 _this.limitInPlot = false;
43 // 配置信息存储
44 _this.options = {
45 data: [],
46 animate: true,
47 }; // 初始化为空
48 /** 配置开启的组件插件,默认为全局配置的组件。 */
49 _this.usedControllers = controller_1.getComponentControllerNames();
50 /** 所有的 scales */
51 _this.scalePool = new scale_pool_1.ScalePool();
52 /** 布局函数 */
53 _this.layoutFunc = layout_1.default;
54 /** 当前鼠标是否在 plot 内(CoordinateBBox) */
55 _this.isPreMouseInPlot = false;
56 /** 默认标识位,用于判定数据是否更新 */
57 _this.isDataChanged = false;
58 /** 用于判断坐标系范围是否发生变化的标志位 */
59 _this.isCoordinateChanged = false;
60 /** 从当前这个 view 创建的 scale key */
61 _this.createdScaleKeys = new Map();
62 _this.onCanvasEvent = function (evt) {
63 var name = evt.name;
64 if (!name.includes(':')) {
65 // 非委托事件
66 var e = _this.createViewEvent(evt);
67 // 处理 plot 事件
68 _this.doPlotEvent(e);
69 _this.emit(name, e);
70 }
71 };
72 /**
73 * 触发事件之后
74 * @param evt
75 */
76 _this.onDelegateEvents = function (evt) {
77 // 阻止继续冒泡,防止重复事件触发
78 // evt.preventDefault();
79 var name = evt.name;
80 if (!name.includes(':')) {
81 return;
82 }
83 // 事件在 view 嵌套中冒泡(暂不提供阻止冒泡的机制)
84 var e = _this.createViewEvent(evt);
85 // 包含有基本事件、组合事件
86 _this.emit(name, e);
87 // const currentTarget = evt.currentTarget as IShape;
88 // const inheritNames = currentTarget.get('inheritNames');
89 // if (evt.delegateObject || inheritNames) {
90 // const events = this.getEvents();
91 // each(inheritNames, (subName) => {
92 // const eventName = `${subName}:${type}`;
93 // if (events[eventName]) {
94 // this.emit(eventName, e);
95 // }
96 // });
97 // }
98 };
99 var _a = props.id, id = _a === void 0 ? util_1.uniqueId('view') : _a, parent = props.parent, canvas = props.canvas, backgroundGroup = props.backgroundGroup, middleGroup = props.middleGroup, foregroundGroup = props.foregroundGroup, _b = props.region, region = _b === void 0 ? { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } } : _b, padding = props.padding, appendPadding = props.appendPadding, theme = props.theme, options = props.options, limitInPlot = props.limitInPlot, syncViewPadding = props.syncViewPadding;
100 _this.parent = parent;
101 _this.canvas = canvas;
102 _this.backgroundGroup = backgroundGroup;
103 _this.middleGroup = middleGroup;
104 _this.foregroundGroup = foregroundGroup;
105 _this.region = region;
106 _this.padding = padding;
107 _this.appendPadding = appendPadding;
108 // 接受父 view 传入的参数
109 _this.options = tslib_1.__assign(tslib_1.__assign({}, _this.options), options);
110 _this.limitInPlot = limitInPlot;
111 _this.id = id;
112 _this.syncViewPadding = syncViewPadding;
113 // 初始化 theme
114 _this.themeObject = util_1.isObject(theme) ? util_1.deepMix({}, theme_1.getTheme('default'), util_2.createTheme(theme)) : theme_1.getTheme(theme);
115 _this.init();
116 return _this;
117 }
118 /**
119 * 设置 layout 布局函数
120 * @param layout 布局函数
121 * @returns void
122 */
123 View.prototype.setLayout = function (layout) {
124 this.layoutFunc = layout;
125 };
126 /**
127 * 生命周期:初始化
128 * @returns voids
129 */
130 View.prototype.init = function () {
131 // 计算画布的 viewBBox
132 this.calculateViewBBox();
133 // 事件委托机制
134 this.initEvents();
135 // 初始化组件 controller
136 this.initComponentController();
137 this.initOptions();
138 };
139 /**
140 * 生命周期:渲染流程,渲染过程需要处理数据更新的情况。
141 * render 函数仅仅会处理 view 和子 view。
142 * @param isUpdate 是否触发更新流程。
143 * @param params render 事件参数
144 */
145 View.prototype.render = function (isUpdate, payload) {
146 if (isUpdate === void 0) { isUpdate = false; }
147 this.emit(constant_1.VIEW_LIFE_CIRCLE.BEFORE_RENDER, event_1.default.fromData(this, constant_1.VIEW_LIFE_CIRCLE.BEFORE_RENDER, payload));
148 // 递归渲染
149 this.paint(isUpdate);
150 this.emit(constant_1.VIEW_LIFE_CIRCLE.AFTER_RENDER, event_1.default.fromData(this, constant_1.VIEW_LIFE_CIRCLE.AFTER_RENDER, payload));
151 if (this.visible === false) {
152 // 用户在初始化的时候声明 visible: false
153 this.changeVisible(false);
154 }
155 };
156 /**
157 * 生命周期:清空图表上所有的绘制内容,但是不销毁图表,chart 仍可使用。
158 * @returns void
159 */
160 View.prototype.clear = function () {
161 var _this = this;
162 this.emit(constant_1.VIEW_LIFE_CIRCLE.BEFORE_CLEAR);
163 // 1. 清空缓存和计算数据
164 this.filteredData = [];
165 this.coordinateInstance = undefined;
166 this.isDataChanged = false; // 复位
167 this.isCoordinateChanged = false; // 复位
168 // 2. 清空 geometries
169 var geometries = this.geometries;
170 for (var i = 0; i < geometries.length; i++) {
171 geometries[i].clear();
172 // view 中使用 geometry 的时候,还需要清空它的容器,不然下一次 chart.geometry() 的时候,又创建了一个,导致泄露, #2799。
173 geometries[i].container.remove(true);
174 }
175 this.geometries = [];
176 // 3. 清空 controllers
177 var controllers = this.controllers;
178 for (var i = 0; i < controllers.length; i++) {
179 if (controllers[i].name === 'annotation') {
180 // 需要清空配置项
181 controllers[i].clear(true);
182 }
183 else {
184 controllers[i].clear();
185 }
186 }
187 // 4. 删除 scale 缓存
188 this.createdScaleKeys.forEach(function (v, k) {
189 _this.getRootView().scalePool.deleteScale(k);
190 });
191 this.createdScaleKeys.clear();
192 // 递归处理子 view
193 var views = this.views;
194 for (var i = 0; i < views.length; i++) {
195 views[i].clear();
196 }
197 this.emit(constant_1.VIEW_LIFE_CIRCLE.AFTER_CLEAR);
198 };
199 /**
200 * 生命周期:销毁,完全无法使用。
201 * @returns void
202 */
203 View.prototype.destroy = function () {
204 // 销毁前事件,销毁之后已经没有意义了,所以不抛出事件
205 this.emit(constant_1.VIEW_LIFE_CIRCLE.BEFORE_DESTROY);
206 var interactions = this.interactions;
207 // 销毁 interactions
208 util_1.each(interactions, function (interaction) {
209 if (interaction) {
210 // 有可能已经销毁,设置了 undefined
211 interaction.destroy();
212 }
213 });
214 this.clear();
215 // 销毁 controller 中的组件
216 var controllers = this.controllers;
217 for (var i = 0, len = controllers.length; i < len; i++) {
218 var controller = controllers[i];
219 controller.destroy();
220 }
221 this.backgroundGroup.remove(true);
222 this.middleGroup.remove(true);
223 this.foregroundGroup.remove(true);
224 _super.prototype.destroy.call(this);
225 };
226 /* end 生命周期函数 */
227 /**
228 * 显示或者隐藏整个 view。
229 * @param visible 是否可见
230 * @returns View
231 */
232 View.prototype.changeVisible = function (visible) {
233 _super.prototype.changeVisible.call(this, visible);
234 var geometries = this.geometries;
235 for (var i = 0, len = geometries.length; i < len; i++) {
236 var geometry = geometries[i];
237 geometry.changeVisible(visible);
238 }
239 var controllers = this.controllers;
240 for (var i = 0, len = controllers.length; i < len; i++) {
241 var controller = controllers[i];
242 controller.changeVisible(visible);
243 }
244 this.foregroundGroup.set('visible', visible);
245 this.middleGroup.set('visible', visible);
246 this.backgroundGroup.set('visible', visible);
247 // group.set('visible', visible) 不会触发自动刷新
248 this.getCanvas().draw();
249 return this;
250 };
251 /**
252 * 装载数据源。
253 *
254 * ```ts
255 * view.data([{ city: '杭州', sale: 100 }, { city: '上海', sale: 110 } ]);
256 * ```
257 *
258 * @param data 数据源,json 数组。
259 * @returns View
260 */
261 View.prototype.data = function (data) {
262 util_1.set(this.options, 'data', data);
263 this.isDataChanged = true;
264 return this;
265 };
266 /**
267 * @deprecated
268 * This method will be removed at G2 V4.1. Replaced by {@link #data(data)}
269 */
270 View.prototype.source = function (data) {
271 console.warn('This method will be removed at G2 V4.1. Please use chart.data() instead.');
272 return this.data(data);
273 };
274 /**
275 * 设置数据筛选规则。
276 *
277 * ```ts
278 * view.filter('city', (value: any, datum: Datum) => value !== '杭州');
279 *
280 * // 删除 'city' 字段对应的筛选规则。
281 * view.filter('city', null);
282 * ```
283 *
284 * @param field 数据字段
285 * @param condition 筛选规则
286 * @returns View
287 */
288 View.prototype.filter = function (field, condition) {
289 if (util_1.isFunction(condition)) {
290 util_1.set(this.options, ['filters', field], condition);
291 return this;
292 }
293 // condition 为空,则表示删除过滤条件
294 if (!condition && util_1.get(this.options, ['filters', field])) {
295 delete this.options.filters[field];
296 }
297 return this;
298 };
299 View.prototype.axis = function (field, axisOption) {
300 if (util_1.isBoolean(field)) {
301 util_1.set(this.options, ['axes'], field);
302 }
303 else {
304 util_1.set(this.options, ['axes', field], axisOption);
305 }
306 return this;
307 };
308 View.prototype.legend = function (field, legendOption) {
309 if (util_1.isBoolean(field)) {
310 util_1.set(this.options, ['legends'], field);
311 }
312 else if (util_1.isString(field)) {
313 util_1.set(this.options, ['legends', field], legendOption);
314 if (util_1.isPlainObject(legendOption) && (legendOption === null || legendOption === void 0 ? void 0 : legendOption.selected)) {
315 util_1.set(this.options, ['filters', field], function (name) {
316 var _a;
317 return (_a = legendOption === null || legendOption === void 0 ? void 0 : legendOption.selected[name]) !== null && _a !== void 0 ? _a : true;
318 });
319 }
320 }
321 else {
322 // 设置全局的 legend 配置
323 util_1.set(this.options, ['legends'], field);
324 }
325 return this;
326 };
327 View.prototype.scale = function (field, scaleOption) {
328 var _this = this;
329 if (util_1.isString(field)) {
330 util_1.set(this.options, ['scales', field], scaleOption);
331 }
332 else if (util_1.isObject(field)) {
333 util_1.each(field, function (v, k) {
334 util_1.set(_this.options, ['scales', k], v);
335 });
336 }
337 return this;
338 };
339 /**
340 * tooltip 提示信息配置。
341 *
342 * ```ts
343 * view.tooltip(false); // 关闭 tooltip
344 *
345 * view.tooltip({
346 * shared: true
347 * });
348 * ```
349 *
350 * @param cfg Tooltip 配置,更详细的配置项参考:https://github.com/antvis/component#tooltip
351 * @returns View
352 */
353 View.prototype.tooltip = function (cfg) {
354 util_1.set(this.options, 'tooltip', cfg);
355 return this;
356 };
357 /**
358 * 辅助标记配置。
359 *
360 * ```ts
361 * view.annotation().line({
362 * start: ['min', 85],
363 * end: ['max', 85],
364 * style: {
365 * stroke: '#595959',
366 * lineWidth: 1,
367 * lineDash: [3, 3],
368 * },
369 * });
370 * ```
371 * 更详细的配置项:https://github.com/antvis/component#annotation
372 * @returns [[Annotation]]
373 */
374 View.prototype.annotation = function () {
375 return this.getController('annotation');
376 };
377 /**
378 * @deprecated
379 * This method will be removed at G2 V4.1. Replaced by {@link #guide()}
380 */
381 View.prototype.guide = function () {
382 console.warn('This method will be removed at G2 V4.1. Please use chart.annotation() instead.');
383 return this.annotation();
384 };
385 View.prototype.coordinate = function (type, coordinateCfg) {
386 // 提供语法糖,使用更简单
387 if (util_1.isString(type)) {
388 util_1.set(this.options, 'coordinate', { type: type, cfg: coordinateCfg });
389 }
390 else {
391 util_1.set(this.options, 'coordinate', type);
392 }
393 // 更新 coordinate 配置
394 this.coordinateController.update(this.options.coordinate);
395 return this.coordinateController;
396 };
397 /**
398 * @deprecated
399 * This method will be removed at G2 V4.1. Replaced by {@link #coordinate()}
400 */
401 View.prototype.coord = function (type, coordinateCfg) {
402 console.warn('This method will be removed at G2 V4.1. Please use chart.coordinate() instead.');
403 // @ts-ignore
404 return this.coordinate(type, coordinateCfg);
405 };
406 /**
407 * view 分面绘制。
408 *
409 * ```ts
410 * view.facet('rect', {
411 * rowField: 'province',
412 * columnField: 'category',
413 * eachView: (innerView: View, facet?: FacetData) => {
414 * innerView.line().position('city*sale');
415 * },
416 * });
417 * ```
418 *
419 * @param type 分面类型
420 * @param cfg 分面配置, [[FacetCfgMap]]
421 * @returns View
422 */
423 View.prototype.facet = function (type, cfg) {
424 // 先销毁掉之前的分面
425 if (this.facetInstance) {
426 this.facetInstance.destroy();
427 }
428 // 创建新的分面
429 var Ctor = facet_1.getFacet(type);
430 if (!Ctor) {
431 throw new Error("facet '" + type + "' is not exist!");
432 }
433 this.facetInstance = new Ctor(this, tslib_1.__assign(tslib_1.__assign({}, cfg), { type: type }));
434 return this;
435 };
436 /*
437 * 开启或者关闭动画。
438 *
439 * ```ts
440 * view.animate(false);
441 * ```
442 *
443 * @param status 动画状态,true 表示开始,false 表示关闭
444 * @returns View
445 */
446 View.prototype.animate = function (status) {
447 util_1.set(this.options, 'animate', status);
448 return this;
449 };
450 /**
451 * 更新配置项,用于配置项式声明。
452 * @param options 配置项
453 */
454 View.prototype.updateOptions = function (options) {
455 this.clear(); // 清空
456 util_1.mix(this.options, options);
457 // 需要把已存在的 view 销毁,否则会重复创建
458 // 目前针对配置项还没有特别好的 view 更新机制,为了不影响主流流程,所以在这里直接销毁
459 this.views.forEach(function (view) { return view.destroy(); });
460 this.views = [];
461 this.initOptions();
462 // 初始化坐标系大小,保证 padding 计算正确
463 this.coordinateBBox = this.viewBBox;
464 return this;
465 };
466 /**
467 * 往 `view.options` 属性中存储配置项。
468 * @param name 属性名称
469 * @param opt 属性值
470 * @returns view
471 */
472 View.prototype.option = function (name, opt) {
473 // 对于内置的 option,避免覆盖。
474 // name 在原型上,说明可能是内置 API,存在 option 被覆盖的风险,不处理
475 if (View.prototype[name]) {
476 throw new Error("Can't use built in variable name \"" + name + "\", please change another one.");
477 }
478 // 存入到 option 中
479 util_1.set(this.options, name, opt);
480 return this;
481 };
482 /**
483 * 设置主题。
484 *
485 * ```ts
486 * view.theme('dark'); // 'dark' 需要事先通过 `registerTheme()` 接口注册完成
487 *
488 * view.theme({ defaultColor: 'red' });
489 * ```
490 *
491 * @param theme 主题名或者主题配置
492 * @returns View
493 */
494 View.prototype.theme = function (theme) {
495 this.themeObject = util_1.isObject(theme) ? util_1.deepMix({}, this.themeObject, util_2.createTheme(theme)) : theme_1.getTheme(theme);
496 return this;
497 };
498 /* end 一系列传入配置的 API */
499 /**
500 * Call the interaction based on the interaction name
501 *
502 * ```ts
503 * view.interaction('my-interaction', { extra: 'hello world' });
504 * ```
505 * 详细文档可以参考:https://g2.antv.vision/zh/docs/api/general/interaction
506 * @param name interaction name
507 * @param cfg interaction config
508 * @returns
509 */
510 View.prototype.interaction = function (name, cfg) {
511 var existInteraction = this.interactions[name];
512 // 存在则先销毁已有的
513 if (existInteraction) {
514 existInteraction.destroy();
515 }
516 // 新建交互实例
517 var interaction = interaction_1.createInteraction(name, this, cfg);
518 if (interaction) {
519 interaction.init();
520 this.interactions[name] = interaction;
521 }
522 return this;
523 };
524 /**
525 * 移除当前 View 的 interaction
526 * ```ts
527 * view.removeInteraction('my-interaction');
528 * ```
529 * @param name interaction name
530 */
531 View.prototype.removeInteraction = function (name) {
532 var existInteraction = this.interactions[name];
533 // 存在则先销毁已有的
534 if (existInteraction) {
535 existInteraction.destroy();
536 this.interactions[name] = undefined;
537 }
538 };
539 /**
540 * 修改数据,数据更新逻辑,数据更新仅仅影响当前这一层的 view
541 *
542 * ```ts
543 * view.changeData([{ city: '北京', sale: '200' }]);
544 * ```
545 *
546 * @param data
547 * @returns void
548 */
549 View.prototype.changeData = function (data) {
550 this.isDataChanged = true;
551 this.emit(constant_1.VIEW_LIFE_CIRCLE.BEFORE_CHANGE_DATA, event_1.default.fromData(this, constant_1.VIEW_LIFE_CIRCLE.BEFORE_CHANGE_DATA, null));
552 // 1. 保存数据
553 this.data(data);
554 // 2. 渲染
555 this.paint(true);
556 // 3. 遍历子 view 进行 change data
557 var views = this.views;
558 for (var i = 0, len = views.length; i < len; i++) {
559 var view = views[i];
560 // FIXME 子 view 有自己的数据的情况,该如何处理?
561 view.changeData(data);
562 }
563 this.emit(constant_1.VIEW_LIFE_CIRCLE.AFTER_CHANGE_DATA, event_1.default.fromData(this, constant_1.VIEW_LIFE_CIRCLE.AFTER_CHANGE_DATA, null));
564 };
565 /* View 管理相关的 API */
566 /**
567 * 创建子 view
568 *
569 * ```ts
570 * const innerView = view.createView({
571 * start: { x: 0, y: 0 },
572 * end: { x: 0.5, y: 0.5 },
573 * padding: 8,
574 * });
575 * ```
576 *
577 * @param cfg
578 * @returns View
579 */
580 View.prototype.createView = function (cfg) {
581 // 将会在 4.1 版本中移除递归嵌套 view,仅仅只允许 chart - view 两层。
582 // 这个 API 理论上用户量不多,所以暂时不发大版本,所以先暂时打一个 warning。
583 if (this.parent && this.parent.parent) {
584 // 存在 3 层 结构了
585 console.warn('The view nesting recursive feature will be removed at G2 V4.1. Please avoid to use it.');
586 }
587 // 子 view 共享 options 配置数据
588 var sharedOptions = {
589 data: this.options.data,
590 scales: util_1.clone(this.options.scales),
591 axes: util_1.clone(this.options.axes),
592 coordinate: util_1.clone(this.coordinateController.getOption()),
593 tooltip: util_1.clone(this.options.tooltip),
594 legends: util_1.clone(this.options.legends),
595 animate: this.options.animate,
596 visible: this.visible,
597 };
598 var v = new View(tslib_1.__assign(tslib_1.__assign({ parent: this, canvas: this.canvas,
599 // 子 view 共用三层 group
600 backgroundGroup: this.backgroundGroup.addGroup({ zIndex: constant_1.GROUP_Z_INDEX.BG }), middleGroup: this.middleGroup.addGroup({ zIndex: constant_1.GROUP_Z_INDEX.MID }), foregroundGroup: this.foregroundGroup.addGroup({ zIndex: constant_1.GROUP_Z_INDEX.FORE }), theme: this.themeObject, padding: this.padding }, cfg), { options: tslib_1.__assign(tslib_1.__assign({}, sharedOptions), util_1.get(cfg, 'options', {})) }));
601 this.views.push(v);
602 return v;
603 };
604 /**
605 * @deprecated
606 * This method will be removed at G2 V4.1. Replaced by {@link #createView()}
607 */
608 View.prototype.view = function (cfg) {
609 console.warn('This method will be removed at G2 V4.1. Please use chart.createView() instead.');
610 return this.createView(cfg);
611 };
612 /**
613 * 删除一个子 view
614 * @param view
615 * @return removedView
616 */
617 View.prototype.removeView = function (view) {
618 var removedView = util_1.remove(this.views, function (v) { return v === view; })[0];
619 if (removedView) {
620 removedView.destroy();
621 }
622 return removedView;
623 };
624 /* end View 管理相关的 API */
625 // 一些 get 方法
626 /**
627 * 获取当前坐标系实例。
628 * @returns [[Coordinate]]
629 */
630 View.prototype.getCoordinate = function () {
631 return this.coordinateInstance;
632 };
633 /**
634 * 获取当前 view 的主题配置。
635 * @returns themeObject
636 */
637 View.prototype.getTheme = function () {
638 return this.themeObject;
639 };
640 /**
641 * 获得 x 轴字段的 scale 实例。
642 * @returns view 中 Geometry 对于的 x scale
643 */
644 View.prototype.getXScale = function () {
645 // 拿第一个 Geometry 的 X scale
646 // 隐藏逻辑:一个 view 中的 Geometry 必须 x 字段一致
647 var g = this.geometries[0];
648 return g ? g.getXScale() : null;
649 };
650 /**
651 * 获取 y 轴字段的 scales 实例。
652 * @returns view 中 Geometry 对于的 y scale 数组
653 */
654 View.prototype.getYScales = function () {
655 // 拿到所有的 Geometry 的 Y scale,然后去重
656 var tmpMap = {};
657 return this.geometries.map(function (g) {
658 var yScale = g.getYScale();
659 var field = yScale.field;
660 if (!tmpMap[field]) {
661 tmpMap[field] = true;
662 return yScale;
663 }
664 });
665 };
666 /**
667 * 获取 x 轴或者 y 轴对应的所有 scale 实例。
668 * @param dimType x | y
669 * @returns x 轴或者 y 轴对应的所有 scale 实例。
670 */
671 View.prototype.getScalesByDim = function (dimType) {
672 var geometries = this.geometries;
673 var scales = {};
674 for (var i = 0, len = geometries.length; i < len; i++) {
675 var geometry = geometries[i];
676 var scale = dimType === 'x' ? geometry.getXScale() : geometry.getYScale();
677 if (scale && !scales[scale.field]) {
678 scales[scale.field] = scale;
679 }
680 }
681 return scales;
682 };
683 /**
684 * 根据字段名去获取 scale 实例。
685 * @param field 数据字段名称
686 * @param key id
687 */
688 View.prototype.getScaleByField = function (field, key) {
689 var defaultKey = key ? key : this.getScaleKey(field);
690 // 调用根节点 view 的方法获取
691 return this.getRootView().scalePool.getScale(defaultKey);
692 };
693 /**
694 * 返回所有配置信息。
695 * @returns 所有的 view API 配置。
696 */
697 View.prototype.getOptions = function () {
698 return this.options;
699 };
700 /**
701 * 获取 view 的数据(过滤后的数据)。
702 * @returns 处理过滤器之后的数据。
703 */
704 View.prototype.getData = function () {
705 return this.filteredData;
706 };
707 /**
708 * 获得绘制的层级 group。
709 * @param layer 层级名称。
710 * @returns 对应层级的 Group。
711 */
712 View.prototype.getLayer = function (layer) {
713 return layer === constant_1.LAYER.BG
714 ? this.backgroundGroup
715 : layer === constant_1.LAYER.MID
716 ? this.middleGroup
717 : layer === constant_1.LAYER.FORE
718 ? this.foregroundGroup
719 : this.foregroundGroup;
720 };
721 /**
722 * 对外暴露方法,判断一个点是否在绘图区域(即坐标系范围)内部。
723 * @param point 坐标点
724 */
725 View.prototype.isPointInPlot = function (point) {
726 return coordinate_1.isPointInCoordinate(this.getCoordinate(), point);
727 };
728 /**
729 * 获得所有的 legend 对应的 attribute 实例。
730 * @returns 维度字段的 Attribute 数组
731 */
732 View.prototype.getLegendAttributes = function () {
733 return util_1.flatten(this.geometries.map(function (g) { return g.getGroupAttributes(); }));
734 };
735 /**
736 * 获取所有的分组字段的 scale 实例。
737 * @returns 获得分组字段的 scale 实例数组。
738 */
739 View.prototype.getGroupScales = function () {
740 // 拿到所有的 Geometry 的 分组字段 scale,然后打平去重
741 var scales = this.geometries.map(function (g) { return g.getGroupScales(); });
742 return helper_1.uniq(util_1.flatten(scales));
743 };
744 /**
745 * 获取 G.Canvas 实例。
746 * @returns G.Canvas 画布实例。
747 */
748 View.prototype.getCanvas = function () {
749 return this.getRootView().canvas;
750 };
751 /**
752 * 获得根节点 view。
753 */
754 View.prototype.getRootView = function () {
755 var v = this;
756 while (true) {
757 if (v.parent) {
758 v = v.parent;
759 continue;
760 }
761 break;
762 }
763 return v;
764 };
765 /**
766 * 获取该数据在可视化后,对应的画布坐标点。
767 * @param data 原始数据记录
768 * @returns 对应的画布坐标点
769 */
770 View.prototype.getXY = function (data) {
771 var coordinate = this.getCoordinate();
772 var xScales = this.getScalesByDim('x');
773 var yScales = this.getScalesByDim('y');
774 var x;
775 var y;
776 util_1.each(data, function (value, key) {
777 if (xScales[key]) {
778 x = xScales[key].scale(value);
779 }
780 if (yScales[key]) {
781 y = yScales[key].scale(value);
782 }
783 });
784 if (!util_1.isNil(x) && !util_1.isNil(y)) {
785 return coordinate.convert({ x: x, y: y });
786 }
787 };
788 /**
789 * 获取 name 对应的 controller 实例
790 * @param name
791 */
792 View.prototype.getController = function (name) {
793 return util_1.find(this.controllers, function (c) { return c.name === name; });
794 };
795 /**
796 * 显示 point 坐标点对应的 tooltip。
797 * @param point 画布坐标点
798 * @returns View
799 */
800 View.prototype.showTooltip = function (point) {
801 var tooltip = this.getController('tooltip');
802 if (tooltip) {
803 tooltip.showTooltip(point);
804 }
805 return this;
806 };
807 /**
808 * 隐藏 tooltip。
809 * @returns View
810 */
811 View.prototype.hideTooltip = function () {
812 var tooltip = this.getController('tooltip');
813 if (tooltip) {
814 tooltip.hideTooltip();
815 }
816 return this;
817 };
818 /**
819 * 将 tooltip 锁定到当前位置不能移动。
820 * @returns View
821 */
822 View.prototype.lockTooltip = function () {
823 var tooltip = this.getController('tooltip');
824 if (tooltip) {
825 tooltip.lockTooltip();
826 }
827 return this;
828 };
829 /**
830 * 将 tooltip 锁定解除。
831 * @returns View
832 */
833 View.prototype.unlockTooltip = function () {
834 var tooltip = this.getController('tooltip');
835 if (tooltip) {
836 tooltip.unlockTooltip();
837 }
838 return this;
839 };
840 /**
841 * 是否锁定 tooltip。
842 * @returns 是否锁定
843 */
844 View.prototype.isTooltipLocked = function () {
845 var tooltip = this.getController('tooltip');
846 return tooltip && tooltip.isTooltipLocked();
847 };
848 /**
849 * 获取当前 point 对应的 tooltip 数据项。
850 * @param point 坐标点
851 * @returns tooltip 数据项
852 */
853 View.prototype.getTooltipItems = function (point) {
854 var tooltip = this.getController('tooltip');
855 return tooltip ? tooltip.getTooltipItems(point) : [];
856 };
857 /**
858 * 获取逼近的点的数据集合
859 * @param point 当前坐标点
860 * @returns 数据
861 */
862 View.prototype.getSnapRecords = function (point) {
863 var geometries = this.geometries;
864 var rst = [];
865 for (var i = 0, len = geometries.length; i < len; i++) {
866 var geom = geometries[i];
867 var dataArray = geom.dataArray;
868 geom.sort(dataArray); // 先进行排序,便于 tooltip 查找
869 var record = void 0;
870 for (var j = 0, dataLen = dataArray.length; j < dataLen; j++) {
871 var data = dataArray[j];
872 record = tooltip_1.findDataByPoint(point, data, geom);
873 if (record) {
874 rst.push(record);
875 }
876 }
877 }
878 // 同样递归处理子 views
879 var views = this.views;
880 for (var i = 0, len = views.length; i < len; i++) {
881 var view = views[i];
882 var snapRecords = view.getSnapRecords(point);
883 rst = rst.concat(snapRecords);
884 }
885 return rst;
886 };
887 /**
888 * 获取所有的 pure component 组件,用于布局。
889 */
890 View.prototype.getComponents = function () {
891 var components = [];
892 var controllers = this.controllers;
893 for (var i = 0, len = controllers.length; i < len; i++) {
894 var controller = controllers[i];
895 components = components.concat(controller.getComponents());
896 }
897 return components;
898 };
899 /**
900 * 将 data 数据进行过滤。
901 * @param data
902 * @returns 过滤之后的数据
903 */
904 View.prototype.filterData = function (data) {
905 var filters = this.options.filters;
906 // 不存在 filters,则不需要进行数据过滤
907 if (util_1.size(filters) === 0) {
908 return data;
909 }
910 // 存在过滤器,则逐个执行过滤,过滤器之间是 与 的关系
911 return util_1.filter(data, function (datum, idx) {
912 // 所有的 filter 字段
913 var fields = Object.keys(filters);
914 // 所有的条件都通过,才算通过
915 return fields.every(function (field) {
916 var condition = filters[field];
917 // condition 返回 true,则保留
918 return condition(datum[field], datum, idx);
919 });
920 });
921 };
922 /**
923 * 对某一个字段进行过滤
924 * @param field
925 * @param data
926 */
927 View.prototype.filterFieldData = function (field, data) {
928 var filters = this.options.filters;
929 var condition = util_1.get(filters, field);
930 if (util_1.isUndefined(condition)) {
931 return data;
932 }
933 return data.filter(function (datum, idx) { return condition(datum[field], datum, idx); });
934 };
935 /**
936 * 调整 coordinate 的坐标范围。
937 */
938 View.prototype.adjustCoordinate = function () {
939 var _a = this.getCoordinate(), curStart = _a.start, curEnd = _a.end;
940 var start = this.coordinateBBox.bl;
941 var end = this.coordinateBBox.tr;
942 // 在 defaultLayoutFn 中只会在 coordinateBBox 发生变化的时候会调用 adjustCoordinate(),所以不用担心被置位
943 if (util_1.isEqual(curStart, start) && util_1.isEqual(curEnd, end)) {
944 this.isCoordinateChanged = false;
945 // 如果大小没有变化则不更新
946 return;
947 }
948 this.isCoordinateChanged = true;
949 this.coordinateInstance = this.coordinateController.adjust(start, end);
950 };
951 View.prototype.paint = function (isUpdate) {
952 this.renderDataRecursive(isUpdate);
953 // 处理 sync scale 的逻辑
954 this.syncScale();
955 this.emit(constant_1.VIEW_LIFE_CIRCLE.BEFORE_PAINT);
956 // 初始化图形、组件位置,计算 padding
957 this.renderPaddingRecursive(isUpdate);
958 // 布局图形、组件
959 this.renderLayoutRecursive(isUpdate);
960 // 背景色 shape
961 this.renderBackgroundStyleShape();
962 // 最终的绘制 render
963 this.renderPaintRecursive(isUpdate);
964 this.emit(constant_1.VIEW_LIFE_CIRCLE.AFTER_PAINT);
965 this.isDataChanged = false; // 渲染完毕复位
966 };
967 /**
968 * 渲染背景样式的 shape。
969 * 放到 view 中创建的原因是让使用 view 绘制图形的时候,也能够处理背景色
970 */
971 View.prototype.renderBackgroundStyleShape = function () {
972 // 只有根节点才处理
973 if (this.parent) {
974 return;
975 }
976 var background = util_1.get(this.themeObject, 'background');
977 // 配置了背景色
978 if (background) {
979 // 1. 不存在则创建
980 if (!this.backgroundStyleRectShape) {
981 this.backgroundStyleRectShape = this.backgroundGroup.addShape('rect', {
982 attrs: {},
983 zIndex: -1,
984 // 背景色 shape 不设置事件捕获
985 capture: false,
986 });
987 this.backgroundStyleRectShape.toBack();
988 }
989 // 2. 有了 shape 之后设置背景,位置(更新的时候)
990 var _a = this.viewBBox, x = _a.x, y = _a.y, width = _a.width, height = _a.height;
991 this.backgroundStyleRectShape.attr({
992 fill: background,
993 x: x,
994 y: y,
995 width: width,
996 height: height,
997 });
998 }
999 else {
1000 // 没有配置背景色
1001 if (this.backgroundStyleRectShape) {
1002 this.backgroundStyleRectShape.remove(true);
1003 this.backgroundStyleRectShape = undefined;
1004 }
1005 }
1006 };
1007 /**
1008 * 递归计算每个 view 的 padding 值,coordinateBBox 和 coordinateInstance
1009 * @param isUpdate
1010 */
1011 View.prototype.renderPaddingRecursive = function (isUpdate) {
1012 // 1. 子 view 大小相对 coordinateBBox,changeSize 的时候需要重新计算
1013 this.calculateViewBBox();
1014 // 2. 更新 coordinate
1015 this.adjustCoordinate();
1016 // 3. 初始化组件 component
1017 this.initComponents(isUpdate);
1018 // 4. 布局计算每隔 view 的 padding 值
1019 // 4.1. 自动加 auto padding -> absolute padding,并且增加 appendPadding
1020 this.autoPadding = auto_1.calculatePadding(this).shrink(padding_1.parsePadding(this.appendPadding));
1021 // 4.2. 计算出新的 coordinateBBox,更新 Coordinate
1022 // 这里必须保留,原因是后面子 view 的 viewBBox 或根据 parent 的 coordinateBBox
1023 this.coordinateBBox = this.viewBBox.shrink(this.autoPadding.getPadding());
1024 this.adjustCoordinate();
1025 // 同样递归处理子 views
1026 var views = this.views;
1027 for (var i = 0, len = views.length; i < len; i++) {
1028 var view = views[i];
1029 view.renderPaddingRecursive(isUpdate);
1030 }
1031 };
1032 /**
1033 * 递归处理 view 的布局,最终是计算各个 view 的 coordinateBBox 和 coordinateInstance
1034 * @param isUpdate
1035 */
1036 View.prototype.renderLayoutRecursive = function (isUpdate) {
1037 // 1. 同步子 view padding
1038 // 根据配置获取 padding
1039 var syncViewPaddingFn = this.syncViewPadding === true
1040 ? sync_view_padding_1.defaultSyncViewPadding
1041 : util_1.isFunction(this.syncViewPadding)
1042 ? this.syncViewPadding
1043 : undefined;
1044 if (syncViewPaddingFn) {
1045 syncViewPaddingFn(this, this.views, padding_cal_1.PaddingCal);
1046 // 同步 padding 之后,更新 coordinate
1047 this.views.forEach(function (v) {
1048 v.coordinateBBox = v.viewBBox.shrink(v.autoPadding.getPadding());
1049 v.adjustCoordinate();
1050 });
1051 }
1052 // 3. 将 view 中的组件按照 view padding 移动到对应的位置
1053 this.doLayout();
1054 // 同样递归处理子 views
1055 var views = this.views;
1056 for (var i = 0, len = views.length; i < len; i++) {
1057 var view = views[i];
1058 view.renderLayoutRecursive(isUpdate);
1059 }
1060 };
1061 /**
1062 * 最终递归绘制组件和图形
1063 * @param isUpdate
1064 */
1065 View.prototype.renderPaintRecursive = function (isUpdate) {
1066 var middleGroup = this.middleGroup;
1067 if (this.limitInPlot) {
1068 var _a = coordinate_1.getCoordinateClipCfg(this.coordinateInstance), type = _a.type, attrs = _a.attrs;
1069 middleGroup.setClip({
1070 type: type,
1071 attrs: attrs,
1072 });
1073 }
1074 else {
1075 // 清除已有的 clip
1076 middleGroup.setClip(undefined);
1077 }
1078 // 1. 渲染几何标记
1079 this.paintGeometries(isUpdate);
1080 // 2. 绘制组件
1081 this.renderComponents(isUpdate);
1082 // 同样递归处理子 views
1083 var views = this.views;
1084 for (var i = 0, len = views.length; i < len; i++) {
1085 var view = views[i];
1086 view.renderPaintRecursive(isUpdate);
1087 }
1088 };
1089 // end Get 方法
1090 /**
1091 * 创建 scale,递归到顶层 view 去创建和缓存 scale
1092 * @param field
1093 * @param data
1094 * @param scaleDef
1095 * @param key
1096 */
1097 View.prototype.createScale = function (field, data, scaleDef, key) {
1098 // 1. 合并 field 对应的 scaleDef,合并原则是底层覆盖顶层(就近原则)
1099 var currentScaleDef = util_1.get(this.options.scales, [field]);
1100 var mergedScaleDef = tslib_1.__assign(tslib_1.__assign({}, currentScaleDef), scaleDef);
1101 // 2. 是否存在父 view,在则递归,否则创建
1102 if (this.parent) {
1103 return this.parent.createScale(field, data, mergedScaleDef, key);
1104 }
1105 // 3. 在根节点 view 通过 scalePool 创建
1106 return this.scalePool.createScale(field, data, mergedScaleDef, key);
1107 };
1108 /**
1109 * 递归渲染中的数据处理
1110 * @param isUpdate
1111 */
1112 View.prototype.renderDataRecursive = function (isUpdate) {
1113 // 1. 处理数据
1114 this.doFilterData();
1115 // 2. 创建实例
1116 this.createCoordinate();
1117 // 3. 初始化 Geometry
1118 this.initGeometries(isUpdate);
1119 // 4. 处理分面逻辑,最终都是生成子 view 和 geometry
1120 this.renderFacet(isUpdate);
1121 // 同样递归处理子 views
1122 var views = this.views;
1123 for (var i = 0, len = views.length; i < len; i++) {
1124 var view = views[i];
1125 view.renderDataRecursive(isUpdate);
1126 }
1127 };
1128 /**
1129 * 计算 region,计算实际的像素范围坐标
1130 * @private
1131 */
1132 View.prototype.calculateViewBBox = function () {
1133 var x;
1134 var y;
1135 var width;
1136 var height;
1137 if (this.parent) {
1138 var bbox = this.parent.coordinateBBox;
1139 // 存在 parent, 那么就是通过父容器大小计算
1140 x = bbox.x;
1141 y = bbox.y;
1142 width = bbox.width;
1143 height = bbox.height;
1144 }
1145 else {
1146 // 顶层容器,从 canvas 中取值 宽高
1147 x = 0;
1148 y = 0;
1149 width = this.canvas.get('width');
1150 height = this.canvas.get('height');
1151 }
1152 var _a = this.region, start = _a.start, end = _a.end;
1153 // 根据 region 计算当前 view 的 bbox 大小。
1154 var viewBBox = new bbox_1.BBox(x + width * start.x, y + height * start.y, width * (end.x - start.x), height * (end.y - start.y));
1155 if (!this.viewBBox || !this.viewBBox.isEqual(viewBBox)) {
1156 // viewBBox 发生变化的时候进行更新
1157 this.viewBBox = new bbox_1.BBox(x + width * start.x, y + height * start.y, width * (end.x - start.x), height * (end.y - start.y));
1158 }
1159 // 初始的 coordinate bbox 大小
1160 this.coordinateBBox = this.viewBBox;
1161 };
1162 /**
1163 * 初始化事件机制:G 4.0 底层内置支持 name:event 的机制,那么只要所有组件都有自己的 name 即可。
1164 *
1165 * G2 的事件只是获取事件委托,然后在 view 嵌套结构中,形成事件冒泡机制。
1166 * 当前 view 只委托自己 view 中的 Component 和 Geometry 事件,并向上冒泡
1167 * @private
1168 */
1169 View.prototype.initEvents = function () {
1170 // 三层 group 中的 shape 事件都会通过 G 冒泡上来的
1171 this.foregroundGroup.on('*', this.onDelegateEvents);
1172 this.middleGroup.on('*', this.onDelegateEvents);
1173 this.backgroundGroup.on('*', this.onDelegateEvents);
1174 this.canvas.on('*', this.onCanvasEvent);
1175 };
1176 /**
1177 * 初始化插件
1178 */
1179 View.prototype.initComponentController = function () {
1180 var usedControllers = this.usedControllers;
1181 for (var i = 0, len = usedControllers.length; i < len; i++) {
1182 var controllerName = usedControllers[i];
1183 var Ctor = controller_1.getComponentController(controllerName);
1184 if (Ctor) {
1185 this.controllers.push(new Ctor(this));
1186 }
1187 }
1188 };
1189 View.prototype.createViewEvent = function (evt) {
1190 var shape = evt.shape, name = evt.name;
1191 var data = shape ? shape.get('origin') : null;
1192 // 事件在 view 嵌套中冒泡(暂不提供阻止冒泡的机制)
1193 var e = new event_1.default(this, evt, data);
1194 e.type = name;
1195 return e;
1196 };
1197 /**
1198 * 处理 PLOT_EVENTS
1199 * plot event 需要处理所有的基础事件,并判断是否在画布中,然后再决定是否要 emit。
1200 * 对于 mouseenter、mouseleave 比较特殊,需要做一下数学比较。
1201 * @param e
1202 */
1203 View.prototype.doPlotEvent = function (e) {
1204 var type = e.type, x = e.x, y = e.y;
1205 var point = { x: x, y: y };
1206 var ALL_EVENTS = [
1207 'mousedown',
1208 'mouseup',
1209 'mousemove',
1210 'mouseleave',
1211 'mousewheel',
1212 'touchstart',
1213 'touchmove',
1214 'touchend',
1215 'touchcancel',
1216 'click',
1217 'dblclick',
1218 'contextmenu',
1219 ];
1220 if (ALL_EVENTS.includes(type)) {
1221 var currentInPlot = this.isPointInPlot(point);
1222 var newEvent = e.clone();
1223 if (currentInPlot) {
1224 var TYPE = "plot:" + type; // 组合 plot 事件
1225 newEvent.type = TYPE;
1226 this.emit(TYPE, newEvent);
1227 if (type === 'mouseleave' || type === 'touchend') {
1228 // 在plot 内部却离开画布
1229 this.isPreMouseInPlot = false;
1230 }
1231 }
1232 // 对于 mouseenter, mouseleave 的计算处理
1233 if (type === 'mousemove' || type === 'touchmove') {
1234 if (this.isPreMouseInPlot && !currentInPlot) {
1235 if (type === 'mousemove') {
1236 newEvent.type = constant_1.PLOT_EVENTS.MOUSE_LEAVE;
1237 this.emit(constant_1.PLOT_EVENTS.MOUSE_LEAVE, newEvent);
1238 }
1239 newEvent.type = constant_1.PLOT_EVENTS.LEAVE;
1240 this.emit(constant_1.PLOT_EVENTS.LEAVE, newEvent);
1241 }
1242 else if (!this.isPreMouseInPlot && currentInPlot) {
1243 if (type === 'mousemove') {
1244 newEvent.type = constant_1.PLOT_EVENTS.MOUSE_ENTER;
1245 this.emit(constant_1.PLOT_EVENTS.MOUSE_ENTER, newEvent);
1246 }
1247 newEvent.type = constant_1.PLOT_EVENTS.ENTER;
1248 this.emit(constant_1.PLOT_EVENTS.ENTER, newEvent);
1249 }
1250 // 赋新的状态值
1251 this.isPreMouseInPlot = currentInPlot;
1252 }
1253 else if (type === 'mouseleave' || type === 'touchend') {
1254 // 可能不在 currentInPlot 中
1255 if (this.isPreMouseInPlot) {
1256 if (type === 'mouseleave') {
1257 newEvent.type = constant_1.PLOT_EVENTS.MOUSE_LEAVE;
1258 this.emit(constant_1.PLOT_EVENTS.MOUSE_LEAVE, newEvent);
1259 }
1260 newEvent.type = constant_1.PLOT_EVENTS.LEAVE;
1261 this.emit(constant_1.PLOT_EVENTS.LEAVE, newEvent);
1262 this.isPreMouseInPlot = false;
1263 }
1264 }
1265 }
1266 };
1267 // view 生命周期 —— 渲染流程
1268 /**
1269 * 处理筛选器,筛选数据
1270 * @private
1271 */
1272 View.prototype.doFilterData = function () {
1273 var data = this.options.data;
1274 this.filteredData = this.filterData(data);
1275 };
1276 /**
1277 * 初始化 Geometries
1278 * @private
1279 */
1280 View.prototype.initGeometries = function (isUpdate) {
1281 // 初始化图形的之前,先创建 / 更新 scales
1282 this.createOrUpdateScales();
1283 // 实例化 Geometry,然后 view 将所有的 scale 管理起来
1284 var coordinate = this.getCoordinate();
1285 var scaleDefs = util_1.get(this.options, 'scales', {});
1286 var geometries = this.geometries;
1287 for (var i = 0, len = geometries.length; i < len; i++) {
1288 var geometry = geometries[i];
1289 // 保持 scales 引用不要变化
1290 geometry.scales = this.getGeometryScales();
1291 var cfg = {
1292 coordinate: coordinate,
1293 scaleDefs: scaleDefs,
1294 data: this.filteredData,
1295 theme: this.themeObject,
1296 isDataChanged: this.isDataChanged,
1297 isCoordinateChanged: this.isCoordinateChanged,
1298 };
1299 if (isUpdate) {
1300 // 数据发生更新
1301 geometry.update(cfg);
1302 }
1303 else {
1304 geometry.init(cfg);
1305 }
1306 }
1307 // Geometry 初始化之后,生成了 scale,然后进行调整 scale 配置
1308 this.adjustScales();
1309 };
1310 /**
1311 * 根据 Geometry 的所有字段创建 scales
1312 * 如果存在,则更新,不存在则创建
1313 */
1314 View.prototype.createOrUpdateScales = function () {
1315 var fields = this.getScaleFields();
1316 var groupedFields = this.getGroupedFields();
1317 var _a = this.getOptions(), data = _a.data, _b = _a.scales, scales = _b === void 0 ? {} : _b;
1318 var filteredData = this.filteredData;
1319 for (var i = 0, len = fields.length; i < len; i++) {
1320 var field = fields[i];
1321 var scaleDef = scales[field];
1322 // 调用方法,递归去创建
1323 var key = this.getScaleKey(field);
1324 this.createScale(field,
1325 // 分组字段的 scale 使用未过滤的数据创建
1326 groupedFields.includes(field) ? data : filteredData, scaleDef, key);
1327 // 缓存从当前 view 创建的 scale key
1328 this.createdScaleKeys.set(key, true);
1329 }
1330 };
1331 /**
1332 * 处理 scale 同步逻辑
1333 */
1334 View.prototype.syncScale = function () {
1335 // 最终调用 root view 的
1336 this.getRootView().scalePool.sync(this.getCoordinate(), this.theme);
1337 };
1338 /**
1339 * 获得 Geometry 中的 scale 对象
1340 */
1341 View.prototype.getGeometryScales = function () {
1342 var fields = this.getScaleFields();
1343 var scales = {};
1344 for (var i = 0; i < fields.length; i++) {
1345 var field = fields[i];
1346 scales[field] = this.getScaleByField(field);
1347 }
1348 return scales;
1349 };
1350 View.prototype.getScaleFields = function () {
1351 var fields = [];
1352 var tmpMap = {};
1353 var geometries = this.geometries;
1354 for (var i = 0; i < geometries.length; i++) {
1355 var geometry = geometries[i];
1356 var geometryScales = geometry.getScaleFields();
1357 helper_1.uniq(geometryScales, fields, tmpMap);
1358 }
1359 return fields;
1360 };
1361 View.prototype.getGroupedFields = function () {
1362 var fields = [];
1363 var tmpMap = {};
1364 var geometries = this.geometries;
1365 for (var i = 0; i < geometries.length; i++) {
1366 var geometry = geometries[i];
1367 var groupFields = geometry.getGroupFields();
1368 helper_1.uniq(groupFields, fields, tmpMap);
1369 }
1370 return fields;
1371 };
1372 /**
1373 * 调整 scale 配置
1374 * @private
1375 */
1376 View.prototype.adjustScales = function () {
1377 // 调整目前包括:
1378 // 分类 scale,调整 range 范围
1379 this.adjustCategoryScaleRange();
1380 };
1381 /**
1382 * 调整分类 scale 的 range,防止超出坐标系外面
1383 * @private
1384 */
1385 View.prototype.adjustCategoryScaleRange = function () {
1386 var _this = this;
1387 var xyScales = tslib_1.__spreadArrays([this.getXScale()], this.getYScales()).filter(function (e) { return !!e; });
1388 var coordinate = this.getCoordinate();
1389 var scaleOptions = this.options.scales;
1390 util_1.each(xyScales, function (scale) {
1391 var field = scale.field, values = scale.values, isCategory = scale.isCategory, isIdentity = scale.isIdentity;
1392 // 分类或者 identity 的 scale 才进行处理
1393 if (isCategory || isIdentity) {
1394 // 存在 value 值,且用户没有配置 range 配置
1395 if (values && !util_1.get(scaleOptions, [field, 'range'])) {
1396 // 更新 range
1397 scale.range = scale_1.getDefaultCategoryScaleRange(scale, coordinate, _this.theme);
1398 }
1399 }
1400 });
1401 };
1402 /**
1403 * 根据 options 配置、Geometry 字段配置,自动生成 components
1404 * @param isUpdate 是否是更新
1405 * @private
1406 */
1407 View.prototype.initComponents = function (isUpdate) {
1408 // 先全部清空,然后 render
1409 var controllers = this.controllers;
1410 for (var i = 0; i < controllers.length; i++) {
1411 var controller = controllers[i];
1412 // 更新则走更新逻辑;否则清空载重绘
1413 if (isUpdate) {
1414 controller.update();
1415 }
1416 else {
1417 controller.clear();
1418 controller.render();
1419 }
1420 }
1421 };
1422 View.prototype.doLayout = function () {
1423 this.layoutFunc(this);
1424 };
1425 /**
1426 * 创建坐标系
1427 * @private
1428 */
1429 View.prototype.createCoordinate = function () {
1430 var start = this.coordinateBBox.bl;
1431 var end = this.coordinateBBox.tr;
1432 this.coordinateInstance = this.coordinateController.create(start, end);
1433 };
1434 /**
1435 * 根据 options 配置自动渲染 geometry
1436 * @private
1437 */
1438 View.prototype.paintGeometries = function (isUpdate) {
1439 var doAnimation = this.options.animate;
1440 // geometry 的 paint 阶段
1441 var coordinate = this.getCoordinate();
1442 var canvasRegion = {
1443 x: this.viewBBox.x,
1444 y: this.viewBBox.y,
1445 minX: this.viewBBox.minX,
1446 minY: this.viewBBox.minY,
1447 maxX: this.viewBBox.maxX,
1448 maxY: this.viewBBox.maxY,
1449 width: this.viewBBox.width,
1450 height: this.viewBBox.height,
1451 };
1452 var geometries = this.geometries;
1453 for (var i = 0; i < geometries.length; i++) {
1454 var geometry = geometries[i];
1455 geometry.coordinate = coordinate;
1456 geometry.canvasRegion = canvasRegion;
1457 if (!doAnimation) {
1458 // 如果 view 不执行动画,那么 view 下所有的 geometry 都不执行动画
1459 geometry.animate(false);
1460 }
1461 geometry.paint(isUpdate);
1462 }
1463 };
1464 /**
1465 * 最后的绘制组件
1466 * @param isUpdate
1467 */
1468 View.prototype.renderComponents = function (isUpdate) {
1469 // 先全部清空,然后 render
1470 for (var i = 0; i < this.getComponents().length; i++) {
1471 var co = this.getComponents()[i];
1472 co.component.render();
1473 }
1474 };
1475 /**
1476 * 渲染分面,会在其中进行数据分面,然后进行子 view 创建
1477 * @param isUpdate
1478 */
1479 View.prototype.renderFacet = function (isUpdate) {
1480 if (this.facetInstance) {
1481 if (isUpdate) {
1482 this.facetInstance.update();
1483 }
1484 else {
1485 this.facetInstance.clear();
1486 // 计算分面数据
1487 this.facetInstance.init();
1488 // 渲染组件和 views
1489 this.facetInstance.render();
1490 }
1491 }
1492 };
1493 View.prototype.initOptions = function () {
1494 var _this = this;
1495 var _a = this.options, _b = _a.geometries, geometries = _b === void 0 ? [] : _b, _c = _a.interactions, interactions = _c === void 0 ? [] : _c, _d = _a.views, views = _d === void 0 ? [] : _d, _e = _a.annotations, annotations = _e === void 0 ? [] : _e, coordinate = _a.coordinate, events = _a.events, facets = _a.facets;
1496 // 设置坐标系
1497 if (this.coordinateController) {
1498 // 更新 coordinate controller
1499 coordinate && this.coordinateController.update(coordinate);
1500 }
1501 else {
1502 // 创建 coordinate controller
1503 this.coordinateController = new coordinate_2.default(coordinate);
1504 }
1505 // 创建 geometry 实例
1506 for (var i = 0; i < geometries.length; i++) {
1507 var geometryOption = geometries[i];
1508 this.createGeometry(geometryOption);
1509 }
1510 // 创建 interactions 实例
1511 for (var j = 0; j < interactions.length; j++) {
1512 var interactionOption = interactions[j];
1513 var type = interactionOption.type, cfg = interactionOption.cfg;
1514 this.interaction(type, cfg);
1515 }
1516 // 创建 view 实例
1517 for (var k = 0; k < views.length; k++) {
1518 var viewOption = views[k];
1519 this.createView(viewOption);
1520 }
1521 // 设置 annotation
1522 var annotationComponent = this.getController('annotation');
1523 for (var l = 0; l < annotations.length; l++) {
1524 var annotationOption = annotations[l];
1525 annotationComponent.annotation(annotationOption);
1526 }
1527 // 设置 events
1528 if (events) {
1529 util_1.each(events, function (eventCallback, eventName) {
1530 _this.on(eventName, eventCallback);
1531 });
1532 }
1533 if (facets) {
1534 util_1.each(facets, function (facet) {
1535 var type = facet.type, rest = tslib_1.__rest(facet, ["type"]);
1536 _this.facet(type, rest);
1537 });
1538 }
1539 };
1540 View.prototype.createGeometry = function (geometryOption) {
1541 var type = geometryOption.type, _a = geometryOption.cfg, cfg = _a === void 0 ? {} : _a;
1542 if (this[type]) {
1543 var geometry_1 = this[type](cfg);
1544 util_1.each(geometryOption, function (v, k) {
1545 if (util_1.isFunction(geometry_1[k])) {
1546 geometry_1[k](v);
1547 }
1548 });
1549 }
1550 };
1551 /**
1552 * scale key 的创建方式
1553 * @param field
1554 */
1555 View.prototype.getScaleKey = function (field) {
1556 return this.id + "-" + field;
1557 };
1558 return View;
1559}(base_1.default));
1560exports.View = View;
1561/**
1562 * 注册 geometry 组件
1563 * @param name
1564 * @param Ctor
1565 * @returns Geometry
1566 */
1567function registerGeometry(name, Ctor) {
1568 // 语法糖,在 view API 上增加原型方法
1569 View.prototype[name.toLowerCase()] = function (cfg) {
1570 if (cfg === void 0) { cfg = {}; }
1571 var props = tslib_1.__assign({
1572 /** 图形容器 */
1573 container: this.middleGroup.addGroup(), labelsContainer: this.foregroundGroup.addGroup() }, cfg);
1574 var geometry = new Ctor(props);
1575 this.geometries.push(geometry);
1576 return geometry;
1577 };
1578}
1579exports.registerGeometry = registerGeometry;
1580exports.default = View;
1581//# sourceMappingURL=view.js.map
\No newline at end of file