@easyv/charts 1.4.14 → 1.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.husky/commit-msg +1 -1
  2. package/lib/components/AnimateData.js +8 -16
  3. package/lib/components/Axis.js +199 -191
  4. package/lib/components/Background.js +16 -24
  5. package/lib/components/Band.js +98 -91
  6. package/lib/components/BaseLine.js +39 -40
  7. package/lib/components/Brush.js +29 -46
  8. package/lib/components/Carousel.js +13 -40
  9. package/lib/components/CartesianChart.js +66 -86
  10. package/lib/components/Chart.js +23 -36
  11. package/lib/components/ChartContainer.js +18 -27
  12. package/lib/components/ConicalGradient.js +35 -68
  13. package/lib/components/ExtentData.js +9 -17
  14. package/lib/components/FilterData.js +16 -27
  15. package/lib/components/Indicator.js +6 -8
  16. package/lib/components/Label.js +120 -134
  17. package/lib/components/Legend.js +41 -66
  18. package/lib/components/Lighter.js +19 -48
  19. package/lib/components/Line.js +39 -59
  20. package/lib/components/LinearGradient.js +14 -20
  21. package/lib/components/Mapping.js +9 -34
  22. package/lib/components/Marquee.js +14 -30
  23. package/lib/components/PieChart.js +306 -400
  24. package/lib/components/StackData.js +8 -18
  25. package/lib/components/StereoBar.js +65 -105
  26. package/lib/components/TextOverflow.js +9 -22
  27. package/lib/components/Tooltip.js +41 -55
  28. package/lib/components/index.js +0 -27
  29. package/lib/context/index.js +0 -2
  30. package/lib/element/ConicGradient.js +29 -35
  31. package/lib/element/Line.js +9 -13
  32. package/lib/element/index.js +0 -2
  33. package/lib/formatter/index.js +0 -2
  34. package/lib/formatter/legend.js +30 -41
  35. package/lib/hooks/index.js +0 -8
  36. package/lib/hooks/useAnimateData.js +3 -16
  37. package/lib/hooks/useAxes.js +253 -114
  38. package/lib/hooks/useCarouselAxisX.js +26 -56
  39. package/lib/hooks/useExtentData.js +47 -44
  40. package/lib/hooks/useFilterData.js +8 -29
  41. package/lib/hooks/useStackData.js +7 -30
  42. package/lib/hooks/useTooltip.js +26 -43
  43. package/lib/index.js +0 -15
  44. package/lib/utils/index.js +95 -247
  45. package/package.json +55 -54
  46. package/src/components/Axis.tsx +246 -157
  47. package/src/components/Band.tsx +91 -56
  48. package/src/components/BaseLine.js +22 -5
  49. package/src/components/CartesianChart.js +1 -0
  50. package/src/components/Label.js +56 -46
  51. package/src/components/TextOverflow.tsx +1 -2
  52. package/src/hooks/useAxes.js +336 -117
  53. package/src/hooks/useExtentData.js +37 -10
