UNPKG

@antv/f2

Version:

Charts for mobile visualization.

677 lines 21.7 kB
import { __assign, __extends, __rest } from "tslib"; import { isFunction, each, upperFirst, mix, groupToMap, isObject, flatten, isNull, find } from '@antv/util'; import Selection from './selection'; import { Dodge, Jitter, Stack, Symmetric } from '../../deps/f2-adjust/src'; import { toTimeStamp } from '../../util/index'; import AttrController from '../../controller/attr'; import { isEqual } from '@antv/f-engine'; var AdjustMap = { Stack: Stack, Dodge: Dodge, Jitter: Jitter, Symmetric: Symmetric }; // 保留原始数据的字段 var FIELD_ORIGIN = 'origin'; var Geometry = /** @class */function (_super) { __extends(Geometry, _super); function Geometry(props, context) { var _this = _super.call(this, props, context) || this; _this.isGeometry = true; // x 轴居中 _this.justifyContent = false; // y 轴是否从0开始 _this.startOnZero = false; // 是否连接空值 _this.connectNulls = false; // 是否需要排序 _this.sortable = false; mix(_this, _this.getDefaultCfg()); var chart = props.chart, coord = props.coord; var attrsRange = _this._getThemeAttrsRange(); _this.attrController = new AttrController(chart.scale, attrsRange); var _a = _this, attrController = _a.attrController, justifyContent = _a.justifyContent; var attrOptions = attrController.getAttrOptions(props, !coord.isCyclic() || justifyContent); attrController.create(attrOptions); return _this; } Geometry.prototype.getDefaultCfg = function () { return {}; }; Geometry.prototype.willReceiveProps = function (nextProps) { var _a = this, lastProps = _a.props, attrController = _a.attrController, justifyContent = _a.justifyContent; var nextData = nextProps.data, nextAdjust = nextProps.adjust, coord = nextProps.coord, selection = nextProps.selection; var lastData = lastProps.data, lastAdjust = lastProps.adjust, lastSelection = lastProps.selection; var justifyContentCenter = !coord.isCyclic() || justifyContent; var lastAttrOptions = attrController.getAttrOptions(lastProps, justifyContentCenter); attrController.attrsRange = this._getThemeAttrsRange(); var nextAttrOptions = attrController.getAttrOptions(nextProps, justifyContentCenter); if (!isEqual(nextAttrOptions, lastAttrOptions)) { attrController.update(nextAttrOptions); this.dataRecords = null; } // 重新处理数据 if (nextData !== lastData) { this.dataRecords = null; } // 重新处理数据 if (nextAdjust !== lastAdjust) { this.dataRecords = null; } // selection 发生变化 if (!isEqual(selection, lastSelection)) { _super.prototype.willReceiveProps.call(this, nextProps); } }; Geometry.prototype.willMount = function () { this._createAttrs(); if (!this.dataRecords) { this._processData(); } }; Geometry.prototype.willUpdate = function () { this._createAttrs(); if (!this.dataRecords) { this._processData(); } else { this._readjustData(this.dataRecords); } }; Geometry.prototype.didMount = function () { this._initEvent(); _super.prototype.didMount.call(this); // 更新 attrController this.attrController.attrsRange = this._getThemeAttrsRange(); }; Geometry.prototype._initEvent = function () { var _this = this; var props = this.props; var chart = props.chart; ['onPressStart', 'onPress', 'onPressEnd', 'onPan', 'onPanStart', 'onPanEnd'].forEach(function (eventName) { if (props[eventName]) { chart.on(eventName.substr(2).toLowerCase(), function (ev) { ev.geometry = _this; props[eventName](ev); }); } }); }; Geometry.prototype._createAttrs = function () { var attrController = this.attrController; attrController.attrs = {}; this.attrs = attrController.getAttrs(); }; Geometry.prototype._getThemeAttrsRange = function () { var _a = this, context = _a.context, props = _a.props, geomType = _a.geomType; var coord = props.coord; var theme = context.theme; var colors = theme.colors, sizes = theme.sizes, shapes = theme.shapes; return { x: coord.x, y: coord.y, color: colors, size: sizes, shape: shapes[geomType] }; }; Geometry.prototype._createAdjust = function () { var _a = this, attrs = _a.attrs, props = _a.props; var adjust = props.adjust; if (!adjust) { return null; } var adjustCfg = typeof adjust === 'string' ? { type: adjust } : adjust; var adjustType = upperFirst(adjustCfg.type); var AdjustConstructor = AdjustMap[adjustType]; if (!AdjustConstructor) { throw new Error('not support such adjust : ' + adjust); } if (adjustType === 'Dodge') { // @ts-ignore adjustCfg.adjustNames = ['x']; } var x = attrs.x, y = attrs.y; // @ts-ignore adjustCfg.xField = x.field; // @ts-ignore adjustCfg.yField = y.field; var adjustInstance = new AdjustConstructor(adjustCfg); this.adjust = { type: adjustCfg.type, adjust: adjustInstance }; return this.adjust; }; Geometry.prototype._adjustScales = function () { var _a = this, attrs = _a.attrs, props = _a.props, defaultStartOnZero = _a.startOnZero; var chart = props.chart, _b = props.startOnZero, startOnZero = _b === void 0 ? defaultStartOnZero : _b, coord = props.coord, adjust = props.adjust; var isPolar = coord.isPolar, transposed = coord.transposed; var y = attrs.y; var yField = y.field; // 如果从 0 开始,只调整 y 轴 scale if (startOnZero) { var y_1 = attrs.y; chart.scale.adjustStartZero(y_1.scale); } // 饼图的scale调整,关闭nice if (isPolar && transposed && (adjust === 'stack' || (adjust === null || adjust === void 0 ? void 0 : adjust.type) === 'stack')) { var y_2 = attrs.y; chart.scale.adjustPieScale(y_2.scale); } if (adjust === 'stack' || (adjust === null || adjust === void 0 ? void 0 : adjust.type) === 'stack') { this._updateStackRange(yField, y.scale, this.dataArray); } }; Geometry.prototype._groupData = function (data) { var attrController = this.attrController; var groupScales = attrController.getGroupScales(); if (!groupScales.length) { return [{ children: data }]; } var names = []; groupScales.forEach(function (scale) { var field = scale.field; names.push(field); }); var groups = groupToMap(data, names); var records = []; for (var key in groups) { records.push({ key: key.replace(/^_/, ''), children: groups[key] }); } return records; }; Geometry.prototype._saveOrigin = function (originData) { var _a; var len = originData.length; var data = new Array(len); for (var i = 0; i < len; i++) { var record = originData[i]; data[i] = __assign(__assign({}, record), (_a = {}, _a[FIELD_ORIGIN] = record, _a)); } return data; }; Geometry.prototype._numberic = function (data) { var attrs = this.attrs; var scales = [attrs.x.scale, attrs.y.scale]; for (var j = 0, len = data.length; j < len; j++) { var obj = data[j]; var count = scales.length; for (var i = 0; i < count; i++) { var scale = scales[i]; if (scale.isCategory) { var field = scale.field; var value = scale.translate(obj.origin[field]); obj[field] = value; } } } }; Geometry.prototype._adjustData = function (records) { var adjust = this.adjust; // groupedArray 是二维数组 var groupedArray = records.map(function (record) { return record.children; }); if (!adjust) { return groupedArray; } var attrs = this.attrs; var scales = [attrs.x.scale, attrs.y.scale]; for (var i = 0, len = groupedArray.length; i < len; i++) { var records_1 = groupedArray[i]; for (var j = 0, len_1 = records_1.length; j < len_1; j++) { var record = records_1[j]; var count = scales.length; for (var i_1 = 0; i_1 < count; i_1++) { var scale = scales[i_1]; var field = scale.field; record[field] = record.origin[field]; } } } if (adjust.type === 'dodge') { for (var i = 0, len = groupedArray.length; i < len; i++) { // 如果是dodge, 需要处理数字再处理 this._numberic(groupedArray[i]); } } var adjustData = adjust.adjust.process(groupedArray); // process 返回的是新数组,所以要修改 records records.forEach(function (record, index) { record.children = adjustData[index]; }); return adjustData; }; Geometry.prototype._updateStackRange = function (field, scale, dataArray) { var flattenArray = flatten(dataArray); var min = Infinity; var max = -Infinity; for (var i = 0, len = flattenArray.length; i < len; i++) { var obj = flattenArray[i]; 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 }); } }; Geometry.prototype._processData = function () { var props = this.props; var originData = props.data; var data = this._saveOrigin(originData); // 根据分类度量进行数据分组 var records = this._groupData(data); this._createAdjust(); // 根据adjust分组 var dataArray = this._adjustData(records); this.dataArray = dataArray; // scale适配调整,主要是调整 y 轴是否从 0 开始 以及 饼图 this._adjustScales(); // 数据排序(非必须) if (this.sortable) { this._sortData(records); } this.dataRecords = records; }; Geometry.prototype._readjustData = function (records) { var adjust = this.adjust; if (!adjust) return; // 根据adjust分组 var dataArray = this._adjustData(records); this.dataArray = dataArray; }; Geometry.prototype._sortData = function (records) { var xScale = this.getXScale(); var field = xScale.field, type = xScale.type; if (type !== 'identity' && xScale.values.length > 1) { each(records, function (_a) { var children = _a.children; children.sort(function (record1, record2) { if (type === 'timeCat') { return toTimeStamp(record1[FIELD_ORIGIN][field]) - toTimeStamp(record2[FIELD_ORIGIN][field]); } var normalized1 = xScale.translate(record1[FIELD_ORIGIN][field]); var normalized2 = xScale.translate(record2[FIELD_ORIGIN][field]); if (isNaN(normalized1)) { return 1; } if (isNaN(normalized2)) { return -1; } return normalized1 - normalized2; }); }); } }; Geometry.prototype.getY0Value = function () { var _a = this, attrs = _a.attrs, props = _a.props; var chart = props.chart; var field = attrs.y.field; var scale = chart.getScale(field); return chart.scale.getZeroValue(scale); }; // 根据各属性映射的值域来获取真正的绘图属性 Geometry.prototype._getShapeStyle = function (shape, origin) { var _a = this, context = _a.context, props = _a.props, geomType = _a.geomType; var theme = context.theme; var shapeTheme = theme.shape[geomType] || {}; var defaultShapeStyle = shapeTheme.default; var shapeThemeStyle = shapeTheme[shape]; var style = props.style; var shapeStyle = __assign(__assign({}, defaultShapeStyle), shapeThemeStyle); if (!style || !isObject(style)) { return shapeStyle; } // @ts-ignore var field = style.field, styles = __rest(style, ["field"]); var value = field ? origin[field] : origin; each(styles, function (attr, key) { if (isFunction(attr)) { var attrValue = attr(value); if (!attrValue) { return; } shapeStyle[key] = attrValue; return; } shapeStyle[key] = attr; }); return shapeStyle; }; /** * 数据映射到视图属性核心逻辑 * x、y 每个元素走 normalize 然后 convertPoint * color、size、shape * 如果是Linear,则每个元素 走 mapping * 如果是Category/Identity 则第一个元素走 mapping */ Geometry.prototype._mapping = function (records) { var _a = this, attrs = _a.attrs, props = _a.props, attrController = _a.attrController; var coord = props.coord; var _b = attrController.getAttrsByLinear(), linearAttrs = _b.linearAttrs, nonlinearAttrs = _b.nonlinearAttrs; var defaultAttrValues = attrController.getDefaultAttrValues(); var mappedRecords = []; for (var i = 0, len = records.length; i < len; i++) { var record = records[i]; var children = record.children; var attrValues = __assign({}, defaultAttrValues); var firstChild = children[0]; if (children.length === 0) { mappedRecords.push(__assign({}, record)); continue; } // 非线性映射 for (var k = 0, len_2 = nonlinearAttrs.length; k < len_2; k++) { var attrName = nonlinearAttrs[k]; var attr = attrs[attrName]; // 非线性映射只用映射第一项就可以了 attrValues[attrName] = attr.mapping(firstChild[attr.field], firstChild.origin); } // 线性属性映射 var mappedChildren = []; for (var j = 0, childrenLen = children.length; j < childrenLen; j++) { var child = children[j]; var normalized = {}; for (var k = 0; k < linearAttrs.length; k++) { var attrName = linearAttrs[k]; var attr = attrs[attrName]; var value = child[attr.field]; // 分类属性的线性映射 if (attrController.isGroupAttr(attrName)) { attrValues[attrName] = attr.mapping(value, child); } else { normalized[attrName] = attr.normalize(value); } } var _c = coord.convertPoint({ x: normalized.x, y: normalized.y }), x = _c.x, y = _c.y; // 获取 shape 的 style var origin_1 = child.origin; var shapeName = attrValues.shape; var shape = this._getShapeStyle(shapeName, origin_1); var selected = this.isSelected(child); mappedChildren.push(__assign(__assign(__assign({}, child), attrValues), { normalized: normalized, x: x, y: y, shapeName: shapeName, shape: shape, selected: selected })); } mappedRecords.push(__assign(__assign({}, record), { children: mappedChildren })); } return mappedRecords; }; // 数据映射 Geometry.prototype.mapping = function () { var dataRecords = this.dataRecords; // 数据映射 this.records = this._mapping(dataRecords); return this.records; }; Geometry.prototype.getClip = function () { var _a = this.props, coord = _a.coord, viewClip = _a.viewClip; var contentWidth = coord.width, contentHeight = coord.height, left = coord.left, top = coord.top; if (viewClip) { return { type: 'rect', style: { x: left, y: top, width: contentWidth, height: contentHeight } }; } return null; }; Geometry.prototype.getAttr = function (attrName) { return this.attrController.getAttr(attrName); }; Geometry.prototype.getXScale = function () { return this.getAttr('x').scale; }; Geometry.prototype.getYScale = function () { return this.getAttr('y').scale; }; Geometry.prototype._getXSnap = function (invertPointX) { var xScale = this.getXScale(); if (xScale.isCategory) { return xScale.invert(invertPointX); } // linear 类型 var invertValue = xScale.invert(invertPointX); var values = xScale.values; var len = values.length; // 如果只有1个点直接返回第1个点 if (len === 1) { return values[0]; } // 第1个点和第2个点之间 if ((values[0] + values[1]) / 2 > invertValue) { return values[0]; } // 最后2个点 if ((values[len - 2] + values[len - 1]) / 2 <= invertValue) { return values[len - 1]; } for (var i = 1; i < len; i++) { // 中间的点 if ((values[i - 1] + values[i]) / 2 <= invertValue && (values[i + 1] + values[i]) / 2 > invertValue) { return values[i]; } } return null; }; Geometry.prototype._getYSnapRecords = function (invertPointY, records) { var yScale = this.getYScale(); var yField = yScale.field; var yValue = yScale.invert(invertPointY); // category if (yScale.isCategory) { return records.filter(function (record) { return record[FIELD_ORIGIN][yField] === yValue; }); } // linear return records.filter(function (record) { var rangeY = record[yField]; if (rangeY[0] <= yValue && rangeY[1] >= yValue) { return true; } return false; }); }; // 把 records 拍平 Geometry.prototype.flatRecords = function () { var records = this.records; return records.reduce(function (prevRecords, record) { return prevRecords.concat(record.children); }, []); }; Geometry.prototype.getSnapRecords = function (point, inCoordRange) { var props = this.props; var coord = props.coord, adjust = props.adjust; var invertPoint = coord.invertPoint(point); var xScale = this.getXScale(); var yScale = this.getYScale(); // 如果不在coord坐标范围内,直接返回空 // if (invertPoint.x < 0 || invertPoint.y < 0) { // return []; // } // 是否调整 point,默认为不调整 if (inCoordRange) { var xRange = xScale.range; var yRange = yScale.range; // 如果 inCoordRange=true,当 point 不在 coord 坐标范围内时,调整到 range 内 invertPoint.x = Math.min(Math.max(invertPoint.x, xRange[0]), xRange[1]); invertPoint.y = Math.min(Math.max(invertPoint.y, yRange[0]), yRange[1]); } var records = this.flatRecords(); var xValue = xScale.invert(invertPoint.x); var yValue = yScale.invert(invertPoint.y); var coordPoint = coord.convertPoint(invertPoint); var coordRecord = { // 坐标点 x: coordPoint.x, y: coordPoint.y, xValue: xValue, yValue: yValue, xText: xScale.getText(xValue), yText: yScale.getText(yValue) }; // 处理饼图 if (adjust === 'stack' && coord.isPolar && coord.transposed) { // 弧度在半径范围内 if (invertPoint.x >= 0 && invertPoint.x <= 1) { var snapRecords = this._getYSnapRecords(invertPoint.y, records); return snapRecords; } } var rst = []; var value = this._getXSnap(invertPoint.x); if (isNull(value)) { return rst; } var xField = xScale.field; var yField = yScale.field; for (var i = 0, len = records.length; i < len; i++) { var record = __assign(__assign({}, records[i]), { xField: xField, yField: yField, coord: coordRecord }); var originValue = record[FIELD_ORIGIN][xField]; if (xScale.type === 'timeCat' && toTimeStamp(originValue) === value) { rst.push(record); } else if (originValue === value) { rst.push(record); } } return rst; }; Geometry.prototype.getRecords = function (data, field) { if (field === void 0) { field = 'xfield'; } var records = this.flatRecords(); var xScale = this.getXScale(); var yScale = this.getYScale(); var xField = xScale.field; var yField = yScale.field; var value = data[xField]; var rst = []; for (var i = 0, len = records.length; i < len; i++) { var record = __assign(__assign({}, records[i]), { xField: xField, yField: yField }); var originValue = record[FIELD_ORIGIN][field === 'xfield' ? xField : yField]; if (originValue === value) { rst.push(record); } } return rst; }; Geometry.prototype.getLegendItems = function () { var _a = this, attrController = _a.attrController, records = _a.records; var colorAttr = attrController.getAttr('color'); if (!colorAttr) return null; var scale = colorAttr.scale; var isCategory = scale.isCategory, field = scale.field; if (!isCategory) return null; var flatRecords = records ? this.flatRecords() : []; var ticks = scale.getTicks(); var items = ticks.map(function (tick) { var text = tick.text, tickValue = tick.tickValue; var record = find(flatRecords, function (item) { if (!item) return false; var origin = item.origin; return origin[field] === tickValue; }); // @ts-ignore var color = record ? record.color : colorAttr.mapping(tickValue); return { field: scale.field, color: color, name: text, tickValue: tickValue }; }); return items; }; return Geometry; }(Selection); export default Geometry;