NuclearDispersionSystem/ant-design-vue-jeecg/node_modules/@antv/g2/esm/chart/controller/legend.js
2023-09-14 14:47:11 +08:00

497 lines
18 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { __extends } from "tslib";
import { deepMix, each, find, get, head, isBoolean, last } from '@antv/util';
import { COMPONENT_MAX_VIEW_PERCENTAGE, COMPONENT_TYPE, DIRECTION, LAYER } from '../../constant';
import { CategoryLegend, ContinuousLegend } from '../../dependents';
import { DEFAULT_ANIMATE_CFG } from '../../animate';
import { BBox } from '../../util/bbox';
import { directionToPosition } from '../../util/direction';
import { omit } from '../../util/helper';
import { getCustomLegendItems, getLegendItems, getLegendLayout } from '../../util/legend';
import { getName } from '../../util/scale';
import { Controller } from './base';
/**
* 从配置中获取单个字段的 legend 配置
* @param legends
* @param field
* @returns the option of one legend field
*/
function getLegendOption(legends, field) {
if (isBoolean(legends)) {
return legends === false ? false : {};
}
return get(legends, [field], legends);
}
function getDirection(legendOption) {
return get(legendOption, 'position', DIRECTION.BOTTOM);
}
/**
* @ignore
* legend Controller
*/
var Legend = /** @class */ (function (_super) {
__extends(Legend, _super);
function Legend(view) {
var _this = _super.call(this, view) || this;
_this.container = _this.view.getLayer(LAYER.FORE).addGroup();
return _this;
}
Object.defineProperty(Legend.prototype, "name", {
get: function () {
return 'legend';
},
enumerable: false,
configurable: true
});
Legend.prototype.init = function () { };
/**
* render the legend component by legend options
*/
Legend.prototype.render = function () {
var _this = this;
this.option = this.view.getOptions().legends;
var doEachLegend = function (geometry, attr, scale) {
var legend = _this.createFieldLegend(geometry, attr, scale);
if (legend) {
legend.component.init();
_this.components.push(legend);
}
};
// 全局自定义图例
if (get(this.option, 'custom')) {
var component = this.createCustomLegend(undefined, undefined, undefined, this.option);
if (component) {
component.init();
var layer = LAYER.FORE;
var direction = getDirection(this.option);
this.components.push({
id: 'global-custom',
component: component,
layer: layer,
direction: direction,
type: COMPONENT_TYPE.LEGEND,
extra: undefined,
});
}
}
else {
// 遍历处理每一个创建逻辑
this.loopLegends(doEachLegend);
}
};
/**
* layout legend
* 计算出 legend 的 direction 位置 x, y
*/
Legend.prototype.layout = function () {
var _this = this;
this.layoutBBox = this.view.viewBBox;
each(this.components, function (co) {
var component = co.component, direction = co.direction;
var layout = getLegendLayout(direction);
var maxSize = _this.getCategoryLegendSizeCfg(layout);
var maxWidth = component.get('maxWidth');
var maxHeight = component.get('maxHeight');
// 先更新 maxSize更新 layoutBBox以便计算正确的 x y
component.update({
maxWidth: Math.min(maxSize.maxWidth, maxWidth || 0),
maxHeight: Math.min(maxSize.maxHeight, maxHeight || 0),
});
var bboxObject = component.getLayoutBBox(); // 这里只需要他的 width、height 信息做位置调整
var bbox = new BBox(bboxObject.x, bboxObject.y, bboxObject.width, bboxObject.height);
var _a = directionToPosition(_this.view.coordinateBBox, bbox, direction), x1 = _a[0], y1 = _a[1];
var _b = directionToPosition(_this.layoutBBox, bbox, direction), x2 = _b[0], y2 = _b[1];
var x = 0;
var y = 0;
// 因为 legend x y 要和 coordinateBBox 对齐,所以要做一个简单的判断
if (direction.startsWith('top') || direction.startsWith('bottom')) {
x = x1;
y = y2;
}
else {
x = x2;
y = y1;
}
// 更新位置
component.update({
x: x,
y: y,
});
_this.layoutBBox = _this.layoutBBox.cut(bbox, direction);
});
};
/**
* legend 的更新逻辑
*/
Legend.prototype.update = function () {
var _this = this;
this.option = this.view.getOptions().legends;
// 已经处理过的 legend
var updated = {};
var eachLegend = function (geometry, attr, scale) {
var id = _this.getId(scale.field);
var existCo = _this.getComponentById(id);
// 存在则 update
if (existCo) {
var cfg = void 0;
var legendOption = getLegendOption(_this.option, scale.field);
// if the legend option is not false, means legend should be created.
if (legendOption !== false) {
if (get(legendOption, 'custom')) {
cfg = _this.getCategoryCfg(geometry, attr, scale, legendOption, true);
}
else {
if (scale.isLinear) {
// linear field, create continuous legend
cfg = _this.getContinuousCfg(geometry, attr, scale, legendOption);
}
else if (scale.isCategory) {
// category field, create category legend
cfg = _this.getCategoryCfg(geometry, attr, scale, legendOption);
}
}
}
// 如果 cfg 为空,则不在 updated 标记,那么会在后面逻辑中删除
if (cfg) {
// omit 掉一些属性,比如 container 等
omit(cfg, ['container']);
existCo.direction = getDirection(legendOption);
existCo.component.update(cfg);
// 标记为新的
updated[id] = true;
}
}
else {
// 不存在则 create
var legend = _this.createFieldLegend(geometry, attr, scale);
if (legend) {
legend.component.init();
_this.components.push(legend);
// 标记为新的
updated[id] = true;
}
}
};
// 全局自定义图例
if (get(this.option, 'custom')) {
var id = 'global-custom';
var existCo = this.getComponentById(id);
if (existCo) {
var customCfg = this.getCategoryCfg(undefined, undefined, undefined, this.option, true);
omit(customCfg, ['container']);
existCo.component.update(customCfg);
updated[id] = true;
}
else {
var component = this.createCustomLegend(undefined, undefined, undefined, this.option);
if (component) {
component.init();
var layer = LAYER.FORE;
var direction = getDirection(this.option);
this.components.push({
id: id,
component: component,
layer: layer,
direction: direction,
type: COMPONENT_TYPE.LEGEND,
extra: undefined,
});
// 标记为更新
updated[id] = true;
}
}
}
else {
// 遍历处理每一个创建逻辑
this.loopLegends(eachLegend);
}
// 处理完成之后,销毁删除的
// 不在处理中的
var components = [];
each(this.getComponents(), function (co) {
if (updated[co.id]) {
components.push(co);
}
else {
co.component.destroy();
}
});
// 更新当前已有的 components
this.components = components;
};
Legend.prototype.clear = function () {
_super.prototype.clear.call(this);
this.container.clear();
};
Legend.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this.container.remove(true);
};
/**
* 递归获取所有的 Geometry
*/
Legend.prototype.getGeometries = function (view) {
var _this = this;
var geometries = view.geometries;
each(view.views, function (v) {
geometries = geometries.concat(_this.getGeometries(v));
});
return geometries;
};
/**
* 遍历 Geometry处理 legend 逻辑
* @param doEach 每个 loop 中的处理方法
*/
Legend.prototype.loopLegends = function (doEach) {
var isRootView = this.view.getRootView() === this.view;
// 非根 view不处理 legend
if (!isRootView) {
return;
}
// 递归 view 中所有的 Geometry进行创建 legend
var geometries = this.getGeometries(this.view);
var looped = {}; // 防止一个字段创建两个 legend
each(geometries, function (geometry) {
var attributes = geometry.getGroupAttributes();
each(attributes, function (attr) {
var scale = attr.getScale(attr.type);
// 如果在视觉通道上映射常量值,如 size(2) shape('circle') 不创建 legend
if (!scale || scale.type === 'identity' || looped[scale.field]) {
return;
}
doEach(geometry, attr, scale);
looped[scale.field] = true;
});
});
};
/**
* 创建一个 legend
* @param geometry
* @param attr
* @param scale
*/
Legend.prototype.createFieldLegend = function (geometry, attr, scale) {
var component;
var legendOption = getLegendOption(this.option, scale.field);
var layer = LAYER.FORE;
var direction = getDirection(legendOption);
// if the legend option is not false, means legend should be created.
if (legendOption !== false) {
if (get(legendOption, 'custom')) {
component = this.createCustomLegend(geometry, attr, scale, legendOption);
}
else {
if (scale.isLinear) {
// linear field, create continuous legend
component = this.createContinuousLegend(geometry, attr, scale, legendOption);
}
else if (scale.isCategory) {
// category field, create category legend
component = this.createCategoryLegend(geometry, attr, scale, legendOption);
}
}
}
if (component) {
component.set('field', scale.field);
return {
id: this.getId(scale.field),
component: component,
layer: layer,
direction: direction,
type: COMPONENT_TYPE.LEGEND,
extra: { scale: scale },
};
}
};
/**
* 自定义图例使用 category 图例去渲染
* @param geometry
* @param attr
* @param scale
* @param legendOption
*/
Legend.prototype.createCustomLegend = function (geometry, attr, scale, legendOption) {
// 直接使用 分类图例渲染
var cfg = this.getCategoryCfg(geometry, attr, scale, legendOption, true);
return new CategoryLegend(cfg);
};
/**
* 创建连续图例
* @param geometry
* @param attr
* @param scale
* @param legendOption
*/
Legend.prototype.createContinuousLegend = function (geometry, attr, scale, legendOption) {
var cfg = this.getContinuousCfg(geometry, attr, scale, legendOption);
return new ContinuousLegend(cfg);
};
/**
* 创建分类图例
* @param geometry
* @param attr
* @param scale
* @param legendOption
*/
Legend.prototype.createCategoryLegend = function (geometry, attr, scale, legendOption) {
var cfg = this.getCategoryCfg(geometry, attr, scale, legendOption);
return new CategoryLegend(cfg);
};
/**
* 获得连续图例的配置
* @param geometry
* @param attr
* @param scale
* @param legendOption
*/
Legend.prototype.getContinuousCfg = function (geometry, attr, scale, legendOption) {
var ticks = scale.getTicks();
var containMin = find(ticks, function (tick) { return tick.value === 0; });
var containMax = find(ticks, function (tick) { return tick.value === 1; });
var items = ticks.map(function (tick) {
var value = tick.value, tickValue = tick.tickValue;
var attrValue = attr.mapping(scale.invert(value)).join('');
return {
value: tickValue,
attrValue: attrValue,
color: attrValue,
scaleValue: value,
};
});
if (!containMin) {
items.push({
value: scale.min,
attrValue: attr.mapping(scale.invert(0)).join(''),
color: attr.mapping(scale.invert(0)).join(''),
scaleValue: 0,
});
}
if (!containMax) {
items.push({
value: scale.max,
attrValue: attr.mapping(scale.invert(1)).join(''),
color: attr.mapping(scale.invert(1)).join(''),
scaleValue: 1,
});
}
// 排序
items.sort(function (a, b) { return a.value - b.value; });
// 跟 attr 相关的配置
// size color 区别的配置
var attrLegendCfg = {
min: head(items).value,
max: last(items).value,
colors: [],
rail: {
type: attr.type,
},
track: {},
};
if (attr.type === 'size') {
attrLegendCfg.track = {
style: {
// size 的选中前景色,对于 color则直接使用 color 标识
// @ts-ignore
fill: attr.type === 'size' ? this.view.getTheme().defaultColor : undefined,
},
};
}
if (attr.type === 'color') {
attrLegendCfg.colors = items.map(function (item) { return item.attrValue; });
}
var container = this.container;
// if position is not set, use top as default
var direction = getDirection(legendOption);
var layout = getLegendLayout(direction);
var title = get(legendOption, 'title');
if (title) {
title = deepMix({
text: getName(scale),
}, title);
}
// 基础配置,从当前数据中读到的配置
attrLegendCfg.container = container;
attrLegendCfg.layout = layout;
attrLegendCfg.title = title;
attrLegendCfg.animateOption = DEFAULT_ANIMATE_CFG;
// @ts-ignore
return this.mergeLegendCfg(attrLegendCfg, legendOption, 'continuous');
};
/**
* 获取分类图例的配置项
* @param geometry
* @param attr
* @param scale
* @param custom
* @param legendOption
*/
Legend.prototype.getCategoryCfg = function (geometry, attr, scale, legendOption, custom) {
var container = this.container;
// if position is not set, use top as default
var direction = get(legendOption, 'position', DIRECTION.BOTTOM);
// the default marker style
var themeMarker = get(this.view.getTheme(), ['components', 'legend', direction, 'marker']);
var userMarker = get(legendOption, 'marker');
var layout = getLegendLayout(direction);
var items = custom ?
getCustomLegendItems(themeMarker, userMarker, legendOption.items) :
getLegendItems(this.view, geometry, attr, themeMarker, userMarker);
var title = get(legendOption, 'title');
if (title) {
title = deepMix({
text: scale ? getName(scale) : '',
}, title);
}
var baseCfg = this.getCategoryLegendSizeCfg(layout);
baseCfg.container = container;
baseCfg.layout = layout;
baseCfg.items = items;
baseCfg.title = title;
baseCfg.animateOption = DEFAULT_ANIMATE_CFG;
var categoryCfg = this.mergeLegendCfg(baseCfg, legendOption, direction);
if (categoryCfg.reversed) {
// 图例项需要逆序
categoryCfg.items.reverse();
}
return categoryCfg;
};
/**
* get legend config, use option > suggestion > theme
* @param baseCfg
* @param legendOption
* @param direction
*/
Legend.prototype.mergeLegendCfg = function (baseCfg, legendOption, direction) {
var themeObject = get(this.view.getTheme(), ['components', 'legend', direction], {});
return deepMix({}, themeObject, baseCfg, legendOption);
};
/**
* 生成 id
* @param key
*/
Legend.prototype.getId = function (key) {
return this.name + "-" + key;
};
/**
* 根据 id 来获取组件
* @param id
*/
Legend.prototype.getComponentById = function (id) {
return find(this.components, function (co) { return co.id === id; });
};
Legend.prototype.getCategoryLegendSizeCfg = function (layout) {
var _a = this.view.viewBBox, vw = _a.width, vh = _a.height;
var _b = this.view.coordinateBBox, cw = _b.width, ch = _b.height;
return layout === 'vertical'
? {
maxWidth: vw * COMPONENT_MAX_VIEW_PERCENTAGE,
maxHeight: ch,
}
: {
maxWidth: cw,
maxHeight: vh * COMPONENT_MAX_VIEW_PERCENTAGE,
};
};
return Legend;
}(Controller));
export default Legend;
//# sourceMappingURL=legend.js.map