@@ -1,47 +1,47 @@
1
- import { useMemo } from 'react';
2
- import { scaleLinear, scaleLog, scaleTime, scaleUtc } from 'd3-scale';
3
- import { band, getTicksOfAxis } from '../utils';
4
- import {
5
- getYTicksByStep,
6
- getYTicks,
7
- } from '@easyv/utils/lib/common/utils';
8
-
9
- const getCount = (num)=>{
10
- let i=0, num_ = Math.abs(num);
11
- if(num_<1) return i;
12
- while(num_>10){
1
+ import { useMemo } from "react";
2
+ import { scaleLinear, scaleLog, scaleTime, scaleUtc } from "d3-scale";
3
+ import { band, getTicksOfAxis } from "../utils";
4
+ import { getYTicksByStep, getYTicks } from "@easyv/utils/lib/common/utils";
5
+
6
+ const getCount = (num) => {
7
+ let i = 0,
8
+ num_ = Math.abs(num);
9
+ if (num_ < 1) return i;
10
+ while (num_ > 10) {
13
11
  i++;
14
- num_=Math.floor(num_/10);
12
+ num_ = Math.floor(num_ / 10);
15
13
  }
16
14
  return i;
17
- }
18
-
19
- const getNewDomain=(domain, mode, step)=>{
20
- let newDomain=[];
21
- let min = domain[0], max = domain[1];
22
- let minCount = Math.pow(10,getCount(min)), maxCount = Math.pow(10,getCount(max));
23
- switch(mode){
24
- case 'count':
25
- newDomain[0] = Math.floor(domain[0]/minCount)*minCount;
26
- newDomain[1] = Math.ceil(domain[1]/maxCount)*maxCount;
15
+ };
16
+
17
+ const getNewDomain = (domain, mode, step) => {
18
+ let newDomain = [];
19
+ let min = domain[0],
20
+ max = domain[1];
21
+ let minCount = Math.pow(10, getCount(min)),
22
+ maxCount = Math.pow(10, getCount(max));
23
+ switch (mode) {
24
+ case "count":
25
+ newDomain[0] = Math.floor(domain[0] / minCount) * minCount;
26
+ newDomain[1] = Math.ceil(domain[1] / maxCount) * maxCount;
27
27
  break;
28
- case 'step':
29
- newDomain = [domain[0],domain[0]];
30
- while(newDomain[1]<domain[1]){
31
- newDomain[1]+=step
28
+ case "step":
29
+ newDomain = [domain[0], domain[0]];
30
+ while (newDomain[1] < domain[1]) {
31
+ newDomain[1] += step;
32
32
  }
33
33
  }
34
34
  return newDomain;
35
- }
35
+ };
36
36
 
37
- const getTickCount=(domain, count, decimal)=>{
38
- let multiple = Math.pow(10,decimal);
39
- let gap = domain[1]*multiple-domain[0]*multiple;
40
- if(gap<count){
41
- return Math.max(2,1+gap);
37
+ const getTickCount = (domain, count, decimal) => {
38
+ let multiple = Math.pow(10, decimal);
39
+ let gap = domain[1] * multiple - domain[0] * multiple;
40
+ if (gap < count) {
41
+ return Math.max(2, 1 + gap);
42
42
  }
43
43
  return count;
44
- }
44
+ };
45
45
 
46
46
  const scales = {
47
47
  linear: scaleLinear,
@@ -64,11 +64,13 @@ export default ({ axes, context: { width, height } }) => {
64
64
  const xAxisPositions = [];
65
65
  axes.forEach((item) => {
66
66
  const {
67
- config: { label: { showLast = false, decimal = 0 } },
67
+ config: {
68
+ label: { showLast = false, decimal = 0 },
69
+ },
68
70
  type,
69
71
  orientation,
70
72
  ticks,
71
- tickCount :count = 1,
73
+ tickCount: count = 1,
72
74
  step = 1,
73
75
  domain,
74
76
  axisType,
@@ -77,104 +79,321 @@ export default ({ axes, context: { width, height } }) => {
77
79
  mode,
78
80
  carousel,
79
81
  config,
82
+ //断轴相关
83
+ isClipAxis,
84
+ bottomClipAxisCount,
85
+ topClipAxisCount,
86
+ bottomClipAxisStep,
87
+ topClipAxisStep,
88
+ clipValue,
89
+ clipPosition,
90
+ clipDifferenceValue,
91
+ clipAxisMode,
80
92
  } = item;
81
- //计算真正需要的tickCount,如果domain区间太小,不能完全按照count来,需要减少count数
82
- const tickCount = type=="ordinal"?count:getTickCount(domain,count,decimal);
83
- const direction =
84
- orientation === 'top' || orientation === 'bottom'
85
- ? 'horizontal'
86
- : orientation === 'left' || orientation === 'right'
87
- ? 'vertical'
88
- : '';
89
- const length =
90
- direction === 'horizontal'
91
- ? width
92
- : direction === 'vertical'
93
+ //如果是断轴类型,输出一套完全不同的values
94
+
95
+ /**
96
+ * 获取轴的:朝向direction,起点位置start,终点位置end
97
+ * @param {*} orientation
98
+ * @param {*} width
99
+ * @param {*} height
100
+ * @param {*} paddingOuter
101
+ * @returns {start,end,direction}
102
+ */
103
+ function getChartsConfig(orientation, width, height, paddingOuter) {
104
+ const direction =
105
+ orientation === "top" || orientation === "bottom"
106
+ ? "horizontal"
107
+ : orientation === "left" || orientation === "right"
108
+ ? "vertical"
109
+ : "";
110
+ const length =
111
+ direction === "horizontal"
112
+ ? width
113
+ : direction === "vertical"
93
114
  ? height
94
115
  : 0;
95
116
 
96
- const _paddingOuter = paddingOuter * length;
97
- const start = _paddingOuter / 2;
98
- const end = length - start;
117
+ const _paddingOuter = paddingOuter * length;
118
+ const start = _paddingOuter / 2;
119
+ const end = length - start;
120
+ return {
121
+ start,
122
+ end,
123
+ direction,
124
+ length,
125
+ _paddingOuter,
126
+ };
127
+ }
99
128
 
100
- const range =
101
- direction === 'horizontal'
102
- ? [start, end]
103
- : direction === 'vertical'
104
- ? [end, start]
105
- : [0, 0];
106
- let newDomain = domain;
107
- if(type !== 'ordinal' && !isNaN(domain[1]) && !auto){
108
- newDomain = getNewDomain(domain, mode, step);
129
+ /**
130
+ * 设置scaler
131
+ * @param {*} scales
132
+ * @param {*} type 轴类型
133
+ * @param {*} domain 数据的范围
134
+ * @param {*} range 屏幕坐标的范围
135
+ * @returns
136
+ */
137
+ function setScaler(scales, type, domain, range) {
138
+ //比例将抽象数据的维度映射到可视表示形式。虽然最常用于将数据编码为位置,例如将时间和温度映射到散点图中的水平和垂直位置
139
+ const scaler = scales[type]().domain(domain).range(range);
140
+ scaler.type = type;
141
+ if (type !== "ordinal") scaler.clamp(true); //scaler.nice().clamp(true)
142
+ return scaler;
109
143
  }
110
-
111
- const scaler = scales[type]().domain(newDomain).range(range);
112
-
113
- scaler.type = type;
114
- if (type !== 'ordinal') scaler.clamp(true); //scaler.nice().clamp(true)
115
- const allTicks = ticks
116
- ? ticks
117
- : scaler.ticks
144
+
145
+ /**
146
+ * 获取所有标签数据
147
+ * @param {*} scaler
148
+ * @param {*} ticks 传入的ticks
149
+ * @param {*} tickCount
150
+ * @returns
151
+ */
152
+ function getAllTicks(scaler, ticks, tickCount) {
153
+ return ticks
154
+ ? ticks
155
+ : scaler.ticks
118
156
  ? scaler.ticks(tickCount)
119
157
  : scaler.domain();
120
- let _ticks = allTicks;
121
- //
122
- if (type === 'ordinal') {
123
- if (carousel === false) {
124
- _ticks = getTicksOfAxis(_ticks, +tickCount, showLast);
158
+ }
159
+
160
+ /**
161
+ * 计算非自动模式下的标签集合
162
+ * @param {*} allTicks
163
+ * @param {*} type
164
+ * @param {*} carousel
165
+ * @param {*} showLast
166
+ * @param {*} auto
167
+ * @param {*} mode
168
+ * @param {*} newDomain
169
+ * @param {*} tickCount
170
+ * @param {*} step
171
+ * @returns
172
+ */
173
+ function getTicks(
174
+ allTicks,
175
+ type,
176
+ carousel,
177
+ showLast,
178
+ auto,
179
+ mode,
180
+ newDomain,
181
+ tickCount,
182
+ step
183
+ ) {
184
+ let _ticks = allTicks;
185
+ if (type === "ordinal") {
186
+ if (carousel === false) {
187
+ _ticks = getTicksOfAxis(_ticks, +tickCount, showLast);
188
+ }
189
+ } else {
190
+ if (auto === false) {
191
+ switch (mode) {
192
+ case "count":
193
+ _ticks = getYTicks(newDomain[1], newDomain[0], +tickCount);
194
+ break;
195
+ case "step":
196
+ _ticks = getYTicksByStep(newDomain[1], newDomain[0], +step);
197
+ break;
198
+ }
199
+ }
200
+ }
201
+ return _ticks;
202
+ }
203
+ //断轴相关
204
+ let _isClipAxis = isClipAxis,
205
+ clipAxisDomain = [],
206
+ clipAxisCount = [],
207
+ clipAxisTickCount = [],
208
+ clipAxisStep = [];
209
+
210
+ let _count = count,
211
+ _step = step;
212
+ //计算topDomain,bottomDomain,当断轴值为设置不合理的时候,不开启断轴
213
+ if (isClipAxis && (clipValue > domain[1] || clipValue < domain[0])) {
214
+ _isClipAxis = false;
215
+ _count = topClipAxisCount;
216
+ _step = topClipAxisStep;
217
+ } else {
218
+ clipAxisDomain = [
219
+ [clipValue, domain[1]],
220
+ [domain[0], clipValue],
221
+ ];
222
+ clipAxisCount = [topClipAxisCount, bottomClipAxisCount];
223
+ clipAxisStep = [topClipAxisStep, bottomClipAxisStep];
224
+ }
225
+ if (_isClipAxis) {
226
+ clipAxisDomain.forEach((domain, index) => {
227
+ clipAxisTickCount.push(
228
+ getTickCount(domain, clipAxisCount[index], decimal)
229
+ );
230
+ });
231
+
232
+ const { start, end, direction, _paddingOuter, length } =
233
+ getChartsConfig(orientation, width, height, paddingOuter);
234
+
235
+ const clipMargin = 10;
236
+ function getClipAxisRange(start, end, clipPosition, clipMargin) {
237
+ let topRange = [
238
+ (end - start) * (clipPosition / 100) - clipMargin / 2,
239
+ start,
240
+ ];
241
+ let bottomRange = [
242
+ end,
243
+ (end - start) * (clipPosition / 100) + clipMargin / 2,
244
+ ];
245
+ return [topRange, bottomRange];
246
+ }
247
+
248
+ //计算range填入scaler参数
249
+ const clipAxisRange = getClipAxisRange(
250
+ start,
251
+ end,
252
+ clipPosition,
253
+ clipMargin
254
+ );
255
+ let newClipAxisDomain = [];
256
+ //如果非自适应模式,计算新的domain,传入scaler,适配强制步长或者数量强制
257
+ if (!isNaN(domain[1]) && !auto) {
258
+ clipAxisDomain.forEach((domain, index) => {
259
+ newClipAxisDomain.push(
260
+ getNewDomain(domain, mode, clipAxisStep[index])
261
+ );
262
+ });
263
+ } else {
264
+ //如果为刻度自适应,使用原先domain直接传入scaler,使用scaler.ticks来计算出标签集合
265
+ newClipAxisDomain = clipAxisDomain;
125
266
  }
267
+ //设置scaler,scaler会将数值映射真实的坐标(svg坐标)
268
+ let clipAxisScaler = [];
269
+ newClipAxisDomain.forEach((domain, index) => {
270
+ clipAxisScaler.push(
271
+ setScaler(scales, type, domain, clipAxisRange[index])
272
+ );
273
+ });
274
+ //clipAxisAllTicks作用是使用scaler.ticks方法,来计算标签集合
275
+ let clipAxisAllTicks = [];
276
+ clipAxisScaler.forEach((scaler, index) => {
277
+ clipAxisAllTicks.push(
278
+ getAllTicks(scaler, ticks, clipAxisTickCount[index])
279
+ );
280
+ });
281
+
282
+ let clipAxisTicks = [];
283
+ clipAxisAllTicks.forEach((allTicks, index) => {
284
+ clipAxisTicks.push(
285
+ getTicks(
286
+ allTicks,
287
+ type,
288
+ carousel,
289
+ showLast,
290
+ auto,
291
+ mode,
292
+ newClipAxisDomain[index],
293
+ clipAxisTickCount[index],
294
+ clipAxisStep[index]
295
+ )
296
+ );
297
+ });
298
+ let lengthWithoutPaddingOuter = length - _paddingOuter;
299
+ tmp.set(axisType, {
300
+ ...item,
301
+ isClipAxis: _isClipAxis,
302
+ scaler: clipAxisScaler,
303
+ length,
304
+ direction,
305
+ start,
306
+ end,
307
+ clipAxisRange,
308
+ lengthWithoutPaddingOuter,
309
+ step: [
310
+ lengthWithoutPaddingOuter / clipAxisAllTicks[0].length,
311
+ lengthWithoutPaddingOuter / clipAxisAllTicks[1].length,
312
+ ],
313
+ allTicks: clipAxisAllTicks,
314
+ ticks: clipAxisTicks,
315
+ clipValue,
316
+ });
126
317
  } else {
127
- if (auto === false) {
128
- switch (mode) {
129
- case 'count':
130
- _ticks = getYTicks(
131
- newDomain[1],
132
- newDomain[0],
133
- +tickCount
134
- );
135
- break;
136
- case 'step':
137
- _ticks = getYTicksByStep(
138
- newDomain[1],
139
- newDomain[0],
140
- +step
141
- );
142
- break;
318
+ //计算真正需要的tickCount,如果domain区间太小,不能完全按照count来,需要减少count数
319
+ const tickCount =
320
+ type == "ordinal" ? _count : getTickCount(domain, _count, decimal);
321
+ const { start, end, direction, _paddingOuter, length } =
322
+ getChartsConfig(orientation, width, height, paddingOuter);
323
+
324
+ const range =
325
+ direction === "horizontal"
326
+ ? [start, end]
327
+ : direction === "vertical"
328
+ ? [end, start]
329
+ : [0, 0];
330
+ let newDomain = domain;
331
+ if (type !== "ordinal" && !isNaN(domain[1]) && !auto) {
332
+ newDomain = getNewDomain(domain, mode, _step);
333
+ }
334
+
335
+ const scaler = scales[type]().domain(newDomain).range(range);
336
+
337
+ scaler.type = type;
338
+ if (type !== "ordinal") scaler.clamp(true); //scaler.nice().clamp(true)
339
+ const allTicks = ticks
340
+ ? ticks
341
+ : scaler.ticks
342
+ ? scaler.ticks(tickCount)
343
+ : scaler.domain();
344
+ let _ticks = allTicks;
345
+ //
346
+ if (type === "ordinal") {
347
+ if (carousel === false) {
348
+ _ticks = getTicksOfAxis(_ticks, +tickCount, showLast);
349
+ }
350
+ } else {
351
+ if (auto === false) {
352
+ switch (mode) {
353
+ case "count":
354
+ _ticks = getYTicks(newDomain[1], newDomain[0], +tickCount);
355
+ break;
356
+ case "step":
357
+ _ticks = getYTicksByStep(newDomain[1], newDomain[0], +_step);
358
+ break;
359
+ }
143
360
  }
144
361
  }
145
- }
146
- const lengthWithoutPaddingOuter = length - _paddingOuter;
147
- if (type == 'linear' && config.on) {
148
- const zeroPosition = scaler(0);
149
- if (zeroPosition !== range[0] && !isNaN(zeroPosition)) {
150
- if (direction === 'horizontal') {
151
- xAxisPositions.push({
152
- x: zeroPosition,
153
- y: 0,
154
- });
155
- } else if (direction === 'vertical') {
156
- xAxisPositions.push({
157
- x: 0,
158
- y: zeroPosition,
159
- });
362
+ const lengthWithoutPaddingOuter = length - _paddingOuter;
363
+ if (type == "linear" && config.on) {
364
+ const zeroPosition = scaler(0);
365
+ if (zeroPosition !== range[0] && !isNaN(zeroPosition)) {
366
+ if (direction === "horizontal") {
367
+ xAxisPositions.push({
368
+ x: zeroPosition,
369
+ y: 0,
370
+ });
371
+ } else if (direction === "vertical") {
372
+ xAxisPositions.push({
373
+ x: 0,
374
+ y: zeroPosition,
375
+ });
376
+ }
160
377
  }
161
378
  }
379
+ tmp.set(axisType, {
380
+ ...item,
381
+ count: _count,
382
+ isClipAxis: _isClipAxis,
383
+ scaler,
384
+ length,
385
+ direction,
386
+ start,
387
+ end,
388
+ lengthWithoutPaddingOuter,
389
+ step: lengthWithoutPaddingOuter / allTicks.length,
390
+ allTicks,
391
+ ticks: _ticks,
392
+ });
162
393
  }
163
- tmp.set(axisType, {
164
- ...item,
165
- scaler,
166
- length,
167
- direction,
168
- start,
169
- end,
170
- lengthWithoutPaddingOuter,
171
- step: lengthWithoutPaddingOuter / allTicks.length,
172
- allTicks,
173
- ticks: _ticks,
174
- });
175
394
  });
176
395
 
177
- tmp.get('x') && (tmp.get('x').positions = xAxisPositions);
396
+ tmp.get("x") && (tmp.get("x").positions = xAxisPositions);
178
397
  return tmp;
179
398
  }, [axes]);
180
399
  return _axes;
@@ -1,12 +1,12 @@
1
- import { useMemo } from 'react';
2
- import { group, extent, min as d3Min } from 'd3v7';
1
+ import { useMemo } from "react";
2
+ import { group, extent, min as d3Min, max as d3Max } from "d3v7";
3
3
  import {
4
4
  getStacks,
5
5
  dataYOrZ,
6
6
  seriesYOrZ,
7
7
  resetStacks,
8
8
  getCurrentStack,
9
- } from '../utils';
9
+ } from "../utils";
10
10
 
11
11
  const stackData = (data, series) => {
12
12
  const dataMap = group(data, (d) => d.x);
@@ -40,7 +40,7 @@ const stackData = (data, series) => {
40
40
  max = Math.max(...arr, max);
41
41
  });
42
42
  dataMap.clear();
43
- return [min || 0, max || ((min||0)+100)];
43
+ return [min || 0, max || (min || 0) + 100];
44
44
  };
45
45
 
46
46
  /**
@@ -61,21 +61,48 @@ export default ({ axes, series, data }) => {
61
61
  const x = useMemo(() => [...group(data, (d) => d.x).keys()], [data]);
62
62
  const y = stackData(dataY, _series.y);
63
63
  const z = stackData(dataZ, _series.z);
64
-
64
+ //clipAxisMode如果是auto根据clipDifferenceValue设置clipValue
65
65
  return axes.map((item) => {
66
66
  const { axisType, domain, type } = item;
67
67
  switch (axisType) {
68
- case 'x':
68
+ case "x":
69
69
  return {
70
70
  ...item,
71
- domain: type == 'linear' ? extent(x) : x,
71
+ domain: type == "linear" ? extent(x) : x,
72
72
  };
73
- case 'y':
73
+ case "y": {
74
+ if (
75
+ item.axisType == "y" &&
76
+ item.isClipAxis === true &&
77
+ item.clipAxisMode == "auto"
78
+ ) {
79
+ const _dataY = dataY.map((_item) => _item.y);
80
+ let _clipValue = Infinity;
81
+ if (_dataY.length >= 2) {
82
+ _dataY.sort((a, b) => a - b);
83
+ let clipDifferenceValueArr = [];
84
+ for (let i = 0; i < _dataY.length - 1; i++) {
85
+ clipDifferenceValueArr.push(_dataY[i + 1] - _dataY[i]);
86
+ }
87
+ let max = d3Max(clipDifferenceValueArr);
88
+ if (max >= item.clipDifferenceValue) {
89
+ let index = clipDifferenceValueArr.indexOf(max);
90
+ _clipValue = _dataY[index];
91
+ }
92
+ }
93
+ return {
94
+ ...item,
95
+ domain: domain ? getDomain(y, domain) : y,
96
+ clipValue: _clipValue,
97
+ };
98
+ }
74
99
  return {
75
100
  ...item,
76
101
  domain: domain ? getDomain(y, domain) : y,
77
102
  };
78
- case 'z':
103
+ }
104
+
105
+ case "z":
79
106
  return {
80
107
  ...item,
81
108
  domain: domain ? getDomain(z, domain) : z,
@@ -85,5 +112,5 @@ export default ({ axes, series, data }) => {
85
112
  };
86
113
 
87
114
  const getDomain = ([min1, max1], { min, max }) => {
88
- return [min !== '' ? +min : min1, max !== '' ? +max : max1];
115
+ return [min !== "" ? +min : min1, max !== "" ? +max : max1];
89
116
  };