UNPKG

22.5 kBJavaScriptView Raw
1import { __assign, __extends, __rest } from "tslib";
2import { isFunction, each, upperFirst, mix, groupToMap, isObject, flatten, isNull, find } from '@antv/util';
3import Selection from './selection';
4import { Dodge, Jitter, Stack, Symmetric } from '../../deps/f2-adjust/src';
5import { toTimeStamp } from '../../util/index';
6import AttrController from '../../controller/attr';
7import { isEqual } from '@antv/f-engine';
8var AdjustMap = {
9 Stack: Stack,
10 Dodge: Dodge,
11 Jitter: Jitter,
12 Symmetric: Symmetric
13};
14// 保留原始数据的字段
15var FIELD_ORIGIN = 'origin';
16var Geometry = /** @class */function (_super) {
17 __extends(Geometry, _super);
18 function Geometry(props, context) {
19 var _this = _super.call(this, props, context) || this;
20 _this.isGeometry = true;
21 // x 轴居中
22 _this.justifyContent = false;
23 // y 轴是否从0开始
24 _this.startOnZero = false;
25 // 是否连接空值
26 _this.connectNulls = false;
27 // 是否需要排序
28 _this.sortable = false;
29 mix(_this, _this.getDefaultCfg());
30 var chart = props.chart,
31 coord = props.coord;
32 var attrsRange = _this._getThemeAttrsRange();
33 _this.attrController = new AttrController(chart.scale, attrsRange);
34 var _a = _this,
35 attrController = _a.attrController,
36 justifyContent = _a.justifyContent;
37 var attrOptions = attrController.getAttrOptions(props, !coord.isCyclic() || justifyContent);
38 attrController.create(attrOptions);
39 return _this;
40 }
41 Geometry.prototype.getDefaultCfg = function () {
42 return {};
43 };
44 Geometry.prototype.willReceiveProps = function (nextProps) {
45 var _a = this,
46 lastProps = _a.props,
47 attrController = _a.attrController,
48 justifyContent = _a.justifyContent;
49 var nextData = nextProps.data,
50 nextAdjust = nextProps.adjust,
51 coord = nextProps.coord,
52 selection = nextProps.selection;
53 var lastData = lastProps.data,
54 lastAdjust = lastProps.adjust,
55 lastSelection = lastProps.selection;
56 var justifyContentCenter = !coord.isCyclic() || justifyContent;
57 var lastAttrOptions = attrController.getAttrOptions(lastProps, justifyContentCenter);
58 attrController.attrsRange = this._getThemeAttrsRange();
59 var nextAttrOptions = attrController.getAttrOptions(nextProps, justifyContentCenter);
60 if (!isEqual(nextAttrOptions, lastAttrOptions)) {
61 attrController.update(nextAttrOptions);
62 this.dataRecords = null;
63 }
64 // 重新处理数据
65 if (nextData !== lastData) {
66 this.dataRecords = null;
67 }
68 // 重新处理数据
69 if (nextAdjust !== lastAdjust) {
70 this.dataRecords = null;
71 }
72 // selection 发生变化
73 if (!isEqual(selection, lastSelection)) {
74 _super.prototype.willReceiveProps.call(this, nextProps);
75 }
76 };
77 Geometry.prototype.willMount = function () {
78 this._createAttrs();
79 if (!this.dataRecords) {
80 this._processData();
81 }
82 };
83 Geometry.prototype.willUpdate = function () {
84 this._createAttrs();
85 if (!this.dataRecords) {
86 this._processData();
87 } else {
88 this._readjustData(this.dataRecords);
89 }
90 };
91 Geometry.prototype.didMount = function () {
92 this._initEvent();
93 _super.prototype.didMount.call(this);
94 // 更新 attrController
95 this.attrController.attrsRange = this._getThemeAttrsRange();
96 };
97 Geometry.prototype._initEvent = function () {
98 var _this = this;
99 var props = this.props;
100 var chart = props.chart;
101 ['onPressStart', 'onPress', 'onPressEnd', 'onPan', 'onPanStart', 'onPanEnd'].forEach(function (eventName) {
102 if (props[eventName]) {
103 chart.on(eventName.substr(2).toLowerCase(), function (ev) {
104 ev.geometry = _this;
105 props[eventName](ev);
106 });
107 }
108 });
109 };
110 Geometry.prototype._createAttrs = function () {
111 var attrController = this.attrController;
112 attrController.attrs = {};
113 this.attrs = attrController.getAttrs();
114 };
115 Geometry.prototype._getThemeAttrsRange = function () {
116 var _a = this,
117 context = _a.context,
118 props = _a.props,
119 geomType = _a.geomType;
120 var coord = props.coord;
121 var theme = context.theme;
122 var colors = theme.colors,
123 sizes = theme.sizes,
124 shapes = theme.shapes;
125 return {
126 x: coord.x,
127 y: coord.y,
128 color: colors,
129 size: sizes,
130 shape: shapes[geomType]
131 };
132 };
133 Geometry.prototype._createAdjust = function () {
134 var _a = this,
135 attrs = _a.attrs,
136 props = _a.props;
137 var adjust = props.adjust;
138 if (!adjust) {
139 return null;
140 }
141 var adjustCfg = typeof adjust === 'string' ? {
142 type: adjust
143 } : adjust;
144 var adjustType = upperFirst(adjustCfg.type);
145 var AdjustConstructor = AdjustMap[adjustType];
146 if (!AdjustConstructor) {
147 throw new Error('not support such adjust : ' + adjust);
148 }
149 if (adjustType === 'Dodge') {
150 // @ts-ignore
151 adjustCfg.adjustNames = ['x'];
152 }
153 var x = attrs.x,
154 y = attrs.y;
155 // @ts-ignore
156 adjustCfg.xField = x.field;
157 // @ts-ignore
158 adjustCfg.yField = y.field;
159 var adjustInstance = new AdjustConstructor(adjustCfg);
160 this.adjust = {
161 type: adjustCfg.type,
162 adjust: adjustInstance
163 };
164 return this.adjust;
165 };
166 Geometry.prototype._adjustScales = function () {
167 var _a = this,
168 attrs = _a.attrs,
169 props = _a.props,
170 defaultStartOnZero = _a.startOnZero;
171 var chart = props.chart,
172 _b = props.startOnZero,
173 startOnZero = _b === void 0 ? defaultStartOnZero : _b,
174 coord = props.coord,
175 adjust = props.adjust;
176 var isPolar = coord.isPolar,
177 transposed = coord.transposed;
178 var y = attrs.y;
179 var yField = y.field;
180 // 如果从 0 开始,只调整 y 轴 scale
181 if (startOnZero) {
182 var y_1 = attrs.y;
183 chart.scale.adjustStartZero(y_1.scale);
184 }
185 // 饼图的scale调整,关闭nice
186 if (isPolar && transposed && (adjust === 'stack' || (adjust === null || adjust === void 0 ? void 0 : adjust.type) === 'stack')) {
187 var y_2 = attrs.y;
188 chart.scale.adjustPieScale(y_2.scale);
189 }
190 if (adjust === 'stack' || (adjust === null || adjust === void 0 ? void 0 : adjust.type) === 'stack') {
191 this._updateStackRange(yField, y.scale, this.dataArray);
192 }
193 };
194 Geometry.prototype._groupData = function (data) {
195 var attrController = this.attrController;
196 var groupScales = attrController.getGroupScales();
197 if (!groupScales.length) {
198 return [{
199 children: data
200 }];
201 }
202 var names = [];
203 groupScales.forEach(function (scale) {
204 var field = scale.field;
205 names.push(field);
206 });
207 var groups = groupToMap(data, names);
208 var records = [];
209 for (var key in groups) {
210 records.push({
211 key: key.replace(/^_/, ''),
212 children: groups[key]
213 });
214 }
215 return records;
216 };
217 Geometry.prototype._saveOrigin = function (originData) {
218 var _a;
219 var len = originData.length;
220 var data = new Array(len);
221 for (var i = 0; i < len; i++) {
222 var record = originData[i];
223 data[i] = __assign(__assign({}, record), (_a = {}, _a[FIELD_ORIGIN] = record, _a));
224 }
225 return data;
226 };
227 Geometry.prototype._numberic = function (data) {
228 var attrs = this.attrs;
229 var scales = [attrs.x.scale, attrs.y.scale];
230 for (var j = 0, len = data.length; j < len; j++) {
231 var obj = data[j];
232 var count = scales.length;
233 for (var i = 0; i < count; i++) {
234 var scale = scales[i];
235 if (scale.isCategory) {
236 var field = scale.field;
237 var value = scale.translate(obj.origin[field]);
238 obj[field] = value;
239 }
240 }
241 }
242 };
243 Geometry.prototype._adjustData = function (records) {
244 var adjust = this.adjust;
245 // groupedArray 是二维数组
246 var groupedArray = records.map(function (record) {
247 return record.children;
248 });
249 if (!adjust) {
250 return groupedArray;
251 }
252 var attrs = this.attrs;
253 var scales = [attrs.x.scale, attrs.y.scale];
254 for (var i = 0, len = groupedArray.length; i < len; i++) {
255 var records_1 = groupedArray[i];
256 for (var j = 0, len_1 = records_1.length; j < len_1; j++) {
257 var record = records_1[j];
258 var count = scales.length;
259 for (var i_1 = 0; i_1 < count; i_1++) {
260 var scale = scales[i_1];
261 var field = scale.field;
262 record[field] = record.origin[field];
263 }
264 }
265 }
266 if (adjust.type === 'dodge') {
267 for (var i = 0, len = groupedArray.length; i < len; i++) {
268 // 如果是dodge, 需要处理数字再处理
269 this._numberic(groupedArray[i]);
270 }
271 }
272 var adjustData = adjust.adjust.process(groupedArray);
273 // process 返回的是新数组,所以要修改 records
274 records.forEach(function (record, index) {
275 record.children = adjustData[index];
276 });
277 return adjustData;
278 };
279 Geometry.prototype._updateStackRange = function (field, scale, dataArray) {
280 var flattenArray = flatten(dataArray);
281 var min = Infinity;
282 var max = -Infinity;
283 for (var i = 0, len = flattenArray.length; i < len; i++) {
284 var obj = flattenArray[i];
285 var tmpMin = Math.min.apply(null, obj[field]);
286 var tmpMax = Math.max.apply(null, obj[field]);
287 if (tmpMin < min) {
288 min = tmpMin;
289 }
290 if (tmpMax > max) {
291 max = tmpMax;
292 }
293 }
294 if (min !== scale.min || max !== scale.max) {
295 scale.change({
296 min: min,
297 max: max
298 });
299 }
300 };
301 Geometry.prototype._processData = function () {
302 var props = this.props;
303 var originData = props.data;
304 var data = this._saveOrigin(originData);
305 // 根据分类度量进行数据分组
306 var records = this._groupData(data);
307 this._createAdjust();
308 // 根据adjust分组
309 var dataArray = this._adjustData(records);
310 this.dataArray = dataArray;
311 // scale适配调整,主要是调整 y 轴是否从 0 开始 以及 饼图
312 this._adjustScales();
313 // 数据排序(非必须)
314 if (this.sortable) {
315 this._sortData(records);
316 }
317 this.dataRecords = records;
318 };
319 Geometry.prototype._readjustData = function (records) {
320 var adjust = this.adjust;
321 if (!adjust) return;
322 // 根据adjust分组
323 var dataArray = this._adjustData(records);
324 this.dataArray = dataArray;
325 };
326 Geometry.prototype._sortData = function (records) {
327 var xScale = this.getXScale();
328 var field = xScale.field,
329 type = xScale.type;
330 if (type !== 'identity' && xScale.values.length > 1) {
331 each(records, function (_a) {
332 var children = _a.children;
333 children.sort(function (record1, record2) {
334 if (type === 'timeCat') {
335 return toTimeStamp(record1[FIELD_ORIGIN][field]) - toTimeStamp(record2[FIELD_ORIGIN][field]);
336 }
337 var normalized1 = xScale.translate(record1[FIELD_ORIGIN][field]);
338 var normalized2 = xScale.translate(record2[FIELD_ORIGIN][field]);
339 if (isNaN(normalized1)) {
340 return 1;
341 }
342 if (isNaN(normalized2)) {
343 return -1;
344 }
345 return normalized1 - normalized2;
346 });
347 });
348 }
349 };
350 Geometry.prototype.getY0Value = function () {
351 var _a = this,
352 attrs = _a.attrs,
353 props = _a.props;
354 var chart = props.chart;
355 var field = attrs.y.field;
356 var scale = chart.getScale(field);
357 return chart.scale.getZeroValue(scale);
358 };
359 // 根据各属性映射的值域来获取真正的绘图属性
360 Geometry.prototype._getShapeStyle = function (shape, origin) {
361 var _a = this,
362 context = _a.context,
363 props = _a.props,
364 geomType = _a.geomType;
365 var theme = context.theme;
366 var shapeTheme = theme.shape[geomType] || {};
367 var defaultShapeStyle = shapeTheme.default;
368 var shapeThemeStyle = shapeTheme[shape];
369 var style = props.style;
370 var shapeStyle = __assign(__assign({}, defaultShapeStyle), shapeThemeStyle);
371 if (!style || !isObject(style)) {
372 return shapeStyle;
373 }
374 // @ts-ignore
375 var field = style.field,
376 styles = __rest(style, ["field"]);
377 var value = field ? origin[field] : origin;
378 each(styles, function (attr, key) {
379 if (isFunction(attr)) {
380 var attrValue = attr(value);
381 if (!attrValue) {
382 return;
383 }
384 shapeStyle[key] = attrValue;
385 return;
386 }
387 shapeStyle[key] = attr;
388 });
389 return shapeStyle;
390 };
391 /**
392 * 数据映射到视图属性核心逻辑
393 * x、y 每个元素走 normalize 然后 convertPoint
394 * color、size、shape
395 * 如果是Linear,则每个元素 走 mapping
396 * 如果是Category/Identity 则第一个元素走 mapping
397 */
398 Geometry.prototype._mapping = function (records) {
399 var _a = this,
400 attrs = _a.attrs,
401 props = _a.props,
402 attrController = _a.attrController;
403 var coord = props.coord;
404 var _b = attrController.getAttrsByLinear(),
405 linearAttrs = _b.linearAttrs,
406 nonlinearAttrs = _b.nonlinearAttrs;
407 var defaultAttrValues = attrController.getDefaultAttrValues();
408 var mappedRecords = [];
409 for (var i = 0, len = records.length; i < len; i++) {
410 var record = records[i];
411 var children = record.children;
412 var attrValues = __assign({}, defaultAttrValues);
413 var firstChild = children[0];
414 if (children.length === 0) {
415 mappedRecords.push(__assign({}, record));
416 continue;
417 }
418 // 非线性映射
419 for (var k = 0, len_2 = nonlinearAttrs.length; k < len_2; k++) {
420 var attrName = nonlinearAttrs[k];
421 var attr = attrs[attrName];
422 // 非线性映射只用映射第一项就可以了
423 attrValues[attrName] = attr.mapping(firstChild[attr.field], firstChild.origin);
424 }
425 // 线性属性映射
426 var mappedChildren = [];
427 for (var j = 0, childrenLen = children.length; j < childrenLen; j++) {
428 var child = children[j];
429 var normalized = {};
430 for (var k = 0; k < linearAttrs.length; k++) {
431 var attrName = linearAttrs[k];
432 var attr = attrs[attrName];
433 var value = child[attr.field];
434 // 分类属性的线性映射
435 if (attrController.isGroupAttr(attrName)) {
436 attrValues[attrName] = attr.mapping(value, child);
437 } else {
438 normalized[attrName] = attr.normalize(value);
439 }
440 }
441 var _c = coord.convertPoint({
442 x: normalized.x,
443 y: normalized.y
444 }),
445 x = _c.x,
446 y = _c.y;
447 // 获取 shape 的 style
448 var origin_1 = child.origin;
449 var shapeName = attrValues.shape;
450 var shape = this._getShapeStyle(shapeName, origin_1);
451 var selected = this.isSelected(child);
452 mappedChildren.push(__assign(__assign(__assign({}, child), attrValues), {
453 normalized: normalized,
454 x: x,
455 y: y,
456 shapeName: shapeName,
457 shape: shape,
458 selected: selected
459 }));
460 }
461 mappedRecords.push(__assign(__assign({}, record), {
462 children: mappedChildren
463 }));
464 }
465 return mappedRecords;
466 };
467 // 数据映射
468 Geometry.prototype.mapping = function () {
469 var dataRecords = this.dataRecords;
470 // 数据映射
471 this.records = this._mapping(dataRecords);
472 return this.records;
473 };
474 Geometry.prototype.getClip = function () {
475 var _a = this.props,
476 coord = _a.coord,
477 viewClip = _a.viewClip;
478 var contentWidth = coord.width,
479 contentHeight = coord.height,
480 left = coord.left,
481 top = coord.top;
482 if (viewClip) {
483 return {
484 type: 'rect',
485 style: {
486 x: left,
487 y: top,
488 width: contentWidth,
489 height: contentHeight
490 }
491 };
492 }
493 return null;
494 };
495 Geometry.prototype.getAttr = function (attrName) {
496 return this.attrController.getAttr(attrName);
497 };
498 Geometry.prototype.getXScale = function () {
499 return this.getAttr('x').scale;
500 };
501 Geometry.prototype.getYScale = function () {
502 return this.getAttr('y').scale;
503 };
504 Geometry.prototype._getXSnap = function (invertPointX) {
505 var xScale = this.getXScale();
506 if (xScale.isCategory) {
507 return xScale.invert(invertPointX);
508 }
509 // linear 类型
510 var invertValue = xScale.invert(invertPointX);
511 var values = xScale.values;
512 var len = values.length;
513 // 如果只有1个点直接返回第1个点
514 if (len === 1) {
515 return values[0];
516 }
517 // 第1个点和第2个点之间
518 if ((values[0] + values[1]) / 2 > invertValue) {
519 return values[0];
520 }
521 // 最后2个点
522 if ((values[len - 2] + values[len - 1]) / 2 <= invertValue) {
523 return values[len - 1];
524 }
525 for (var i = 1; i < len; i++) {
526 // 中间的点
527 if ((values[i - 1] + values[i]) / 2 <= invertValue && (values[i + 1] + values[i]) / 2 > invertValue) {
528 return values[i];
529 }
530 }
531 return null;
532 };
533 Geometry.prototype._getYSnapRecords = function (invertPointY, records) {
534 var yScale = this.getYScale();
535 var yField = yScale.field;
536 var yValue = yScale.invert(invertPointY);
537 // category
538 if (yScale.isCategory) {
539 return records.filter(function (record) {
540 return record[FIELD_ORIGIN][yField] === yValue;
541 });
542 }
543 // linear
544 return records.filter(function (record) {
545 var rangeY = record[yField];
546 if (rangeY[0] <= yValue && rangeY[1] >= yValue) {
547 return true;
548 }
549 return false;
550 });
551 };
552 Geometry.prototype._getXSnapRecords = function (invertPointX, records) {
553 var xScale = this.getXScale();
554 var xField = xScale.field;
555 var xValue = xScale.invert(invertPointX);
556 // category
557 if (xScale.isCategory) {
558 return records.filter(function (record) {
559 return record[FIELD_ORIGIN][xField] === xValue;
560 });
561 }
562 // linear
563 return records.filter(function (record) {
564 var rangeX = record[xField];
565 if (rangeX[0] <= xValue && rangeX[1] >= xValue) {
566 return true;
567 }
568 return false;
569 });
570 };
571 // 把 records 拍平
572 Geometry.prototype.flatRecords = function () {
573 var records = this.records;
574 return records.reduce(function (prevRecords, record) {
575 return prevRecords.concat(record.children);
576 }, []);
577 };
578 Geometry.prototype.getSnapRecords = function (point, inCoordRange) {
579 var props = this.props;
580 var coord = props.coord,
581 adjust = props.adjust;
582 var invertPoint = coord.invertPoint(point);
583 var xScale = this.getXScale();
584 var yScale = this.getYScale();
585 // 如果不在coord坐标范围内,直接返回空
586 // if (invertPoint.x < 0 || invertPoint.y < 0) {
587 // return [];
588 // }
589 // 是否调整 point,默认为不调整
590 if (inCoordRange) {
591 var xRange = xScale.range;
592 var yRange = yScale.range;
593 // 如果 inCoordRange=true,当 point 不在 coord 坐标范围内时,调整到 range 内
594 invertPoint.x = Math.min(Math.max(invertPoint.x, xRange[0]), xRange[1]);
595 invertPoint.y = Math.min(Math.max(invertPoint.y, yRange[0]), yRange[1]);
596 }
597 var records = this.flatRecords();
598 var xValue = xScale.invert(invertPoint.x);
599 var yValue = yScale.invert(invertPoint.y);
600 var coordPoint = coord.convertPoint(invertPoint);
601 var coordRecord = {
602 // 坐标点
603 x: coordPoint.x,
604 y: coordPoint.y,
605 xValue: xValue,
606 yValue: yValue,
607 xText: xScale.getText(xValue),
608 yText: yScale.getText(yValue)
609 };
610 // 处理饼图
611 if (adjust === 'stack' && coord.isPolar) {
612 if (coord.transposed) {
613 // 弧度在半径范围内
614 if (invertPoint.x >= 0 && invertPoint.x <= 1) {
615 var snapRecords = this._getYSnapRecords(invertPoint.y, records);
616 return snapRecords;
617 }
618 } else {
619 if (invertPoint.y >= 0 && invertPoint.y <= 1) {
620 var snapRecords = this._getXSnapRecords(invertPoint.x, records);
621 return snapRecords;
622 }
623 }
624 }
625 var rst = [];
626 var value = this._getXSnap(invertPoint.x);
627 if (isNull(value)) {
628 return rst;
629 }
630 var xField = xScale.field;
631 var yField = yScale.field;
632 for (var i = 0, len = records.length; i < len; i++) {
633 var record = __assign(__assign({}, records[i]), {
634 xField: xField,
635 yField: yField,
636 coord: coordRecord
637 });
638 var originValue = record[FIELD_ORIGIN][xField];
639 if (xScale.type === 'timeCat' && toTimeStamp(originValue) === value) {
640 rst.push(record);
641 } else if (originValue === value) {
642 rst.push(record);
643 }
644 }
645 return rst;
646 };
647 Geometry.prototype.getRecords = function (data, field) {
648 if (field === void 0) {
649 field = 'xfield';
650 }
651 var records = this.flatRecords();
652 var xScale = this.getXScale();
653 var yScale = this.getYScale();
654 var xField = xScale.field;
655 var yField = yScale.field;
656 var value = data[xField];
657 var rst = [];
658 for (var i = 0, len = records.length; i < len; i++) {
659 var record = __assign(__assign({}, records[i]), {
660 xField: xField,
661 yField: yField
662 });
663 var originValue = record[FIELD_ORIGIN][field === 'xfield' ? xField : yField];
664 if (originValue === value) {
665 rst.push(record);
666 }
667 }
668 return rst;
669 };
670 Geometry.prototype.getLegendItems = function () {
671 var _a = this,
672 attrController = _a.attrController,
673 records = _a.records;
674 var colorAttr = attrController.getAttr('color');
675 if (!colorAttr) return null;
676 var scale = colorAttr.scale;
677 var isCategory = scale.isCategory,
678 field = scale.field;
679 if (!isCategory) return null;
680 var flatRecords = records ? this.flatRecords() : [];
681 var ticks = scale.getTicks();
682 var items = ticks.map(function (tick) {
683 var text = tick.text,
684 tickValue = tick.tickValue;
685 var record = find(flatRecords, function (item) {
686 if (!item) return false;
687 var origin = item.origin;
688 return origin[field] === tickValue;
689 });
690 // @ts-ignore
691 var color = record ? record.color : colorAttr.mapping(tickValue);
692 return {
693 field: scale.field,
694 color: color,
695 name: text,
696 tickValue: tickValue
697 };
698 });
699 return items;
700 };
701 return Geometry;
702}(Selection);
703export default Geometry;
\No newline at end of file