NuclearDispersionSystem/ant-design-vue-jeecg/node_modules/@antv/g2/esm/chart/view.js

1476 lines
52 KiB
Java
Raw Normal View History

2023-09-14 14:47:11 +08:00
import { __assign, __extends, __spreadArrays } from "tslib";
import { clone, deepMix, each, filter, find, flatten, get, isBoolean, isFunction, isNil, isObject, isString, isUndefined, mix, remove, set, size, uniqueId, isEqual, } from '@antv/util';
import { GROUP_Z_INDEX, LAYER, PLOT_EVENTS, VIEW_LIFE_CIRCLE } from '../constant';
import Base from '../base';
import { getFacet } from '../facet';
import { createInteraction } from '../interaction';
import { getTheme } from '../theme';
import { BBox } from '../util/bbox';
import { getCoordinateClipCfg, isFullCircle, isPointInCoordinate } from '../util/coordinate';
import { uniq } from '../util/helper';
import { findDataByPoint } from '../util/tooltip';
import { getComponentController, getComponentControllerNames } from './controller';
import CoordinateController from './controller/coordinate';
import Event from './event';
import defaultLayout from './layout';
import { ScalePool } from './util/scale-pool';
/**
* G2 视图 View
*/
var View = /** @class */ (function (_super) {
__extends(View, _super);
function View(props) {
var _this = _super.call(this, { visible: props.visible }) || this;
/** view id全局唯一。 */
_this.id = uniqueId('view');
/** 所有的子 view。 */
_this.views = [];
/** 所有的 geometry 实例。 */
_this.geometries = [];
/** 所有的组件 controllers。 */
_this.controllers = [];
/** 所有的 Interaction 实例。 */
_this.interactions = {};
/** 是否对超出坐标系范围的 Geometry 进行剪切 */
_this.limitInPlot = false;
// 配置信息存储
_this.options = {
data: [],
animate: true,
}; // 初始化为空
/** 配置开启的组件插件,默认为全局配置的组件。 */
_this.usedControllers = getComponentControllerNames();
/** 所有的 scales */
_this.scalePool = new ScalePool();
/** 布局函数 */
_this.layoutFunc = defaultLayout;
/** 当前鼠标是否在 plot 内CoordinateBBox */
_this.isPreMouseInPlot = false;
/** 默认标识位,用于判定数据是否更新 */
_this.isDataChanged = false;
/** 用于判断坐标系范围是否发生变化的标志位 */
_this.isCoordinateChanged = false;
/** 从当前这个 view 创建的 scale key */
_this.createdScaleKeys = new Map();
_this.onCanvasEvent = function (evt) {
var name = evt.name;
if (!name.includes(':')) { // 非委托事件
var e = _this.createViewEvent(evt);
// 处理 plot 事件
_this.doPlotEvent(e);
_this.emit(name, e);
}
};
/**
* 触发事件之后
* @param evt
*/
_this.onDelegateEvents = function (evt) {
// 阻止继续冒泡,防止重复事件触发
// evt.preventDefault();
var name = evt.name;
if (!name.includes(':')) {
return;
}
// 事件在 view 嵌套中冒泡(暂不提供阻止冒泡的机制)
var e = _this.createViewEvent(evt);
// 包含有基本事件、组合事件
_this.emit(name, e);
// const currentTarget = evt.currentTarget as IShape;
// const inheritNames = currentTarget.get('inheritNames');
// if (evt.delegateObject || inheritNames) {
// const events = this.getEvents();
// each(inheritNames, (subName) => {
// const eventName = `${subName}:${type}`;
// if (events[eventName]) {
// this.emit(eventName, e);
// }
// });
// }
};
var parent = props.parent, canvas = props.canvas, backgroundGroup = props.backgroundGroup, middleGroup = props.middleGroup, foregroundGroup = props.foregroundGroup, _a = props.region, region = _a === void 0 ? { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } } : _a, padding = props.padding, theme = props.theme, options = props.options, limitInPlot = props.limitInPlot;
_this.parent = parent;
_this.canvas = canvas;
_this.backgroundGroup = backgroundGroup;
_this.middleGroup = middleGroup;
_this.foregroundGroup = foregroundGroup;
_this.region = region;
_this.padding = padding;
// 接受父 view 传入的参数
_this.options = __assign(__assign({}, _this.options), options);
_this.limitInPlot = limitInPlot;
// 初始化 theme
_this.themeObject = isObject(theme) ? deepMix({}, getTheme('default'), theme) : getTheme(theme);
_this.init();
return _this;
}
/**
* 设置 layout 布局函数
* @param layout 布局函数
* @returns void
*/
View.prototype.setLayout = function (layout) {
this.layoutFunc = layout;
};
/**
* 生命周期初始化
* @returns voids
*/
View.prototype.init = function () {
// 计算画布的 viewBBox
this.calculateViewBBox();
// 事件委托机制
this.initEvents();
// 初始化组件 controller
this.initComponentController();
// 创建 coordinate controller
this.coordinateController = new CoordinateController(this.options.coordinate);
this.initOptions();
// 递归初始化子 view
var views = this.views;
for (var i = 0; i < views.length; i++) {
views[i].init();
}
};
/**
* 生命周期渲染流程渲染过程需要处理数据更新的情况
* render 函数仅仅会处理 view 和子 view
* @param isUpdate 是否触发更新流程
*/
View.prototype.render = function (isUpdate) {
if (isUpdate === void 0) { isUpdate = false; }
this.emit(VIEW_LIFE_CIRCLE.BEFORE_RENDER);
// 递归渲染
this.paint(isUpdate);
this.emit(VIEW_LIFE_CIRCLE.AFTER_RENDER);
if (this.visible === false) {
// 用户在初始化的时候声明 visible: false
this.changeVisible(false);
}
};
/**
* 生命周期清空图表上所有的绘制内容但是不销毁图表chart 仍可使用
* @returns void
*/
View.prototype.clear = function () {
var _this = this;
this.emit(VIEW_LIFE_CIRCLE.BEFORE_CLEAR);
// 1. 清空缓存和计算数据
this.filteredData = [];
this.coordinateInstance = undefined;
this.isDataChanged = false; // 复位
this.isCoordinateChanged = false; // 复位
// 2. 清空 geometries
var geometries = this.geometries;
for (var i = 0; i < geometries.length; i++) {
geometries[i].clear();
}
this.geometries = [];
// 3. 清空 controllers
var controllers = this.controllers;
for (var i = 0; i < controllers.length; i++) {
controllers[i].clear();
}
// 4. 删除 scale 缓存
this.createdScaleKeys.forEach(function (v, k) {
_this.getRootView().scalePool.deleteScale(k);
});
this.createdScaleKeys.clear();
// 递归处理子 view
var views = this.views;
for (var i = 0; i < views.length; i++) {
views[i].clear();
}
this.emit(VIEW_LIFE_CIRCLE.AFTER_CLEAR);
};
/**
* 生命周期销毁完全无法使用
* @returns void
*/
View.prototype.destroy = function () {
// 销毁前事件,销毁之后已经没有意义了,所以不抛出事件
this.emit(VIEW_LIFE_CIRCLE.BEFORE_DESTROY);
var interactions = this.interactions;
// 销毁 interactions
each(interactions, function (interaction) {
if (interaction) {
// 有可能已经销毁,设置了 undefined
interaction.destroy();
}
});
this.clear();
// 销毁 controller 中的组件
var controllers = this.controllers;
for (var i = 0, len = controllers.length; i < len; i++) {
var controller = controllers[i];
controller.destroy();
}
this.backgroundGroup.remove(true);
this.middleGroup.remove(true);
this.foregroundGroup.remove(true);
_super.prototype.destroy.call(this);
};
/* end 生命周期函数 */
/**
* 显示或者隐藏整个 view
* @param visible 是否可见
* @returns View
*/
View.prototype.changeVisible = function (visible) {
_super.prototype.changeVisible.call(this, visible);
var geometries = this.geometries;
for (var i = 0, len = geometries.length; i < len; i++) {
var geometry = geometries[i];
geometry.changeVisible(visible);
}
var controllers = this.controllers;
for (var i = 0, len = controllers.length; i < len; i++) {
var controller = controllers[i];
controller.changeVisible(visible);
}
this.foregroundGroup.set('visible', visible);
this.middleGroup.set('visible', visible);
this.backgroundGroup.set('visible', visible);
// group.set('visible', visible) 不会触发自动刷新
this.getCanvas().draw();
return this;
};
/**
* 装载数据源
*
* ```ts
* view.data([{ city: '杭州', sale: 100 }, { city: '上海', sale: 110 } ]);
* ```
*
* @param data 数据源json 数组
* @returns View
*/
View.prototype.data = function (data) {
set(this.options, 'data', data);
this.isDataChanged = true;
return this;
};
/**
* @deprecated
* This method will be removed at G2 V4.1. Replaced by {@link #data(data)}
*/
View.prototype.source = function (data) {
console.warn('This method will be removed at G2 V4.1. Please use chart.data() instead.');
return this.data(data);
};
/**
* 设置数据筛选规则
*
* ```ts
* view.filter('city', (value: any, datum: Datum) => value !== '杭州');
*
* // 删除 'city' 字段对应的筛选规则。
* view.filter('city', null);
* ```
*
* @param field 数据字段
* @param condition 筛选规则
* @returns View
*/
View.prototype.filter = function (field, condition) {
if (isFunction(condition)) {
set(this.options, ['filters', field], condition);
return this;
}
// condition 为空,则表示删除过滤条件
if (!condition && get(this.options, ['filters', field])) {
delete this.options.filters[field];
}
return this;
};
View.prototype.axis = function (field, axisOption) {
if (isBoolean(field)) {
set(this.options, ['axes'], field);
}
else {
set(this.options, ['axes', field], axisOption);
}
return this;
};
View.prototype.legend = function (field, legendOption) {
if (isBoolean(field)) {
set(this.options, ['legends'], field);
}
else if (isString(field)) {
set(this.options, ['legends', field], legendOption);
}
else {
// 设置全局的 legend 配置
set(this.options, ['legends'], field);
}
return this;
};
View.prototype.scale = function (field, scaleOption) {
var _this = this;
if (isString(field)) {
set(this.options, ['scales', field], scaleOption);
}
else if (isObject(field)) {
each(field, function (v, k) {
set(_this.options, ['scales', k], v);
});
}
return this;
};
/**
* tooltip 提示信息配置
*
* ```ts
* view.tooltip(false); // 关闭 tooltip
*
* view.tooltip({
* shared: true
* });
* ```
*
* @param cfg Tooltip 配置更详细的配置项参考https://github.com/antvis/component#tooltip
* @returns View
*/
View.prototype.tooltip = function (cfg) {
set(this.options, 'tooltip', cfg);
return this;
};
/**
* 辅助标记配置
*
* ```ts
* view.annotation().line({
* start: ['min', 85],
* end: ['max', 85],
* style: {
* stroke: '#595959',
* lineWidth: 1,
* lineDash: [3, 3],
* },
* });
* ```
* 更详细的配置项https://github.com/antvis/component#annotation
* @returns [[Annotation]]
*/
View.prototype.annotation = function () {
return this.getController('annotation');
};
/**
* @deprecated
* This method will be removed at G2 V4.1. Replaced by {@link #guide()}
*/
View.prototype.guide = function () {
console.warn('This method will be removed at G2 V4.1. Please use chart.annotation() instead.');
return this.annotation();
};
View.prototype.coordinate = function (type, coordinateCfg) {
// 提供语法糖,使用更简单
if (isString(type)) {
set(this.options, 'coordinate', { type: type, cfg: coordinateCfg });
}
else {
set(this.options, 'coordinate', type);
}
// 更新 coordinate 配置
this.coordinateController.update(this.options.coordinate);
return this.coordinateController;
};
/**
* @deprecated
* This method will be removed at G2 V4.1. Replaced by {@link #coordinate()}
*/
View.prototype.coord = function (type, coordinateCfg) {
console.warn('This method will be removed at G2 V4.1. Please use chart.coordinate() instead.');
// @ts-ignore
return this.coordinate(type, coordinateCfg);
};
/**
* view 分面绘制
*
* ```ts
* view.facet('rect', {
* rowField: 'province',
* columnField: 'category',
* eachView: (innerView: View, facet?: FacetData) => {
* innerView.line().position('city*sale');
* },
* });
* ```
*
* @param type 分面类型
* @param cfg 分面配置 [[FacetCfgMap]]
* @returns View
*/
View.prototype.facet = function (type, cfg) {
// 先销毁掉之前的分面
if (this.facetInstance) {
this.facetInstance.destroy();
}
// 创建新的分面
var Ctor = getFacet(type);
if (!Ctor) {
throw new Error("facet '" + type + "' is not exist!");
}
this.facetInstance = new Ctor(this, __assign(__assign({}, cfg), { type: type }));
return this;
};
/*
* 开启或者关闭动画
*
* ```ts
* view.animate(false);
* ```
*
* @param status 动画状态true 表示开始false 表示关闭
* @returns View
*/
View.prototype.animate = function (status) {
set(this.options, 'animate', status);
return this;
};
/**
* 更新配置项用于配置项式声明
* @param options 配置项
*/
View.prototype.updateOptions = function (options) {
this.clear(); // 清空
mix(this.options, options);
this.initOptions();
return this;
};
/**
* `view.options` 属性中存储配置项
* @param name 属性名称
* @param opt 属性值
* @returns view
*/
View.prototype.option = function (name, opt) {
// 对于内置的 option避免覆盖。
// name 在原型上,说明可能是内置 API存在 option 被覆盖的风险,不处理
if (View.prototype[name]) {
throw new Error("Can't use built in variable name \"" + name + "\", please change another one.");
}
// 存入到 option 中
set(this.options, name, opt);
return this;
};
/**
* 设置主题
*
* ```ts
* view.theme('dark'); // 'dark' 需要事先通过 `registerTheme()` 接口注册完成
*
* view.theme({ defaultColor: 'red' });
* ```
*
* @param theme 主题名或者主题配置
* @returns View
*/
View.prototype.theme = function (theme) {
this.themeObject = isObject(theme) ? deepMix({}, this.themeObject, theme) : getTheme(theme);
return this;
};
/* end 一系列传入配置的 API */
/**
* Call the interaction based on the interaction name
*
* ```ts
* view.interaction('my-interaction', { extra: 'hello world' });
* ```
* 详细文档可以参考https://g2.antv.vision/zh/docs/manual/tutorial/interaction
* @param name interaction name
* @param cfg interaction config
* @returns
*/
View.prototype.interaction = function (name, cfg) {
var existInteraction = this.interactions[name];
// 存在则先销毁已有的
if (existInteraction) {
existInteraction.destroy();
}
// 新建交互实例
var interaction = createInteraction(name, this, cfg);
if (interaction) {
interaction.init();
this.interactions[name] = interaction;
}
return this;
};
/**
* 移除当前 View interaction
* ```ts
* view.removeInteraction('my-interaction');
* ```
* @param name interaction name
*/
View.prototype.removeInteraction = function (name) {
var existInteraction = this.interactions[name];
// 存在则先销毁已有的
if (existInteraction) {
existInteraction.destroy();
this.interactions[name] = undefined;
}
};
/**
* 修改数据数据更新逻辑数据更新仅仅影响当前这一层的 view
*
* ```ts
* view.changeData([{ city: '北京', sale: '200' }]);
* ```
*
* @param data
* @returns void
*/
View.prototype.changeData = function (data) {
this.isDataChanged = true;
this.emit(VIEW_LIFE_CIRCLE.BEFORE_CHANGE_DATA);
// 1. 保存数据
this.data(data);
// 2. 渲染
this.paint(true);
// 3. 遍历子 view 进行 change data
var views = this.views;
for (var i = 0, len = views.length; i < len; i++) {
var view = views[i];
// FIXME 子 view 有自己的数据的情况,该如何处理?
view.changeData(data);
}
this.emit(VIEW_LIFE_CIRCLE.AFTER_CHANGE_DATA);
};
/* View 管理相关的 API */
/**
* 创建子 view
*
* ```ts
* const innerView = view.createView({
* start: { x: 0, y: 0 },
* end: { x: 0.5, y: 0.5 },
* padding: 8,
* });
* ```
*
* @param cfg
* @returns View
*/
View.prototype.createView = function (cfg) {
// 子 view 共享 options 配置数据
var sharedOptions = {
data: this.options.data,
scales: clone(this.options.scales),
axes: clone(this.options.axes),
coordinate: clone(this.coordinateController.getOption()),
tooltip: clone(this.options.tooltip),
legends: clone(this.options.legends),
animate: this.options.animate,
visible: this.visible,
};
var v = new View(__assign(__assign({ parent: this, canvas: this.canvas,
// 子 view 共用三层 group
backgroundGroup: this.backgroundGroup.addGroup({ zIndex: GROUP_Z_INDEX.BG }), middleGroup: this.middleGroup.addGroup({ zIndex: GROUP_Z_INDEX.MID }), foregroundGroup: this.foregroundGroup.addGroup({ zIndex: GROUP_Z_INDEX.FORE }), theme: this.themeObject, padding: this.padding }, cfg), { options: __assign(__assign({}, sharedOptions), get(cfg, 'options', {})) }));
this.views.push(v);
return v;
};
/**
* @deprecated
* This method will be removed at G2 V4.1. Replaced by {@link #createView()}
*/
View.prototype.view = function (cfg) {
console.warn('This method will be removed at G2 V4.1. Please use chart.createView() instead.');
return this.createView(cfg);
};
/**
* 删除一个子 view
* @param view
* @return removedView
*/
View.prototype.removeView = function (view) {
var removedView = remove(this.views, function (v) { return v === view; })[0];
if (removedView) {
removedView.destroy();
}
return removedView;
};
/* end View 管理相关的 API */
// 一些 get 方法
/**
* 获取当前坐标系实例
* @returns [[Coordinate]]
*/
View.prototype.getCoordinate = function () {
return this.coordinateInstance;
};
/**
* 获取当前 view 的主题配置
* @returns themeObject
*/
View.prototype.getTheme = function () {
return this.themeObject;
};
/**
* 获得 x 轴字段的 scale 实例
* @returns view Geometry 对于的 x scale
*/
View.prototype.getXScale = function () {
// 拿第一个 Geometry 的 X scale
// 隐藏逻辑:一个 view 中的 Geometry 必须 x 字段一致
var g = this.geometries[0];
return g ? g.getXScale() : null;
};
/**
* 获取 y 轴字段的 scales 实例
* @returns view Geometry 对于的 y scale 数组
*/
View.prototype.getYScales = function () {
// 拿到所有的 Geometry 的 Y scale然后去重
var tmpMap = {};
return this.geometries.map(function (g) {
var yScale = g.getYScale();
var field = yScale.field;
if (!tmpMap[field]) {
tmpMap[field] = true;
return yScale;
}
});
};
/**
* 获取 x 轴或者 y 轴对应的所有 scale 实例
* @param dimType x | y
* @returns x 轴或者 y 轴对应的所有 scale 实例
*/
View.prototype.getScalesByDim = function (dimType) {
var geometries = this.geometries;
var scales = {};
for (var i = 0, len = geometries.length; i < len; i++) {
var geometry = geometries[i];
var scale = dimType === 'x' ? geometry.getXScale() : geometry.getYScale();
if (scale && !scales[scale.field]) {
scales[scale.field] = scale;
}
}
return scales;
};
/**
* 根据字段名去获取 scale 实例
* @param field 数据字段名称
* @param key id
*/
View.prototype.getScaleByField = function (field, key) {
var defaultKey = key ? key : this.getScaleKey(field);
// 调用根节点 view 的方法获取
return this.getRootView().scalePool.getScale(defaultKey);
};
/**
* 返回所有配置信息
* @returns 所有的 view API 配置
*/
View.prototype.getOptions = function () {
return this.options;
};
/**
* 获取 view 的数据过滤后的数据
* @returns 处理过滤器之后的数据
*/
View.prototype.getData = function () {
return this.filteredData;
};
/**
* 获得绘制的层级 group
* @param layer 层级名称
* @returns 对应层级的 Group
*/
View.prototype.getLayer = function (layer) {
return layer === LAYER.BG
? this.backgroundGroup
: layer === LAYER.MID
? this.middleGroup
: layer === LAYER.FORE
? this.foregroundGroup
: this.foregroundGroup;
};
/**
* 对外暴露方法判断一个点是否在绘图区域即坐标系范围内部
* @param point 坐标点
*/
View.prototype.isPointInPlot = function (point) {
return isPointInCoordinate(this.getCoordinate(), point);
};
/**
* 获得所有的 legend 对应的 attribute 实例
* @returns 维度字段的 Attribute 数组
*/
View.prototype.getLegendAttributes = function () {
return flatten(this.geometries.map(function (g) { return g.getGroupAttributes(); }));
};
/**
* 获取所有的分组字段的 scale 实例
* @returns 获得分组字段的 scale 实例数组
*/
View.prototype.getGroupScales = function () {
// 拿到所有的 Geometry 的 分组字段 scale然后打平去重
var scales = this.geometries.map(function (g) { return g.getGroupScales(); });
return uniq(flatten(scales));
};
/**
* 获取 G.Canvas 实例
* @returns G.Canvas 画布实例
*/
View.prototype.getCanvas = function () {
return this.getRootView().canvas;
};
/**
* 获得根节点 view
*/
View.prototype.getRootView = function () {
var v = this;
while (true) {
if (v.parent) {
v = v.parent;
continue;
}
break;
}
return v;
};
/**
* 获取该数据在可视化后对应的画布坐标点
* @param data 原始数据记录
* @returns 对应的画布坐标点
*/
View.prototype.getXY = function (data) {
var coordinate = this.getCoordinate();
var xScales = this.getScalesByDim('x');
var yScales = this.getScalesByDim('y');
var x;
var y;
each(data, function (value, key) {
if (xScales[key]) {
x = xScales[key].scale(value);
}
if (yScales[key]) {
y = yScales[key].scale(value);
}
});
if (!isNil(x) && !isNil(y)) {
return coordinate.convert({ x: x, y: y });
}
};
/**
* 获取 name 对应的 controller 实例
* @param name
*/
View.prototype.getController = function (name) {
return find(this.controllers, function (c) { return c.name === name; });
};
/**
* 显示 point 坐标点对应的 tooltip
* @param point 画布坐标点
* @returns View
*/
View.prototype.showTooltip = function (point) {
var tooltip = this.getController('tooltip');
if (tooltip) {
tooltip.showTooltip(point);
}
return this;
};
/**
* 隐藏 tooltip
* @returns View
*/
View.prototype.hideTooltip = function () {
var tooltip = this.getController('tooltip');
if (tooltip) {
tooltip.hideTooltip();
}
return this;
};
/**
* tooltip 锁定到当前位置不能移动
* @returns View
*/
View.prototype.lockTooltip = function () {
var tooltip = this.getController('tooltip');
if (tooltip) {
tooltip.lockTooltip();
}
return this;
};
/**
* tooltip 锁定解除
* @returns View
*/
View.prototype.unlockTooltip = function () {
var tooltip = this.getController('tooltip');
if (tooltip) {
tooltip.unlockTooltip();
}
return this;
};
/**
* 是否锁定 tooltip
* @returns 是否锁定
*/
View.prototype.isTooltipLocked = function () {
var tooltip = this.getController('tooltip');
return tooltip && tooltip.isTooltipLocked();
};
/**
* 获取当前 point 对应的 tooltip 数据项
* @param point 坐标点
* @returns tooltip 数据项
*/
View.prototype.getTooltipItems = function (point) {
var tooltip = this.getController('tooltip');
return tooltip ? tooltip.getTooltipItems(point) : [];
};
/**
* 获取逼近的点的数据集合
* @param point 当前坐标点
* @returns 数据
*/
View.prototype.getSnapRecords = function (point) {
var geometries = this.geometries;
var rst = [];
for (var i = 0, len = geometries.length; i < len; i++) {
var geom = geometries[i];
var dataArray = geom.dataArray;
geom.sort(dataArray); // 先进行排序,便于 tooltip 查找
var record = void 0;
for (var j = 0, dataLen = dataArray.length; j < dataLen; j++) {
var data = dataArray[j];
record = findDataByPoint(point, data, geom);
if (record) {
rst.push(record);
}
}
}
// 同样递归处理子 views
var views = this.views;
for (var i = 0, len = views.length; i < len; i++) {
var view = views[i];
var snapRecords = view.getSnapRecords(point);
rst = rst.concat(snapRecords);
}
return rst;
};
/**
* 获取所有的 pure component 组件用于布局
*/
View.prototype.getComponents = function () {
var components = [];
var controllers = this.controllers;
for (var i = 0, len = controllers.length; i < len; i++) {
var controller = controllers[i];
components = components.concat(controller.getComponents());
}
return components;
};
/**
* data 数据进行过滤
* @param data
* @returns 过滤之后的数据
*/
View.prototype.filterData = function (data) {
var filters = this.options.filters;
// 不存在 filters则不需要进行数据过滤
if (size(filters) === 0) {
return data;
}
// 存在过滤器,则逐个执行过滤,过滤器之间是 与 的关系
return filter(data, function (datum, idx) {
// 所有的 filter 字段
var fields = Object.keys(filters);
// 所有的条件都通过,才算通过
return fields.every(function (field) {
var condition = filters[field];
// condition 返回 true则保留
return condition(datum[field], datum, idx);
});
});
};
/**
* 对某一个字段进行过滤
* @param field
* @param data
*/
View.prototype.filterFieldData = function (field, data) {
var filters = this.options.filters;
var condition = get(filters, field);
if (isUndefined(condition)) {
return data;
}
return data.filter(function (datum, idx) { return condition(datum[field], datum, idx); });
};
/**
* 调整 coordinate 的坐标范围
*/
View.prototype.adjustCoordinate = function () {
var _a = this.getCoordinate(), curStart = _a.start, curEnd = _a.end;
var start = this.coordinateBBox.bl;
var end = this.coordinateBBox.tr;
// 在 defaultLayoutFn 中只会在 coordinateBBox 发生变化的时候会调用 adjustCoorinate(),所以不用担心被置位
if (isEqual(curStart, start) && isEqual(curEnd, end)) {
this.isCoordinateChanged = false;
// 如果大小没有变化则不更新
return;
}
this.isCoordinateChanged = true;
this.coordinateInstance = this.coordinateController.adjust(start, end);
};
View.prototype.paint = function (isUpdate) {
this.renderDataRecursive(isUpdate);
// 处理 sync scale 的逻辑
this.syncScale();
this.emit(VIEW_LIFE_CIRCLE.BEFORE_PAINT);
this.renderLayoutRecursive(isUpdate);
this.renderPaintRecursive(isUpdate);
this.emit(VIEW_LIFE_CIRCLE.AFTER_PAINT);
this.isDataChanged = false; // 渲染完毕复位
};
/**
* 替换处理 view 的布局最终是计算各个 view coordinateBBox coordinateInstance
* @param isUpdate
*/
View.prototype.renderLayoutRecursive = function (isUpdate) {
// 1. 子 view 大小相对 coordinateBBoxchangeSize 的时候需要重新计算
this.calculateViewBBox();
// 2. 更新 coordinate
this.adjustCoordinate();
// 3. 初始化组件 component
this.initComponents(isUpdate);
// 4. 进行布局,计算 coordinateBBox进行组件布局update 位置
this.doLayout();
// 5. 更新并存储最终的 padding 值
var viewBBox = this.viewBBox;
var coordinateBBox = this.coordinateBBox;
if (!this.padding) {
// 用户未设置 padding 时,将自动计算的 padding 保存至 autoPadding 属性中
this.autoPadding = [
coordinateBBox.tl.y - viewBBox.tl.y,
viewBBox.tr.x - coordinateBBox.tr.x,
viewBBox.bl.y - coordinateBBox.bl.y,
coordinateBBox.tl.x - viewBBox.tl.x
];
}
// 同样递归处理子 views
var views = this.views;
for (var i = 0, len = views.length; i < len; i++) {
var view = views[i];
view.renderLayoutRecursive(isUpdate);
}
};
/**
* 最终递归绘制组件和图形
* @param isUpdate
*/
View.prototype.renderPaintRecursive = function (isUpdate) {
if (this.limitInPlot) {
var middleGroup = this.middleGroup;
var _a = getCoordinateClipCfg(this.coordinateInstance), type = _a.type, attrs = _a.attrs;
middleGroup.setClip({
type: type,
attrs: attrs,
});
}
// 1. 渲染几何标记
this.paintGeometries(isUpdate);
// 2. 绘制组件
this.renderComponents(isUpdate);
// 同样递归处理子 views
var views = this.views;
for (var i = 0, len = views.length; i < len; i++) {
var view = views[i];
view.renderPaintRecursive(isUpdate);
}
};
// end Get 方法
/**
* 创建 scale递归到顶层 view 去创建和缓存 scale
* @param field
* @param data
* @param scaleDef
* @param key
*/
View.prototype.createScale = function (field, data, scaleDef, key) {
// 1. 合并 field 对应的 scaleDef合并原则是底层覆盖顶层就近原则
var currentScaleDef = get(this.options.scales, [field]);
var mergedScaleDef = __assign(__assign({}, currentScaleDef), scaleDef);
// 2. 是否存在父 view在则递归否则创建
if (this.parent) {
return this.parent.createScale(field, data, mergedScaleDef, key);
}
// 3. 在根节点 view 通过 scalePool 创建
return this.scalePool.createScale(field, data, mergedScaleDef, key);
};
/**
* 递归渲染中的数据处理
* @param isUpdate
*/
View.prototype.renderDataRecursive = function (isUpdate) {
// 1. 处理数据
this.doFilterData();
// 2. 创建实例
this.createCoordinate();
// 3. 初始化 Geometry
this.initGeometries(isUpdate);
// 4. 处理分面逻辑,最终都是生成子 view 和 geometry
this.renderFacet(isUpdate);
// 同样递归处理子 views
var views = this.views;
for (var i = 0, len = views.length; i < len; i++) {
var view = views[i];
view.renderDataRecursive(isUpdate);
}
};
/**
* 计算 region计算实际的像素范围坐标
* @private
*/
View.prototype.calculateViewBBox = function () {
var x;
var y;
var width;
var height;
if (this.parent) {
var bbox = this.parent.coordinateBBox;
// 存在 parent 那么就是通过父容器大小计算
x = bbox.x;
y = bbox.y;
width = bbox.width;
height = bbox.height;
}
else {
// 顶层容器,从 canvas 中取值 宽高
x = 0;
y = 0;
width = this.canvas.get('width');
height = this.canvas.get('height');
}
var _a = this.region, start = _a.start, end = _a.end;
// 根据 region 计算当前 view 的 bbox 大小。
var viewBBox = new BBox(x + width * start.x, y + height * start.y, width * (end.x - start.x), height * (end.y - start.y));
if (!this.viewBBox || !this.viewBBox.isEqual(viewBBox)) {
// viewBBox 发生变化的时候进行更新
this.viewBBox = new BBox(x + width * start.x, y + height * start.y, width * (end.x - start.x), height * (end.y - start.y));
// 初始的 coordinate bbox 大小
this.coordinateBBox = this.viewBBox;
}
};
/**
* 初始化事件机制G 4.0 底层内置支持 name:event 的机制那么只要所有组件都有自己的 name 即可
*
* G2 的事件只是获取事件委托然后在 view 嵌套结构中形成事件冒泡机制
* 当前 view 只委托自己 view 中的 Component Geometry 事件并向上冒泡
* @private
*/
View.prototype.initEvents = function () {
// 三层 group 中的 shape 事件都会通过 G 冒泡上来的
this.foregroundGroup.on('*', this.onDelegateEvents);
this.middleGroup.on('*', this.onDelegateEvents);
this.backgroundGroup.on('*', this.onDelegateEvents);
this.canvas.on('*', this.onCanvasEvent);
};
/**
* 初始化插件
*/
View.prototype.initComponentController = function () {
var usedControllers = this.usedControllers;
for (var i = 0, len = usedControllers.length; i < len; i++) {
var controllerName = usedControllers[i];
var Ctor = getComponentController(controllerName);
if (Ctor) {
this.controllers.push(new Ctor(this));
}
}
};
View.prototype.createViewEvent = function (evt) {
var shape = evt.shape, name = evt.name;
var data = shape ? shape.get('origin') : null;
// 事件在 view 嵌套中冒泡(暂不提供阻止冒泡的机制)
var e = new Event(this, evt, data);
e.type = name;
return e;
};
/**
* 处理 PLOT_EVENTS
* plot event 需要处理所有的基础事件并判断是否在画布中然后再决定是否要 emit
* 对于 mouseentermouseleave 比较特殊需要做一下数学比较
* @param e
*/
View.prototype.doPlotEvent = function (e) {
var type = e.type, x = e.x, y = e.y;
var point = { x: x, y: y };
var ALL_EVENTS = [
'mousedown',
'mouseup',
'mousemove',
'mouseleave',
'mousewheel',
'touchstart',
'touchmove',
'touchend',
'touchcancel',
'click',
'dblclick',
'contextmenu',
];
if (ALL_EVENTS.includes(type)) {
var currentInPlot = this.isPointInPlot(point);
if (currentInPlot) {
var TYPE = "plot:" + type; // 组合 plot 事件
e.type = TYPE;
this.emit(TYPE, e);
if (type === 'mouseleave' || type === 'touchend') { // 在plot 内部却离开画布
this.isPreMouseInPlot = false;
}
}
// 对于 mouseenter, mouseleave 的计算处理
if (type === 'mousemove' || type === 'touchmove') {
if (this.isPreMouseInPlot && !currentInPlot) {
if (type === 'mousemove') {
e.type = PLOT_EVENTS.MOUSE_LEAVE;
this.emit(PLOT_EVENTS.MOUSE_LEAVE, e);
}
e.type = PLOT_EVENTS.LEAVE;
this.emit(PLOT_EVENTS.LEAVE, e);
}
else if (!this.isPreMouseInPlot && currentInPlot) {
if (type === 'mousemove') {
e.type = PLOT_EVENTS.MOUSE_ENTER;
this.emit(PLOT_EVENTS.MOUSE_ENTER, e);
}
e.type = PLOT_EVENTS.ENTER;
this.emit(PLOT_EVENTS.ENTER, e);
}
// 赋新的状态值
this.isPreMouseInPlot = currentInPlot;
}
else if (type === 'mouseleave' || type === 'touchend') { // 可能不在 currentInPlot 中
if (this.isPreMouseInPlot) {
if (type === 'mouseleave') {
e.type = PLOT_EVENTS.MOUSE_LEAVE;
this.emit(PLOT_EVENTS.MOUSE_LEAVE, e);
}
e.type = PLOT_EVENTS.LEAVE;
this.emit(PLOT_EVENTS.LEAVE, e);
this.isPreMouseInPlot = false;
}
}
}
};
// view 生命周期 —— 渲染流程
/**
* 处理筛选器筛选数据
* @private
*/
View.prototype.doFilterData = function () {
var data = this.options.data;
this.filteredData = this.filterData(data);
};
/**
* 初始化 Geometries
* @private
*/
View.prototype.initGeometries = function (isUpdate) {
// 初始化图形的之前,先创建 / 更新 scales
this.createOrUpdateScales();
// 实例化 Geometry然后 view 将所有的 scale 管理起来
var coordinate = this.getCoordinate();
var scaleDefs = get(this.options, 'scales', {});
var geometries = this.geometries;
for (var i = 0, len = geometries.length; i < len; i++) {
var geometry = geometries[i];
// 保持 scales 引用不要变化
geometry.scales = this.getGeometryScales();
var cfg = {
coordinate: coordinate,
scaleDefs: scaleDefs,
data: this.filteredData,
theme: this.themeObject,
isDataChanged: this.isDataChanged,
isCoordinateChanged: this.isCoordinateChanged,
};
if (isUpdate) {
// 数据发生更新
geometry.update(cfg);
}
else {
geometry.init(cfg);
}
}
// Geometry 初始化之后,生成了 scale然后进行调整 scale 配置
this.adjustScales();
};
/**
* 根据 Geometry 的所有字段创建 scales
* 如果存在则更新不存在则创建
*/
View.prototype.createOrUpdateScales = function () {
var fields = this.getScaleFields();
var groupedFields = this.getGroupedFields();
var _a = this.getOptions(), data = _a.data, _b = _a.scales, scales = _b === void 0 ? {} : _b;
var filteredData = this.filteredData;
for (var i = 0, len = fields.length; i < len; i++) {
var field = fields[i];
var scaleDef = scales[field];
// 调用方法,递归去创建
var key = this.getScaleKey(field);
this.createScale(field,
// 分组字段的 scale 使用未过滤的数据创建
groupedFields.includes(field) ? data : filteredData, scaleDef, key);
// 缓存从当前 view 创建的 scale key
this.createdScaleKeys.set(key, true);
}
};
/**
* 处理 scale 同步逻辑
*/
View.prototype.syncScale = function () {
// 最终调用 root view 的
this.getRootView().scalePool.sync();
};
/**
* 获得 Geometry 中的 scale 对象
*/
View.prototype.getGeometryScales = function () {
var fields = this.getScaleFields();
var scales = {};
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
scales[field] = this.getScaleByField(field);
}
return scales;
};
View.prototype.getScaleFields = function () {
var fields = [];
var tmpMap = {};
var geometries = this.geometries;
for (var i = 0; i < geometries.length; i++) {
var geometry = geometries[i];
var geometryScales = geometry.getScaleFields();
uniq(geometryScales, fields, tmpMap);
}
return fields;
};
View.prototype.getGroupedFields = function () {
var fields = [];
var tmpMap = {};
var geometries = this.geometries;
for (var i = 0; i < geometries.length; i++) {
var geometry = geometries[i];
var groupFields = geometry.getGroupFields();
uniq(groupFields, fields, tmpMap);
}
return fields;
};
/**
* 调整 scale 配置
* @private
*/
View.prototype.adjustScales = function () {
// 调整目前包括:
// 分类 scale调整 range 范围
this.adjustCategoryScaleRange();
};
/**
* 调整分类 scale range防止超出坐标系外面
* @private
*/
View.prototype.adjustCategoryScaleRange = function () {
var _this = this;
var xyScales = __spreadArrays([this.getXScale()], this.getYScales()).filter(function (e) { return !!e; });
var coordinate = this.getCoordinate();
var scaleOptions = this.options.scales;
each(xyScales, function (scale) {
var field = scale.field, values = scale.values, isCategory = scale.isCategory, isIdentity = scale.isIdentity;
// 分类或者 identity 的 scale 才进行处理
if (isCategory || isIdentity) {
// 存在 value 值,且用户没有配置 range 配置
if (values && !get(scaleOptions, [field, 'range'])) {
var count = values.length;
var range = void 0;
if (count === 1) {
range = [0.5, 1]; // 只有一个分类时,防止计算出现 [0.5,0.5] 的状态
}
else {
var widthRatio = 1;
var offset = 0;
if (isFullCircle(coordinate)) {
if (!coordinate.isTransposed) {
range = [0, 1 - 1 / count];
}
else {
widthRatio = get(_this.theme, 'widthRatio.multiplePie', 1 / 1.3);
offset = (1 / count) * widthRatio;
range = [offset / 2, 1 - offset / 2];
}
}
else {
offset = 1 / count / 2; // 两边留下分类空间的一半
range = [offset, 1 - offset]; // 坐标轴最前面和最后面留下空白防止绘制柱状图时
}
}
// 更新 range
scale.range = range;
}
}
});
};
/**
* 根据 options 配置Geometry 字段配置自动生成 components
* @param isUpdate 是否是更新
* @private
*/
View.prototype.initComponents = function (isUpdate) {
// 先全部清空,然后 render
var controllers = this.controllers;
for (var i = 0; i < controllers.length; i++) {
var controller = controllers[i];
// 更新则走更新逻辑;否则清空载重绘
if (isUpdate) {
controller.update();
}
else {
controller.clear();
controller.render();
}
}
;
};
View.prototype.doLayout = function () {
this.layoutFunc(this);
};
/**
* 创建坐标系
* @private
*/
View.prototype.createCoordinate = function () {
var start = this.coordinateBBox.bl;
var end = this.coordinateBBox.tr;
this.coordinateInstance = this.coordinateController.create(start, end);
};
/**
* 根据 options 配置自动渲染 geometry
* @private
*/
View.prototype.paintGeometries = function (isUpdate) {
var doAnimation = this.options.animate;
// geometry 的 paint 阶段
var coordinate = this.getCoordinate();
var canvasRegion = {
x: this.viewBBox.x,
y: this.viewBBox.y,
minX: this.viewBBox.minX,
minY: this.viewBBox.minY,
maxX: this.viewBBox.maxX,
maxY: this.viewBBox.maxY,
width: this.viewBBox.width,
height: this.viewBBox.height,
};
var geometries = this.geometries;
for (var i = 0; i < geometries.length; i++) {
var geometry = geometries[i];
geometry.coordinate = coordinate;
geometry.canvasRegion = canvasRegion;
if (!doAnimation) {
// 如果 view 不执行动画,那么 view 下所有的 geometry 都不执行动画
geometry.animate(false);
}
geometry.paint(isUpdate);
}
};
/**
* 最后的绘制组件
* @param isUpdate
*/
View.prototype.renderComponents = function (isUpdate) {
// 先全部清空,然后 render
for (var i = 0; i < this.getComponents().length; i++) {
var co = this.getComponents()[i];
co.component.render();
}
};
/**
* 渲染分面会在其中进行数据分面然后进行子 view 创建
* @param isUpdate
*/
View.prototype.renderFacet = function (isUpdate) {
if (this.facetInstance) {
if (isUpdate) {
this.facetInstance.update();
}
else {
this.facetInstance.clear();
// 计算分面数据
this.facetInstance.init();
// 渲染组件和 views
this.facetInstance.render();
}
}
};
View.prototype.initOptions = function () {
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;
// 创建 geometry 实例
for (var i = 0; i < geometries.length; i++) {
var geometryOption = geometries[i];
this.createGeometry(geometryOption);
}
// 创建 interactions 实例
for (var j = 0; j < interactions.length; j++) {
var interactionOption = interactions[j];
var type = interactionOption.type, cfg = interactionOption.cfg;
this.interaction(type, cfg);
}
// 创建 view 实例
for (var k = 0; k < views.length; k++) {
var viewOption = views[k];
this.createView(viewOption);
}
// 设置 annotation
var annotationComponent = this.getController('annotation');
for (var l = 0; l < annotations.length; l++) {
var annotationOption = annotations[l];
annotationComponent.annotation(annotationOption);
}
};
View.prototype.createGeometry = function (geometryOption) {
var type = geometryOption.type, _a = geometryOption.cfg, cfg = _a === void 0 ? {} : _a;
if (this[type]) {
var geometry_1 = this[type](cfg);
each(geometryOption, function (v, k) {
if (isFunction(geometry_1[k])) {
geometry_1[k](v);
}
});
}
};
/**
* scale key 的创建方式
* @param field
*/
View.prototype.getScaleKey = function (field) {
return this.id + "-" + field;
};
return View;
}(Base));
export { View };
/**
* 注册 geometry 组件
* @param name
* @param Ctor
* @returns Geometry
*/
export function registerGeometry(name, Ctor) {
// 语法糖,在 view API 上增加原型方法
View.prototype[name.toLowerCase()] = function (cfg) {
if (cfg === void 0) { cfg = {}; }
var props = __assign({
/** 图形容器 */
container: this.middleGroup.addGroup(), labelsContainer: this.foregroundGroup.addGroup() }, cfg);
var geometry = new Ctor(props);
this.geometries.push(geometry);
return geometry;
};
}
export default View;
//# sourceMappingURL=view.js.map