1232 lines
30 KiB
Java
1232 lines
30 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 view
|
||
* @author dxq613@gmail.com
|
||
*/
|
||
var Base = require('../base');
|
||
|
||
var Geom = require('../geom/base');
|
||
|
||
var Util = require('../util');
|
||
|
||
var Controller = require('./controller/index');
|
||
|
||
var Global = require('../global');
|
||
|
||
var Theme = require('../theme/index');
|
||
|
||
var FIELD_ORIGIN = '_origin';
|
||
|
||
var Animate = require('../animate/index');
|
||
|
||
function isFullCircle(coord) {
|
||
var startAngle = coord.startAngle;
|
||
var endAngle = coord.endAngle;
|
||
|
||
if (!Util.isNil(startAngle) && !Util.isNil(endAngle) && endAngle - startAngle < Math.PI * 2) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
function isBetween(value, start, end) {
|
||
var tmp = (value - start) / (end - start);
|
||
return tmp >= 0 && tmp <= 1;
|
||
}
|
||
|
||
function isPointInCoord(coord, point) {
|
||
var result = false;
|
||
|
||
if (coord) {
|
||
var type = coord.type;
|
||
|
||
if (type === 'theta') {
|
||
var start = coord.start;
|
||
var end = coord.end;
|
||
result = isBetween(point.x, start.x, end.x) && isBetween(point.y, start.y, end.y);
|
||
} else {
|
||
var invertPoint = coord.invert(point);
|
||
result = invertPoint.x >= 0 && invertPoint.y >= 0 && invertPoint.x <= 1 && invertPoint.y <= 1;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
var ViewGeoms = {};
|
||
Util.each(Geom, function (geomConstructor, className) {
|
||
var methodName = Util.lowerFirst(className);
|
||
|
||
ViewGeoms[methodName] = function (cfg) {
|
||
var geom = new geomConstructor(cfg);
|
||
this.addGeom(geom);
|
||
return geom;
|
||
};
|
||
});
|
||
/**
|
||
* 图表中的视图
|
||
* @class View
|
||
*/
|
||
|
||
var View = /*#__PURE__*/function (_Base) {
|
||
_inheritsLoose(View, _Base);
|
||
|
||
var _proto = View.prototype;
|
||
|
||
/**
|
||
* 获取默认的配置属性
|
||
* @protected
|
||
* @return {Object} 默认属性
|
||
*/
|
||
_proto.getDefaultCfg = function getDefaultCfg() {
|
||
return {
|
||
viewContainer: null,
|
||
coord: null,
|
||
start: {
|
||
x: 0,
|
||
y: 0
|
||
},
|
||
end: {
|
||
x: 1,
|
||
y: 1
|
||
},
|
||
geoms: [],
|
||
scales: {},
|
||
options: {},
|
||
scaleController: null,
|
||
padding: 0,
|
||
theme: null,
|
||
parent: null,
|
||
tooltipEnable: true,
|
||
// 是否展示 tooltip
|
||
animate: Global.animate,
|
||
visible: true
|
||
};
|
||
};
|
||
|
||
function View(cfg) {
|
||
var _this;
|
||
|
||
_this = _Base.call(this, cfg) || this;
|
||
|
||
var self = _assertThisInitialized(_this);
|
||
|
||
self._setTheme();
|
||
|
||
Util.each(Geom, function (GeomConstructor, className) {
|
||
var methodName = Util.lowerFirst(className);
|
||
|
||
self[methodName] = function (cfg) {
|
||
if (cfg === void 0) {
|
||
cfg = {};
|
||
}
|
||
|
||
cfg.viewTheme = self.get('viewTheme');
|
||
var geom = new GeomConstructor(cfg);
|
||
self.addGeom(geom);
|
||
return geom;
|
||
};
|
||
}); // Util.mix(this, ViewGeoms);
|
||
|
||
self.init();
|
||
return _this;
|
||
}
|
||
|
||
_proto._setTheme = function _setTheme() {
|
||
var self = this;
|
||
var theme = self.get('theme');
|
||
var viewTheme = {};
|
||
var newTheme = {};
|
||
|
||
if (Util.isObject(theme)) {
|
||
newTheme = theme;
|
||
} else if (Util.indexOf(Object.keys(Theme), theme) !== -1) {
|
||
newTheme = Theme[theme];
|
||
}
|
||
|
||
Util.deepMix(viewTheme, Global, newTheme);
|
||
self.set('viewTheme', viewTheme);
|
||
}
|
||
/**
|
||
* @protected
|
||
* 初始化
|
||
*/
|
||
;
|
||
|
||
_proto.init = function init() {
|
||
this._initViewPlot(); // 先创建容器
|
||
|
||
|
||
if (this.get('data')) {
|
||
this._initData(this.get('data'));
|
||
}
|
||
|
||
this._initOptions();
|
||
|
||
this._initControllers();
|
||
|
||
this._bindEvents();
|
||
} // 初始化配置项
|
||
;
|
||
|
||
_proto._initOptions = function _initOptions() {
|
||
var self = this;
|
||
var options = Util.mix({}, self.get('options')); // 防止修改原始值
|
||
|
||
if (!options.scales) {
|
||
options.scales = {};
|
||
}
|
||
|
||
if (!options.coord) {
|
||
options.coord = {};
|
||
}
|
||
|
||
if (options.animate === false) {
|
||
this.set('animate', false);
|
||
}
|
||
|
||
if (options.tooltip === false || Util.isNull(options.tooltip)) {
|
||
// 配置项方式关闭 tooltip
|
||
this.set('tooltipEnable', false);
|
||
}
|
||
|
||
if (options.geoms && options.geoms.length) {
|
||
Util.each(options.geoms, function (geomOption) {
|
||
self._createGeom(geomOption);
|
||
});
|
||
}
|
||
|
||
var scaleController = self.get('scaleController');
|
||
|
||
if (scaleController) {
|
||
scaleController.defs = options.scales;
|
||
}
|
||
|
||
var coordController = self.get('coordController');
|
||
|
||
if (coordController) {
|
||
coordController.reset(options.coord);
|
||
}
|
||
|
||
this.set('options', options);
|
||
};
|
||
|
||
_proto._createGeom = function _createGeom(cfg) {
|
||
var type = cfg.type;
|
||
var geom;
|
||
|
||
if (this[type]) {
|
||
geom = this[type]();
|
||
Util.each(cfg, function (v, k) {
|
||
if (geom[k]) {
|
||
if (Util.isObject(v) && v.field) {
|
||
// 配置项传入
|
||
if (v === 'label') {
|
||
geom[k](v.field, v.callback, v.cfg);
|
||
} else {
|
||
var _cfg;
|
||
|
||
Util.each(v, function (value, key) {
|
||
if (key !== 'field') {
|
||
_cfg = value;
|
||
}
|
||
});
|
||
geom[k](v.field, _cfg);
|
||
}
|
||
} else {
|
||
geom[k](v);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
} // 初始化所有的控制器
|
||
;
|
||
|
||
_proto._initControllers = function _initControllers() {
|
||
var self = this;
|
||
var options = self.get('options');
|
||
var viewTheme = self.get('viewTheme');
|
||
var canvas = self.get('canvas');
|
||
var scaleController = new Controller.Scale({
|
||
viewTheme: viewTheme,
|
||
defs: options.scales
|
||
});
|
||
var coordController = new Controller.Coord(options.coord);
|
||
this.set('scaleController', scaleController);
|
||
this.set('coordController', coordController);
|
||
var axisController = new Controller.Axis({
|
||
canvas: canvas,
|
||
viewTheme: viewTheme
|
||
});
|
||
this.set('axisController', axisController);
|
||
var guideController = new Controller.Guide({
|
||
viewTheme: viewTheme,
|
||
options: options.guides || []
|
||
});
|
||
this.set('guideController', guideController);
|
||
};
|
||
|
||
_proto._initViewPlot = function _initViewPlot() {
|
||
if (!this.get('viewContainer')) {
|
||
// 用于 geom 的绘制
|
||
this.set('viewContainer', this.get('middlePlot'));
|
||
}
|
||
};
|
||
|
||
_proto._initGeoms = function _initGeoms() {
|
||
var geoms = this.get('geoms');
|
||
var filteredData = this.get('filteredData');
|
||
var coord = this.get('coord');
|
||
var viewId = this.get('_id');
|
||
|
||
for (var i = 0; i < geoms.length; i++) {
|
||
var geom = geoms[i];
|
||
geom.set('data', filteredData);
|
||
geom.set('coord', coord);
|
||
geom.set('_id', viewId + '-geom' + i);
|
||
geom.set('keyFields', this.get('keyFields'));
|
||
geom.init();
|
||
}
|
||
};
|
||
|
||
_proto._clearGeoms = function _clearGeoms() {
|
||
var self = this;
|
||
var geoms = self.get('geoms');
|
||
|
||
for (var i = 0; i < geoms.length; i++) {
|
||
var geom = geoms[i];
|
||
geom.clear();
|
||
}
|
||
};
|
||
|
||
_proto._removeGeoms = function _removeGeoms() {
|
||
var self = this;
|
||
var geoms = self.get('geoms');
|
||
|
||
while (geoms.length > 0) {
|
||
var geom = geoms.shift();
|
||
geom.destroy();
|
||
}
|
||
};
|
||
|
||
_proto._drawGeoms = function _drawGeoms() {
|
||
this.emit('beforedrawgeoms');
|
||
var geoms = this.get('geoms');
|
||
var coord = this.get('coord');
|
||
|
||
for (var i = 0; i < geoms.length; i++) {
|
||
var geom = geoms[i];
|
||
geom.setCoord(coord);
|
||
geom.paint();
|
||
}
|
||
|
||
this.emit('afterdrawgeoms');
|
||
};
|
||
|
||
_proto.isShapeInView = function isShapeInView(shape) {
|
||
var id = this.get('_id');
|
||
var shapeId = shape._id;
|
||
|
||
if (shapeId) {
|
||
return shapeId.split('-')[0] === id;
|
||
}
|
||
|
||
var parent = shape;
|
||
|
||
while (parent) {
|
||
if (parent.get('viewId') === id) {
|
||
return true;
|
||
}
|
||
|
||
parent = parent.get('parent');
|
||
}
|
||
|
||
return false;
|
||
}
|
||
/**
|
||
* View 所在的范围
|
||
* @protected
|
||
* @return {Object} View 所在的范围
|
||
*/
|
||
;
|
||
|
||
_proto.getViewRegion = function getViewRegion() {
|
||
var self = this;
|
||
var parent = self.get('parent');
|
||
var start;
|
||
var end;
|
||
|
||
if (parent) {
|
||
var region = parent.getViewRegion();
|
||
|
||
var viewRegion = self._getViewRegion(region.start, region.end);
|
||
|
||
start = viewRegion.start;
|
||
end = viewRegion.end;
|
||
} else {
|
||
start = self.get('start');
|
||
end = self.get('end');
|
||
}
|
||
|
||
return {
|
||
start: start,
|
||
end: end
|
||
};
|
||
} // 获取 range 所在的范围
|
||
;
|
||
|
||
_proto._getViewRegion = function _getViewRegion(plotStart, plotEnd) {
|
||
var start = this.get('start');
|
||
var end = this.get('end');
|
||
var startX = start.x;
|
||
var startY = 1 - end.y;
|
||
var endX = end.x;
|
||
var endY = 1 - start.y;
|
||
var padding = this.get('padding'); // 转换成 上、右、下、左的模式
|
||
|
||
var allPadding = Util.toAllPadding(padding);
|
||
var top = allPadding[0];
|
||
var right = allPadding[1];
|
||
var bottom = allPadding[2];
|
||
var left = allPadding[3];
|
||
var startPoint = {
|
||
x: startX * (plotEnd.x - plotStart.x) + plotStart.x + left,
|
||
y: startY * (plotEnd.y - plotStart.y) + plotStart.y - bottom
|
||
};
|
||
var endPoint = {
|
||
x: endX * (plotEnd.x - plotStart.x) + plotStart.x - right,
|
||
y: endY * (plotEnd.y - plotStart.y) + plotStart.y + top
|
||
};
|
||
return {
|
||
start: startPoint,
|
||
end: endPoint
|
||
};
|
||
};
|
||
|
||
_proto._createCoord = function _createCoord() {
|
||
var coordController = this.get('coordController');
|
||
var region = this.getViewRegion();
|
||
var coord = coordController.createCoord(region.start, region.end);
|
||
this.set('coord', coord);
|
||
};
|
||
|
||
_proto._renderAxes = function _renderAxes() {
|
||
var options = this.get('options');
|
||
var axesOptions = options.axes;
|
||
|
||
if (axesOptions === false) {
|
||
// 不渲染坐标轴
|
||
return;
|
||
}
|
||
|
||
var axisController = this.get('axisController');
|
||
axisController.container = this.get('backPlot');
|
||
axisController.coord = this.get('coord');
|
||
axisController.options = axesOptions || {};
|
||
var xScale = this.getXScale();
|
||
var yScales = this.getYScales();
|
||
var viewId = this.get('_id');
|
||
axisController.createAxis(xScale, yScales, viewId);
|
||
};
|
||
|
||
_proto._renderGuides = function _renderGuides() {
|
||
var guideController = this.get('guideController');
|
||
|
||
if (!Util.isEmpty(guideController.options)) {
|
||
var coord = this.get('coord');
|
||
guideController.view = this;
|
||
guideController.backContainer = this.get('backPlot');
|
||
guideController.frontContainer = this.get('frontPlot');
|
||
guideController.xScales = this._getScales('x');
|
||
guideController.yScales = this._getScales('y');
|
||
guideController.render(coord);
|
||
}
|
||
} // 注册事件
|
||
;
|
||
|
||
_proto._bindEvents = function _bindEvents() {
|
||
var eventController = new Controller.Event({
|
||
view: this,
|
||
canvas: this.get('canvas')
|
||
});
|
||
eventController.bindEvents();
|
||
this.set('eventController', eventController);
|
||
} // 清理时间
|
||
;
|
||
|
||
_proto._clearEvents = function _clearEvents() {
|
||
var eventController = this.get('eventController');
|
||
eventController && eventController.clearEvents();
|
||
};
|
||
|
||
_proto._getScales = function _getScales(dimType) {
|
||
var geoms = this.get('geoms');
|
||
var result = {};
|
||
|
||
for (var i = 0; i < geoms.length; i++) {
|
||
var geom = geoms[i];
|
||
var scale = dimType === 'x' ? geom.getXScale() : geom.getYScale();
|
||
|
||
if (scale && !result[scale.field]) {
|
||
result[scale.field] = scale;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
};
|
||
|
||
_proto._adjustScale = function _adjustScale() {
|
||
this._setCatScalesRange();
|
||
|
||
var geoms = this.get('geoms');
|
||
var scaleController = this.get('scaleController');
|
||
var colDefs = scaleController.defs;
|
||
|
||
for (var i = 0; i < geoms.length; i++) {
|
||
var geom = geoms[i];
|
||
|
||
if (geom.get('type') === 'interval') {
|
||
var yScale = geom.getYScale();
|
||
var field = yScale.field,
|
||
min = yScale.min,
|
||
max = yScale.max,
|
||
type = yScale.type;
|
||
|
||
if (!(colDefs[field] && colDefs[field].min) && type !== 'time') {
|
||
if (min > 0) {
|
||
yScale.change({
|
||
min: 0
|
||
});
|
||
} else if (max <= 0) {
|
||
// 当柱状图全为负值时也需要从 0 开始生长
|
||
yScale.change({
|
||
max: 0
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
_proto._setCatScalesRange = function _setCatScalesRange() {
|
||
var self = this;
|
||
var coord = self.get('coord');
|
||
var viewTheme = self.get('viewTheme');
|
||
var xScale = self.getXScale();
|
||
var yScales = self.getYScales();
|
||
var scales = [];
|
||
xScale && scales.push(xScale);
|
||
scales = scales.concat(yScales);
|
||
var inFullCircle = coord.isPolar && isFullCircle(coord);
|
||
var scaleController = self.get('scaleController');
|
||
var colDefs = scaleController.defs;
|
||
Util.each(scales, function (scale) {
|
||
if ((scale.isCategory || scale.isIdentity) && scale.values && !(colDefs[scale.field] && colDefs[scale.field].range)) {
|
||
var count = scale.values.length;
|
||
var range;
|
||
|
||
if (count === 1) {
|
||
range = [0.5, 1]; // 只有一个分类时,防止计算出现 [0.5,0.5]的状态
|
||
} else {
|
||
var widthRatio = 1;
|
||
var offset = 0;
|
||
|
||
if (inFullCircle) {
|
||
if (!coord.isTransposed) {
|
||
range = [0, 1 - 1 / count];
|
||
} else {
|
||
widthRatio = viewTheme.widthRatio.multiplePie;
|
||
offset = 1 / count * widthRatio;
|
||
range = [offset / 2, 1 - offset / 2];
|
||
}
|
||
} else {
|
||
offset = 1 / count * 1 / 2; // 两边留下分类空间的一半
|
||
|
||
range = [offset, 1 - offset]; // 坐标轴最前面和最后面留下空白防止绘制柱状图时
|
||
}
|
||
}
|
||
|
||
scale.range = range;
|
||
}
|
||
});
|
||
};
|
||
|
||
_proto.getXScale = function getXScale() {
|
||
var geoms = this.get('geoms'); // 如果进行过滤,那么 geom 默认隐藏时会出现不一致
|
||
// 默认隐藏时坐标轴不绘制,但是调用了 geom.show() 后,则图形显示了,坐标轴依然不见
|
||
|
||
/* .filter(function(geom) {
|
||
return geom.get('visible');
|
||
}); */
|
||
|
||
var xScale = null;
|
||
|
||
if (!Util.isEmpty(geoms)) {
|
||
xScale = geoms[0].getXScale();
|
||
}
|
||
|
||
return xScale;
|
||
};
|
||
|
||
_proto.getYScales = function getYScales() {
|
||
var geoms = this.get('geoms');
|
||
/* .filter(function(geom) {
|
||
return geom.get('visible');
|
||
}); */
|
||
|
||
var rst = [];
|
||
|
||
for (var i = 0; i < geoms.length; i++) {
|
||
var geom = geoms[i];
|
||
var yScale = geom.getYScale();
|
||
|
||
if (yScale && Util.indexOf(rst, yScale) === -1) {
|
||
rst.push(yScale);
|
||
}
|
||
}
|
||
|
||
return rst;
|
||
}
|
||
/**
|
||
* 获取数据对应在画布空间的坐标
|
||
* @param {Object} item 原始数据
|
||
* @return {Object} 返回对应的画布上的坐标点
|
||
*/
|
||
;
|
||
|
||
_proto.getXY = function getXY(item) {
|
||
var self = this;
|
||
var coord = self.get('coord');
|
||
|
||
var xScales = self._getScales('x');
|
||
|
||
var yScales = self._getScales('y');
|
||
|
||
var x;
|
||
var y;
|
||
|
||
for (var field in item) {
|
||
if (xScales[field]) {
|
||
x = xScales[field].scale(item[field]);
|
||
}
|
||
|
||
if (yScales[field]) {
|
||
y = yScales[field].scale(item[field]);
|
||
}
|
||
}
|
||
|
||
if (!Util.isNil(x) && !Util.isNil(y)) {
|
||
return coord.convert({
|
||
x: x,
|
||
y: y
|
||
});
|
||
}
|
||
|
||
return null;
|
||
}
|
||
/**
|
||
* 获取逼近的点的数据集合
|
||
* @param {Object} point 画布上的像素点
|
||
* @return {Array} 数据
|
||
*/
|
||
;
|
||
|
||
_proto.getSnapRecords = function getSnapRecords(point) {
|
||
var self = this;
|
||
var geoms = self.get('geoms');
|
||
var rst = [];
|
||
Util.each(geoms, function (geom) {
|
||
var dataArray = geom.get('dataArray');
|
||
var record;
|
||
Util.each(dataArray, function (data) {
|
||
record = geom.findPoint(point, data);
|
||
record && rst.push(record);
|
||
});
|
||
});
|
||
return rst;
|
||
}
|
||
/**
|
||
* @protected
|
||
* 添加几何标记
|
||
* @param {Geom} geom 几何标记
|
||
*/
|
||
;
|
||
|
||
_proto.addGeom = function addGeom(geom) {
|
||
var self = this;
|
||
var geoms = self.get('geoms');
|
||
geoms.push(geom);
|
||
geom.set('view', self);
|
||
var container = self.get('viewContainer');
|
||
geom.set('container', container);
|
||
geom.set('animate', self.get('animate'));
|
||
geom.bindEvents();
|
||
}
|
||
/**
|
||
* @protected
|
||
* 移除几何标记
|
||
* @param {Geom} geom 几何标记
|
||
*/
|
||
;
|
||
|
||
_proto.removeGeom = function removeGeom(geom) {
|
||
var geoms = this.get('geoms');
|
||
Util.Array.remove(geoms, geom);
|
||
geom.destroy();
|
||
};
|
||
|
||
_proto.createScale = function createScale(field, data) {
|
||
var scales = this.get('scales');
|
||
var parent = this.get('parent');
|
||
var scale = scales[field]; // const filters = this._getFilters();
|
||
|
||
if (!data) {
|
||
var filteredData = this.get('filteredData');
|
||
|
||
var legendFields = this._getFieldsForLegend(); // 过滤导致数据为空时,需要使用全局数据
|
||
// 参与过滤的字段的度量也根据全局数据来生成
|
||
|
||
|
||
if (filteredData.length && !legendFields.includes(field)) {
|
||
data = filteredData;
|
||
} else {
|
||
data = this.get('data');
|
||
}
|
||
}
|
||
|
||
var scaleController = this.get('scaleController');
|
||
|
||
if (!scale) {
|
||
scale = scaleController.createScale(field, data);
|
||
|
||
if (scale.sync && parent) {
|
||
var parentScale = parent.createScale(field, data);
|
||
scale = this._getSyncScale(parentScale, scale);
|
||
}
|
||
|
||
scales[field] = scale;
|
||
} else if (scale.sync) {
|
||
// 防止 view 内部创建的scale,Chart 上的scale 范围更大
|
||
var newScale = scaleController.createScale(field, data);
|
||
|
||
this._syncScale(scale, newScale);
|
||
}
|
||
|
||
return scale;
|
||
};
|
||
|
||
_proto._getFieldsForLegend = function _getFieldsForLegend() {
|
||
var fields = [];
|
||
var geoms = this.get('geoms');
|
||
Util.each(geoms, function (geom) {
|
||
var geomFields = geom.getFieldsForLegend();
|
||
fields = fields.concat(geomFields);
|
||
});
|
||
return Util.uniq(fields);
|
||
} // 如果需要同步度量,则使得 values,min,max的范围最大
|
||
;
|
||
|
||
_proto._getSyncScale = function _getSyncScale(parentScale, scale) {
|
||
if (parentScale.type !== scale.type) {
|
||
return scale;
|
||
}
|
||
|
||
this._syncScale(parentScale, scale);
|
||
|
||
return parentScale;
|
||
};
|
||
|
||
_proto._syncScale = function _syncScale(distScale, sourceScale) {
|
||
var mergeValues = Util.union(distScale.values, sourceScale.values);
|
||
|
||
if (sourceScale.isLinear) {
|
||
var max = Math.max(distScale.max, sourceScale.max);
|
||
var min = Math.min(distScale.min, sourceScale.min);
|
||
|
||
if (distScale.max !== max || distScale.min !== min) {
|
||
distScale.change({
|
||
min: min,
|
||
max: max,
|
||
values: mergeValues
|
||
});
|
||
}
|
||
}
|
||
|
||
if (mergeValues.length !== distScale.values.length) {
|
||
distScale.change({
|
||
values: mergeValues
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* @protected
|
||
* 获取过滤后的值(需要显示的值)
|
||
* @param {String} field 度量
|
||
* @return {Array.<String>} 滤后的值
|
||
*/
|
||
;
|
||
|
||
_proto.getFilteredValues = function getFilteredValues(field) {
|
||
var scale = this.get('scales')[field];
|
||
var values = scale.values;
|
||
|
||
var filters = this._getFilters();
|
||
|
||
var rst;
|
||
|
||
if (filters && filters[field]) {
|
||
rst = values.filter(filters[field]);
|
||
} else {
|
||
rst = values.slice(0);
|
||
}
|
||
|
||
return rst;
|
||
}
|
||
/**
|
||
* @protected
|
||
* 获取被过滤的值(不需显示的值)
|
||
* @param {String} field 度量
|
||
* @return {Array.<String>} 滤出的值
|
||
*/
|
||
;
|
||
|
||
_proto.getFilteredOutValues = function getFilteredOutValues(field) {
|
||
var scale = this.get('scales')[field];
|
||
var values = scale.values;
|
||
|
||
var filters = this._getFilters();
|
||
|
||
var rst;
|
||
|
||
if (filters && filters[field]) {
|
||
rst = values.filter(function () {
|
||
return !filters[field].apply(filters, arguments);
|
||
});
|
||
} else {
|
||
rst = [];
|
||
}
|
||
|
||
return rst;
|
||
};
|
||
|
||
_proto.filter = function filter(field, condition) {
|
||
var options = this.get('options');
|
||
|
||
if (!options.filters) {
|
||
options.filters = {};
|
||
}
|
||
|
||
options.filters[field] = condition;
|
||
this.get('scaleController').filters = options.filters;
|
||
} // 获取 filters
|
||
;
|
||
|
||
_proto._getFilters = function _getFilters() {
|
||
var options = this.get('options');
|
||
return options.filters;
|
||
} // 执行 filter 数据
|
||
;
|
||
|
||
_proto.execFilter = function execFilter(data) {
|
||
var self = this;
|
||
|
||
var filters = self._getFilters();
|
||
|
||
if (filters) {
|
||
data = data.filter(function (obj) {
|
||
var rst = true;
|
||
Util.each(filters, function (fn, k) {
|
||
if (fn) {
|
||
rst = fn(obj[k], obj);
|
||
|
||
if (!rst) {
|
||
return false;
|
||
}
|
||
}
|
||
});
|
||
return rst;
|
||
});
|
||
}
|
||
|
||
return data;
|
||
};
|
||
|
||
_proto.axis = function axis(field, cfg) {
|
||
var options = this.get('options');
|
||
|
||
if (field === false) {
|
||
options.axes = false;
|
||
} else {
|
||
if (!options.axes) {
|
||
options.axes = {};
|
||
}
|
||
|
||
var axisOptions = options.axes;
|
||
axisOptions[field] = cfg;
|
||
}
|
||
|
||
return this;
|
||
};
|
||
|
||
_proto.guide = function guide() {
|
||
return this.get('guideController');
|
||
};
|
||
|
||
_proto._getKeyFields = function _getKeyFields(scaleDefs) {
|
||
var keyFields = [];
|
||
Util.each(scaleDefs, function (def, field) {
|
||
if (def.key) {
|
||
keyFields.push(field);
|
||
}
|
||
});
|
||
this.set('keyFields', keyFields);
|
||
};
|
||
|
||
_proto.scale = function scale(field, cfg) {
|
||
var options = this.get('options');
|
||
var scaleDefs = options.scales;
|
||
|
||
if (Util.isObject(field)) {
|
||
Util.mix(scaleDefs, field);
|
||
} else {
|
||
scaleDefs[field] = cfg;
|
||
}
|
||
|
||
this._getKeyFields(scaleDefs);
|
||
|
||
return this;
|
||
};
|
||
|
||
_proto.tooltip = function tooltip(visible) {
|
||
this.set('tooltipEnable', visible);
|
||
return this;
|
||
};
|
||
|
||
_proto.animate = function animate(enable) {
|
||
var options = this.get('options');
|
||
options.animate = enable;
|
||
this.set('animate', enable);
|
||
return this;
|
||
};
|
||
|
||
_proto.changeOptions = function changeOptions(options) {
|
||
this.set('options', options);
|
||
|
||
this._initOptions(options);
|
||
|
||
return this;
|
||
}
|
||
/**
|
||
* @internal 查找包含指定点的视图
|
||
* @param {Object} point 点的位置
|
||
* @return {Array} 多个视图
|
||
*/
|
||
;
|
||
|
||
_proto.getViewsByPoint = function getViewsByPoint(point) {
|
||
var rst = [];
|
||
var views = this.get('views');
|
||
|
||
if (isPointInCoord(this.get('coord'), point)) {
|
||
rst.push(this);
|
||
}
|
||
|
||
Util.each(views, function (view) {
|
||
if (view.get('visible') && isPointInCoord(view.get('coord'), point)) {
|
||
rst.push(view);
|
||
}
|
||
});
|
||
return rst;
|
||
}
|
||
/**
|
||
* 遍历所有的 shape ,用户更改 shape 后进行刷新
|
||
* @param {Function} fn 回调函数包含参数:record,shape,geom,view
|
||
* @return {View} 当前视图
|
||
*/
|
||
;
|
||
|
||
_proto.eachShape = function eachShape(fn) {
|
||
var self = this;
|
||
var views = self.get('views');
|
||
var canvas = self.get('canvas');
|
||
Util.each(views, function (view) {
|
||
view.eachShape(fn);
|
||
});
|
||
var geoms = this.get('geoms');
|
||
Util.each(geoms, function (geom) {
|
||
var shapes = geom.getShapes();
|
||
Util.each(shapes, function (shape) {
|
||
var origin = shape.get('origin');
|
||
|
||
if (Util.isArray(origin)) {
|
||
var arr = origin.map(function (subOrigin) {
|
||
return subOrigin[FIELD_ORIGIN];
|
||
});
|
||
fn(arr, shape, geom, self);
|
||
} else {
|
||
var obj = origin[FIELD_ORIGIN];
|
||
fn(obj, shape, geom, self);
|
||
}
|
||
});
|
||
});
|
||
canvas.draw();
|
||
return this;
|
||
}
|
||
/**
|
||
* 遍历所有的 shape ,回调函数中 true / false 控制图形是否显示
|
||
* @param {Function} fn 回调函数包含参数:record,shape,geom,view
|
||
* @return {View} 当前视图
|
||
*/
|
||
;
|
||
|
||
_proto.filterShape = function filterShape(fn) {
|
||
var callback = function callback(record, shape, geom, view) {
|
||
if (!fn(record, shape, geom, view)) {
|
||
shape.hide();
|
||
} else {
|
||
shape.show();
|
||
}
|
||
};
|
||
|
||
this.eachShape(callback);
|
||
return this;
|
||
};
|
||
|
||
_proto.clearInner = function clearInner() {
|
||
this.set('scales', {});
|
||
this.emit('beforeclearinner');
|
||
var options = this.get('options');
|
||
options.geoms = null;
|
||
|
||
this._clearGeoms(); // reset guide
|
||
|
||
|
||
this.get('guideController') && this.get('guideController').reset(); // clear axis
|
||
|
||
this.get('axisController') && this.get('axisController').clear();
|
||
this.emit('afterclearinner');
|
||
}
|
||
/**
|
||
* 清除视图内容,包括 geoms
|
||
* @return {View} 当前视图
|
||
*/
|
||
;
|
||
|
||
_proto.clear = function clear() {
|
||
var options = this.get('options');
|
||
options.filters = null;
|
||
|
||
this._removeGeoms(); // const container = this.get('viewContainer');
|
||
// container.clear();
|
||
|
||
|
||
this.clearInner();
|
||
this.get('guideController') && this.get('guideController').clear();
|
||
this.set('isUpdate', false);
|
||
this.set('keyFields', []);
|
||
return this;
|
||
}
|
||
/**
|
||
* 设置坐标系信息
|
||
* @param {String} type 类型
|
||
* @param {Object} cfg 配置项
|
||
* @return {Object} coordController 坐标系的管理器
|
||
*/
|
||
;
|
||
|
||
_proto.coord = function coord(type, cfg) {
|
||
var coordController = this.get('coordController');
|
||
coordController.reset({
|
||
type: type,
|
||
cfg: cfg
|
||
});
|
||
return coordController;
|
||
}
|
||
/**
|
||
* 当父元素边框发生改变时坐标系需要重新调整
|
||
* @protected
|
||
*/
|
||
;
|
||
|
||
_proto.resetCoord = function resetCoord() {
|
||
this._createCoord();
|
||
};
|
||
|
||
_proto.source = function source(data, scales) {
|
||
this._initData(data);
|
||
|
||
if (scales) {
|
||
this.scale(scales);
|
||
}
|
||
|
||
this.emit('setdata');
|
||
return this;
|
||
};
|
||
|
||
_proto.changeData = function changeData(data) {
|
||
this.emit('beforechangedata');
|
||
|
||
this._initData(data);
|
||
|
||
this.emit('afterchangedata');
|
||
this.repaint();
|
||
return this;
|
||
};
|
||
|
||
_proto._initData = function _initData(data) {
|
||
var dataView = this.get('dataView');
|
||
|
||
if (dataView) {
|
||
dataView.off('change', Util.getWrapBehavior(this, '_onViewChange'));
|
||
this.set('dataView', null);
|
||
}
|
||
|
||
if (data && data.isDataView) {
|
||
data.on('change', Util.wrapBehavior(this, '_onViewChange'));
|
||
this.set('dataView', data);
|
||
data = data.rows;
|
||
}
|
||
|
||
this.set('data', data);
|
||
};
|
||
|
||
_proto._onViewChange = function _onViewChange() {
|
||
this.emit('beforechangedata');
|
||
var dataView = this.get('dataView');
|
||
var rows = dataView.rows;
|
||
this.set('data', rows);
|
||
this.emit('afterchangedata');
|
||
this.repaint();
|
||
} // 初始化各个 view 和绘制辅助元素
|
||
;
|
||
|
||
_proto.beforeRender = function beforeRender() {
|
||
var views = this.get('views'); // 如果存在 views 则初始化子 view 的方法
|
||
|
||
Util.each(views, function (view) {
|
||
view.beforeRender();
|
||
});
|
||
this.initView();
|
||
} // 绘制坐标轴、图例、辅助元素等图表组件
|
||
;
|
||
|
||
_proto.drawComponents = function drawComponents() {
|
||
var views = this.get('views'); // 如果存在 views 则初始化子 view 的方法
|
||
|
||
Util.each(views, function (view) {
|
||
view.drawComponents();
|
||
});
|
||
|
||
this._renderAxes();
|
||
|
||
this._renderGuides();
|
||
} // 绘制图形
|
||
;
|
||
|
||
_proto.drawCanvas = function drawCanvas(stopDraw) {
|
||
if (!stopDraw) {
|
||
var views = this.get('views');
|
||
var backPlot = this.get('backPlot');
|
||
backPlot.sort();
|
||
var canvas = this.get('canvas');
|
||
var animate = this.get('animate');
|
||
|
||
if (animate) {
|
||
var isUpdate = this.get('isUpdate');
|
||
Util.each(views, function (view) {
|
||
Animate.execAnimation(view, isUpdate);
|
||
});
|
||
Animate.execAnimation(this, isUpdate);
|
||
} else {
|
||
canvas.draw();
|
||
}
|
||
}
|
||
};
|
||
|
||
_proto.render = function render(stopDraw) {
|
||
this.clearInner();
|
||
this.emit('beforerender');
|
||
this.beforeRender();
|
||
this.emit('beforepaint');
|
||
this.drawComponents();
|
||
this.paint();
|
||
this.emit('afterpaint');
|
||
this.drawCanvas(stopDraw);
|
||
this.emit('afterrender');
|
||
this.set('rendered', true);
|
||
return this;
|
||
};
|
||
|
||
_proto.initView = function initView() {
|
||
var data = this.get('data') || [];
|
||
var filteredData = this.execFilter(data);
|
||
this.set('filteredData', filteredData); // if (!Util.isEmpty(data)) {
|
||
|
||
this._createCoord(); // draw geometry 前绘制区域可能会发生改变
|
||
|
||
|
||
this.emit('beforeinitgeoms');
|
||
|
||
this._initGeoms();
|
||
|
||
this._adjustScale(); // }
|
||
|
||
};
|
||
|
||
_proto.paint = function paint() {
|
||
var views = this.get('views'); // 绘制
|
||
|
||
Util.each(views, function (view) {
|
||
view.paint();
|
||
});
|
||
var data = this.get('data');
|
||
|
||
if (!Util.isEmpty(data)) {
|
||
this._drawGeoms();
|
||
} // 如果 view 隐藏了,隐藏所有的图形和坐标轴
|
||
|
||
|
||
if (!this.get('visible')) {
|
||
this.changeVisible(false, true); // 隐藏所有的图形,但是不绘制
|
||
}
|
||
};
|
||
|
||
_proto.changeVisible = function changeVisible(visible, stopDraw) {
|
||
var geoms = this.get('geoms');
|
||
Util.each(geoms, function (geom) {
|
||
// if (geom.get('visible')) { // geom 隐藏时不受
|
||
geom.changeVisible(visible, true); // }
|
||
});
|
||
this.get('axisController') && this.get('axisController').changeVisible(visible);
|
||
this.get('guideController') && this.get('guideController').changeVisible(visible);
|
||
|
||
if (!stopDraw) {
|
||
var canvas = this.get('canvas');
|
||
canvas.draw();
|
||
}
|
||
};
|
||
|
||
_proto.repaint = function repaint() {
|
||
this.set('isUpdate', true);
|
||
this.clearInner();
|
||
this.render();
|
||
};
|
||
|
||
_proto.destroy = function destroy() {
|
||
this._clearEvents();
|
||
|
||
var dataView = this.get('dataView');
|
||
dataView && dataView.off('change', Util.getWrapBehavior(this, '_onViewChange'));
|
||
this.clear();
|
||
|
||
_Base.prototype.destroy.call(this);
|
||
};
|
||
|
||
return View;
|
||
}(Base);
|
||
|
||
module.exports = View; |