1425 lines
34 KiB
Java
1425 lines
34 KiB
Java
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
||
|
||
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
|
||
|
||
/**
|
||
* @fileOverview 所有 Geometry 的基类
|
||
* @author dxq613@gmail.com
|
||
*/
|
||
var Attr = require('@antv/attr/lib');
|
||
|
||
var Adjust = require('@antv/adjust/lib');
|
||
|
||
var Base = require('../base');
|
||
|
||
var Util = require('../util');
|
||
|
||
var Global = require('../global');
|
||
|
||
var Labels = require('./label/index');
|
||
|
||
var Shape = require('./shape/shape');
|
||
|
||
var TooltipMixin = require('./mixin/tooltip');
|
||
|
||
var ActiveMixin = require('./mixin/active');
|
||
|
||
var SelectMixin = require('./mixin/select');
|
||
|
||
var parseFields = require('./util/parse-fields');
|
||
|
||
var GROUP_ATTRS = ['color', 'shape', 'size'];
|
||
var FIELD_ORIGIN = '_origin'; // 转换成对象的数组 [{type: 'adjust'}]
|
||
|
||
function parseAdjusts(adjusts) {
|
||
// 如果是字符串或者对象转换成数组
|
||
if (Util.isString(adjusts) || Util.isPlainObject(adjusts)) {
|
||
adjusts = [adjusts];
|
||
}
|
||
|
||
Util.each(adjusts, function (adjust, index) {
|
||
if (!Util.isObject(adjust)) {
|
||
adjusts[index] = {
|
||
type: adjust
|
||
};
|
||
}
|
||
});
|
||
return adjusts;
|
||
}
|
||
/**
|
||
* 几何标记
|
||
* @class Geom
|
||
*/
|
||
|
||
|
||
var GeomBase = /*#__PURE__*/function (_Base) {
|
||
_inheritsLoose(GeomBase, _Base);
|
||
|
||
var _proto = GeomBase.prototype;
|
||
|
||
/**
|
||
* 获取默认的配置属性
|
||
* @protected
|
||
* @return {Object} 默认属性
|
||
*/
|
||
_proto.getDefaultCfg = function getDefaultCfg() {
|
||
return {
|
||
/**
|
||
* 标记 _id 用于区分执行动画
|
||
* @type {String}
|
||
*/
|
||
_id: null,
|
||
|
||
/**
|
||
* 类型
|
||
* @type {String}
|
||
*/
|
||
type: 'base',
|
||
|
||
/**
|
||
* 坐标系
|
||
* @type {Object}
|
||
*/
|
||
coord: null,
|
||
|
||
/**
|
||
* 属性映射集
|
||
* @protected
|
||
* @type {Object}
|
||
*/
|
||
attrs: {},
|
||
|
||
/**
|
||
* 所属的View
|
||
* @type {View}
|
||
*/
|
||
view: null,
|
||
|
||
/**
|
||
* 几何标记显示的数据
|
||
* @type {Array}
|
||
*/
|
||
data: [],
|
||
|
||
/**
|
||
* 相关的度量
|
||
* @type {Object}
|
||
*/
|
||
scales: {},
|
||
|
||
/**
|
||
* 绘图容器
|
||
* @type {Object}
|
||
*/
|
||
container: null,
|
||
|
||
/**
|
||
* 文本容器
|
||
* @type {Object}
|
||
*/
|
||
labelContainer: null,
|
||
|
||
/**
|
||
* 图形容器
|
||
* @type {Object}
|
||
*/
|
||
shapeContainer: null,
|
||
|
||
/**
|
||
* 几何标记的一些配置项,用于延迟生成图表
|
||
* @type {Object}
|
||
*/
|
||
attrOptions: {},
|
||
// 样式配置项
|
||
styleOptions: null,
|
||
// 选中时的配置项
|
||
selectedOptions: null,
|
||
// active 时的配置项
|
||
activedOptions: null,
|
||
|
||
/**
|
||
* 某些类存在默认的adjust,不能更改 adjust
|
||
* @type {Boolean}
|
||
*/
|
||
hasDefaultAdjust: false,
|
||
// 数据调整类型
|
||
adjusts: null,
|
||
|
||
/**
|
||
* 使用形状的类型
|
||
* @protected
|
||
* @type {String}
|
||
*/
|
||
shapeType: null,
|
||
|
||
/**
|
||
* 是否生成多个点来绘制图形
|
||
* @protected
|
||
* @type {Boolean}
|
||
*/
|
||
generatePoints: false,
|
||
|
||
/**
|
||
* 数据是否进行排序
|
||
* @type {Boolean}
|
||
*/
|
||
sortable: false,
|
||
labelCfg: null,
|
||
|
||
/**
|
||
* 是否共享 tooltip
|
||
* @type {Boolean}
|
||
*/
|
||
shareTooltip: true,
|
||
tooltipCfg: null,
|
||
|
||
/**
|
||
* 是否执行动画,默认执行
|
||
* @type {Boolean}
|
||
*/
|
||
animate: true,
|
||
|
||
/**
|
||
* 动画配置
|
||
* @type {[type]}
|
||
*/
|
||
animateCfg: null,
|
||
visible: true
|
||
};
|
||
};
|
||
|
||
function GeomBase(cfg) {
|
||
var _this;
|
||
|
||
_this = _Base.call(this, cfg) || this;
|
||
_this.viewTheme = _this.get('viewTheme');
|
||
Util.assign(_assertThisInitialized(_this), TooltipMixin, ActiveMixin, SelectMixin);
|
||
|
||
if (_this.get('container')) {
|
||
_this._initContainer();
|
||
}
|
||
|
||
_this._initOptions();
|
||
|
||
return _this;
|
||
} // 初始化时对配置项的格式化
|
||
|
||
|
||
_proto._initOptions = function _initOptions() {
|
||
var adjusts = this.get('adjusts');
|
||
|
||
if (adjusts) {
|
||
adjusts = parseAdjusts(adjusts);
|
||
this.set('adjusts', adjusts);
|
||
}
|
||
};
|
||
|
||
_proto._createScale = function _createScale(field, data) {
|
||
var scales = this.get('scales');
|
||
var scale = scales[field];
|
||
|
||
if (!scale) {
|
||
scale = this.get('view').createScale(field, data);
|
||
scales[field] = scale;
|
||
}
|
||
|
||
return scale;
|
||
};
|
||
|
||
_proto._setAttrOptions = function _setAttrOptions(attrName, attrCfg) {
|
||
var options = this.get('attrOptions');
|
||
options[attrName] = attrCfg;
|
||
};
|
||
|
||
_proto._createAttrOption = function _createAttrOption(attrName, field, cfg, defaultValues) {
|
||
var attrCfg = {};
|
||
attrCfg.field = field;
|
||
|
||
if (cfg) {
|
||
if (Util.isFunction(cfg)) {
|
||
attrCfg.callback = cfg;
|
||
} else {
|
||
attrCfg.values = cfg;
|
||
}
|
||
} else if (attrName !== 'color') {
|
||
attrCfg.values = defaultValues;
|
||
}
|
||
|
||
this._setAttrOptions(attrName, attrCfg);
|
||
}
|
||
/**
|
||
* 位置属性映射
|
||
* @chainable
|
||
* @param {String} field 字段名
|
||
* @return {Geom} geom 当前几何标记
|
||
*/
|
||
;
|
||
|
||
_proto.position = function position(field) {
|
||
this._setAttrOptions('position', {
|
||
field: field
|
||
});
|
||
|
||
return this;
|
||
}
|
||
/**
|
||
* 颜色属性映射
|
||
* @chainable
|
||
* @param {String} field 字段名
|
||
* @param {Array|Function} values 颜色的数组或者回调函数
|
||
* @return {Geom} geom 当前几何标记
|
||
*/
|
||
;
|
||
|
||
_proto.color = function color(field, values) {
|
||
var viewTheme = this.viewTheme || Global;
|
||
|
||
this._createAttrOption('color', field, values, viewTheme.colors);
|
||
|
||
return this;
|
||
}
|
||
/**
|
||
* 大小属性映射
|
||
* @chainable
|
||
* @param {String} field 字段名
|
||
* @param {Array|Function} values 大小的数组或者回调函数
|
||
* @return {Geom} geom 当前几何标记
|
||
*/
|
||
;
|
||
|
||
_proto.size = function size(field, values) {
|
||
var viewTheme = this.viewTheme || Global;
|
||
|
||
this._createAttrOption('size', field, values, viewTheme.sizes);
|
||
|
||
return this;
|
||
}
|
||
/**
|
||
* 形状属性映射
|
||
* @chainable
|
||
* @param {String} field 字段名
|
||
* @param {Array|Function} values 大小的数组或者回调函数
|
||
* @return {Geom} geom 当前几何标记
|
||
*/
|
||
;
|
||
|
||
_proto.shape = function shape(field, values) {
|
||
var viewTheme = this.viewTheme || Global;
|
||
var type = this.get('type');
|
||
var shapes = viewTheme.shapes[type] || [];
|
||
|
||
this._createAttrOption('shape', field, values, shapes);
|
||
|
||
return this;
|
||
}
|
||
/**
|
||
* 透明度属性映射
|
||
* @chainable
|
||
* @param {String} field 字段名
|
||
* @param {Array|Function} values 透明度的数组或者回调函数
|
||
* @return {Geom} geom 当前几何标记
|
||
*/
|
||
;
|
||
|
||
_proto.opacity = function opacity(field, values) {
|
||
var viewTheme = this.viewTheme || Global;
|
||
|
||
this._createAttrOption('opacity', field, values, viewTheme.opacities);
|
||
|
||
return this;
|
||
};
|
||
|
||
_proto.style = function style(field, cfg) {
|
||
var styleOptions = this.get('styleOptions');
|
||
|
||
if (!styleOptions) {
|
||
styleOptions = {};
|
||
this.set('styleOptions', styleOptions);
|
||
}
|
||
|
||
if (Util.isObject(field)) {
|
||
cfg = field;
|
||
field = null;
|
||
}
|
||
|
||
var fields;
|
||
|
||
if (field) {
|
||
fields = parseFields(field);
|
||
}
|
||
|
||
styleOptions.fields = fields;
|
||
styleOptions.style = cfg;
|
||
return this;
|
||
};
|
||
|
||
_proto.label = function label(field, callback, cfg) {
|
||
var self = this;
|
||
var labelCfg = self.get('labelCfg'); // const scales = Util.map(self.get('labelCfg').fields, field => self._createScale(field));
|
||
|
||
if (!labelCfg) {
|
||
labelCfg = {};
|
||
self.set('labelCfg', labelCfg);
|
||
}
|
||
|
||
var fields;
|
||
|
||
if (field) {
|
||
fields = parseFields(field);
|
||
}
|
||
|
||
labelCfg.fields = fields; // 如果存在回调函数
|
||
|
||
if (Util.isFunction(callback)) {
|
||
if (!cfg) {
|
||
cfg = {};
|
||
}
|
||
|
||
labelCfg.callback = callback;
|
||
} else if (Util.isObject(callback)) {
|
||
// 如果没有设置回调函数
|
||
cfg = callback;
|
||
}
|
||
|
||
labelCfg.globalCfg = cfg;
|
||
return this;
|
||
};
|
||
|
||
_proto.tooltip = function tooltip(field, cfg) {
|
||
var tooltipCfg = this.get('tooltipCfg');
|
||
|
||
if (!tooltipCfg) {
|
||
tooltipCfg = {};
|
||
}
|
||
|
||
if (field === false) {
|
||
// geom 关闭 tooltip
|
||
this.set('tooltipCfg', false);
|
||
} else {
|
||
var tooltipFields;
|
||
|
||
if (field) {
|
||
tooltipFields = parseFields(field);
|
||
}
|
||
|
||
tooltipCfg.fields = tooltipFields;
|
||
tooltipCfg.cfg = cfg;
|
||
}
|
||
|
||
this.set('tooltipCfg', tooltipCfg);
|
||
return this;
|
||
};
|
||
|
||
_proto.animate = function animate(cfg) {
|
||
this.set('animateCfg', cfg);
|
||
return this;
|
||
}
|
||
/**
|
||
* 是否允许使用默认的图形激活交互
|
||
* @param {Boolean} enable 是否允许激活开关
|
||
* @param {Object} cfg 激活的配置项
|
||
* @return {Geom} 返回 geom 自身
|
||
*/
|
||
;
|
||
|
||
_proto.active = function active(enable, cfg) {
|
||
if (enable === false) {
|
||
this.set('allowActive', false);
|
||
} else if (Util.isObject(enable)) {
|
||
this.set('allowActive', true);
|
||
this.set('activedOptions', enable);
|
||
} else {
|
||
this.set('allowActive', true);
|
||
this.set('activedOptions', cfg);
|
||
}
|
||
|
||
return this;
|
||
}
|
||
/**
|
||
* 对 geometry 进行数据调整
|
||
* @chainable
|
||
* @param {String|Array|null} adjusts 数据调整的类型
|
||
* @return {Object} geometry 对象
|
||
*/
|
||
;
|
||
|
||
_proto.adjust = function adjust(adjusts) {
|
||
if (!this.get('hasDefaultAdjust')) {
|
||
if (adjusts) {
|
||
adjusts = parseAdjusts(adjusts);
|
||
}
|
||
|
||
this.set('adjusts', adjusts);
|
||
}
|
||
|
||
return this;
|
||
}
|
||
/**
|
||
* 设置图形的选中模式
|
||
* @param {Boolean|Object} enable 布尔类型用于模式开关,对象类型用于配置
|
||
* @param {Object} cfg 选中配置项
|
||
* @return {Geom} 返回 geom 自身
|
||
*/
|
||
;
|
||
|
||
_proto.select = function select(enable, cfg) {
|
||
if (enable === false) {
|
||
this.set('allowSelect', false);
|
||
} else if (Util.isObject(enable)) {
|
||
this.set('allowSelect', true);
|
||
this.set('selectedOptions', enable);
|
||
} else {
|
||
this.set('allowSelect', true);
|
||
this.set('selectedOptions', cfg);
|
||
}
|
||
|
||
return this;
|
||
};
|
||
|
||
_proto.hasAdjust = function hasAdjust(adjustType) {
|
||
var self = this;
|
||
var adjusts = self.get('adjusts');
|
||
|
||
if (!adjustType) {
|
||
return false;
|
||
}
|
||
|
||
var rst = false;
|
||
Util.each(adjusts, function (adjust) {
|
||
if (adjust.type === adjustType) {
|
||
rst = true;
|
||
return false;
|
||
}
|
||
});
|
||
return rst;
|
||
};
|
||
|
||
_proto.hasStack = function hasStack() {
|
||
var isStacked = this.get('isStacked');
|
||
|
||
if (Util.isNil(isStacked)) {
|
||
isStacked = this.hasAdjust('stack');
|
||
this.set('isStacked', isStacked);
|
||
}
|
||
|
||
return isStacked;
|
||
};
|
||
|
||
_proto.isInCircle = function isInCircle() {
|
||
var coord = this.get('coord');
|
||
return coord && coord.isPolar;
|
||
};
|
||
|
||
_proto._initContainer = function _initContainer() {
|
||
var self = this;
|
||
var shapeContainer = self.get('shapeContainer');
|
||
|
||
if (!shapeContainer) {
|
||
var container = self.get('container');
|
||
var view = self.get('view');
|
||
var viewId = view && view.get('_id');
|
||
shapeContainer = container.addGroup({
|
||
viewId: viewId,
|
||
visible: self.get('visible')
|
||
});
|
||
self.set('shapeContainer', shapeContainer);
|
||
}
|
||
};
|
||
|
||
_proto.init = function init() {
|
||
var self = this;
|
||
|
||
self._initContainer();
|
||
|
||
self._initAttrs();
|
||
|
||
if (self.get('tooltipCfg') && self.get('tooltipCfg').fields) {
|
||
var tooltipFields = self.get('tooltipCfg').fields;
|
||
Util.each(tooltipFields, function (field) {
|
||
self._createScale(field);
|
||
});
|
||
}
|
||
|
||
var dataArray = self._processData();
|
||
|
||
if (self.get('adjusts')) {
|
||
self._adjust(dataArray);
|
||
}
|
||
|
||
self.set('dataArray', dataArray);
|
||
} // step 1: init attrs
|
||
;
|
||
|
||
_proto._initAttrs = function _initAttrs() {
|
||
var self = this;
|
||
var attrs = self.get('attrs');
|
||
var attrOptions = self.get('attrOptions');
|
||
var coord = self.get('coord');
|
||
var viewTheme = self.viewTheme || Global;
|
||
var isPie = false;
|
||
|
||
for (var type in attrOptions) {
|
||
if (attrOptions.hasOwnProperty(type)) {
|
||
var option = attrOptions[type];
|
||
var className = Util.upperFirst(type);
|
||
var fields = parseFields(option.field);
|
||
|
||
if (type === 'position') {
|
||
option.coord = coord; // 饼图坐标系下,填充一维
|
||
|
||
if (fields.length === 1 && coord.type === 'theta') {
|
||
fields.unshift('1');
|
||
isPie = true;
|
||
}
|
||
}
|
||
|
||
var scales = [];
|
||
|
||
for (var i = 0; i < fields.length; i++) {
|
||
var field = fields[i];
|
||
|
||
var scale = self._createScale(field);
|
||
|
||
if (type === 'color' && Util.isNil(option.values)) {
|
||
// 设置 color 的默认色值
|
||
if (scale.values.length <= 8) {
|
||
option.values = isPie ? viewTheme.colors_pie : viewTheme.colors;
|
||
} else if (scale.values.length <= 16) {
|
||
option.values = isPie ? viewTheme.colors_pie_16 : viewTheme.colors_16;
|
||
} else {
|
||
option.values = viewTheme.colors_24;
|
||
}
|
||
|
||
if (Util.isNil(option.values)) {
|
||
option.values = viewTheme.colors; // 防止主题没有声明诸如 colors_pie 的属性
|
||
}
|
||
}
|
||
|
||
scales.push(scale);
|
||
} // 饼图需要填充满整个空间
|
||
|
||
|
||
if (coord.type === 'theta' && type === 'position' && scales.length > 1) {
|
||
var yScale = scales[1];
|
||
var min = 0;
|
||
var max = Math.max.apply(null, yScale.values);
|
||
|
||
if (!isFinite(max)) {
|
||
max = 1;
|
||
}
|
||
|
||
yScale.change({
|
||
nice: false,
|
||
min: min,
|
||
max: max
|
||
});
|
||
}
|
||
|
||
option.scales = scales;
|
||
var attr = new Attr[className](option);
|
||
attrs[type] = attr;
|
||
}
|
||
}
|
||
} // step 2: 处理数据
|
||
;
|
||
|
||
_proto._processData = function _processData() {
|
||
var self = this;
|
||
var data = this.get('data');
|
||
var dataArray = [];
|
||
|
||
var groupedArray = this._groupData(data);
|
||
|
||
for (var i = 0; i < groupedArray.length; i++) {
|
||
var subData = groupedArray[i];
|
||
|
||
var tempData = self._saveOrigin(subData);
|
||
|
||
dataArray.push(self._numberic(tempData));
|
||
}
|
||
|
||
return dataArray;
|
||
} // step 2.1 数据分组
|
||
;
|
||
|
||
_proto._groupData = function _groupData(data) {
|
||
var groupScales = this._getGroupScales();
|
||
|
||
var fields = groupScales.map(function (scale) {
|
||
return scale.field;
|
||
});
|
||
return Util.Array.group(data, fields);
|
||
} // step 2.2 数据调整前保存原始数据
|
||
;
|
||
|
||
_proto._saveOrigin = function _saveOrigin(data) {
|
||
var rst = [];
|
||
|
||
for (var i = 0; i < data.length; i++) {
|
||
var origin = data[i];
|
||
var obj = {};
|
||
|
||
for (var k in origin) {
|
||
obj[k] = origin[k];
|
||
} // const obj = Util.mix({}, origin);
|
||
|
||
|
||
obj[FIELD_ORIGIN] = origin;
|
||
rst.push(obj);
|
||
}
|
||
|
||
return rst;
|
||
} // step 2.3 将分类数据翻译成数据, 仅对位置相关的度量进行数字化处理
|
||
;
|
||
|
||
_proto._numberic = function _numberic(data) {
|
||
var positionAttr = this.getAttr('position');
|
||
var scales = positionAttr.scales;
|
||
var result = [];
|
||
|
||
for (var j = 0; j < data.length; j++) {
|
||
var obj = data[j];
|
||
var isValidate = true;
|
||
|
||
for (var i = 0; i < Math.min(2, scales.length); i++) {
|
||
var scale = scales[i];
|
||
|
||
if (scale.isCategory) {
|
||
var field = scale.field;
|
||
obj[field] = scale.translate(obj[field]);
|
||
|
||
if (Number.isNaN(obj[field])) {
|
||
// 当分类为 NaN 时,说明该条数据不在定义域内,需要过滤掉
|
||
isValidate = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (isValidate) {
|
||
result.push(obj);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
};
|
||
|
||
_proto._getGroupScales = function _getGroupScales() {
|
||
var self = this;
|
||
var scales = self.get('groupScales');
|
||
|
||
if (!scales) {
|
||
scales = [];
|
||
var attrs = self.get('attrs');
|
||
Util.each(attrs, function (attr) {
|
||
if (GROUP_ATTRS.includes(attr.type)) {
|
||
var attrScales = attr.scales;
|
||
Util.each(attrScales, function (scale) {
|
||
if (scale.isCategory && Util.indexOf(scales, scale) === -1) {
|
||
scales.push(scale);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
self.set('groupScales', scales);
|
||
}
|
||
|
||
return scales;
|
||
};
|
||
|
||
_proto._updateStackRange = function _updateStackRange(field, scale, dataArray) {
|
||
var mergeArray = Util.Array.merge(dataArray);
|
||
var min = scale.min;
|
||
var max = scale.max;
|
||
|
||
for (var i = 0; i < mergeArray.length; i++) {
|
||
var obj = mergeArray[i]; // 过滤掉非法数据
|
||
|
||
if (!Util.isArray(obj[field])) {
|
||
continue;
|
||
}
|
||
|
||
var tmpMin = Math.min.apply(null, obj[field]);
|
||
var tmpMax = Math.max.apply(null, obj[field]);
|
||
|
||
if (tmpMin < min) {
|
||
min = tmpMin;
|
||
}
|
||
|
||
if (tmpMax > max) {
|
||
max = tmpMax;
|
||
}
|
||
}
|
||
|
||
if (min < scale.min || max > scale.max) {
|
||
scale.change({
|
||
min: min,
|
||
max: max
|
||
});
|
||
}
|
||
} // step 2.2 调整数据
|
||
;
|
||
|
||
_proto._adjust = function _adjust(dataArray) {
|
||
// 当数据为空的时候,就不需要对数据进行调整了
|
||
if (!dataArray || !dataArray.length) {
|
||
return;
|
||
}
|
||
|
||
var self = this;
|
||
var adjusts = self.get('adjusts');
|
||
var viewTheme = this.viewTheme || Global;
|
||
var yScale = self.getYScale();
|
||
var xScale = self.getXScale();
|
||
var xField = xScale.field;
|
||
var yField = yScale ? yScale.field : null;
|
||
Util.each(adjusts, function (adjust) {
|
||
var adjustCfg = Util.mix({
|
||
xField: xField,
|
||
yField: yField
|
||
}, adjust);
|
||
var adjustType = Util.upperFirst(adjust.type);
|
||
|
||
if (adjustType === 'Dodge') {
|
||
var adjustNames = [];
|
||
|
||
if (xScale.isCategory || xScale.isIdentity) {
|
||
adjustNames.push('x');
|
||
} else if (!yScale) {
|
||
adjustNames.push('y');
|
||
} else {
|
||
throw new Error('dodge is not support linear attribute, please use category attribute!');
|
||
}
|
||
|
||
adjustCfg.adjustNames = adjustNames;
|
||
adjustCfg.dodgeRatio = adjustCfg.dodgeRatio || viewTheme.widthRatio.column;
|
||
/* if (self.isInCircle()) {
|
||
adjustCfg.dodgeRatio = 1;
|
||
adjustCfg.marginRatio = 0;
|
||
}*/
|
||
} else if (adjustType === 'Stack') {
|
||
var coord = self.get('coord');
|
||
|
||
if (!yScale) {
|
||
// 一维的情况下获取高度和默认size
|
||
adjustCfg.height = coord.getHeight();
|
||
var size = self.getDefaultValue('size') || 3;
|
||
adjustCfg.size = size;
|
||
} // 不进行 transpose 时,用户又没有设置这个参数时,默认从上向下
|
||
|
||
|
||
if (!coord.isTransposed && Util.isNil(adjustCfg.reverseOrder)) {
|
||
adjustCfg.reverseOrder = true;
|
||
}
|
||
}
|
||
|
||
var adjustElement = new Adjust[adjustType](adjustCfg);
|
||
adjustElement.processAdjust(dataArray);
|
||
|
||
if (adjustType === 'Stack' && yScale) {
|
||
self._updateStackRange(yField, yScale, dataArray);
|
||
}
|
||
});
|
||
}
|
||
/**
|
||
* @internal 设置coord,通常外部容器变化时,coord 会发生变化
|
||
* @param {Object} coord 坐标系
|
||
*/
|
||
;
|
||
|
||
_proto.setCoord = function setCoord(coord) {
|
||
this.set('coord', coord);
|
||
var position = this.getAttr('position');
|
||
var shapeContainer = this.get('shapeContainer');
|
||
shapeContainer.setMatrix(coord.matrix);
|
||
|
||
if (position) {
|
||
position.coord = coord;
|
||
}
|
||
} // step 3 绘制
|
||
;
|
||
|
||
_proto.paint = function paint() {
|
||
var self = this;
|
||
var dataArray = self.get('dataArray');
|
||
var mappedArray = [];
|
||
var shapeFactory = self.getShapeFactory();
|
||
shapeFactory.setCoord(self.get('coord'));
|
||
self.set('shapeFactory', shapeFactory);
|
||
var shapeContainer = self.get('shapeContainer');
|
||
|
||
self._beforeMapping(dataArray);
|
||
|
||
for (var i = 0; i < dataArray.length; i++) {
|
||
var data = dataArray[i];
|
||
var index = i;
|
||
data = self._mapping(data);
|
||
mappedArray.push(data);
|
||
self.draw(data, shapeContainer, shapeFactory, index);
|
||
}
|
||
|
||
if (self.get('labelCfg')) {
|
||
self._addLabels(Util.union.apply(null, mappedArray), shapeContainer.get('children'));
|
||
}
|
||
|
||
if (!self.get('sortable')) {
|
||
self._sort(mappedArray); // 便于数据的查找,需要对数据进行排序,用于 geom.findPoint()
|
||
|
||
} else {
|
||
self.set('dataArray', mappedArray);
|
||
}
|
||
};
|
||
|
||
_proto._sort = function _sort(mappedArray) {
|
||
var self = this;
|
||
var xScale = self.getXScale();
|
||
var xField = xScale.field;
|
||
Util.each(mappedArray, function (itemArr) {
|
||
itemArr.sort(function (obj1, obj2) {
|
||
return xScale.translate(obj1[FIELD_ORIGIN][xField]) - xScale.translate(obj2[FIELD_ORIGIN][xField]);
|
||
});
|
||
});
|
||
self.set('dataArray', mappedArray);
|
||
} // step 3.1 before mapping
|
||
;
|
||
|
||
_proto._beforeMapping = function _beforeMapping(dataArray) {
|
||
var self = this;
|
||
|
||
if (self.get('sortable')) {
|
||
var xScale = self.getXScale();
|
||
var field = xScale.field;
|
||
Util.each(dataArray, function (data) {
|
||
data.sort(function (v1, v2) {
|
||
return xScale.translate(v1[field]) - xScale.translate(v2[field]);
|
||
});
|
||
});
|
||
}
|
||
|
||
if (self.get('generatePoints')) {
|
||
Util.each(dataArray, function (data) {
|
||
self._generatePoints(data);
|
||
});
|
||
Util.each(dataArray, function (data, index) {
|
||
var nextData = dataArray[index + 1];
|
||
|
||
if (nextData) {
|
||
data[0].nextPoints = nextData[0].points;
|
||
}
|
||
});
|
||
}
|
||
} // step 3.2 add labels
|
||
;
|
||
|
||
_proto._addLabels = function _addLabels(points, shapes) {
|
||
var self = this;
|
||
var type = self.get('type');
|
||
var viewTheme = self.get('viewTheme') || Global;
|
||
var coord = self.get('coord');
|
||
var C = Labels.getLabelsClass(coord.type, type);
|
||
var container = self.get('container');
|
||
var scales = Util.map(self.get('labelCfg').fields, function (field) {
|
||
return self._createScale(field);
|
||
});
|
||
var labelContainer = container.addGroup(C, {
|
||
_id: this.get('_id'),
|
||
labelCfg: Util.mix({
|
||
scales: scales
|
||
}, self.get('labelCfg')),
|
||
coord: coord,
|
||
geom: self,
|
||
geomType: type,
|
||
yScale: self.getYScale(),
|
||
viewTheme: viewTheme,
|
||
visible: self.get('visible')
|
||
});
|
||
labelContainer.showLabels(points, shapes);
|
||
self.set('labelContainer', labelContainer);
|
||
}
|
||
/**
|
||
* @protected
|
||
* 获取图形的工厂类
|
||
* @return {Object} 工厂类对象
|
||
*/
|
||
;
|
||
|
||
_proto.getShapeFactory = function getShapeFactory() {
|
||
var shapeFactory = this.get('shapeFactory');
|
||
|
||
if (!shapeFactory) {
|
||
var shapeType = this.get('shapeType');
|
||
shapeFactory = Shape.getShapeFactory(shapeType);
|
||
this.set('shapeFactory', shapeFactory);
|
||
}
|
||
|
||
return shapeFactory;
|
||
} // step 3.2 generate points
|
||
;
|
||
|
||
_proto._generatePoints = function _generatePoints(data) {
|
||
var self = this;
|
||
var shapeFactory = self.getShapeFactory();
|
||
var shapeAttr = self.getAttr('shape');
|
||
|
||
for (var i = 0; i < data.length; i++) {
|
||
var obj = data[i];
|
||
var cfg = self.createShapePointsCfg(obj);
|
||
var shape = shapeAttr ? self._getAttrValues(shapeAttr, obj) : null;
|
||
var points = shapeFactory.getShapePoints(shape, cfg);
|
||
obj.points = points;
|
||
}
|
||
}
|
||
/**
|
||
* 获取图形对应点的配置项
|
||
* @protected
|
||
* @param {Object} obj 数据对象
|
||
* @return {Object} cfg 获取图形对应点的配置项
|
||
*/
|
||
;
|
||
|
||
_proto.createShapePointsCfg = function createShapePointsCfg(obj) {
|
||
var xScale = this.getXScale();
|
||
var yScale = this.getYScale();
|
||
|
||
var x = this._normalizeValues(obj[xScale.field], xScale);
|
||
|
||
var y; // 存在没有 y 的情况
|
||
|
||
if (yScale) {
|
||
y = this._normalizeValues(obj[yScale.field], yScale);
|
||
} else {
|
||
y = obj.y ? obj.y : 0.1;
|
||
}
|
||
|
||
return {
|
||
x: x,
|
||
y: y,
|
||
y0: yScale ? yScale.scale(this.getYMinValue()) : undefined
|
||
};
|
||
}
|
||
/**
|
||
* @protected
|
||
* 如果y轴的最小值小于0则返回0,否则返回最小值
|
||
* @return {Number} y轴上的最小值
|
||
*/
|
||
;
|
||
|
||
_proto.getYMinValue = function getYMinValue() {
|
||
var yScale = this.getYScale();
|
||
var min = yScale.min,
|
||
max = yScale.max;
|
||
var value;
|
||
|
||
if (min >= 0) {
|
||
value = min;
|
||
} else if (max <= 0) {
|
||
// 当值全位于负区间时,需要保证 ymin 在区域内,不可为 0
|
||
value = max;
|
||
} else {
|
||
value = 0;
|
||
}
|
||
|
||
return value;
|
||
} // 将数据归一化
|
||
;
|
||
|
||
_proto._normalizeValues = function _normalizeValues(values, scale) {
|
||
var rst = [];
|
||
|
||
if (Util.isArray(values)) {
|
||
for (var i = 0; i < values.length; i++) {
|
||
var v = values[i];
|
||
rst.push(scale.scale(v));
|
||
}
|
||
} else {
|
||
rst = scale.scale(values);
|
||
}
|
||
|
||
return rst;
|
||
} // step 3.2 mapping
|
||
;
|
||
|
||
_proto._mapping = function _mapping(data) {
|
||
var self = this;
|
||
var attrs = self.get('attrs');
|
||
var mappedData = [];
|
||
|
||
for (var i = 0; i < data.length; i++) {
|
||
var record = data[i];
|
||
var newRecord = {};
|
||
newRecord[FIELD_ORIGIN] = record[FIELD_ORIGIN];
|
||
newRecord.points = record.points;
|
||
newRecord.nextPoints = record.nextPoints;
|
||
|
||
for (var k in attrs) {
|
||
if (attrs.hasOwnProperty(k)) {
|
||
var attr = attrs[k];
|
||
var names = attr.names;
|
||
|
||
var values = self._getAttrValues(attr, record);
|
||
|
||
if (names.length > 1) {
|
||
// position 之类的生成多个字段的属性
|
||
for (var j = 0; j < values.length; j++) {
|
||
var val = values[j];
|
||
var name = names[j];
|
||
newRecord[name] = Util.isArray(val) && val.length === 1 ? val[0] : val; // 只有一个值时返回第一个属性值
|
||
}
|
||
} else {
|
||
newRecord[names[0]] = values.length === 1 ? values[0] : values;
|
||
}
|
||
}
|
||
}
|
||
|
||
mappedData.push(newRecord);
|
||
}
|
||
|
||
return mappedData;
|
||
} // 获取属性映射的值
|
||
;
|
||
|
||
_proto._getAttrValues = function _getAttrValues(attr, record) {
|
||
var scales = attr.scales;
|
||
var params = [];
|
||
|
||
for (var i = 0; i < scales.length; i++) {
|
||
var scale = scales[i];
|
||
var field = scale.field;
|
||
|
||
if (scale.type === 'identity') {
|
||
params.push(scale.value);
|
||
} else {
|
||
params.push(record[field]);
|
||
}
|
||
}
|
||
|
||
var values = attr.mapping.apply(attr, params);
|
||
return values;
|
||
};
|
||
|
||
_proto.getAttrValue = function getAttrValue(attrName, record) {
|
||
var attr = this.getAttr(attrName);
|
||
var rst = null;
|
||
|
||
if (attr) {
|
||
var values = this._getAttrValues(attr, record);
|
||
|
||
rst = values[0];
|
||
}
|
||
|
||
return rst;
|
||
};
|
||
|
||
_proto.getDefaultValue = function getDefaultValue(attrName) {
|
||
var value = this.get(attrName);
|
||
var attr = this.getAttr(attrName);
|
||
|
||
if (attr) {
|
||
var scale = attr.getScale(attrName);
|
||
|
||
if (scale.type === 'identity') {
|
||
value = scale.value;
|
||
}
|
||
}
|
||
|
||
return value;
|
||
}
|
||
/**
|
||
* step 3.3 draw
|
||
* @protected
|
||
* @param {Array} data 绘制图形
|
||
* @param {Object} container 绘图容器
|
||
* @param {Object} shapeFactory 绘制图形的工厂类
|
||
* @param {Number} index 每个 shape 的索引值
|
||
*/
|
||
;
|
||
|
||
_proto.draw = function draw(data, container, shapeFactory, index) {
|
||
var self = this;
|
||
|
||
for (var i = 0; i < data.length; i++) {
|
||
var obj = data[i];
|
||
self.drawPoint(obj, container, shapeFactory, index + i);
|
||
}
|
||
};
|
||
|
||
_proto.getCallbackCfg = function getCallbackCfg(fields, cfg, origin) {
|
||
if (!fields) {
|
||
return cfg;
|
||
}
|
||
|
||
var tmpCfg = {};
|
||
var params = fields.map(function (field) {
|
||
return origin[field];
|
||
});
|
||
Util.each(cfg, function (v, k) {
|
||
if (Util.isFunction(v)) {
|
||
tmpCfg[k] = v.apply(null, params);
|
||
} else {
|
||
tmpCfg[k] = v;
|
||
}
|
||
});
|
||
return tmpCfg;
|
||
};
|
||
|
||
_proto._getShapeId = function _getShapeId(dataObj) {
|
||
var id = this.get('_id');
|
||
var keyFields = this.get('keyFields');
|
||
|
||
if (keyFields && keyFields.length > 0) {
|
||
Util.each(keyFields, function (key) {
|
||
id += '-' + dataObj[key];
|
||
});
|
||
} else {
|
||
var type = this.get('type');
|
||
var xScale = this.getXScale();
|
||
var yScale = this.getYScale();
|
||
var xField = xScale.field || 'x';
|
||
var yField = yScale.field || 'y';
|
||
var yVal = dataObj[yField];
|
||
var xVal;
|
||
|
||
if (xScale.isIdentity) {
|
||
xVal = xScale.value;
|
||
} else {
|
||
xVal = dataObj[xField];
|
||
}
|
||
|
||
if (type === 'interval' || type === 'schema') {
|
||
id += '-' + xVal;
|
||
} else if (type === 'line' || type === 'area' || type === 'path') {
|
||
id += '-' + type;
|
||
} else {
|
||
id += '-' + xVal + '-' + yVal;
|
||
}
|
||
|
||
var groupScales = this._getGroupScales();
|
||
|
||
if (!Util.isEmpty(groupScales)) {
|
||
Util.each(groupScales, function (groupScale) {
|
||
var field = groupScale.field;
|
||
|
||
if (groupScale.type !== 'identity') {
|
||
id += '-' + dataObj[field];
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
return id;
|
||
};
|
||
|
||
_proto.getDrawCfg = function getDrawCfg(obj) {
|
||
var self = this;
|
||
var cfg = {
|
||
origin: obj,
|
||
x: obj.x,
|
||
y: obj.y,
|
||
color: obj.color,
|
||
size: obj.size,
|
||
shape: obj.shape,
|
||
isInCircle: self.isInCircle(),
|
||
opacity: obj.opacity
|
||
};
|
||
var styleOptions = self.get('styleOptions');
|
||
|
||
if (styleOptions && styleOptions.style) {
|
||
cfg.style = self.getCallbackCfg(styleOptions.fields, styleOptions.style, obj[FIELD_ORIGIN]);
|
||
}
|
||
|
||
if (self.get('generatePoints')) {
|
||
cfg.points = obj.points;
|
||
cfg.nextPoints = obj.nextPoints;
|
||
}
|
||
|
||
if (self.get('animate')) {
|
||
// _id 字段仅用于动画
|
||
cfg._id = self._getShapeId(obj[FIELD_ORIGIN]);
|
||
}
|
||
|
||
return cfg;
|
||
};
|
||
|
||
_proto.appendShapeInfo = function appendShapeInfo(shape, index) {
|
||
if (shape) {
|
||
shape.setSilent('index', index);
|
||
shape.setSilent('coord', this.get('coord'));
|
||
|
||
if (this.get('animate') && this.get('animateCfg')) {
|
||
shape.setSilent('animateCfg', this.get('animateCfg'));
|
||
}
|
||
}
|
||
};
|
||
|
||
_proto._applyViewThemeShapeStyle = function _applyViewThemeShapeStyle(cfg, shape, shapeFactory) {
|
||
// applying view theme
|
||
var self = this;
|
||
var viewTheme = self.viewTheme || Global;
|
||
var shapeName = shapeFactory.name;
|
||
|
||
if (shape) {
|
||
if (shape && (shape.indexOf('hollow') > -1 || shape.indexOf('liquid') > -1)) {
|
||
shapeName = "hollow" + Util.upperFirst(shapeName);
|
||
}
|
||
} else if (shapeFactory.defaultShapeType.indexOf('hollow') > -1) {
|
||
shapeName = "hollow" + Util.upperFirst(shapeName);
|
||
}
|
||
|
||
var defaultStyle = viewTheme.shape[shapeName] || {};
|
||
cfg.style = Util.mix({}, defaultStyle, cfg.style);
|
||
};
|
||
|
||
_proto.drawPoint = function drawPoint(obj, container, shapeFactory, index) {
|
||
var self = this;
|
||
var shape = obj.shape;
|
||
var cfg = self.getDrawCfg(obj);
|
||
|
||
self._applyViewThemeShapeStyle(cfg, shape, shapeFactory);
|
||
|
||
var geomShape = shapeFactory.drawShape(shape, cfg, container);
|
||
self.appendShapeInfo(geomShape, index);
|
||
}
|
||
/**
|
||
* 获取属性
|
||
* @protected
|
||
* @param {String} name 属性名
|
||
* @return {Scale} 度量
|
||
*/
|
||
;
|
||
|
||
_proto.getAttr = function getAttr(name) {
|
||
return this.get('attrs')[name];
|
||
}
|
||
/**
|
||
* 获取 x 对应的度量
|
||
* @return {Scale} x 对应的度量
|
||
*/
|
||
;
|
||
|
||
_proto.getXScale = function getXScale() {
|
||
return this.getAttr('position').scales[0];
|
||
}
|
||
/**
|
||
* 获取 y 对应的度量
|
||
* @return {Scale} y 对应的度量
|
||
*/
|
||
;
|
||
|
||
_proto.getYScale = function getYScale() {
|
||
return this.getAttr('position').scales[1];
|
||
};
|
||
|
||
_proto.getShapes = function getShapes() {
|
||
var result = [];
|
||
var shapeContainer = this.get('shapeContainer');
|
||
var children = shapeContainer.get('children');
|
||
Util.each(children, function (child) {
|
||
if (child.get('origin')) {
|
||
// 过滤 label
|
||
result.push(child);
|
||
}
|
||
});
|
||
return result;
|
||
};
|
||
|
||
_proto.getAttrsForLegend = function getAttrsForLegend() {
|
||
var attrs = this.get('attrs');
|
||
var rst = [];
|
||
Util.each(attrs, function (attr) {
|
||
if (GROUP_ATTRS.includes(attr.type)) {
|
||
rst.push(attr);
|
||
}
|
||
});
|
||
return rst;
|
||
};
|
||
|
||
_proto.getFieldsForLegend = function getFieldsForLegend() {
|
||
var fields = [];
|
||
var attrOptions = this.get('attrOptions');
|
||
Util.each(GROUP_ATTRS, function (attrName) {
|
||
var attrCfg = attrOptions[attrName];
|
||
|
||
if (attrCfg && attrCfg.field && Util.isString(attrCfg.field)) {
|
||
fields = fields.concat(attrCfg.field.split('*'));
|
||
}
|
||
});
|
||
return Util.uniq(fields);
|
||
};
|
||
|
||
_proto.changeVisible = function changeVisible(visible, stopDraw) {
|
||
var me = this;
|
||
me.set('visible', visible);
|
||
var shapeContainer = this.get('shapeContainer');
|
||
|
||
if (shapeContainer) {
|
||
shapeContainer.set('visible', visible);
|
||
}
|
||
|
||
var labelContainer = this.get('labelContainer');
|
||
|
||
if (labelContainer) {
|
||
labelContainer.set('visible', visible);
|
||
}
|
||
|
||
if (!stopDraw && shapeContainer) {
|
||
var canvas = shapeContainer.get('canvas');
|
||
canvas.draw();
|
||
}
|
||
};
|
||
|
||
_proto.reset = function reset() {
|
||
this.set('attrOptions', {});
|
||
this.clearInner();
|
||
};
|
||
|
||
_proto.clearInner = function clearInner() {
|
||
this.clearActivedShapes();
|
||
this.clearSelected();
|
||
var shapeContainer = this.get('shapeContainer');
|
||
shapeContainer && shapeContainer.clear(); // 由于 Labels 对应的模块需要生成group,所以这个地方需要删除
|
||
|
||
var labelContainer = this.get('labelContainer');
|
||
labelContainer && labelContainer.remove();
|
||
this.set('attrs', {});
|
||
this.set('groupScales', null); // if (!this.get('hasDefaultAdjust')) {
|
||
// this.set('adjusts', null);
|
||
// }
|
||
|
||
this.set('labelContainer', null);
|
||
this.set('xDistance', null);
|
||
this.set('isStacked', null);
|
||
};
|
||
|
||
_proto.clear = function clear() {
|
||
this.clearInner();
|
||
this.set('scales', {});
|
||
};
|
||
|
||
_proto.destroy = function destroy() {
|
||
this.clear();
|
||
var shapeContainer = this.get('shapeContainer');
|
||
shapeContainer && shapeContainer.remove();
|
||
this.offEvents();
|
||
|
||
_Base.prototype.destroy.call(this);
|
||
};
|
||
|
||
_proto.bindEvents = function bindEvents() {
|
||
if (this.get('view')) {
|
||
this._bindActiveAction();
|
||
|
||
this._bindSelectedAction();
|
||
}
|
||
};
|
||
|
||
_proto.offEvents = function offEvents() {
|
||
if (this.get('view')) {
|
||
this._offActiveAction();
|
||
|
||
this._offSelectedAction();
|
||
}
|
||
};
|
||
|
||
return GeomBase;
|
||
}(Base);
|
||
|
||
module.exports = GeomBase; |