UNPKG

@antv/f2

Version:

Charts for mobile visualization.

653 lines 21.7 kB
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties"; import _defineProperty from "@babel/runtime/helpers/esm/defineProperty"; import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck"; import _createClass from "@babel/runtime/helpers/esm/createClass"; import _assertThisInitialized from "@babel/runtime/helpers/esm/assertThisInitialized"; import _get from "@babel/runtime/helpers/esm/get"; import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf"; import _inherits from "@babel/runtime/helpers/esm/inherits"; import _createSuper from "@babel/runtime/helpers/esm/createSuper"; var _excluded = ["field"]; import { isFunction, each, upperFirst, mix, groupToMap, isObject, flatten } from '@antv/util'; import Selection from './selection'; import { getAdjust } from '@antv/adjust'; import { toTimeStamp } from '../../util/index'; import AttrController from '../../controller/attr'; import equal from '../../base/equal'; // 保留原始数据的字段 var FIELD_ORIGIN = 'origin'; var Geometry = /*#__PURE__*/function (_Selection) { _inherits(Geometry, _Selection); var _super = _createSuper(Geometry); function Geometry(props, context) { var _this; _classCallCheck(this, Geometry); _this = _super.call(this, props, context); _this.isGeometry = true; // x 轴居中 _this.justifyContent = false; // y 轴是否从0开始 _this.startOnZero = false; // 是否连接空值 _this.connectNulls = false; // 是否需要排序 _this.sortable = false; mix(_assertThisInitialized(_this), _this.getDefaultCfg()); var chart = props.chart, coord = props.coord; var attrsRange = _this._getThemeAttrsRange(); _this.attrController = new AttrController(chart.scale, attrsRange); var _assertThisInitialize = _assertThisInitialized(_this), attrController = _assertThisInitialize.attrController, justifyContent = _assertThisInitialize.justifyContent; var attrOptions = attrController.getAttrOptions(props, !coord.isCyclic() || justifyContent); attrController.create(attrOptions); return _this; } _createClass(Geometry, [{ key: "getDefaultCfg", value: function getDefaultCfg() { return {}; } }, { key: "willReceiveProps", value: function willReceiveProps(nextProps) { _get(_getPrototypeOf(Geometry.prototype), "willReceiveProps", this).call(this, nextProps); var lastProps = this.props, attrController = this.attrController, justifyContent = this.justifyContent; var nextData = nextProps.data, nextAdjust = nextProps.adjust, nextZoomRange = nextProps.zoomRange, coord = nextProps.coord; var lastData = lastProps.data, lastAdjust = lastProps.adjust, lastZoomRange = lastProps.zoomRange; var justifyContentCenter = !coord.isCyclic() || justifyContent; var nextAttrOptions = attrController.getAttrOptions(nextProps, justifyContentCenter); var lastAttrOptions = attrController.getAttrOptions(lastProps, justifyContentCenter); if (!equal(nextAttrOptions, lastAttrOptions)) { attrController.update(nextAttrOptions); this.records = null; } // 重新处理数据 if (nextData !== lastData) { this.records = null; } // 重新处理数据 if (nextAdjust !== lastAdjust) { this.records = null; } // zoomRange发生变化,records也需要重新计算 if (!equal(nextZoomRange, lastZoomRange)) { this.records = null; } } }, { key: "willMount", value: function willMount() { this._createAttrs(); if (!this.records) { this._processData(); } } }, { key: "willUpdate", value: function willUpdate() { this._createAttrs(); if (!this.records) { this._processData(); } } }, { key: "didMount", value: function didMount() { _get(_getPrototypeOf(Geometry.prototype), "didMount", this).call(this); this._initEvent(); } }, { key: "_createAttrs", value: function _createAttrs() { var attrController = this.attrController; attrController.attrs = {}; this.attrs = attrController.getAttrs(); } }, { key: "_getThemeAttrsRange", value: function _getThemeAttrsRange() { var context = this.context, props = this.props, geomType = this.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] }; } }, { key: "_adjustScales", value: function _adjustScales() { var attrs = this.attrs, props = this.props, defaultStartOnZero = this.startOnZero; var chart = props.chart, _props$startOnZero = props.startOnZero, startOnZero = _props$startOnZero === void 0 ? defaultStartOnZero : _props$startOnZero, 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 = attrs.y; chart.scale.adjustStartZero(_y.scale); } // 饼图的scale调整,关闭nice if (isPolar && transposed && (adjust === 'stack' || (adjust === null || adjust === void 0 ? void 0 : adjust.type) === 'stack')) { var _y2 = attrs.y; chart.scale.adjustPieScale(_y2.scale); } if (adjust === 'stack' || (adjust === null || adjust === void 0 ? void 0 : adjust.type) === 'stack') { this._updateStackRange(yField, y.scale, this.dataArray); } } }, { key: "_groupData", value: function _groupData(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; } }, { key: "_saveOrigin", value: function _saveOrigin(originData) { var len = originData.length; var data = new Array(len); for (var i = 0; i < len; i++) { var record = originData[i]; data[i] = _objectSpread(_objectSpread({}, record), {}, _defineProperty({}, FIELD_ORIGIN, record)); } return data; } }, { key: "_numberic", value: function _numberic(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; obj[field] = scale.translate(obj[field]); } } } } }, { key: "_adjustData", value: function _adjustData(records) { var attrs = this.attrs, props = this.props; var adjust = props.adjust; // groupedArray 是二维数组 var groupedArray = records.map(function (record) { return record.children; }); if (!adjust) { return groupedArray; } var adjustCfg = typeof adjust === 'string' ? { type: adjust } : adjust; var adjustType = upperFirst(adjustCfg.type); var AdjustConstructor = getAdjust(adjustType); if (!AdjustConstructor) { throw new Error('not support such adjust : ' + adjust); } if (adjustType === 'Dodge') { for (var i = 0, len = groupedArray.length; i < len; i++) { // 如果是dodge, 需要处理数字再处理 this._numberic(groupedArray[i]); } adjustCfg.adjustNames = ['x']; } var x = attrs.x, y = attrs.y; adjustCfg.xField = x.field; adjustCfg.yField = y.field; var adjustInstance = new AdjustConstructor(adjustCfg); var adjustData = adjustInstance.process(groupedArray); this.adjust = { type: adjustCfg.type, adjust: adjustInstance }; // process 返回的是新数组,所以要修改 records records.forEach(function (record, index) { record.children = adjustData[index]; }); return adjustData; } }, { key: "_updateStackRange", value: function _updateStackRange(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 }); } } }, { key: "_processData", value: function _processData() { var props = this.props; var originData = props.data; var data = this._saveOrigin(originData); // 根据分类度量进行数据分组 var records = this._groupData(data); // 根据adjust分组 var dataArray = this._adjustData(records); this.dataArray = dataArray; // scale适配调整,主要是调整 y 轴是否从 0 开始 以及 饼图 this._adjustScales(); // 数据排序(非必须) if (this.sortable) { this._sortData(records); } this.records = records; } }, { key: "_sortData", value: function _sortData(records) { var xScale = this.getXScale(); var field = xScale.field, type = xScale.type; if (type !== 'identity' && xScale.values.length > 1) { each(records, function (_ref) { var children = _ref.children; children.sort(function (record1, record2) { if (type === 'timeCat') { return toTimeStamp(record1[FIELD_ORIGIN][field]) - toTimeStamp(record2[FIELD_ORIGIN][field]); } return xScale.translate(record1[FIELD_ORIGIN][field]) - xScale.translate(record2[FIELD_ORIGIN][field]); }); }); } } }, { key: "_initEvent", value: function _initEvent() { var _this2 = this; var container = this.container, props = this.props; var canvas = container.get('canvas'); ['onPressStart', 'onPress', 'onPressEnd', 'onPan', 'onPanStart', 'onPanEnd'].forEach(function (eventName) { if (props[eventName]) { canvas.on(eventName.substr(2).toLowerCase(), function (ev) { ev.geometry = _this2; props[eventName](ev); }); } }); } }, { key: "getY0Value", value: function getY0Value() { var attrs = this.attrs, props = this.props; var chart = props.chart; var field = attrs.y.field; var scale = chart.getScale(field); return chart.scale.getZeroValue(scale); } // 根据各属性映射的值域来获取真正的绘图属性 }, { key: "_getShapeStyle", value: function _getShapeStyle(shape, origin) { var context = this.context, props = this.props, geomType = this.geomType; var theme = context.theme; var shapeTheme = theme.shape[geomType] || {}; var defaultShapeStyle = shapeTheme.default; var shapeThemeStyle = shapeTheme[shape]; var style = props.style; var shapeStyle = _objectSpread(_objectSpread({}, defaultShapeStyle), shapeThemeStyle); if (!style || !isObject(style)) { return shapeStyle; } // @ts-ignore var field = style.field, styles = _objectWithoutProperties(style, _excluded); var value = field ? origin[field] : origin; each(styles, function (attr, key) { if (isFunction(attr)) { shapeStyle[key] = attr(value); } else { shapeStyle[key] = attr; } }); return shapeStyle; } /** * 数据映射到视图属性核心逻辑 * x、y 每个元素走 normalize 然后 convertPoint * color、size、shape * 如果是Linear,则每个元素 走 mapping * 如果是Category/Identity 则第一个元素走 mapping */ }, { key: "_mapping", value: function _mapping(records) { var attrs = this.attrs, props = this.props, attrController = this.attrController; var coord = props.coord; var _attrController$getAt = attrController.getAttrsByLinear(), linearAttrs = _attrController$getAt.linearAttrs, nonlinearAttrs = _attrController$getAt.nonlinearAttrs; var defaultAttrValues = attrController.getDefaultAttrValues(); for (var i = 0, len = records.length; i < len; i++) { var record = records[i]; var children = record.children; var attrValues = _objectSpread({}, defaultAttrValues); var firstChild = children[0]; if (children.length === 0) { continue; } // 非线性映射 for (var k = 0, _len = nonlinearAttrs.length; k < _len; k++) { var attrName = nonlinearAttrs[k]; var attr = attrs[attrName]; // 非线性映射只用映射第一项就可以了 attrValues[attrName] = attr.mapping(firstChild[attr.field]); } // 线性属性映射 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]; // 分类属性的线性映射 if (attrController.isGroupAttr(_attrName)) { attrValues[_attrName] = _attr.mapping(child[_attr.field], child); } else { normalized[_attrName] = _attr.normalize(child[_attr.field]); } } var _coord$convertPoint = coord.convertPoint({ x: normalized.x, y: normalized.y }), x = _coord$convertPoint.x, y = _coord$convertPoint.y; // 获取 shape 的 style var shapeName = attrValues.shape; var shape = this._getShapeStyle(shapeName, child.origin); var selected = this.isSelected(child); mix(child, attrValues, { normalized: normalized, x: x, y: y, shapeName: shapeName, shape: shape, selected: selected }); } } return records; } // 数据映射 }, { key: "mapping", value: function mapping() { var records = this.records; // 数据映射 this._mapping(records); return records; } }, { key: "getClip", value: function getClip() { var _this$props = this.props, coord = _this$props.coord, viewClip = _this$props.viewClip; var contentWidth = coord.width, contentHeight = coord.height, left = coord.left, top = coord.top; if (viewClip) { return { type: 'rect', attrs: { x: left, y: top, width: contentWidth, height: contentHeight } }; } return null; } }, { key: "getAttr", value: function getAttr(attrName) { return this.attrController.getAttr(attrName); } }, { key: "getXScale", value: function getXScale() { return this.getAttr('x').scale; } }, { key: "getYScale", value: function getYScale() { return this.getAttr('y').scale; } }, { key: "_getXSnap", value: function _getXSnap(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; } }, { key: "_getYSnapRecords", value: function _getYSnapRecords(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 拍平 }, { key: "flatRecords", value: function flatRecords() { var records = this.records; return records.reduce(function (prevRecords, record) { return prevRecords.concat(record.children); }, []); } }, { key: "getSnapRecords", value: function getSnapRecords(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(); // 处理饼图 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 (!value) { return rst; } var xField = xScale.field; var yField = yScale.field; for (var i = 0, len = records.length; i < len; i++) { var record = _objectSpread(_objectSpread({}, records[i]), {}, { xField: xField, yField: yField }); 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; } }, { key: "getRecords", value: function getRecords(data) { var field = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '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 = _objectSpread(_objectSpread({}, records[i]), {}, { xField: xField, yField: yField }); var originValue = record[FIELD_ORIGIN][field === 'xfield' ? xField : yField]; if (originValue === value) { rst.push(record); } } return rst; } }, { key: "getLegendItems", value: function getLegendItems() { var attrController = this.attrController; var colorAttr = attrController.getAttr('color'); if (!colorAttr) return null; var scale = colorAttr.scale; if (!scale.isCategory) return null; var ticks = scale.getTicks(); var items = ticks.map(function (tick) { var text = tick.text, tickValue = tick.tickValue; var color = colorAttr.mapping(tickValue); return { field: scale.field, color: color, name: text, tickValue: tickValue }; }); return items; } }]); return Geometry; }(Selection); export default Geometry;