UNPKG

5.1 kBPlain TextView Raw
1import { each, mix, isNil, isFunction, isNumber, valuesOfKey, getRange } from '@antv/util';
2import { registerTickMethod, Scale, getScale, ScaleConfig } from '@antv/scale';
3import CatTick from './scale/cat-tick';
4import LinearTick from './scale/linear-tick';
5
6// 覆盖0.3.x的 cat 方法
7registerTickMethod('cat', CatTick);
8registerTickMethod('time-cat', CatTick);
9// 覆盖linear 度量的tick算法
10registerTickMethod('wilkinson-extended', LinearTick);
11
12export type ScaleOption = { type?: string; justifyContent?: boolean } & ScaleConfig;
13
14class ScaleController {
15 // eslint-disable-next-line
16 private data: any;
17 // scale 实例的配置
18 private options: { [field: string]: ScaleOption };
19 // scale 实例
20 private scales: { [field: string]: Scale };
21
22 constructor(data) {
23 this.data = data;
24 this.options = {};
25 this.scales = {};
26 }
27
28 private _getType(option: ScaleOption) {
29 const { type, values, field } = option;
30 if (type) {
31 return type;
32 }
33 if (isNumber(field) || (isNil(values[0]) && field)) {
34 return 'identity';
35 }
36 if (typeof values[0] === 'number') {
37 return 'linear';
38 }
39 return 'cat';
40 }
41
42 private _getOption(option: ScaleOption) {
43 const { values, field, justifyContent } = option;
44 const type = this._getType(option);
45
46 option.type = type;
47
48 // identity
49 if (type === 'identity') {
50 option.field = field.toString();
51 option.values = [field];
52 return option;
53 }
54 // linear 类型
55 if (type === 'linear') {
56 // 设置默认nice
57 if (typeof option.nice !== 'boolean') {
58 option.nice = true;
59 }
60 // 重置最大最小
61 const { min, max } = getRange(values);
62 if (isNil(option.min)) {
63 option.min = min;
64 }
65 if (isNil(option.max)) {
66 option.max = max;
67 }
68 return option;
69 }
70 // 分类类型和 timeCat 类型,调整 range
71 if (type === 'cat' || type === 'timeCat') {
72 if (option.range) {
73 return option;
74 }
75 const count = values.length;
76 let range = [0, 1];
77 // 如果只有一项,显示在中间
78 if (count === 1) {
79 range = [0.5, 1];
80 } else if (justifyContent) {
81 // 居中
82 const offset = (1 / count) * 0.5;
83 range = [offset, 1 - offset];
84 } else {
85 // 最后留 1 / count
86 const offset = 1 / count;
87 range = [0, 1 - offset];
88 }
89 option.range = range;
90 }
91
92 return option;
93 }
94
95 private createScale(option) {
96 const { type } = option;
97 if (isFunction(type)) {
98 return new type(option);
99 }
100 const ScaleClass = getScale(type);
101 return new ScaleClass(option);
102 }
103
104 // 更新或创建scale
105 setScale(field: string, option?: ScaleOption) {
106 const { options, scales } = this;
107 options[field] = mix({}, options[field], option);
108 // 如果scale有更新,scale 也需要重新创建
109 if (scales[field]) {
110 delete scales[field];
111 }
112 }
113
114 create(options: { [k: string]: ScaleOption }) {
115 this.update(options);
116 }
117
118 update(options: { [k: string]: ScaleOption }) {
119 if (!options) return;
120 each(options, (option: ScaleOption, field: string) => {
121 this.setScale(field, option);
122 });
123 // 为了让外部感知到scale有变化
124 this.scales = {
125 ...this.scales,
126 };
127 }
128
129 changeData(data) {
130 this.data = data;
131 this.scales = {};
132 }
133
134 getData() {
135 return this.data;
136 }
137
138 getScale(field: string): Scale {
139 const { scales, options, data } = this;
140
141 const scale = scales[field];
142 if (scale) {
143 return scale;
144 }
145 const option = options[field];
146 if (!option) {
147 return null;
148 }
149 const values = option.values ? option.values : data ? valuesOfKey(data, field) : [];
150 const scaleOption = this._getOption({
151 ...option,
152 field,
153 values,
154 });
155 const newScale = this.createScale(scaleOption);
156 scales[field] = newScale;
157 return newScale;
158 }
159
160 getScales() {
161 const { options, scales } = this;
162 each(options, (option, field: string) => {
163 this.getScale(field);
164 });
165 return scales;
166 }
167
168 adjustStartZero(scale: Scale) {
169 const { options } = this;
170 const { field, min, max } = scale;
171 const option = options[field];
172 // 如果有定义,则不处理
173 if (option && option.min) {
174 return;
175 }
176 if (min > 0) {
177 scale.change({
178 min: 0,
179 });
180 } else if (max < 0) {
181 scale.change({
182 max: 0,
183 });
184 }
185 }
186
187 // 饼图下的scale调整
188 adjustPieScale(scale: Scale) {
189 const { options } = this;
190 const { field } = scale;
191 const option = options[field];
192
193 if (option && !isNil(option.nice)) {
194 return null;
195 }
196 scale.change({
197 nice: false,
198 });
199 }
200
201 // 获取scale 在 0点对位置的值
202 getZeroValue(scale) {
203 const { min, max } = scale;
204 let value;
205
206 if (min >= 0) {
207 value = min;
208 } else if (max <= 0) {
209 value = max;
210 } else {
211 value = 0;
212 }
213 return scale.scale(value);
214 }
215}
216
217export default ScaleController;