UNPKG

4.09 kBTypeScriptView Raw
1import { jsx } from '../../jsx';
2import Component from '../../base/component';
3import { isString, isNil, isFunction } from '@antv/util';
4import { Ref } from '../../types';
5import Chart from '../../chart';
6import { renderShape } from '../../base/diff';
7
8function isInBBox(bbox, point) {
9 const { minX, maxX, minY, maxY } = bbox;
10 const { x, y } = point;
11 return minX <= x && maxX >= x && minY <= y && maxY >= y;
12}
13
14export default (View) => {
15 return class Guide extends Component {
16 chart: Chart;
17 triggerRef: Ref;
18
19 constructor(props) {
20 super(props);
21 // 创建ref
22 this.triggerRef = {};
23 this.state = {};
24 }
25
26 willMount() {
27 super.willMount();
28 this.getGuideBBox();
29 }
30
31 didMount() {
32 const { context, props } = this;
33 const { canvas } = context;
34 const { onClick } = props;
35
36 canvas.on('click', (ev) => {
37 const { points } = ev;
38 const shape = this.triggerRef.current;
39 if (!shape || shape.isDestroyed()) return;
40 const bbox = shape.getBBox();
41 if (isInBBox(bbox, points[0])) {
42 ev.shape = shape;
43 onClick && onClick(ev);
44 }
45 });
46 }
47
48 didUpdate() {
49 super.didUpdate();
50 const shape = this.triggerRef.current;
51 if (!shape || shape.isDestroyed()) return;
52 const { x, y, width, height } = shape.get('attrs');
53 const bbox = {
54 minX: x,
55 minY: y,
56 maxX: x + width,
57 maxY: y + height,
58 width,
59 height,
60 };
61 this.setState({
62 guideBBox: bbox,
63 });
64 }
65
66 getGuideBBox() {
67 const shape = renderShape(this, this.render(), false);
68 const { x, y, width, height } = shape.get('attrs');
69 // getBBox 没有包含 padding 所以这里手动计算 bbox
70 const bbox = {
71 minX: x,
72 minY: y,
73 maxX: x + width,
74 maxY: y + height,
75 width,
76 height,
77 };
78 this.setState({
79 guideBBox: bbox,
80 });
81 shape.destroy();
82 }
83
84 // 解析record里的模板字符串,如min、max、50%...
85 parseReplaceStr(value, scale) {
86 const replaceMap = {
87 min: 0,
88 max: 1,
89 median: 0.5,
90 };
91
92 // 传入的是 min、max、median 的
93 if (!isNil(replaceMap[value])) {
94 return replaceMap[value];
95 }
96
97 // 传入的是 xx%
98 if (isString(value) && value.indexOf('%') != -1 && !isNaN(Number(value.slice(0, -1)))) {
99 const rateValue = Number(value.slice(0, -1));
100 const percent = rateValue / 100;
101 return percent;
102 }
103
104 return scale.scale(value);
105 }
106
107 parsePoint(record) {
108 const { props } = this;
109 const { chart, coord } = props;
110 const xScale = chart.getXScales()[0];
111 // 只取第一个yScale
112 const yScale = chart.getYScales()[0];
113
114 // 解析 record 为归一化后的坐标
115 const x = this.parseReplaceStr(record[xScale.field], xScale);
116 const y = this.parseReplaceStr(record[yScale.field], yScale);
117
118 return coord.convertPoint({ x, y });
119 }
120
121 convertPoints(records) {
122 return records.map((record) => this.parsePoint(record));
123 }
124
125 getGuideTheme() {
126 const { context } = this;
127 const { theme } = context;
128 return theme.guide;
129 }
130
131 render() {
132 const { props, context } = this;
133 const { coord, records = [], animation, chart } = props;
134 const { width, height } = context;
135 const points = this.convertPoints(records);
136 const theme = this.getGuideTheme();
137 const { guideBBox } = this.state;
138
139 let animationCfg = animation;
140 if (isFunction(animation)) {
141 // 透传绘制关键点和chart实例
142 animationCfg = animation(points, chart);
143 }
144
145 return (
146 <View
147 triggerRef={this.triggerRef}
148 points={points}
149 theme={theme}
150 coord={coord}
151 {...props}
152 canvasWidth={width}
153 canvasHeight={height}
154 guideBBox={guideBBox}
155 animation={animationCfg}
156 />
157 );
158 }
159 };
160};