@easyv/charts 1.8.24 → 1.8.26
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.
- package/lib/components/Axis.js +8 -5
- package/lib/components/Band.js +7 -3
- package/lib/components/CartesianChart.js +25 -6
- package/lib/components/Chart.js +17 -7
- package/lib/components/Label.js +16 -5
- package/lib/components/Line.js +9 -4
- package/package.json +1 -1
- package/src/components/Axis.tsx +72 -63
- package/src/components/Band.tsx +153 -113
- package/src/components/CartesianChart.js +288 -211
- package/src/components/Chart.js +94 -60
- package/src/components/Label.js +90 -42
- package/src/components/Line.js +6 -1
|
@@ -9,7 +9,7 @@ import React, {
|
|
|
9
9
|
useContext,
|
|
10
10
|
useCallback,
|
|
11
11
|
useEffect,
|
|
12
|
-
useLayoutEffect
|
|
12
|
+
useLayoutEffect,
|
|
13
13
|
} from "react";
|
|
14
14
|
import { useAxes, useTooltip, useCarouselAxisX, useAiData } from "../hooks";
|
|
15
15
|
import { chartContext } from "../context";
|
|
@@ -33,7 +33,8 @@ import {
|
|
|
33
33
|
|
|
34
34
|
const Chart = memo(
|
|
35
35
|
({
|
|
36
|
-
width,
|
|
36
|
+
width,
|
|
37
|
+
height,
|
|
37
38
|
seriesData,
|
|
38
39
|
config: {
|
|
39
40
|
axes: axesConfig,
|
|
@@ -42,7 +43,7 @@ const Chart = memo(
|
|
|
42
43
|
margin: { marginLeft, marginTop, marginRight, marginBottom },
|
|
43
44
|
animation,
|
|
44
45
|
legend,
|
|
45
|
-
selectStyle
|
|
46
|
+
selectStyle,
|
|
46
47
|
},
|
|
47
48
|
series,
|
|
48
49
|
bandLength,
|
|
@@ -64,21 +65,23 @@ const Chart = memo(
|
|
|
64
65
|
originData,
|
|
65
66
|
filterData,
|
|
66
67
|
aiFormatter,
|
|
67
|
-
id
|
|
68
|
+
id,
|
|
68
69
|
}) => {
|
|
69
70
|
const context = useContext(chartContext);
|
|
70
71
|
const [isHover, setIsHover] = useState(false);
|
|
71
72
|
const [curXLabel, setCurXLabel] = useState("");
|
|
72
|
-
|
|
73
|
+
|
|
73
74
|
const {
|
|
74
75
|
isIOS,
|
|
76
|
+
scale,
|
|
77
|
+
screenPos,
|
|
75
78
|
width: chartWidth,
|
|
76
79
|
height: chartHeight,
|
|
77
80
|
triggerOnRelative,
|
|
78
81
|
onEmit,
|
|
79
82
|
} = context;
|
|
80
|
-
const defaultSelected = selectStyle?selectStyle.defaultSelected:"";
|
|
81
|
-
const { zIndex="bottom" } = indicator;
|
|
83
|
+
const defaultSelected = selectStyle ? selectStyle.defaultSelected : "";
|
|
84
|
+
const { zIndex = "bottom" } = indicator;
|
|
82
85
|
const xLineRange = chartWidth;
|
|
83
86
|
let yLineRange = chartHeight;
|
|
84
87
|
//获取控制条相关的参数
|
|
@@ -88,37 +91,50 @@ const Chart = memo(
|
|
|
88
91
|
let dragPercent = 1;
|
|
89
92
|
let controlHeight = 0;
|
|
90
93
|
let controlX = 0;
|
|
91
|
-
if(control){
|
|
92
|
-
const {
|
|
94
|
+
if (control) {
|
|
95
|
+
const {
|
|
96
|
+
height,
|
|
97
|
+
margin: { left, right },
|
|
98
|
+
drag: { start, width: dragWidth },
|
|
99
|
+
} = control;
|
|
93
100
|
isControl = true;
|
|
94
101
|
controlHeight = height;
|
|
95
|
-
controlWidth = width-left-right;
|
|
96
|
-
dragPercent = dragWidth/100;
|
|
97
|
-
controlBarWidth = controlWidth*dragPercent;
|
|
98
|
-
controlX = (controlWidth-controlBarWidth)*start/100;
|
|
102
|
+
controlWidth = width - left - right;
|
|
103
|
+
dragPercent = dragWidth / 100;
|
|
104
|
+
controlBarWidth = controlWidth * dragPercent;
|
|
105
|
+
controlX = ((controlWidth - controlBarWidth) * start) / 100;
|
|
99
106
|
}
|
|
100
|
-
const [controlInfo, setControlInfo] = useState({
|
|
107
|
+
const [controlInfo, setControlInfo] = useState({
|
|
108
|
+
isC: isControl,
|
|
109
|
+
cWidth: controlWidth,
|
|
110
|
+
cHeight: controlHeight,
|
|
111
|
+
cBarWidth: controlBarWidth,
|
|
112
|
+
cPercent: dragPercent,
|
|
113
|
+
cBarX: controlX,
|
|
114
|
+
});
|
|
101
115
|
const { cWidth, cHeight, cBarWidth, cPercent, cBarX } = controlInfo;
|
|
102
116
|
|
|
103
117
|
yLineRange -= cHeight;
|
|
104
|
-
useEffect(()=>{
|
|
118
|
+
useEffect(() => {
|
|
105
119
|
setControlInfo({
|
|
106
|
-
isC:isControl,
|
|
107
|
-
cWidth:controlWidth,
|
|
108
|
-
cHeight:controlHeight,
|
|
109
|
-
cBarWidth:controlBarWidth,
|
|
110
|
-
cPercent:dragPercent,
|
|
111
|
-
cBarX:controlX
|
|
120
|
+
isC: isControl,
|
|
121
|
+
cWidth: controlWidth,
|
|
122
|
+
cHeight: controlHeight,
|
|
123
|
+
cBarWidth: controlBarWidth,
|
|
124
|
+
cPercent: dragPercent,
|
|
125
|
+
cBarX: controlX,
|
|
112
126
|
});
|
|
113
|
-
},[JSON.stringify(control)]);
|
|
127
|
+
}, [JSON.stringify(control)]);
|
|
114
128
|
|
|
115
129
|
const svg = useRef();
|
|
116
130
|
const axes = useAxes({
|
|
117
131
|
axes: axesConfig,
|
|
118
132
|
context,
|
|
119
|
-
controlInfo
|
|
133
|
+
controlInfo,
|
|
120
134
|
});
|
|
121
|
-
const aiData = aiFormatter
|
|
135
|
+
const aiData = aiFormatter
|
|
136
|
+
? aiFormatter(originData, axes, series)
|
|
137
|
+
: useAiData(originData, axes, series);
|
|
122
138
|
const axisX = useCarouselAxisX(
|
|
123
139
|
axes.get("x"),
|
|
124
140
|
animation,
|
|
@@ -128,52 +144,52 @@ const Chart = memo(
|
|
|
128
144
|
);
|
|
129
145
|
|
|
130
146
|
//初始化控制图提示框状态的函数
|
|
131
|
-
const initCtlTip = ()=>{
|
|
147
|
+
const initCtlTip = () => {
|
|
132
148
|
return {
|
|
133
|
-
show:false,
|
|
134
|
-
xName:"",
|
|
135
|
-
x:undefined,
|
|
136
|
-
indicatorList:axisX.ticks.map((tick) => {
|
|
149
|
+
show: false,
|
|
150
|
+
xName: "",
|
|
151
|
+
x: undefined,
|
|
152
|
+
indicatorList: axisX.ticks.map((tick) => {
|
|
137
153
|
return {
|
|
138
154
|
tick: tick,
|
|
139
155
|
isShow: false,
|
|
140
156
|
};
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
const [ctlTip, setCtlTip] = useState(initCtlTip);
|
|
157
|
+
}),
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
const [ctlTip, setCtlTip] = useState(initCtlTip); //控制图的提示框
|
|
145
161
|
|
|
146
|
-
useEffect(()=>{
|
|
147
|
-
const handleAction=(e)=>{
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
const handleAction = (e) => {
|
|
148
164
|
const { dynamicData } = e;
|
|
149
165
|
setCurXLabel(dynamicData);
|
|
150
|
-
}
|
|
151
|
-
document.addEventListener(`setCurrent_${id}`,handleAction,false);
|
|
152
|
-
return ()=>{
|
|
153
|
-
document.removeEventListener(`setCurrent_${id}`,handleAction);
|
|
154
|
-
}
|
|
155
|
-
},[]);
|
|
156
|
-
useEffect(()=>{
|
|
166
|
+
};
|
|
167
|
+
document.addEventListener(`setCurrent_${id}`, handleAction, false);
|
|
168
|
+
return () => {
|
|
169
|
+
document.removeEventListener(`setCurrent_${id}`, handleAction);
|
|
170
|
+
};
|
|
171
|
+
}, []);
|
|
172
|
+
useEffect(() => {
|
|
157
173
|
setCtlTip(initCtlTip);
|
|
158
|
-
},[JSON.stringify(axisX.ticks)]);
|
|
159
|
-
useEffect(()=>{
|
|
174
|
+
}, [JSON.stringify(axisX.ticks)]);
|
|
175
|
+
useEffect(() => {
|
|
160
176
|
setCurXLabel(defaultSelected);
|
|
161
|
-
},[defaultSelected]);
|
|
162
|
-
useEffect(()=>{
|
|
163
|
-
if(aiData.length){
|
|
164
|
-
if(!window.aiData){
|
|
165
|
-
window.aiData={};
|
|
177
|
+
}, [defaultSelected]);
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if (aiData.length) {
|
|
180
|
+
if (!window.aiData) {
|
|
181
|
+
window.aiData = {};
|
|
166
182
|
}
|
|
167
|
-
window.aiData[id]={
|
|
168
|
-
getAI:()=>{
|
|
183
|
+
window.aiData[id] = {
|
|
184
|
+
getAI: () => {
|
|
169
185
|
return aiData;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
186
|
+
},
|
|
187
|
+
};
|
|
172
188
|
}
|
|
173
|
-
return ()=>{
|
|
189
|
+
return () => {
|
|
174
190
|
window.aiData && window.aiData[id] && delete window.aiData[id];
|
|
175
|
-
}
|
|
176
|
-
},[JSON.stringify(aiData),id]);
|
|
191
|
+
};
|
|
192
|
+
}, [JSON.stringify(aiData), id]);
|
|
177
193
|
|
|
178
194
|
const {
|
|
179
195
|
x: tooltipX,
|
|
@@ -188,39 +204,41 @@ const Chart = memo(
|
|
|
188
204
|
axisX,
|
|
189
205
|
isHover,
|
|
190
206
|
config: tooltipConfig,
|
|
191
|
-
active
|
|
207
|
+
active,
|
|
192
208
|
});
|
|
193
|
-
const setTouchIndex = (e)=>{
|
|
209
|
+
const setTouchIndex = (e) => {
|
|
194
210
|
e.preventDefault();
|
|
195
211
|
const touch = e.touches[0];
|
|
196
|
-
if(isControl){
|
|
197
|
-
const target = document.elementFromPoint(touch.clientX,touch.clientY);
|
|
212
|
+
if (isControl) {
|
|
213
|
+
const target = document.elementFromPoint(touch.clientX, touch.clientY);
|
|
198
214
|
const { mobile } = target.dataset;
|
|
199
|
-
if(mobile){
|
|
215
|
+
if (mobile) {
|
|
200
216
|
const { x, xName } = JSON.parse(mobile);
|
|
201
|
-
setCtlTip((pre)=>({
|
|
202
|
-
show:true,
|
|
203
|
-
|
|
217
|
+
setCtlTip((pre) => ({
|
|
218
|
+
show: true,
|
|
219
|
+
x,
|
|
220
|
+
xName,
|
|
221
|
+
indicatorList: pre.indicatorList.map((item) => {
|
|
204
222
|
if (item.tick === xName) {
|
|
205
223
|
return { ...item, isShow: true };
|
|
206
224
|
} else {
|
|
207
225
|
return item;
|
|
208
226
|
}
|
|
209
|
-
})
|
|
210
|
-
}))
|
|
211
|
-
}else{
|
|
227
|
+
}),
|
|
228
|
+
}));
|
|
229
|
+
} else {
|
|
212
230
|
setCtlTip(initCtlTip);
|
|
213
231
|
}
|
|
214
|
-
}else{
|
|
232
|
+
} else {
|
|
215
233
|
setIndex(touch);
|
|
216
234
|
}
|
|
217
|
-
}
|
|
235
|
+
};
|
|
218
236
|
const tooltipData = useMemo(
|
|
219
237
|
() =>
|
|
220
238
|
tickName !== undefined && originData.filter((d) => d.x === tickName),
|
|
221
239
|
[tickName, JSON.stringify(originData)]
|
|
222
240
|
);
|
|
223
|
-
|
|
241
|
+
|
|
224
242
|
const showTooltip = !!(
|
|
225
243
|
tooltipData &&
|
|
226
244
|
tooltipData.length &&
|
|
@@ -229,7 +247,8 @@ const Chart = memo(
|
|
|
229
247
|
|
|
230
248
|
const isVertical = axisX.direction === "vertical";
|
|
231
249
|
|
|
232
|
-
const indicatorWidth =
|
|
250
|
+
const indicatorWidth =
|
|
251
|
+
(indicator.width * (control ? axisX.controlStep : axisX.step)) / 100;
|
|
233
252
|
const position = axisX.scaler(tickName) - indicatorWidth / 2;
|
|
234
253
|
const indicatorAttr = isVertical
|
|
235
254
|
? { width: chartWidth, height: indicatorWidth, y: position }
|
|
@@ -240,19 +259,30 @@ const Chart = memo(
|
|
|
240
259
|
};
|
|
241
260
|
|
|
242
261
|
const onInteraction = useCallback(
|
|
243
|
-
(e,type) => {
|
|
262
|
+
(e, type) => {
|
|
244
263
|
e.stopPropagation();
|
|
264
|
+
const { pageX, pageY } = e.nativeEvent;
|
|
245
265
|
const { data } = e.currentTarget.dataset;
|
|
246
266
|
const _data = JSON.parse(data);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
267
|
+
const [scaleX, scaleY] = scale.current;
|
|
268
|
+
const { left, top } = screenPos.current;
|
|
269
|
+
const eventData = {
|
|
270
|
+
..._data,
|
|
271
|
+
xPos: (pageX - left) * scaleX,
|
|
272
|
+
yPos: (pageY - top) * scaleY,
|
|
273
|
+
};
|
|
274
|
+
if (type == "setCurrent") {
|
|
275
|
+
setCurXLabel((pre) => {
|
|
276
|
+
return pre == _data.x ? "" : _data.x;
|
|
250
277
|
});
|
|
278
|
+
triggerOnRelative("click", eventData);
|
|
279
|
+
onEmit("click", eventData);
|
|
280
|
+
} else {
|
|
281
|
+
triggerOnRelative(type, eventData);
|
|
282
|
+
onEmit(type, eventData);
|
|
251
283
|
}
|
|
252
|
-
triggerOnRelative('click',_data);
|
|
253
|
-
onEmit("click", _data);
|
|
254
284
|
},
|
|
255
|
-
[triggerOnRelative, onEmit]
|
|
285
|
+
[triggerOnRelative, onEmit, scale]
|
|
256
286
|
);
|
|
257
287
|
|
|
258
288
|
/**
|
|
@@ -262,16 +292,16 @@ const Chart = memo(
|
|
|
262
292
|
const seriesEl = useRef(null);
|
|
263
293
|
const axisElList = useRef([]);
|
|
264
294
|
const curControlPercent = useRef(0);
|
|
265
|
-
const controlTimer = useRef();
|
|
266
|
-
const ctlBarX = useRef(0);
|
|
267
|
-
const isWorking = useRef(false);
|
|
295
|
+
const controlTimer = useRef(); //控制条的动画计时器
|
|
296
|
+
const ctlBarX = useRef(0); //控制条的滑块偏移值
|
|
297
|
+
const isWorking = useRef(false); //控制条是否正在进行各种操作
|
|
268
298
|
|
|
269
299
|
const range = (num, ignoreMax) => {
|
|
270
300
|
let _num = num;
|
|
271
301
|
const min = 0;
|
|
272
302
|
const max = cWidth - cBarWidth;
|
|
273
303
|
_num = Math.max(_num, min);
|
|
274
|
-
if(ignoreMax){
|
|
304
|
+
if (ignoreMax) {
|
|
275
305
|
return _num;
|
|
276
306
|
}
|
|
277
307
|
_num = Math.min(_num, max);
|
|
@@ -280,94 +310,101 @@ const Chart = memo(
|
|
|
280
310
|
//设置滑块和图表主体的偏移值
|
|
281
311
|
const setControlTranslate = (x, needSave) => {
|
|
282
312
|
const moveLen = range(x);
|
|
283
|
-
if(needSave) ctlBarX.current = moveLen;
|
|
284
|
-
setControlInfo(pre=>{
|
|
313
|
+
if (needSave) ctlBarX.current = moveLen;
|
|
314
|
+
setControlInfo((pre) => {
|
|
285
315
|
return {
|
|
286
316
|
...pre,
|
|
287
|
-
cBarX:moveLen
|
|
288
|
-
}
|
|
317
|
+
cBarX: moveLen,
|
|
318
|
+
};
|
|
289
319
|
});
|
|
290
320
|
};
|
|
291
|
-
const setControlWidth = (fn,ignoreMax=false)=>{
|
|
292
|
-
setControlInfo(pre=>{
|
|
321
|
+
const setControlWidth = (fn, ignoreMax = false) => {
|
|
322
|
+
setControlInfo((pre) => {
|
|
293
323
|
const x = pre.cBarX;
|
|
294
324
|
const moveLen = range(x, ignoreMax);
|
|
295
325
|
ctlBarX.current = moveLen;
|
|
296
326
|
return fn(pre);
|
|
297
327
|
});
|
|
298
|
-
}
|
|
328
|
+
};
|
|
299
329
|
//设置控制条是否正在进行拖拽/缩放操作
|
|
300
|
-
const setWorking=(bool)=>{
|
|
330
|
+
const setWorking = (bool) => {
|
|
301
331
|
isWorking.current = bool;
|
|
302
|
-
}
|
|
332
|
+
};
|
|
303
333
|
//cBarX变化后,修改图表的偏移值
|
|
304
|
-
useLayoutEffect(()=>{
|
|
334
|
+
useLayoutEffect(() => {
|
|
305
335
|
const { controlEnd, start } = axisX;
|
|
306
|
-
if(controlEl.current){
|
|
336
|
+
if (controlEl.current) {
|
|
307
337
|
controlEl.current.style.transform = `translate(${cBarX}px,0)`;
|
|
308
338
|
//计算出当前位移的百分比
|
|
309
339
|
let percent = cBarX / (cWidth - cBarWidth);
|
|
310
|
-
percent = isNaN(percent)?1:percent;
|
|
311
|
-
const translateX =
|
|
340
|
+
percent = isNaN(percent) ? 1 : percent;
|
|
341
|
+
const translateX =
|
|
342
|
+
-(controlEnd + start / cPercent - chartWidth) * percent;
|
|
312
343
|
curControlPercent.current = percent;
|
|
313
344
|
seriesEl.current.style.transform = `translate(${translateX}px,${marginTop}px)`;
|
|
314
345
|
axisElList.current[2].style.transform = `translate(${translateX}px,${0}px)`;
|
|
315
346
|
}
|
|
316
|
-
|
|
347
|
+
}, [controlInfo]);
|
|
317
348
|
//控制条轮播动画
|
|
318
349
|
useEffect(() => {
|
|
319
350
|
if (controlEl.current && control) {
|
|
320
|
-
let isEnter = false;
|
|
351
|
+
let isEnter = false; //控制条轮播动画是否处于暂停状态
|
|
321
352
|
const { show, duration, interval, hover } = animation;
|
|
322
|
-
const startAnimate=()=>{
|
|
323
|
-
const loopInterval = interval*1000,
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
353
|
+
const startAnimate = () => {
|
|
354
|
+
const loopInterval = interval * 1000,
|
|
355
|
+
tranDuration = duration * 1000;
|
|
356
|
+
let loopTime,
|
|
357
|
+
timeGap,
|
|
358
|
+
toRight = true;
|
|
359
|
+
const transition = (timeStamp) => {
|
|
360
|
+
if (!loopTime) loopTime = timeStamp;
|
|
361
|
+
if (!isEnter && !isWorking.current) {
|
|
362
|
+
if (timeStamp - loopTime < tranDuration) {
|
|
363
|
+
const percent = Math.min(
|
|
364
|
+
1,
|
|
365
|
+
(timeStamp - loopTime) / tranDuration
|
|
366
|
+
);
|
|
367
|
+
if (toRight) {
|
|
368
|
+
setControlTranslate(cBarWidth * percent + ctlBarX.current);
|
|
369
|
+
} else {
|
|
370
|
+
setControlTranslate(ctlBarX.current * (1 - percent));
|
|
334
371
|
}
|
|
335
372
|
controlTimer.current = requestAnimationFrame(transition);
|
|
336
|
-
}else{
|
|
373
|
+
} else {
|
|
337
374
|
loopTime = timeStamp;
|
|
338
|
-
if(toRight){
|
|
339
|
-
setControlTranslate(cBarWidth+ctlBarX.current, true);
|
|
340
|
-
}else{
|
|
341
|
-
setControlTranslate(0,true);
|
|
375
|
+
if (toRight) {
|
|
376
|
+
setControlTranslate(cBarWidth + ctlBarX.current, true);
|
|
377
|
+
} else {
|
|
378
|
+
setControlTranslate(0, true);
|
|
342
379
|
}
|
|
343
380
|
controlTimer.current = requestAnimationFrame(loop);
|
|
344
381
|
}
|
|
345
|
-
}else{
|
|
346
|
-
loopTime = timeStamp-timeGap;
|
|
382
|
+
} else {
|
|
383
|
+
loopTime = timeStamp - timeGap;
|
|
347
384
|
controlTimer.current = requestAnimationFrame(transition);
|
|
348
385
|
}
|
|
349
386
|
timeGap = timeStamp - loopTime;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const loop=(timeStamp)=>{
|
|
353
|
-
if(!loopTime)loopTime=timeStamp;
|
|
354
|
-
if(!isEnter && !isWorking.current){
|
|
355
|
-
if(timeStamp-loopTime<loopInterval){
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const loop = (timeStamp) => {
|
|
390
|
+
if (!loopTime) loopTime = timeStamp;
|
|
391
|
+
if (!isEnter && !isWorking.current) {
|
|
392
|
+
if (timeStamp - loopTime < loopInterval) {
|
|
356
393
|
controlTimer.current = requestAnimationFrame(loop);
|
|
357
|
-
}else{
|
|
394
|
+
} else {
|
|
358
395
|
loopTime = timeStamp;
|
|
359
|
-
if(ctlBarX.current==cWidth - cBarWidth) toRight=false;
|
|
360
|
-
else toRight=true;
|
|
396
|
+
if (ctlBarX.current == cWidth - cBarWidth) toRight = false;
|
|
397
|
+
else toRight = true;
|
|
361
398
|
controlTimer.current = requestAnimationFrame(transition);
|
|
362
399
|
}
|
|
363
|
-
}else{
|
|
364
|
-
loopTime = timeStamp-timeGap;
|
|
400
|
+
} else {
|
|
401
|
+
loopTime = timeStamp - timeGap;
|
|
365
402
|
controlTimer.current = requestAnimationFrame(loop);
|
|
366
403
|
}
|
|
367
404
|
timeGap = timeStamp - loopTime;
|
|
368
|
-
}
|
|
405
|
+
};
|
|
369
406
|
controlTimer.current = requestAnimationFrame(loop);
|
|
370
|
-
}
|
|
407
|
+
};
|
|
371
408
|
const mouseenter = () => {
|
|
372
409
|
isEnter = true;
|
|
373
410
|
};
|
|
@@ -376,72 +413,94 @@ const Chart = memo(
|
|
|
376
413
|
};
|
|
377
414
|
const svgDom = svg.current;
|
|
378
415
|
const controlDom = controlEl.current;
|
|
379
|
-
if(hover){
|
|
380
|
-
svgDom && svgDom.addEventListener("mouseenter",mouseenter);
|
|
381
|
-
svgDom && svgDom.addEventListener("mouseleave",mouseleave);
|
|
382
|
-
controlDom && controlDom.addEventListener("mouseenter",mouseenter);
|
|
383
|
-
controlDom && controlDom.addEventListener("mouseleave",mouseleave);
|
|
416
|
+
if (hover) {
|
|
417
|
+
svgDom && svgDom.addEventListener("mouseenter", mouseenter);
|
|
418
|
+
svgDom && svgDom.addEventListener("mouseleave", mouseleave);
|
|
419
|
+
controlDom && controlDom.addEventListener("mouseenter", mouseenter);
|
|
420
|
+
controlDom && controlDom.addEventListener("mouseleave", mouseleave);
|
|
384
421
|
}
|
|
385
|
-
if(show){
|
|
422
|
+
if (show) {
|
|
386
423
|
startAnimate();
|
|
387
424
|
}
|
|
388
425
|
return () => {
|
|
389
|
-
svgDom && svgDom.removeEventListener("mouseenter",mouseenter);
|
|
390
|
-
svgDom && svgDom.removeEventListener("mouseleave",mouseleave);
|
|
391
|
-
controlDom &&
|
|
392
|
-
|
|
426
|
+
svgDom && svgDom.removeEventListener("mouseenter", mouseenter);
|
|
427
|
+
svgDom && svgDom.removeEventListener("mouseleave", mouseleave);
|
|
428
|
+
controlDom &&
|
|
429
|
+
controlDom.removeEventListener("mouseenter", mouseenter);
|
|
430
|
+
controlDom &&
|
|
431
|
+
controlDom.removeEventListener("mouseleave", mouseleave);
|
|
393
432
|
cancelAnimationFrame(controlTimer.current);
|
|
394
433
|
};
|
|
395
434
|
}
|
|
396
|
-
}, [
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
435
|
+
}, [
|
|
436
|
+
JSON.stringify(animation),
|
|
437
|
+
control,
|
|
438
|
+
cWidth,
|
|
439
|
+
cHeight,
|
|
440
|
+
cBarWidth,
|
|
441
|
+
cPercent,
|
|
442
|
+
]);
|
|
443
|
+
|
|
444
|
+
const {
|
|
445
|
+
show: showCtl,
|
|
446
|
+
xName: ctlXName,
|
|
447
|
+
x: ctlX,
|
|
448
|
+
indicatorList: ctlIndicatorList,
|
|
449
|
+
} = ctlTip;
|
|
450
|
+
const controlChartTooltipData =
|
|
451
|
+
ctlXName && originData.filter((d) => d.x === ctlXName);
|
|
452
|
+
const bodyWidth = isVertical
|
|
453
|
+
? xLineRange + 100 + marginRight + marginLeft
|
|
454
|
+
: xLineRange,
|
|
455
|
+
bodyHeight = isVertical
|
|
456
|
+
? yLineRange
|
|
457
|
+
: yLineRange + marginTop + marginBottom;
|
|
458
|
+
const hasDuplicateX = (data) => {
|
|
459
|
+
const xValues = new Set();
|
|
460
|
+
for (const item of data) {
|
|
461
|
+
// 跳过空数据项
|
|
462
|
+
if (!item.data || !item.data.length) continue;
|
|
463
|
+
|
|
464
|
+
// 关键修改:遍历当前item.data的所有项(不再只取第1项)
|
|
465
|
+
for (const subItem of item.data) {
|
|
466
|
+
// 确保subItem有data和x属性(兜底防错)
|
|
467
|
+
if (!subItem.data || subItem.data.x === undefined) continue;
|
|
468
|
+
const x = subItem.data.x;
|
|
469
|
+
if (xValues.has(x)) return true; // 任意x重复就返回true
|
|
470
|
+
xValues.add(x);
|
|
416
471
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
472
|
+
}
|
|
473
|
+
return false;
|
|
474
|
+
};
|
|
475
|
+
useEffect(() => {
|
|
476
|
+
setCtlTip((pre) => {
|
|
421
477
|
return {
|
|
422
|
-
show
|
|
423
|
-
|
|
478
|
+
show: !!tickName,
|
|
479
|
+
x: tooltipX,
|
|
480
|
+
xName: tickName,
|
|
481
|
+
indicatorList: pre.indicatorList.map((item) => {
|
|
424
482
|
if (item.tick === tickName) {
|
|
425
|
-
return { ...item, isShow:true };
|
|
483
|
+
return { ...item, isShow: true };
|
|
426
484
|
} else {
|
|
427
|
-
return { ...item, isShow:false };
|
|
485
|
+
return { ...item, isShow: false };
|
|
428
486
|
}
|
|
429
|
-
})
|
|
430
|
-
}
|
|
431
|
-
})
|
|
432
|
-
},[tooltipX, tickName]);
|
|
433
|
-
useEffect(()=>{
|
|
487
|
+
}),
|
|
488
|
+
};
|
|
489
|
+
});
|
|
490
|
+
}, [tooltipX, tickName]);
|
|
491
|
+
useEffect(() => {
|
|
434
492
|
const svgDom = svg.current;
|
|
435
|
-
svgDom &&
|
|
436
|
-
|
|
493
|
+
svgDom &&
|
|
494
|
+
svgDom.addEventListener("touchmove", setTouchIndex, { passive: false });
|
|
495
|
+
return () => {
|
|
437
496
|
svgDom && svgDom.removeEventListener("touchmove", setTouchIndex);
|
|
438
|
-
}
|
|
439
|
-
},[control]);
|
|
497
|
+
};
|
|
498
|
+
}, [control]);
|
|
440
499
|
|
|
441
500
|
return (
|
|
442
501
|
<>
|
|
443
502
|
<ChartContainer
|
|
444
|
-
style={{...style,position:"absolute"}}
|
|
503
|
+
style={{ ...style, position: "absolute" }}
|
|
445
504
|
width={width}
|
|
446
505
|
height={height}
|
|
447
506
|
marginLeft={marginLeft}
|
|
@@ -449,7 +508,7 @@ const Chart = memo(
|
|
|
449
508
|
onMouseEnter={() => {
|
|
450
509
|
setIsHover(true);
|
|
451
510
|
}}
|
|
452
|
-
onMouseMove={!control?setIndex:null}
|
|
511
|
+
onMouseMove={!control ? setIndex : null}
|
|
453
512
|
onMouseLeave={(e) => {
|
|
454
513
|
setIsHover(false);
|
|
455
514
|
setIndex(e);
|
|
@@ -478,21 +537,21 @@ const Chart = memo(
|
|
|
478
537
|
const { axisType } = item;
|
|
479
538
|
let dataUnit = "";
|
|
480
539
|
const config = axisType == "x" ? axisX : item;
|
|
481
|
-
const target = series.find(d=>d.yOrZ==axisType);
|
|
482
|
-
if(target && target.data[0]){
|
|
540
|
+
const target = series.find((d) => d.yOrZ == axisType);
|
|
541
|
+
if (target && target.data[0]) {
|
|
483
542
|
dataUnit = target.data[0].data.__unit__;
|
|
484
543
|
}
|
|
485
544
|
//如果纵坐标没有对应的值,则不显示
|
|
486
|
-
return axisType!="x" && !target?null:(
|
|
545
|
+
return axisType != "x" && !target ? null : (
|
|
487
546
|
<Axis
|
|
488
547
|
ref={(d) => {
|
|
489
548
|
axisElList.current[index] = d;
|
|
490
549
|
}}
|
|
491
|
-
|
|
550
|
+
triggerEvents={onInteraction}
|
|
492
551
|
xLineRange={xLineRange}
|
|
493
552
|
yLineRange={yLineRange}
|
|
494
553
|
controlInfo={controlInfo}
|
|
495
|
-
margin={{marginLeft,marginTop}}
|
|
554
|
+
margin={{ marginLeft, marginTop }}
|
|
496
555
|
dataUnit={dataUnit}
|
|
497
556
|
{...config}
|
|
498
557
|
key={index}
|
|
@@ -500,17 +559,22 @@ const Chart = memo(
|
|
|
500
559
|
);
|
|
501
560
|
})}
|
|
502
561
|
{/* 非控制图下的指示器 */}
|
|
503
|
-
{showTooltip && !control && zIndex=="bottom" && (
|
|
504
|
-
<Indicator
|
|
562
|
+
{showTooltip && !control && zIndex == "bottom" && (
|
|
563
|
+
<Indicator
|
|
564
|
+
{...indicator}
|
|
565
|
+
manual={manual}
|
|
566
|
+
auto={auto}
|
|
567
|
+
{...indicatorAttr}
|
|
568
|
+
/>
|
|
505
569
|
)}
|
|
506
570
|
<foreignObject
|
|
507
|
-
style={
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
571
|
+
style={{
|
|
572
|
+
width: bodyWidth,
|
|
573
|
+
height: bodyHeight,
|
|
574
|
+
transform: isVertical
|
|
575
|
+
? `translateX(${-marginRight}px)`
|
|
576
|
+
: `translateY(${-marginTop}px)`,
|
|
577
|
+
}}
|
|
514
578
|
>
|
|
515
579
|
<svg
|
|
516
580
|
width="100%"
|
|
@@ -520,13 +584,14 @@ const Chart = memo(
|
|
|
520
584
|
overflow: "visible",
|
|
521
585
|
position: "absolute",
|
|
522
586
|
transform: isVertical
|
|
523
|
-
? `translate(${marginRight}px,${isIOS?marginTop:0}px)`
|
|
524
|
-
: `translate(${isIOS?marginLeft:0}px,${marginTop}px)`,
|
|
587
|
+
? `translate(${marginRight}px,${isIOS ? marginTop : 0}px)`
|
|
588
|
+
: `translate(${isIOS ? marginLeft : 0}px,${marginTop}px)`,
|
|
525
589
|
}}
|
|
526
590
|
>
|
|
527
591
|
{/* 控制图指示器部分 */}
|
|
528
592
|
<g>
|
|
529
|
-
{control &&
|
|
593
|
+
{control &&
|
|
594
|
+
zIndex == "bottom" &&
|
|
530
595
|
ctlIndicatorList.map((item, index) => {
|
|
531
596
|
const x = axisX.scaler(item.tick);
|
|
532
597
|
return (
|
|
@@ -542,7 +607,7 @@ const Chart = memo(
|
|
|
542
607
|
xName={item.tick}
|
|
543
608
|
setCtlTip={setCtlTip}
|
|
544
609
|
ctlIndicatorList={ctlIndicatorList}
|
|
545
|
-
manual=
|
|
610
|
+
manual={manual}
|
|
546
611
|
/>
|
|
547
612
|
);
|
|
548
613
|
})}
|
|
@@ -550,7 +615,7 @@ const Chart = memo(
|
|
|
550
615
|
{/**绘制图表主体 */}
|
|
551
616
|
{series.map(({ Component, yOrZ, ...config }, index) => {
|
|
552
617
|
const yAxis = axes.get(yOrZ);
|
|
553
|
-
const isXRepeat=hasDuplicateX(series)
|
|
618
|
+
const isXRepeat = hasDuplicateX(series); //x项有重复项判断
|
|
554
619
|
return (
|
|
555
620
|
yAxis &&
|
|
556
621
|
Component && (
|
|
@@ -566,7 +631,7 @@ const Chart = memo(
|
|
|
566
631
|
// 控制图部分,主要是为了,当鼠标悬浮在指示器上时,显示对应的tooltip
|
|
567
632
|
isControlChart={!!control}
|
|
568
633
|
indicatorWidth={indicatorWidth}
|
|
569
|
-
|
|
634
|
+
triggerEvents={onInteraction}
|
|
570
635
|
setCtlTip={setCtlTip}
|
|
571
636
|
isXRepeat={isXRepeat}
|
|
572
637
|
/>
|
|
@@ -576,11 +641,11 @@ const Chart = memo(
|
|
|
576
641
|
{/* 图表数值标签 */}
|
|
577
642
|
{series.map(({ Component, yOrZ, ...config }, index) => {
|
|
578
643
|
const yAxis = axes.get(yOrZ);
|
|
579
|
-
const isXRepeat=hasDuplicateX(series)
|
|
644
|
+
const isXRepeat = hasDuplicateX(series); //x项有重复项判断
|
|
580
645
|
return (
|
|
581
646
|
yAxis && (
|
|
582
647
|
<Label
|
|
583
|
-
|
|
648
|
+
isXRepeat={isXRepeat}
|
|
584
649
|
key={index}
|
|
585
650
|
{...config}
|
|
586
651
|
curXLabel={curXLabel}
|
|
@@ -588,13 +653,15 @@ const Chart = memo(
|
|
|
588
653
|
bandLength={bandLength}
|
|
589
654
|
xAxis={axisX}
|
|
590
655
|
yAxis={yAxis}
|
|
591
|
-
|
|
656
|
+
triggerEvents={onInteraction}
|
|
657
|
+
isControlChart={!!control}
|
|
592
658
|
/>
|
|
593
659
|
)
|
|
594
660
|
);
|
|
595
661
|
})}
|
|
596
662
|
<g>
|
|
597
|
-
{control &&
|
|
663
|
+
{control &&
|
|
664
|
+
zIndex != "bottom" &&
|
|
598
665
|
ctlIndicatorList.map((item, index) => {
|
|
599
666
|
const x = axisX.scaler(item.tick);
|
|
600
667
|
return (
|
|
@@ -610,7 +677,7 @@ const Chart = memo(
|
|
|
610
677
|
xName={item.tick}
|
|
611
678
|
setCtlTip={setCtlTip}
|
|
612
679
|
ctlIndicatorList={ctlIndicatorList}
|
|
613
|
-
manual=
|
|
680
|
+
manual={manual}
|
|
614
681
|
/>
|
|
615
682
|
);
|
|
616
683
|
})}
|
|
@@ -622,14 +689,19 @@ const Chart = memo(
|
|
|
622
689
|
isVertical={isVertical}
|
|
623
690
|
{...tooltip}
|
|
624
691
|
data={controlChartTooltipData}
|
|
625
|
-
x={
|
|
692
|
+
x={
|
|
693
|
+
ctlX -
|
|
694
|
+
marginLeft -
|
|
695
|
+
(axisX.controlEnd + axisX.start / cPercent - chartWidth) *
|
|
696
|
+
curControlPercent.current
|
|
697
|
+
}
|
|
626
698
|
marginLeft={marginLeft}
|
|
627
699
|
marginTop={marginTop}
|
|
628
700
|
tickName={ctlXName}
|
|
629
701
|
series={series}
|
|
630
702
|
width={width}
|
|
631
703
|
height={height}
|
|
632
|
-
manual=
|
|
704
|
+
manual={manual}
|
|
633
705
|
/>
|
|
634
706
|
)}
|
|
635
707
|
</foreignObject>
|
|
@@ -657,8 +729,13 @@ const Chart = memo(
|
|
|
657
729
|
);
|
|
658
730
|
})}
|
|
659
731
|
{/* 非控制图下的指示器 */}
|
|
660
|
-
{showTooltip && !control && zIndex!="bottom" && (
|
|
661
|
-
<Indicator
|
|
732
|
+
{showTooltip && !control && zIndex != "bottom" && (
|
|
733
|
+
<Indicator
|
|
734
|
+
{...indicator}
|
|
735
|
+
manual={manual}
|
|
736
|
+
auto={auto}
|
|
737
|
+
{...indicatorAttr}
|
|
738
|
+
/>
|
|
662
739
|
)}
|
|
663
740
|
</ChartContainer>
|
|
664
741
|
{/* 控制条逻辑 */}
|
|
@@ -666,16 +743,16 @@ const Chart = memo(
|
|
|
666
743
|
<Control
|
|
667
744
|
ref={controlEl}
|
|
668
745
|
actions={{
|
|
669
|
-
setX:setControlTranslate,
|
|
746
|
+
setX: setControlTranslate,
|
|
670
747
|
setWorking,
|
|
671
|
-
setControlWidth
|
|
748
|
+
setControlWidth,
|
|
672
749
|
}}
|
|
673
750
|
props={{
|
|
674
751
|
control,
|
|
675
752
|
controlInfo,
|
|
676
753
|
axes,
|
|
677
754
|
series,
|
|
678
|
-
top:chartHeight+marginTop,
|
|
755
|
+
top: chartHeight + marginTop,
|
|
679
756
|
bandLength,
|
|
680
757
|
}}
|
|
681
758
|
></Control>
|
|
@@ -694,7 +771,7 @@ const Chart = memo(
|
|
|
694
771
|
series={series}
|
|
695
772
|
width={width}
|
|
696
773
|
height={height}
|
|
697
|
-
manual=
|
|
774
|
+
manual={manual}
|
|
698
775
|
/>
|
|
699
776
|
)}
|
|
700
777
|
{brush && <Brush config={brush} width={width} />}
|