@easyv/charts 1.10.19 → 1.10.21

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.
@@ -1,1570 +1,1629 @@
1
- /**
2
- * 饼环图
3
- */
4
- import React, {
5
- memo,
6
- useMemo,
7
- useCallback,
8
- useRef,
9
- useState,
10
- useEffect,
11
- useContext,
12
- useLayoutEffect,
13
- Fragment,
14
- } from "react";
15
- import {
16
- ChartContainer,
17
- Carousel,
18
- Legend,
19
- ConicalGradient,
20
- Mapping,
21
- TextOverflow,
22
- } from ".";
23
- import { chartContext } from "../context";
24
- import {
25
- getFontStyle,
26
- sortPie,
27
- getDataWithPercent,
28
- getColorList,
29
- } from "../utils";
30
- import { pie, arc, extent, scaleLinear } from "d3v7";
31
- import { animate, linear } from "popmotion";
32
- import LinearGradient from "./LinearGradient";
33
- import { pieLegendFormatter as legendFormatter } from "../formatter";
34
- import ringCss from "../css/piechart.module.css";
35
- import { useAiDataOfPie } from "../hooks";
36
- import { PieTooltip } from "./PieTooltip";
37
-
38
- const PI = Math.PI;
39
-
40
- const defaultChart = {
41
- outerRadius: 1,
42
- innerRadius: 0,
43
- cornerRadius: 0,
44
- rose: false,
45
- roseType: "radius",
46
- baseRadius: 0,
47
- padAngle: 0,
48
- };
49
- const defaultAngle = { startAngle: 0, endAngle: 360, antiClockwise: false };
50
-
51
- // const nameDy = (showValue, showPercent, mode, dir) => {
52
- // if (showValue || showPercent) {
53
- // if (mode == "vertical") {
54
- // return dir == 1 ? "1.1em" : "-2.6em";
55
- // } else {
56
- // return 0;
57
- // }
58
- // } else {
59
- // if (mode == "vertical") {
60
- // return dir * 1.1 + "em";
61
- // } else {
62
- // return 0;
63
- // }
64
- // }
65
- // };
66
- // const valueDy = (value1, mode, dir) => {
67
- // if (value1) {
68
- // if (mode == "vertical") {
69
- // return "1.5em";
70
- // } else {
71
- // return 0;
72
- // }
73
- // } else {
74
- // if (mode == "vertical") {
75
- // return dir == 1 ? "1.1em" : "-1.1em";
76
- // } else {
77
- // return 0;
78
- // }
79
- // }
80
- // };
81
-
82
- // const percentDy_ = (showName, showValue, mode, dir) => {
83
- // if (showValue) {
84
- // return 0;
85
- // }
86
- // if (showName) {
87
- // if (mode == "vertical") {
88
- // return "1.5em";
89
- // } else {
90
- // return 0;
91
- // }
92
- // } else {
93
- // if (mode == "vertical") {
94
- // return dir * 1.1 + "em";
95
- // } else {
96
- // return 0;
97
- // }
98
- // }
99
- // };
100
-
101
- // const percentX = (showName, showValue, mode, x) => {
102
- // if (showValue) {
103
- // return "";
104
- // }
105
- // if (showName) {
106
- // if (mode == "vertical") {
107
- // return x;
108
- // } else {
109
- // return "";
110
- // }
111
- // } else {
112
- // return x;
113
- // }
114
- // };
115
-
116
- // const percentDx = (showName, showValue, mode) => {
117
- // if (showValue) {
118
- // return "0.5em";
119
- // }
120
- // if (showName) {
121
- // if (mode == "vertical") {
122
- // return 0;
123
- // } else {
124
- // return "0.5em";
125
- // }
126
- // } else {
127
- // return 0;
128
- // }
129
- // };
130
-
131
- // const percentDy = (showName, showValue, mode) => {
132
- // if (showValue) {
133
- // return 0;
134
- // }
135
- // if (showName) {
136
- // if (mode == "vertical") {
137
- // return "1.5em";
138
- // } else {
139
- // return 0;
140
- // }
141
- // } else {
142
- // return 0;
143
- // }
144
- // };
145
-
146
- // const valueDx = (showName, mode) => {
147
- // if (!showName) {
148
- // return "";
149
- // }
150
- // if (mode == "vertical") {
151
- // return "";
152
- // } else {
153
- // return "0.5em";
154
- // }
155
- // };
156
-
157
- const getCoord = (deg, radius) => {
158
- var x = Math.cos(deg) * radius,
159
- y = Math.sin(deg) * radius;
160
- return [x, y];
161
- };
162
-
163
- const getRoseRadius = ({ innerRadius, baseRadius }) =>
164
- innerRadius + (1 - innerRadius) * baseRadius;
165
-
166
- const getAngle = ({ startAngle, endAngle, antiClockwise, ...rest }) => {
167
- if (antiClockwise)
168
- return {
169
- ...rest,
170
- startAngle: endAngle - 180,
171
- endAngle: startAngle - 180,
172
- };
173
- return { ...rest, startAngle, endAngle };
174
- };
175
-
176
- const getArc = (
177
- radius,
178
- {
179
- padAngle = 0,
180
- innerRadius = 0,
181
- outerRadius = 1,
182
- cornerRadius = 0,
183
- startAngle = 0,
184
- endAngle = 360,
185
- ...rest
186
- },
187
- series_,
188
- index,
189
- ) => {
190
- const series =
191
- series_.find((s) => s.fieldName == rest.data.s) ||
192
- series_[index % series_.length];
193
- return {
194
- ...rest,
195
- type: "pie",
196
- fieldName: series.fieldName,
197
- displayName: series.displayName || rest.data.s,
198
- series: series,
199
- innerRadius: innerRadius * radius,
200
- outerRadius: outerRadius * radius,
201
- arc: arc()
202
- .innerRadius(innerRadius * radius)
203
- .outerRadius(outerRadius * radius)
204
- .cornerRadius(cornerRadius)
205
- .startAngle(startAngle)
206
- .endAngle(endAngle)
207
- .padAngle(padAngle),
208
- };
209
- };
210
-
211
- const getCircleScale = ({ count, color, width, length } = tick, radius) => {
212
- let data = [],
213
- arcs = [],
214
- centroids = [];
215
- for (let i = 0; i < count; i++) {
216
- data.push(1);
217
- }
218
- let scaleData = pie()(data);
219
- scaleData.map((data) => {
220
- let _arc = arc()
221
- .innerRadius(radius + length / 2)
222
- .outerRadius(radius + length / 2)
223
- .startAngle(data.startAngle)
224
- .endAngle(data.endAngle);
225
- arcs.push(_arc());
226
- centroids.push(_arc.centroid());
227
- });
228
- return (
229
- <g>
230
- {centroids.map((center, index) => {
231
- let x = center[0],
232
- y = center[1];
233
- let rate = length / Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
234
- return (
235
- <path
236
- key={index}
237
- d={`M${x},${y}l${x * rate},${y * rate}`}
238
- strokeWidth={width}
239
- stroke={color}
240
- />
241
- );
242
- })}
243
- </g>
244
- );
245
- };
246
-
247
- const Component = memo(
248
- ({
249
- width,
250
- height,
251
- config: {
252
- chart: {
253
- label,
254
- legend: { formatter = legendFormatter, ...legend },
255
- animation: { ringDuration, labelDuration } = {},
256
- margin: { marginLeft, marginTop, marginRight, marginBottom },
257
- },
258
- fan: {
259
- chart = defaultChart,
260
- chart: { outerRadius = defaultChart.outerRadius, padAngle },
261
- angle = defaultAngle,
262
- stroke: { show, strokeWidth, color } = { show: false },
263
- decorate,
264
- decorate2,
265
- categoryText,
266
- outerDecorate,
267
- current,
268
- } = {},
269
- order,
270
- columnsSeries,
271
- series,
272
- animation: {
273
- on,
274
- current: {
275
- widthen = 0,
276
- heighten = 0,
277
- opacity = 0,
278
- width: radiusWidthAdd = 0,
279
- color: animateColor,
280
- textStyle: animateCTS,
281
- gap = 0,
282
- },
283
- rotate = 0,
284
- },
285
- tooltip = {},
286
- },
287
- config,
288
- state: { currentIndex, trigger },
289
- onEvent,
290
- hoverEvent,
291
- data: originData = [],
292
- }) => {
293
- const data = originData.map((d) => ({ ...d, y: d.y < 0 ? 0 : d.y }));
294
- const prevIndex = useRef(null);
295
- const { precision: legendPrecision } = legend.config.percent;
296
- const {
297
- id,
298
- isIOS,
299
- width: chartWidth,
300
- height: chartHeight,
301
- triggerOnRelative,
302
- onEmit,
303
- } = useContext(chartContext);
304
- // chart.margin 以用户配置为准;Adaptive 仅走 Legend 侧栏/顶底排版,不再 updateConfig 写预设边距
305
- const [y, setY] = useState(1);
306
- const radius = (Math.min(chartWidth, chartHeight) / 2) * outerRadius;
307
-
308
- const arcsFunc = useMemo(() => {
309
- const { startAngle = 0, endAngle = 360 } = getAngle(angle);
310
- const arcsFunc = pie()
311
- .startAngle((startAngle * PI) / 180)
312
- .endAngle((endAngle * PI) / 180)
313
- .value((d) => d.y);
314
- return arcsFunc;
315
- }, [angle]);
316
- //此处创建arcsFuncTwo的原因是为了兼容数据全为零
317
- const arcsFuncTwo = useMemo(() => {
318
- const { startAngle = 0, endAngle = 360 } = getAngle(angle);
319
- const arcsFunc = pie()
320
- .startAngle((startAngle * PI) / 180)
321
- .endAngle((endAngle * PI) / 180)
322
- .value((d) => (d.y == 0 ? 1 : d.y));
323
- return arcsFunc;
324
- }, [angle]);
325
- let judgeData = 0; //此处声明全局变量是为了父子组件传递值来判断数据是否都为零
326
- const arcs = useMemo(() => {
327
- const _chart = Object.assign(defaultChart, chart);
328
- const {
329
- innerRadius,
330
- outerRadius,
331
- rose,
332
- cornerRadius,
333
- padAngle,
334
- roseType,
335
- } = _chart;
336
- const _padAngle = (padAngle * Math.PI) / 180;
337
-
338
- switch (order) {
339
- case "":
340
- arcsFunc.sort(null);
341
- break;
342
- case "desc":
343
- arcsFunc.sort((a, b) => b.y - a.y);
344
- break;
345
- case "asc":
346
- arcsFunc.sort((a, b) => a.y - b.y);
347
- break;
348
- }
349
-
350
- //此处判断data中的y是否都为零,方便饼图都为零时展示
351
-
352
- let arcs = 0;
353
- data.forEach(function (item) {
354
- judgeData += item.y;
355
- });
356
- if (judgeData == 0) {
357
- arcs = arcsFuncTwo(data);
358
- } else {
359
- arcs = arcsFunc(data);
360
- }
361
-
362
- //const arcs = arcsFunc(data); 此处是原本的传输饼图data流程
363
- const legendDataWithPercent = getDataWithPercent(arcs, legendPrecision);
364
- const _legendDataWithPercent = sortPie(legendDataWithPercent, order);
365
-
366
- if (rose) {
367
- const domain = extent(_legendDataWithPercent, (d) => d.value);
368
- const roseRadius = getRoseRadius(_chart);
369
- const scaler = scaleLinear().domain(domain).range([roseRadius, 1]);
370
-
371
- const angle = (PI * 2) / _legendDataWithPercent.length;
372
- return _legendDataWithPercent.map(
373
- ({ startAngle, endAngle, ...arc }, index) => ({
374
- ...arc,
375
- id: id + "_linear_" + index,
376
- startAngle: roseType == "area" ? angle * index : startAngle,
377
- endAngle: roseType == "area" ? angle * (index + 1) : endAngle,
378
- cornerRadius,
379
- padAngle: _padAngle,
380
- innerRadius,
381
- outerRadius: scaler(arc.value),
382
- }),
383
- );
384
- }
385
- return _legendDataWithPercent.map((arc, index) => ({
386
- ...arc,
387
- id: id + "_linear_" + index,
388
- cornerRadius,
389
- padAngle: _padAngle,
390
- innerRadius,
391
- outerRadius,
392
- }));
393
- }, [data, arcsFunc, arcsFuncTwo, chart, legendPrecision, order]);
394
-
395
- const _arcs = useMemo(() => {
396
- const seriesLength = series.size;
397
- if (!seriesLength) return [];
398
- const _series = [...series.values()];
399
- if (_series.length < arcs.length)
400
- console.warn("请检查数据中是否存在相同的s");
401
- return arcs.map((arc, index) => getArc(radius, arc, _series, index));
402
- }, [series, arcs, radius]);
403
-
404
- const onClick = useCallback(
405
- (e) => {
406
- const _data = arcs[+e.currentTarget.dataset.index].data;
407
- triggerOnRelative("onClick", _data);
408
- onEmit("onClick", _data);
409
- onEvent({
410
- currentIndex: +e.currentTarget.dataset.index,
411
- type: "onClick",
412
- });
413
- },
414
- [onEvent],
415
- );
416
-
417
- const onMouseEnter = useCallback(
418
- (e) => {
419
- const _data = arcs[+e.currentTarget.dataset.index].data;
420
- triggerOnRelative("mousehover", _data);
421
- onEmit("mousehover", _data);
422
- onEvent({
423
- currentIndex: +e.currentTarget.dataset.index,
424
- type: "onMouseEnter",
425
- });
426
- },
427
- [onEvent, triggerOnRelative, onEmit],
428
- );
429
-
430
- const onMouseLeave = useCallback(
431
- (e) => {
432
- setMousePos({
433
- x: 0,
434
- y: 0,
435
- });
436
- onEvent({
437
- currentIndex: +e.currentTarget.dataset.index,
438
- type: "onMouseLeave",
439
- });
440
- },
441
- [onEvent],
442
- );
443
-
444
- useLayoutEffect(() => {
445
- let animation;
446
- if (!!on) {
447
- animation = animate({
448
- from: 0,
449
- to: 1,
450
- duration: 500,
451
- ease: linear,
452
- onPlay: () => {},
453
- onUpdate: (v) => {
454
- setY(v);
455
- },
456
- onComplete: () => {
457
- const _data = arcs[+currentIndex] ? arcs[+currentIndex].data : {};
458
- triggerOnRelative("carousel", _data);
459
- onEmit("carousel", _data);
460
- },
461
- });
462
- } else {
463
- if (currentIndex !== null && trigger === "onClick") {
464
- const _data = arcs[+currentIndex] ? arcs[+currentIndex].data : {};
465
- triggerOnRelative("click", _data);
466
- onEmit("click", _data);
467
- }
468
- }
469
- return () => {
470
- prevIndex.current = currentIndex;
471
- animation && animation.stop();
472
- animation = null;
473
- };
474
- }, [
475
- JSON.stringify(arcs),
476
- on,
477
- currentIndex,
478
- trigger,
479
- onEmit,
480
- triggerOnRelative,
481
- ]);
482
- const aiData = useAiDataOfPie(_arcs, legend);
483
- useEffect(() => {
484
- if (aiData.length) {
485
- if (!window.aiData) {
486
- window.aiData = {};
487
- }
488
- window.aiData[id] = {
489
- getAI: () => {
490
- return aiData;
491
- },
492
- };
493
- }
494
- return () => {
495
- window.aiData && window.aiData[id] && delete window.aiData[id];
496
- };
497
- }, [JSON.stringify(aiData), id]);
498
-
499
- const halfChartWidth = chartWidth / 2;
500
- const halfChartHeight = chartHeight / 2;
501
-
502
- const rotate_ = decorate2
503
- ? (-(arcs[+currentIndex].startAngle + arcs[+currentIndex].endAngle) *
504
- 90) /
505
- Math.PI +
506
- rotate
507
- : 0;
508
- let maxRadius = 0;
509
- _arcs.map((d) => {
510
- maxRadius = Math.max(maxRadius, d.outerRadius);
511
- });
512
- let centerRadius = 0.5 * maxRadius + 0.5 * _arcs[0].innerRadius;
513
- const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
514
- const [hoverData, setHoverData] = useState(null);
515
- const pieWarpEl = useRef(null);
516
- const domRef = useRef();
517
- return outerDecorate ? (
518
- <div ref={domRef}>
519
- <ChartContainer //用于生成甜甜圈图,判断依据是外环装饰这个配置项(outerDecorate)
520
- width={width}
521
- height={height}
522
- marginLeft={marginLeft}
523
- marginTop={marginTop}
524
- ref={pieWarpEl}
525
- >
526
- <g
527
- style={{
528
- "--labelDuration": labelDuration + "ms",
529
- "--ringDuration": ringDuration + "ms",
530
- transition: "transform ease-in-out 0.3s",
531
- transform:
532
- "translate(" +
533
- halfChartWidth +
534
- "px, " +
535
- halfChartHeight +
536
- "px) rotate(" +
537
- rotate_ +
538
- "deg)",
539
- }}
540
- >
541
- {
542
- //用于生成外环装饰的刻度
543
- outerDecorate.tick.show &&
544
- getCircleScale(outerDecorate.tick, maxRadius)
545
- }
546
- <circle //外环装饰
547
- cx="0"
548
- cy="0"
549
- r={maxRadius + 2}
550
- fill="none"
551
- stroke={outerDecorate.color}
552
- strokeWidth={outerDecorate.width}
553
- />
554
- {_arcs.map(
555
- ({ id, value, series, arc, innerRadius, outerRadius }, index) => {
556
- const arcWidth = outerRadius - innerRadius;
557
- const path = arc
558
- .innerRadius(centerRadius)
559
- .outerRadius(centerRadius)(value);
560
- const dashLength = Math.ceil(
561
- (Math.PI * centerRadius * 2) / _arcs.length,
562
- );
563
- const pie = getColorList(series.color);
564
- return (
565
- <Fragment key={index}>
566
- <path
567
- className={ringCss["inner-arc"]}
568
- style={{
569
- strokeDasharray: `${dashLength},${2 * dashLength}`,
570
- strokeDashoffset: dashLength,
571
- animationDelay: `${index * ringDuration}ms`,
572
- }}
573
- data-index={index}
574
- onClick={onClick}
575
- onMouseEnter={onMouseEnter}
576
- onMouseLeave={onMouseLeave}
577
- onMouseMove={(e) => {
578
- const _data = arcs[+e.currentTarget.dataset.index];
579
- const warpBoxPos = {
580
- x: pieWarpEl.current.getBoundingClientRect().x,
581
- y: pieWarpEl.current.getBoundingClientRect().y,
582
- };
583
- setMousePos({
584
- x: e.clientX - warpBoxPos.x,
585
- y: e.clientY - warpBoxPos.y,
586
- });
587
- setHoverData(_data);
588
- }}
589
- d={path.split("L")[0]}
590
- stroke={"url(#" + id + ")"}
591
- strokeWidth={arcWidth}
592
- fill="none"
593
- />
594
- <defs>
595
- <LinearGradient
596
- id={id}
597
- colors={pie}
598
- rotate={series.color.linear.angle + 180}
599
- // gradientUnits='objectBoundingBox'
600
- />
601
- </defs>
602
- </Fragment>
603
- );
604
- },
605
- )}
606
- {label && (
607
- <RingLabel
608
- config={{
609
- ...label,
610
- maxRadius: maxRadius + 2,
611
- ringDuration,
612
- labelDuration,
613
- }}
614
- iosStyle={{
615
- isIOS,
616
- left: halfChartWidth + marginLeft,
617
- top: halfChartHeight + marginTop,
618
- }}
619
- arcs={_arcs}
620
- judge={judgeData}
621
- />
622
- )}
623
- </g>
624
- </ChartContainer>
625
-
626
- <Legend
627
- {...legend}
628
- height={chartHeight}
629
- componentWidth={width}
630
- marginLeft={marginLeft}
631
- marginRight={marginRight}
632
- isPieChart
633
- columnsSeries={columnsSeries}
634
- data={data}
635
- series={_arcs.map((arc) => ({
636
- ...arc,
637
- percent: arc.percent.toFixed(legendPrecision),
638
- }))}
639
- pieClick={onClick}
640
- formatter={formatter}
641
- judge={judgeData}
642
- />
643
- {tooltip &&
644
- mousePos &&
645
- mousePos.x != 0 &&
646
- mousePos.y != 0 &&
647
- tooltip.manual && (
648
- <div
649
- style={{
650
- position: "absolute",
651
- pointerEvents: "none",
652
- }}
653
- >
654
- <PieTooltip
655
- series={series}
656
- domRef={domRef}
657
- data={hoverData}
658
- config={tooltip}
659
- pieCenter={{
660
- x: halfChartWidth,
661
- y: maxRadius + marginTop,
662
- }}
663
- mousePos={mousePos}
664
- />
665
- </div>
666
- )}
667
- </div>
668
- ) : (
669
- <div ref={domRef}>
670
- <ChartContainer
671
- width={width}
672
- height={height}
673
- marginLeft={marginLeft}
674
- marginTop={marginTop}
675
- onMouseEnter={() => {
676
- hoverEvent(true);
677
- }}
678
- onMouseLeave={() => {
679
- hoverEvent(false);
680
- }}
681
- ref={pieWarpEl}
682
- >
683
- <g
684
- style={{
685
- transition: "transform ease-in-out 0.3s",
686
- transform:
687
- "translate(" +
688
- halfChartWidth +
689
- "px, " +
690
- halfChartHeight +
691
- "px) rotate(" +
692
- rotate_ +
693
- "deg)",
694
- }}
695
- >
696
- {_arcs.map(
697
- (
698
- {
699
- id,
700
- value,
701
- series,
702
- arc,
703
- innerRadius,
704
- outerRadius,
705
- index: dataIndex,
706
- },
707
- index,
708
- ) => {
709
- const current = index == currentIndex;
710
- const prev = index == prevIndex.current;
711
- const offset = current ? y : prev ? 1 - y : 0;
712
-
713
- const fillOpacity = animateColor
714
- ? 1
715
- : current
716
- ? opacity / 100
717
- : 1;
718
- const deltaWidthen = offset * widthen;
719
- const deltaHeighten = offset * heighten;
720
- const path = arc
721
- .innerRadius(innerRadius + deltaWidthen)
722
- .outerRadius(outerRadius + deltaHeighten + deltaWidthen)(
723
- value,
724
- );
725
- const pie = getColorList(series.color);
726
- const currentPie = animateColor
727
- ? getColorList(animateColor)
728
- : getColorList(series.color);
729
- let textPath = "",
730
- categoryTextStyle = {};
731
- if (categoryText && categoryText.show) {
732
- //如果有类目文本,则需要计算文字路径
733
- //let offsetWidth=decorate2.radiusWidth/2 + radiusWidthAdd/2; //当前文字需生成在装饰物内,故而半径需要减小
734
- let textArc = arc
735
- .innerRadius(
736
- outerRadius + (current ? gap : categoryText.gap),
737
- )
738
- .outerRadius(
739
- outerRadius + (current ? gap : categoryText.gap),
740
- )(value);
741
- let lastA = textArc.lastIndexOf("A");
742
- textPath = textArc.slice(
743
- 0,
744
- lastA > 0 ? lastA : textArc.length,
745
- ); //文字路径
746
- categoryTextStyle = current
747
- ? animateCTS
748
- : categoryText.textStyle; //这里把textstyle拿出来
749
- }
750
-
751
- return (
752
- <Fragment key={index}>
753
- <path
754
- data-index={index}
755
- onClick={onClick}
756
- onMouseEnter={onMouseEnter}
757
- onMouseLeave={onMouseLeave}
758
- onMouseMove={(e) => {
759
- const _data = arcs[+e.currentTarget.dataset.index];
760
- const warpBoxPos = {
761
- x: pieWarpEl.current.getBoundingClientRect().x,
762
- y: pieWarpEl.current.getBoundingClientRect().y,
763
- };
764
- setMousePos({
765
- x: e.clientX - warpBoxPos.x,
766
- y: e.clientY - warpBoxPos.y,
767
- });
768
- setHoverData(_data);
769
- }}
770
- d={path}
771
- stroke={show ? color : "none"}
772
- strokeWidth={show ? strokeWidth : "0"}
773
- fill={"url(#" + id + ")"}
774
- fillOpacity={fillOpacity}
775
- />
776
- {
777
- //装饰物2,产生于每个弧的外部
778
- decorate2 && decorate2.show && (
779
- <path
780
- data-index={index}
781
- onClick={onClick}
782
- onMouseEnter={onMouseEnter}
783
- onMouseLeave={onMouseLeave}
784
- d={arc
785
- .innerRadius(outerRadius)
786
- .outerRadius(
787
- outerRadius +
788
- decorate2.radiusWidth +
789
- (current ? radiusWidthAdd : 0),
790
- )(value)}
791
- stroke={show ? color : "none"}
792
- strokeWidth={show ? strokeWidth : "0"}
793
- fill={"url(#" + id + ")"}
794
- fillOpacity={decorate2.opacity / 100}
795
- />
796
- )
797
- }
798
- {
799
- //类目文本
800
- value && categoryText && categoryText.show && (
801
- <g>
802
- <path
803
- onClick={onClick}
804
- onMouseEnter={onMouseEnter}
805
- onMouseLeave={onMouseLeave}
806
- id={id + "_text_" + index}
807
- d={textPath}
808
- fill="none"
809
- stroke="none"
810
- />
811
- <text
812
- textAnchor="middle"
813
- style={{
814
- ...categoryTextStyle,
815
- fontWeight: categoryTextStyle.bold
816
- ? "bold"
817
- : "normal",
818
- fontStyle: categoryTextStyle.italic
819
- ? "italic"
820
- : "normal",
821
- pointerEvents: "none",
822
- }}
823
- fill={categoryTextStyle.color}
824
- >
825
- <textPath
826
- startOffset="50%"
827
- href={"#" + id + "_text_" + index}
828
- >
829
- {_arcs[index].displayName ||
830
- _arcs[index].fieldName}
831
- </textPath>
832
- </text>
833
- </g>
834
- )
835
- }
836
- <defs>
837
- {/* 此处是环的发生地 */}
838
- <LinearGradient
839
- id={id}
840
- colors={current ? currentPie : pie}
841
- rotate={
842
- current
843
- ? animateColor
844
- ? animateColor.linear.angle + 180
845
- : series.color.linear.angle + 180
846
- : series.color.linear.angle + 180
847
- }
848
- // gradientUnits='objectBoundingBox'
849
- />
850
- </defs>
851
- </Fragment>
852
- );
853
- },
854
- )}
855
- {label && (
856
- <Label
857
- config={label}
858
- iosStyle={{
859
- isIOS,
860
- left: halfChartWidth + marginLeft,
861
- top: halfChartHeight + marginTop,
862
- }}
863
- arcs={_arcs}
864
- judge={judgeData}
865
- />
866
- )}
867
- {current && (
868
- <g
869
- fillOpacity={y}
870
- style={{ transform: "rotate(" + -rotate_ + "deg)" }}
871
- >
872
- <Current
873
- config={current}
874
- width={chartWidth}
875
- height={chartHeight}
876
- iosStyle={{
877
- marginLeft,
878
- marginTop,
879
- isIOS,
880
- }}
881
- data={_arcs}
882
- judge={judgeData}
883
- currentIndex={+currentIndex}
884
- />
885
- </g>
886
- )}
887
- </g>
888
- </ChartContainer>
889
- {decorate && (
890
- <ConicalGradient
891
- width={width}
892
- height={height}
893
- centerX={halfChartWidth + marginLeft}
894
- centerY={halfChartHeight + marginTop}
895
- config={decorate}
896
- arcs={_arcs}
897
- radius={radius}
898
- />
899
- )}
900
-
901
- <Legend
902
- {...legend}
903
- height={chartHeight}
904
- componentWidth={width}
905
- marginLeft={marginLeft}
906
- marginRight={marginRight}
907
- isPieChart
908
- data={data}
909
- columnsSeries={columnsSeries}
910
- series={_arcs.map((arc) => ({
911
- ...arc,
912
- percent: arc.percent.toFixed(legendPrecision),
913
- }))}
914
- pieClick={onClick}
915
- formatter={formatter}
916
- judge={judgeData}
917
- />
918
- {tooltip &&
919
- mousePos &&
920
- mousePos.x != 0 &&
921
- mousePos.y != 0 &&
922
- tooltip.manual && (
923
- <div
924
- style={{
925
- position: "absolute",
926
- pointerEvents: "none",
927
- }}
928
- >
929
- <PieTooltip
930
- series={series}
931
- domRef={domRef}
932
- data={hoverData}
933
- config={tooltip}
934
- pieCenter={{
935
- x: halfChartWidth,
936
- y: maxRadius + marginTop,
937
- }}
938
- mousePos={mousePos}
939
- />
940
- </div>
941
- )}
942
- </div>
943
- );
944
- },
945
- );
946
-
947
- const Current = ({
948
- config: {
949
- show,
950
- gap,
951
- name: {
952
- show: showName,
953
- sameColor: nameColor,
954
- font: nameFont,
955
- translate = { x: 0, y: 0 },
956
- maxWidth,
957
- textOverflow,
958
- speed,
959
- },
960
- percent: {
961
- show: showPercent,
962
- sameColor: percentColor,
963
- font: percentFont,
964
- precision,
965
- translate: { x: translatePercentX, y: translatePercentY },
966
- },
967
- value: {
968
- show: showValue,
969
- sameColor: valueColor,
970
- font: valueFont,
971
- translate: { x: translateValueX, y: translateValueY },
972
- suffix: {
973
- show: showSuffix,
974
- fontSize,
975
- text,
976
- translate: { x: translateSuffixX, y: translateSuffixY },
977
- },
978
- },
979
- },
980
- iosStyle: { isIOS, marginLeft, marginTop },
981
- width,
982
- height,
983
- data,
984
- judge,
985
- currentIndex,
986
- }) => {
987
- const _data = useMemo(() => {
988
- const legendDataWithPercent = getDataWithPercent(data, precision);
989
- return sortPie(legendDataWithPercent, "");
990
- }, [data, precision]);
991
-
992
- //数据容错,当data都为零那么需要进行以下容错
993
- if (judge == 0) {
994
- _data.forEach((d) => {
995
- ((d.percent = 0), (d.value = 0));
996
- });
997
- }
998
-
999
- const currentData = _data[currentIndex];
1000
-
1001
- if (!currentData) return null;
1002
-
1003
- const { displayName, fieldName, value, percent } = currentData;
1004
- let nameTemp = displayName ? displayName : fieldName; //类目名
1005
-
1006
- let foreignStyle = {
1007
- //foreignObject标签样式,
1008
- width,
1009
- height,
1010
- position: "relative",
1011
- overflow: "visible",
1012
- pointerEvents: "none",
1013
- },
1014
- boxStyle = {
1015
- //弹性盒子样式,用于当前值的上下居中对齐等
1016
- width,
1017
- height,
1018
- position: "absolute",
1019
- display: "flex",
1020
- flexDirection: "column",
1021
- justifyContent: "center",
1022
- alignItems: "center",
1023
- transform: isIOS
1024
- ? `translate(${marginLeft}px,${marginTop}px)`
1025
- : `translate(-${width / 2}px,-${height / 2}px)`,
1026
- };
1027
- let seriesColor = currentData.series.color;
1028
- seriesColor =
1029
- seriesColor.type == "pure"
1030
- ? seriesColor.pure
1031
- : seriesColor.linear.stops[0].color;
1032
- return (
1033
- show && (
1034
- <foreignObject style={foreignStyle}>
1035
- <div style={boxStyle}>
1036
- {showName && (
1037
- <TextOverflow
1038
- type={textOverflow}
1039
- value={nameTemp}
1040
- speed={speed}
1041
- style={{
1042
- width: "100%",
1043
- maxWidth,
1044
- textAlign: "center",
1045
- display: textOverflow == "marquee" ? "flex" : "bolck",
1046
- justifyContent: "center",
1047
- ...getFontStyle(nameFont),
1048
- margin: gap / 2 + "px 0",
1049
- color: nameColor ? seriesColor : nameFont.color,
1050
- transform: `translate(${translate.x}px,${translate.y}px)`,
1051
- }}
1052
- ></TextOverflow>
1053
- )}
1054
- {
1055
- //真实值
1056
- showValue && (
1057
- <span
1058
- style={{
1059
- ...getFontStyle(valueFont),
1060
- transform:
1061
- "translate(" +
1062
- translateValueX +
1063
- "px," +
1064
- translateValueY +
1065
- "px)",
1066
- margin: gap / 2 + "px 0",
1067
- color: valueColor ? seriesColor : valueFont.color,
1068
- }}
1069
- >
1070
- {value}
1071
- {showSuffix && text && (
1072
- <span
1073
- style={{
1074
- display: "inline-block",
1075
- transform:
1076
- "translate(" +
1077
- translateSuffixX +
1078
- "px," +
1079
- translateSuffixY +
1080
- "px)",
1081
- fontSize: fontSize,
1082
- }}
1083
- >
1084
- {text}
1085
- </span>
1086
- )}
1087
- </span>
1088
- )
1089
- }
1090
- {
1091
- //百分比值
1092
- showPercent && (
1093
- <span
1094
- style={{
1095
- transform:
1096
- "translate(" +
1097
- translatePercentX +
1098
- "px," +
1099
- translatePercentY +
1100
- "px)",
1101
- ...getFontStyle(percentFont),
1102
- margin: gap / 2 + "px 0",
1103
- color: percentColor ? seriesColor : percentFont.color,
1104
- }}
1105
- >
1106
- {percent + "%"}
1107
- </span>
1108
- )
1109
- }
1110
- </div>
1111
- </foreignObject>
1112
- )
1113
- );
1114
- };
1115
-
1116
- const Label = ({
1117
- config: {
1118
- maxRadius = 0,
1119
- lineLength,
1120
- lineColor,
1121
- distance,
1122
- mode,
1123
- align,
1124
- show,
1125
- translate: { x: translateX, y: translateY },
1126
- name: {
1127
- show: showName,
1128
- font: nameFont,
1129
- maxWidth,
1130
- textOverflow,
1131
- speed,
1132
- translate: NameTranslate,
1133
- },
1134
- value: {
1135
- show: showValue,
1136
- font: valueFont,
1137
- sameColor: valueSameColor,
1138
- suffix: {
1139
- show: showSuffix,
1140
- text,
1141
- fontSize: suffixFontSize,
1142
- translate: { x: suffixTranslateX, y: suffixTranslateY },
1143
- },
1144
- translate: ValueTranslate,
1145
- },
1146
- percent: {
1147
- show: showPercent,
1148
- sameColor: percentSameColor,
1149
- font: percentFont,
1150
- precision,
1151
- translate: PercentTranslate,
1152
- },
1153
- },
1154
- iosStyle: { isIOS, left, top },
1155
- arcs,
1156
- judge,
1157
- animation,
1158
- }) => {
1159
- const _arcs = useMemo(
1160
- () => getDataWithPercent(arcs, precision),
1161
- [arcs, precision],
1162
- );
1163
- //数据做出容错
1164
- if (judge == 0) {
1165
- _arcs.forEach((d) => {
1166
- d.percent = 0;
1167
- });
1168
- }
1169
- return (
1170
- <g>
1171
- {_arcs.map(
1172
- (
1173
- {
1174
- series: {
1175
- color: {
1176
- type,
1177
- pure,
1178
- linear: { stops },
1179
- },
1180
- },
1181
- data,
1182
- displayName,
1183
- value,
1184
- percent,
1185
- arc,
1186
- outerRadius,
1187
- index: actualIndex,
1188
- },
1189
- index,
1190
- ) => {
1191
- const [x, y] = arc.centroid();
1192
- const midAngle = Math.atan2(y, x);
1193
-
1194
- const [x1, y1] = getCoord(
1195
- midAngle,
1196
- maxRadius ? maxRadius : outerRadius,
1197
- );
1198
-
1199
- const radius = (maxRadius ? maxRadius : outerRadius) + distance;
1200
- const [x2, y2] = getCoord(midAngle, radius);
1201
-
1202
- const direction = x2 < 0 ? -1 : 1;
1203
- const x3 = x2 + lineLength * direction;
1204
-
1205
- const _x = x3 + (translateX + 6) * direction;
1206
-
1207
- const _showName = showName && displayName;
1208
- const _showValue = showValue && (value || showSuffix);
1209
- const nameStyle = getFontStyle(nameFont);
1210
- return (
1211
- show &&
1212
- (_showName || showPercent || showValue) && (
1213
- <g key={index}>
1214
- <path
1215
- className={animation ? ringCss["label-line"] : ""}
1216
- style={{
1217
- animationDelay: `${
1218
- animation
1219
- ? (actualIndex + 1) * ringDuration - labelDuration
1220
- : 0
1221
- }ms`,
1222
- }}
1223
- d={
1224
- "M" +
1225
- x1 +
1226
- ", " +
1227
- y1 +
1228
- "L" +
1229
- x2 +
1230
- ", " +
1231
- y2 +
1232
- "L" +
1233
- x3 +
1234
- ", " +
1235
- y2
1236
- }
1237
- stroke={
1238
- lineColor
1239
- ? lineColor
1240
- : type == "pure"
1241
- ? pure
1242
- : stops[0].color
1243
- }
1244
- fill="none"
1245
- />
1246
- <foreignObject
1247
- width="1"
1248
- height="1"
1249
- x={_x}
1250
- y={y2 + translateY}
1251
- style={{ overflow: "visible", position: "relative" }}
1252
- >
1253
- <div
1254
- className={animation ? ringCss["label-text"] : ""}
1255
- style={{
1256
- position: isIOS ? "absolute" : "relative",
1257
- transform: isIOS
1258
- ? `translate(calc(${x3 < 0 ? "-100%" : "0px"} + ${
1259
- left + _x
1260
- }px),calc(-50% + ${top + y2 + translateY}px))`
1261
- : "translate(0,-50%)",
1262
- whiteSpace: "nowrap",
1263
- float: x3 >= 0 ? "left" : "right",
1264
- width: "max-content",
1265
- display: "flex",
1266
- flexDirection: mode == "horizontal" ? "row" : "column",
1267
- alignItems:
1268
- align == "left"
1269
- ? "flex-start"
1270
- : align == "center"
1271
- ? "center"
1272
- : "flex-end",
1273
- justifyContent: "center",
1274
- }}
1275
- >
1276
- {_showName && (
1277
- <TextOverflow
1278
- type={textOverflow}
1279
- value={
1280
- displayName + (showValue || showPercent ? ":" : "")
1281
- }
1282
- speed={speed}
1283
- style={{
1284
- maxWidth,
1285
- ...nameStyle,
1286
- float: mode == "horizontal" ? "left" : "none",
1287
- transform: `translate(${NameTranslate.x}px, ${NameTranslate.y}px)`,
1288
- }}
1289
- ></TextOverflow>
1290
- )}
1291
- {showValue && (
1292
- <span
1293
- style={{
1294
- ...getFontStyle(valueFont),
1295
- color: valueSameColor ? pure : valueFont.color,
1296
- transform: `translate(${ValueTranslate.x}px, ${ValueTranslate.y}px)`,
1297
- }}
1298
- >
1299
- {data.y}
1300
- {showSuffix && (
1301
- <span
1302
- style={{
1303
- position: "relative",
1304
- fontSize: suffixFontSize,
1305
- marginLeft: suffixTranslateX,
1306
- top: suffixTranslateY,
1307
- }}
1308
- >
1309
- {text}
1310
- </span>
1311
- )}
1312
- </span>
1313
- )}
1314
- {showPercent && (
1315
- <span
1316
- style={{
1317
- ...getFontStyle(percentFont),
1318
- color: percentSameColor ? pure : percentFont.color,
1319
- transform: `translate(${PercentTranslate.x}px, ${PercentTranslate.y}px)`,
1320
- }}
1321
- >
1322
- {(_showValue ? "" : "") +
1323
- percent +
1324
- "%" +
1325
- (_showValue ? "" : "")}
1326
- </span>
1327
- )}
1328
- </div>
1329
- </foreignObject>
1330
- </g>
1331
- )
1332
- );
1333
- },
1334
- )}
1335
- </g>
1336
- );
1337
- };
1338
-
1339
- function getAlign(align, reverse) {
1340
- if (align == "center") return "center";
1341
- if (align == "left") return reverse ? "flex-end" : "flex-start";
1342
- return reverse ? "flex-start" : "flex-end";
1343
- }
1344
- function getTranslate(translate, reverse) {
1345
- const { x, y } = translate;
1346
- return `translate(${reverse ? -x : x}px, ${y}px)`;
1347
- }
1348
- const RingLabel = ({
1349
- config: {
1350
- ringDuration,
1351
- labelDuration,
1352
- maxRadius = 0,
1353
- lineLength,
1354
- lineColor,
1355
- distance,
1356
- mode,
1357
- align = "center",
1358
- show,
1359
- translate: { x: translateX, y: translateY },
1360
- name: {
1361
- show: showName,
1362
- font: nameFont,
1363
- maxWidth,
1364
- textOverflow,
1365
- speed,
1366
- translate: nameTranslate,
1367
- },
1368
- value: {
1369
- show: showValue,
1370
- font: valueFont,
1371
- sameColor: valueSameColor,
1372
- suffix: {
1373
- show: showSuffix,
1374
- text,
1375
- fontSize: suffixFontSize,
1376
- translate: { x: suffixTranslateX, y: suffixTranslateY },
1377
- },
1378
- translate: valueTranslate,
1379
- },
1380
- percent: {
1381
- show: showPercent,
1382
- sameColor: percentSameColor,
1383
- font: percentFont,
1384
- precision,
1385
- translate: percentTranslate,
1386
- },
1387
- },
1388
- iosStyle: { isIOS, left, top },
1389
- judge,
1390
- arcs,
1391
- }) => {
1392
- const _arcs = useMemo(
1393
- () => getDataWithPercent(arcs, precision),
1394
- [arcs, precision],
1395
- );
1396
-
1397
- //数据做出容错
1398
- if (judge == 0) {
1399
- _arcs.forEach((d) => {
1400
- d.percent = 0;
1401
- });
1402
- }
1403
-
1404
- return (
1405
- <g>
1406
- {_arcs.map(
1407
- (
1408
- {
1409
- series: {
1410
- color: {
1411
- type,
1412
- pure,
1413
- linear: { stops },
1414
- },
1415
- },
1416
- data: realData,
1417
- displayName,
1418
- value,
1419
- percent,
1420
- arc,
1421
- outerRadius,
1422
- index: actualIndex,
1423
- },
1424
- index,
1425
- ) => {
1426
- const [x, y] = arc.centroid();
1427
- const midAngle = Math.atan2(y, x);
1428
-
1429
- const [x1, y1] = getCoord(
1430
- midAngle,
1431
- maxRadius ? maxRadius : outerRadius,
1432
- );
1433
-
1434
- const radius = (maxRadius ? maxRadius : outerRadius) + distance;
1435
- const [x2, y2] = getCoord(midAngle, radius);
1436
-
1437
- const directionX = x2 < 0 ? -1 : 1;
1438
- const directionY = y2 < 0 ? -1 : 1;
1439
- const x3 = x2 + lineLength * directionX;
1440
- const _x = x3 + (translateX + 6) * directionX;
1441
-
1442
- const _showName = showName && displayName;
1443
- const _showValue = showValue && (value || showSuffix);
1444
-
1445
- return (
1446
- show &&
1447
- (_showName || showPercent || _showValue) && (
1448
- <g key={index}>
1449
- <path
1450
- className={ringCss["label-line"]}
1451
- style={{
1452
- animationDelay: `${
1453
- (actualIndex + 1) * ringDuration - labelDuration
1454
- }ms`,
1455
- }}
1456
- d={
1457
- "M" +
1458
- x1 +
1459
- ", " +
1460
- y1 +
1461
- "L" +
1462
- x2 +
1463
- ", " +
1464
- y2 +
1465
- "L" +
1466
- x3 +
1467
- ", " +
1468
- y2
1469
- }
1470
- stroke={
1471
- lineColor
1472
- ? lineColor
1473
- : type == "pure"
1474
- ? pure
1475
- : stops[0].color
1476
- }
1477
- fill="none"
1478
- />
1479
- <foreignObject
1480
- width="1"
1481
- height="1"
1482
- x={_x}
1483
- y={y2 + translateY}
1484
- style={{ overflow: "visible", position: "relative" }}
1485
- >
1486
- <div
1487
- className={ringCss["label-text"]}
1488
- style={{
1489
- position: isIOS ? "absolute" : "relative",
1490
- transform: isIOS
1491
- ? `translate(calc(${x3 < 0 ? "-100%" : "0px"} + ${
1492
- left + _x
1493
- }px),calc(-50% + ${top + y2 + translateY}px))`
1494
- : "translate(0,-50%)",
1495
- whiteSpace: "nowrap",
1496
- float: x3 >= 0 ? "left" : "right",
1497
- width: "max-content",
1498
- animationDelay: `${
1499
- (actualIndex + 1) * ringDuration - labelDuration
1500
- }ms`,
1501
- display: "flex",
1502
- flexDirection: mode == "horizontal" ? "row" : "column",
1503
- alignItems: getAlign(align, x3 >= 0),
1504
- justifyContent: "center",
1505
- }}
1506
- >
1507
- {_showName && (
1508
- <TextOverflow
1509
- type={textOverflow}
1510
- value={
1511
- displayName + (showValue || showPercent ? ":" : "")
1512
- }
1513
- speed={speed}
1514
- style={{
1515
- maxWidth,
1516
- ...getFontStyle(nameFont),
1517
- float: mode == "horizontal" ? "left" : "none",
1518
- transform: getTranslate(nameTranslate, x3 >= 0),
1519
- }}
1520
- ></TextOverflow>
1521
- )}
1522
- {showValue && (
1523
- <span
1524
- style={{
1525
- ...getFontStyle(valueFont),
1526
- transform: getTranslate(valueTranslate, x3 >= 0),
1527
- color: valueSameColor ? pure : valueFont.color,
1528
- }}
1529
- >
1530
- {realData.y}
1531
- {showSuffix && (
1532
- <span
1533
- style={{
1534
- position: "relative",
1535
- fontSize: suffixFontSize,
1536
- marginLeft: suffixTranslateX,
1537
- top: suffixTranslateY,
1538
- }}
1539
- >
1540
- {text}
1541
- </span>
1542
- )}
1543
- </span>
1544
- )}
1545
- {showPercent && (
1546
- <span
1547
- style={{
1548
- ...getFontStyle(percentFont),
1549
- transform: getTranslate(percentTranslate, x3 >= 0),
1550
- color: percentSameColor ? pure : percentFont.color,
1551
- }}
1552
- >
1553
- {(_showValue ? "(" : "") +
1554
- percent +
1555
- "%" +
1556
- (_showValue ? ")" : "")}
1557
- </span>
1558
- )}
1559
- </div>
1560
- </foreignObject>
1561
- </g>
1562
- )
1563
- );
1564
- },
1565
- )}
1566
- </g>
1567
- );
1568
- };
1569
-
1570
- export default Mapping(Carousel(Component));
1
+ /**
2
+ * 饼环图
3
+ */
4
+ import React, {
5
+ memo,
6
+ useMemo,
7
+ useCallback,
8
+ useRef,
9
+ useState,
10
+ useEffect,
11
+ useContext,
12
+ useLayoutEffect,
13
+ Fragment,
14
+ } from "react";
15
+ import {
16
+ ChartContainer,
17
+ Carousel,
18
+ Legend,
19
+ ConicalGradient,
20
+ Mapping,
21
+ TextOverflow,
22
+ } from ".";
23
+ import { chartContext } from "../context";
24
+ import {
25
+ getFontStyle,
26
+ sortPie,
27
+ getDataWithPercent,
28
+ getColorList,
29
+ } from "../utils";
30
+ import { pie, arc, extent, scaleLinear } from "d3v7";
31
+ import { animate, linear } from "popmotion";
32
+ import LinearGradient from "./LinearGradient";
33
+ import { pieLegendFormatter as legendFormatter } from "../formatter";
34
+ import ringCss from "../css/piechart.module.css";
35
+ import { useAiDataOfPie } from "../hooks";
36
+ import { PieTooltip } from "./PieTooltip";
37
+ import { getPieAdaptiveMarginPreset } from "../utils/legendPlacement";
38
+
39
+ const PI = Math.PI;
40
+
41
+ const PIE_ADAPTIVE_MARGINS = {
42
+ right: { marginTop: 24, marginBottom: 24, marginLeft: 0, marginRight: 200 },
43
+ left: { marginTop: 24, marginBottom: 24, marginLeft: 200, marginRight: 0 },
44
+ top: { marginTop: 110, marginBottom: 24, marginLeft: 24, marginRight: 24 },
45
+ bottom: { marginTop: 24, marginBottom: 110, marginLeft: 24, marginRight: 24 },
46
+ hidden: { marginTop: 24, marginBottom: 24, marginLeft: 24, marginRight: 24 },
47
+ };
48
+
49
+ const getPieAdaptiveMargin = (show, alignment) =>
50
+ getPieAdaptiveMarginPreset(show, alignment, PIE_ADAPTIVE_MARGINS);
51
+
52
+ const defaultChart = {
53
+ outerRadius: 1,
54
+ innerRadius: 0,
55
+ cornerRadius: 0,
56
+ rose: false,
57
+ roseType: "radius",
58
+ baseRadius: 0,
59
+ padAngle: 0,
60
+ };
61
+ const defaultAngle = { startAngle: 0, endAngle: 360, antiClockwise: false };
62
+
63
+ // const nameDy = (showValue, showPercent, mode, dir) => {
64
+ // if (showValue || showPercent) {
65
+ // if (mode == "vertical") {
66
+ // return dir == 1 ? "1.1em" : "-2.6em";
67
+ // } else {
68
+ // return 0;
69
+ // }
70
+ // } else {
71
+ // if (mode == "vertical") {
72
+ // return dir * 1.1 + "em";
73
+ // } else {
74
+ // return 0;
75
+ // }
76
+ // }
77
+ // };
78
+ // const valueDy = (value1, mode, dir) => {
79
+ // if (value1) {
80
+ // if (mode == "vertical") {
81
+ // return "1.5em";
82
+ // } else {
83
+ // return 0;
84
+ // }
85
+ // } else {
86
+ // if (mode == "vertical") {
87
+ // return dir == 1 ? "1.1em" : "-1.1em";
88
+ // } else {
89
+ // return 0;
90
+ // }
91
+ // }
92
+ // };
93
+
94
+ // const percentDy_ = (showName, showValue, mode, dir) => {
95
+ // if (showValue) {
96
+ // return 0;
97
+ // }
98
+ // if (showName) {
99
+ // if (mode == "vertical") {
100
+ // return "1.5em";
101
+ // } else {
102
+ // return 0;
103
+ // }
104
+ // } else {
105
+ // if (mode == "vertical") {
106
+ // return dir * 1.1 + "em";
107
+ // } else {
108
+ // return 0;
109
+ // }
110
+ // }
111
+ // };
112
+
113
+ // const percentX = (showName, showValue, mode, x) => {
114
+ // if (showValue) {
115
+ // return "";
116
+ // }
117
+ // if (showName) {
118
+ // if (mode == "vertical") {
119
+ // return x;
120
+ // } else {
121
+ // return "";
122
+ // }
123
+ // } else {
124
+ // return x;
125
+ // }
126
+ // };
127
+
128
+ // const percentDx = (showName, showValue, mode) => {
129
+ // if (showValue) {
130
+ // return "0.5em";
131
+ // }
132
+ // if (showName) {
133
+ // if (mode == "vertical") {
134
+ // return 0;
135
+ // } else {
136
+ // return "0.5em";
137
+ // }
138
+ // } else {
139
+ // return 0;
140
+ // }
141
+ // };
142
+
143
+ // const percentDy = (showName, showValue, mode) => {
144
+ // if (showValue) {
145
+ // return 0;
146
+ // }
147
+ // if (showName) {
148
+ // if (mode == "vertical") {
149
+ // return "1.5em";
150
+ // } else {
151
+ // return 0;
152
+ // }
153
+ // } else {
154
+ // return 0;
155
+ // }
156
+ // };
157
+
158
+ // const valueDx = (showName, mode) => {
159
+ // if (!showName) {
160
+ // return "";
161
+ // }
162
+ // if (mode == "vertical") {
163
+ // return "";
164
+ // } else {
165
+ // return "0.5em";
166
+ // }
167
+ // };
168
+
169
+ const getCoord = (deg, radius) => {
170
+ var x = Math.cos(deg) * radius,
171
+ y = Math.sin(deg) * radius;
172
+ return [x, y];
173
+ };
174
+
175
+ const getRoseRadius = ({ innerRadius, baseRadius }) =>
176
+ innerRadius + (1 - innerRadius) * baseRadius;
177
+
178
+ const getAngle = ({ startAngle, endAngle, antiClockwise, ...rest }) => {
179
+ if (antiClockwise)
180
+ return {
181
+ ...rest,
182
+ startAngle: endAngle - 180,
183
+ endAngle: startAngle - 180,
184
+ };
185
+ return { ...rest, startAngle, endAngle };
186
+ };
187
+
188
+ const getArc = (
189
+ radius,
190
+ {
191
+ padAngle = 0,
192
+ innerRadius = 0,
193
+ outerRadius = 1,
194
+ cornerRadius = 0,
195
+ startAngle = 0,
196
+ endAngle = 360,
197
+ ...rest
198
+ },
199
+ series_,
200
+ index,
201
+ ) => {
202
+ const series =
203
+ series_.find((s) => s.fieldName == rest.data.s) ||
204
+ series_[index % series_.length];
205
+ return {
206
+ ...rest,
207
+ type: "pie",
208
+ fieldName: series.fieldName,
209
+ displayName: series.displayName || rest.data.s,
210
+ series: series,
211
+ innerRadius: innerRadius * radius,
212
+ outerRadius: outerRadius * radius,
213
+ arc: arc()
214
+ .innerRadius(innerRadius * radius)
215
+ .outerRadius(outerRadius * radius)
216
+ .cornerRadius(cornerRadius)
217
+ .startAngle(startAngle)
218
+ .endAngle(endAngle)
219
+ .padAngle(padAngle),
220
+ };
221
+ };
222
+
223
+ const getCircleScale = ({ count, color, width, length } = tick, radius) => {
224
+ let data = [],
225
+ arcs = [],
226
+ centroids = [];
227
+ for (let i = 0; i < count; i++) {
228
+ data.push(1);
229
+ }
230
+ let scaleData = pie()(data);
231
+ scaleData.map((data) => {
232
+ let _arc = arc()
233
+ .innerRadius(radius + length / 2)
234
+ .outerRadius(radius + length / 2)
235
+ .startAngle(data.startAngle)
236
+ .endAngle(data.endAngle);
237
+ arcs.push(_arc());
238
+ centroids.push(_arc.centroid());
239
+ });
240
+ return (
241
+ <g>
242
+ {centroids.map((center, index) => {
243
+ let x = center[0],
244
+ y = center[1];
245
+ let rate = length / Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
246
+ return (
247
+ <path
248
+ key={index}
249
+ d={`M${x},${y}l${x * rate},${y * rate}`}
250
+ strokeWidth={width}
251
+ stroke={color}
252
+ />
253
+ );
254
+ })}
255
+ </g>
256
+ );
257
+ };
258
+
259
+ const Component = memo(
260
+ ({
261
+ width,
262
+ height,
263
+ config: {
264
+ chart: {
265
+ label,
266
+ legend: { formatter = legendFormatter, ...legend },
267
+ animation: { ringDuration, labelDuration } = {},
268
+ margin: { marginLeft, marginTop, marginRight, marginBottom },
269
+ },
270
+ fan: {
271
+ chart = defaultChart,
272
+ chart: { outerRadius = defaultChart.outerRadius, padAngle },
273
+ angle = defaultAngle,
274
+ stroke: { show, strokeWidth, color } = { show: false },
275
+ decorate,
276
+ decorate2,
277
+ categoryText,
278
+ outerDecorate,
279
+ current,
280
+ } = {},
281
+ order,
282
+ columnsSeries,
283
+ series,
284
+ animation: {
285
+ on,
286
+ current: {
287
+ widthen = 0,
288
+ heighten = 0,
289
+ opacity = 0,
290
+ width: radiusWidthAdd = 0,
291
+ color: animateColor,
292
+ textStyle: animateCTS,
293
+ gap = 0,
294
+ },
295
+ rotate = 0,
296
+ },
297
+ tooltip = {},
298
+ },
299
+ config,
300
+ state: { currentIndex, trigger },
301
+ onEvent,
302
+ hoverEvent,
303
+ data: originData = [],
304
+ }) => {
305
+ const data = originData.map((d) => ({ ...d, y: d.y < 0 ? 0 : d.y }));
306
+ const prevIndex = useRef(null);
307
+ const { precision: legendPrecision } = legend.config.percent;
308
+ const {
309
+ id,
310
+ isIOS,
311
+ width: chartWidth,
312
+ height: chartHeight,
313
+ triggerOnRelative,
314
+ onEmit,
315
+ updateConfig,
316
+ } = useContext(chartContext);
317
+ const { show: legendShow, name: { layoutMode: legendLayoutMode } = {} } =
318
+ legend.config;
319
+ const legendAlignment = legend.config?.layout?.alignment;
320
+ const prevLegendLayoutKey = useRef(null);
321
+ useEffect(() => {
322
+ if (legendLayoutMode !== "Adaptive") {
323
+ prevLegendLayoutKey.current = null;
324
+ return;
325
+ }
326
+ if (!updateConfig) return;
327
+
328
+ const layoutKey = `${legendShow}-${legendAlignment}`;
329
+ const prev = prevLegendLayoutKey.current;
330
+ prevLegendLayoutKey.current = layoutKey;
331
+
332
+ // 挂载/刷新:只记录当前图例布局,margin 始终读配置项
333
+ if (prev === null) return;
334
+ if (prev === layoutKey) return;
335
+
336
+ const target = getPieAdaptiveMargin(legendShow, legendAlignment);
337
+ updateConfig({
338
+ id,
339
+ type: "config",
340
+ payload: [
341
+ {
342
+ path: ["chart", "margin", "marginTop"],
343
+ field: "value",
344
+ value: target.marginTop,
345
+ },
346
+ {
347
+ path: ["chart", "margin", "marginBottom"],
348
+ field: "value",
349
+ value: target.marginBottom,
350
+ },
351
+ {
352
+ path: ["chart", "margin", "marginLeft"],
353
+ field: "value",
354
+ value: target.marginLeft,
355
+ },
356
+ {
357
+ path: ["chart", "margin", "marginRight"],
358
+ field: "value",
359
+ value: target.marginRight,
360
+ },
361
+ ],
362
+ });
363
+ }, [id, updateConfig, legendLayoutMode, legendShow, legendAlignment]);
364
+ const [y, setY] = useState(1);
365
+ const radius = (Math.min(chartWidth, chartHeight) / 2) * outerRadius;
366
+
367
+ const arcsFunc = useMemo(() => {
368
+ const { startAngle = 0, endAngle = 360 } = getAngle(angle);
369
+ const arcsFunc = pie()
370
+ .startAngle((startAngle * PI) / 180)
371
+ .endAngle((endAngle * PI) / 180)
372
+ .value((d) => d.y);
373
+ return arcsFunc;
374
+ }, [angle]);
375
+ //此处创建arcsFuncTwo的原因是为了兼容数据全为零
376
+ const arcsFuncTwo = useMemo(() => {
377
+ const { startAngle = 0, endAngle = 360 } = getAngle(angle);
378
+ const arcsFunc = pie()
379
+ .startAngle((startAngle * PI) / 180)
380
+ .endAngle((endAngle * PI) / 180)
381
+ .value((d) => (d.y == 0 ? 1 : d.y));
382
+ return arcsFunc;
383
+ }, [angle]);
384
+ let judgeData = 0; //此处声明全局变量是为了父子组件传递值来判断数据是否都为零
385
+ const arcs = useMemo(() => {
386
+ const _chart = Object.assign(defaultChart, chart);
387
+ const {
388
+ innerRadius,
389
+ outerRadius,
390
+ rose,
391
+ cornerRadius,
392
+ padAngle,
393
+ roseType,
394
+ } = _chart;
395
+ const _padAngle = (padAngle * Math.PI) / 180;
396
+
397
+ switch (order) {
398
+ case "":
399
+ arcsFunc.sort(null);
400
+ break;
401
+ case "desc":
402
+ arcsFunc.sort((a, b) => b.y - a.y);
403
+ break;
404
+ case "asc":
405
+ arcsFunc.sort((a, b) => a.y - b.y);
406
+ break;
407
+ }
408
+
409
+ //此处判断data中的y是否都为零,方便饼图都为零时展示
410
+
411
+ let arcs = 0;
412
+ data.forEach(function (item) {
413
+ judgeData += item.y;
414
+ });
415
+ if (judgeData == 0) {
416
+ arcs = arcsFuncTwo(data);
417
+ } else {
418
+ arcs = arcsFunc(data);
419
+ }
420
+
421
+ //const arcs = arcsFunc(data); 此处是原本的传输饼图data流程
422
+ const legendDataWithPercent = getDataWithPercent(arcs, legendPrecision);
423
+ const _legendDataWithPercent = sortPie(legendDataWithPercent, order);
424
+
425
+ if (rose) {
426
+ const domain = extent(_legendDataWithPercent, (d) => d.value);
427
+ const roseRadius = getRoseRadius(_chart);
428
+ const scaler = scaleLinear().domain(domain).range([roseRadius, 1]);
429
+
430
+ const angle = (PI * 2) / _legendDataWithPercent.length;
431
+ return _legendDataWithPercent.map(
432
+ ({ startAngle, endAngle, ...arc }, index) => ({
433
+ ...arc,
434
+ id: id + "_linear_" + index,
435
+ startAngle: roseType == "area" ? angle * index : startAngle,
436
+ endAngle: roseType == "area" ? angle * (index + 1) : endAngle,
437
+ cornerRadius,
438
+ padAngle: _padAngle,
439
+ innerRadius,
440
+ outerRadius: scaler(arc.value),
441
+ }),
442
+ );
443
+ }
444
+ return _legendDataWithPercent.map((arc, index) => ({
445
+ ...arc,
446
+ id: id + "_linear_" + index,
447
+ cornerRadius,
448
+ padAngle: _padAngle,
449
+ innerRadius,
450
+ outerRadius,
451
+ }));
452
+ }, [data, arcsFunc, arcsFuncTwo, chart, legendPrecision, order]);
453
+
454
+ const _arcs = useMemo(() => {
455
+ const seriesLength = series.size;
456
+ if (!seriesLength) return [];
457
+ const _series = [...series.values()];
458
+ if (_series.length < arcs.length)
459
+ console.warn("请检查数据中是否存在相同的s");
460
+ return arcs.map((arc, index) => getArc(radius, arc, _series, index));
461
+ }, [series, arcs, radius]);
462
+
463
+ const onClick = useCallback(
464
+ (e) => {
465
+ const _data = arcs[+e.currentTarget.dataset.index].data;
466
+ triggerOnRelative("onClick", _data);
467
+ onEmit("onClick", _data);
468
+ onEvent({
469
+ currentIndex: +e.currentTarget.dataset.index,
470
+ type: "onClick",
471
+ });
472
+ },
473
+ [onEvent],
474
+ );
475
+
476
+ const onMouseEnter = useCallback(
477
+ (e) => {
478
+ const _data = arcs[+e.currentTarget.dataset.index].data;
479
+ triggerOnRelative("mousehover", _data);
480
+ onEmit("mousehover", _data);
481
+ onEvent({
482
+ currentIndex: +e.currentTarget.dataset.index,
483
+ type: "onMouseEnter",
484
+ });
485
+ },
486
+ [onEvent, triggerOnRelative, onEmit],
487
+ );
488
+
489
+ const onMouseLeave = useCallback(
490
+ (e) => {
491
+ setMousePos({
492
+ x: 0,
493
+ y: 0,
494
+ });
495
+ onEvent({
496
+ currentIndex: +e.currentTarget.dataset.index,
497
+ type: "onMouseLeave",
498
+ });
499
+ },
500
+ [onEvent],
501
+ );
502
+
503
+ useLayoutEffect(() => {
504
+ let animation;
505
+ if (!!on) {
506
+ animation = animate({
507
+ from: 0,
508
+ to: 1,
509
+ duration: 500,
510
+ ease: linear,
511
+ onPlay: () => {},
512
+ onUpdate: (v) => {
513
+ setY(v);
514
+ },
515
+ onComplete: () => {
516
+ const _data = arcs[+currentIndex] ? arcs[+currentIndex].data : {};
517
+ triggerOnRelative("carousel", _data);
518
+ onEmit("carousel", _data);
519
+ },
520
+ });
521
+ } else {
522
+ if (currentIndex !== null && trigger === "onClick") {
523
+ const _data = arcs[+currentIndex] ? arcs[+currentIndex].data : {};
524
+ triggerOnRelative("click", _data);
525
+ onEmit("click", _data);
526
+ }
527
+ }
528
+ return () => {
529
+ prevIndex.current = currentIndex;
530
+ animation && animation.stop();
531
+ animation = null;
532
+ };
533
+ }, [
534
+ JSON.stringify(arcs),
535
+ on,
536
+ currentIndex,
537
+ trigger,
538
+ onEmit,
539
+ triggerOnRelative,
540
+ ]);
541
+ const aiData = useAiDataOfPie(_arcs, legend);
542
+ useEffect(() => {
543
+ if (aiData.length) {
544
+ if (!window.aiData) {
545
+ window.aiData = {};
546
+ }
547
+ window.aiData[id] = {
548
+ getAI: () => {
549
+ return aiData;
550
+ },
551
+ };
552
+ }
553
+ return () => {
554
+ window.aiData && window.aiData[id] && delete window.aiData[id];
555
+ };
556
+ }, [JSON.stringify(aiData), id]);
557
+
558
+ const halfChartWidth = chartWidth / 2;
559
+ const halfChartHeight = chartHeight / 2;
560
+
561
+ const rotate_ = decorate2
562
+ ? (-(arcs[+currentIndex].startAngle + arcs[+currentIndex].endAngle) *
563
+ 90) /
564
+ Math.PI +
565
+ rotate
566
+ : 0;
567
+ let maxRadius = 0;
568
+ _arcs.map((d) => {
569
+ maxRadius = Math.max(maxRadius, d.outerRadius);
570
+ });
571
+ let centerRadius = 0.5 * maxRadius + 0.5 * _arcs[0].innerRadius;
572
+ const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
573
+ const [hoverData, setHoverData] = useState(null);
574
+ const pieWarpEl = useRef(null);
575
+ const domRef = useRef();
576
+ return outerDecorate ? (
577
+ <div ref={domRef}>
578
+ <ChartContainer //用于生成甜甜圈图,判断依据是外环装饰这个配置项(outerDecorate)
579
+ width={width}
580
+ height={height}
581
+ marginLeft={marginLeft}
582
+ marginTop={marginTop}
583
+ ref={pieWarpEl}
584
+ >
585
+ <g
586
+ style={{
587
+ "--labelDuration": labelDuration + "ms",
588
+ "--ringDuration": ringDuration + "ms",
589
+ transition: "transform ease-in-out 0.3s",
590
+ transform:
591
+ "translate(" +
592
+ halfChartWidth +
593
+ "px, " +
594
+ halfChartHeight +
595
+ "px) rotate(" +
596
+ rotate_ +
597
+ "deg)",
598
+ }}
599
+ >
600
+ {
601
+ //用于生成外环装饰的刻度
602
+ outerDecorate.tick.show &&
603
+ getCircleScale(outerDecorate.tick, maxRadius)
604
+ }
605
+ <circle //外环装饰
606
+ cx="0"
607
+ cy="0"
608
+ r={maxRadius + 2}
609
+ fill="none"
610
+ stroke={outerDecorate.color}
611
+ strokeWidth={outerDecorate.width}
612
+ />
613
+ {_arcs.map(
614
+ ({ id, value, series, arc, innerRadius, outerRadius }, index) => {
615
+ const arcWidth = outerRadius - innerRadius;
616
+ const path = arc
617
+ .innerRadius(centerRadius)
618
+ .outerRadius(centerRadius)(value);
619
+ const dashLength = Math.ceil(
620
+ (Math.PI * centerRadius * 2) / _arcs.length,
621
+ );
622
+ const pie = getColorList(series.color);
623
+ return (
624
+ <Fragment key={index}>
625
+ <path
626
+ className={ringCss["inner-arc"]}
627
+ style={{
628
+ strokeDasharray: `${dashLength},${2 * dashLength}`,
629
+ strokeDashoffset: dashLength,
630
+ animationDelay: `${index * ringDuration}ms`,
631
+ }}
632
+ data-index={index}
633
+ onClick={onClick}
634
+ onMouseEnter={onMouseEnter}
635
+ onMouseLeave={onMouseLeave}
636
+ onMouseMove={(e) => {
637
+ const _data = arcs[+e.currentTarget.dataset.index];
638
+ const warpBoxPos = {
639
+ x: pieWarpEl.current.getBoundingClientRect().x,
640
+ y: pieWarpEl.current.getBoundingClientRect().y,
641
+ };
642
+ setMousePos({
643
+ x: e.clientX - warpBoxPos.x,
644
+ y: e.clientY - warpBoxPos.y,
645
+ });
646
+ setHoverData(_data);
647
+ }}
648
+ d={path.split("L")[0]}
649
+ stroke={"url(#" + id + ")"}
650
+ strokeWidth={arcWidth}
651
+ fill="none"
652
+ />
653
+ <defs>
654
+ <LinearGradient
655
+ id={id}
656
+ colors={pie}
657
+ rotate={series.color.linear.angle + 180}
658
+ // gradientUnits='objectBoundingBox'
659
+ />
660
+ </defs>
661
+ </Fragment>
662
+ );
663
+ },
664
+ )}
665
+ {label && (
666
+ <RingLabel
667
+ config={{
668
+ ...label,
669
+ maxRadius: maxRadius + 2,
670
+ ringDuration,
671
+ labelDuration,
672
+ }}
673
+ iosStyle={{
674
+ isIOS,
675
+ left: halfChartWidth + marginLeft,
676
+ top: halfChartHeight + marginTop,
677
+ }}
678
+ arcs={_arcs}
679
+ judge={judgeData}
680
+ />
681
+ )}
682
+ </g>
683
+ </ChartContainer>
684
+
685
+ <Legend
686
+ {...legend}
687
+ height={chartHeight}
688
+ componentWidth={width}
689
+ marginLeft={marginLeft}
690
+ marginRight={marginRight}
691
+ isPieChart
692
+ columnsSeries={columnsSeries}
693
+ data={data}
694
+ series={_arcs.map((arc) => ({
695
+ ...arc,
696
+ percent: arc.percent.toFixed(legendPrecision),
697
+ }))}
698
+ pieClick={onClick}
699
+ formatter={formatter}
700
+ judge={judgeData}
701
+ />
702
+ {tooltip &&
703
+ mousePos &&
704
+ mousePos.x != 0 &&
705
+ mousePos.y != 0 &&
706
+ tooltip.manual && (
707
+ <div
708
+ style={{
709
+ position: "absolute",
710
+ pointerEvents: "none",
711
+ }}
712
+ >
713
+ <PieTooltip
714
+ series={series}
715
+ domRef={domRef}
716
+ data={hoverData}
717
+ config={tooltip}
718
+ pieCenter={{
719
+ x: halfChartWidth,
720
+ y: maxRadius + marginTop,
721
+ }}
722
+ mousePos={mousePos}
723
+ />
724
+ </div>
725
+ )}
726
+ </div>
727
+ ) : (
728
+ <div ref={domRef}>
729
+ <ChartContainer
730
+ width={width}
731
+ height={height}
732
+ marginLeft={marginLeft}
733
+ marginTop={marginTop}
734
+ onMouseEnter={() => {
735
+ hoverEvent(true);
736
+ }}
737
+ onMouseLeave={() => {
738
+ hoverEvent(false);
739
+ }}
740
+ ref={pieWarpEl}
741
+ >
742
+ <g
743
+ style={{
744
+ transition: "transform ease-in-out 0.3s",
745
+ transform:
746
+ "translate(" +
747
+ halfChartWidth +
748
+ "px, " +
749
+ halfChartHeight +
750
+ "px) rotate(" +
751
+ rotate_ +
752
+ "deg)",
753
+ }}
754
+ >
755
+ {_arcs.map(
756
+ (
757
+ {
758
+ id,
759
+ value,
760
+ series,
761
+ arc,
762
+ innerRadius,
763
+ outerRadius,
764
+ index: dataIndex,
765
+ },
766
+ index,
767
+ ) => {
768
+ const current = index == currentIndex;
769
+ const prev = index == prevIndex.current;
770
+ const offset = current ? y : prev ? 1 - y : 0;
771
+
772
+ const fillOpacity = animateColor
773
+ ? 1
774
+ : current
775
+ ? opacity / 100
776
+ : 1;
777
+ const deltaWidthen = offset * widthen;
778
+ const deltaHeighten = offset * heighten;
779
+ const path = arc
780
+ .innerRadius(innerRadius + deltaWidthen)
781
+ .outerRadius(outerRadius + deltaHeighten + deltaWidthen)(
782
+ value,
783
+ );
784
+ const pie = getColorList(series.color);
785
+ const currentPie = animateColor
786
+ ? getColorList(animateColor)
787
+ : getColorList(series.color);
788
+ let textPath = "",
789
+ categoryTextStyle = {};
790
+ if (categoryText && categoryText.show) {
791
+ //如果有类目文本,则需要计算文字路径
792
+ //let offsetWidth=decorate2.radiusWidth/2 + radiusWidthAdd/2; //当前文字需生成在装饰物内,故而半径需要减小
793
+ let textArc = arc
794
+ .innerRadius(
795
+ outerRadius + (current ? gap : categoryText.gap),
796
+ )
797
+ .outerRadius(
798
+ outerRadius + (current ? gap : categoryText.gap),
799
+ )(value);
800
+ let lastA = textArc.lastIndexOf("A");
801
+ textPath = textArc.slice(
802
+ 0,
803
+ lastA > 0 ? lastA : textArc.length,
804
+ ); //文字路径
805
+ categoryTextStyle = current
806
+ ? animateCTS
807
+ : categoryText.textStyle; //这里把textstyle拿出来
808
+ }
809
+
810
+ return (
811
+ <Fragment key={index}>
812
+ <path
813
+ data-index={index}
814
+ onClick={onClick}
815
+ onMouseEnter={onMouseEnter}
816
+ onMouseLeave={onMouseLeave}
817
+ onMouseMove={(e) => {
818
+ const _data = arcs[+e.currentTarget.dataset.index];
819
+ const warpBoxPos = {
820
+ x: pieWarpEl.current.getBoundingClientRect().x,
821
+ y: pieWarpEl.current.getBoundingClientRect().y,
822
+ };
823
+ setMousePos({
824
+ x: e.clientX - warpBoxPos.x,
825
+ y: e.clientY - warpBoxPos.y,
826
+ });
827
+ setHoverData(_data);
828
+ }}
829
+ d={path}
830
+ stroke={show ? color : "none"}
831
+ strokeWidth={show ? strokeWidth : "0"}
832
+ fill={"url(#" + id + ")"}
833
+ fillOpacity={fillOpacity}
834
+ />
835
+ {
836
+ //装饰物2,产生于每个弧的外部
837
+ decorate2 && decorate2.show && (
838
+ <path
839
+ data-index={index}
840
+ onClick={onClick}
841
+ onMouseEnter={onMouseEnter}
842
+ onMouseLeave={onMouseLeave}
843
+ d={arc
844
+ .innerRadius(outerRadius)
845
+ .outerRadius(
846
+ outerRadius +
847
+ decorate2.radiusWidth +
848
+ (current ? radiusWidthAdd : 0),
849
+ )(value)}
850
+ stroke={show ? color : "none"}
851
+ strokeWidth={show ? strokeWidth : "0"}
852
+ fill={"url(#" + id + ")"}
853
+ fillOpacity={decorate2.opacity / 100}
854
+ />
855
+ )
856
+ }
857
+ {
858
+ //类目文本
859
+ value && categoryText && categoryText.show && (
860
+ <g>
861
+ <path
862
+ onClick={onClick}
863
+ onMouseEnter={onMouseEnter}
864
+ onMouseLeave={onMouseLeave}
865
+ id={id + "_text_" + index}
866
+ d={textPath}
867
+ fill="none"
868
+ stroke="none"
869
+ />
870
+ <text
871
+ textAnchor="middle"
872
+ style={{
873
+ ...categoryTextStyle,
874
+ fontWeight: categoryTextStyle.bold
875
+ ? "bold"
876
+ : "normal",
877
+ fontStyle: categoryTextStyle.italic
878
+ ? "italic"
879
+ : "normal",
880
+ pointerEvents: "none",
881
+ }}
882
+ fill={categoryTextStyle.color}
883
+ >
884
+ <textPath
885
+ startOffset="50%"
886
+ href={"#" + id + "_text_" + index}
887
+ >
888
+ {_arcs[index].displayName ||
889
+ _arcs[index].fieldName}
890
+ </textPath>
891
+ </text>
892
+ </g>
893
+ )
894
+ }
895
+ <defs>
896
+ {/* 此处是环的发生地 */}
897
+ <LinearGradient
898
+ id={id}
899
+ colors={current ? currentPie : pie}
900
+ rotate={
901
+ current
902
+ ? animateColor
903
+ ? animateColor.linear.angle + 180
904
+ : series.color.linear.angle + 180
905
+ : series.color.linear.angle + 180
906
+ }
907
+ // gradientUnits='objectBoundingBox'
908
+ />
909
+ </defs>
910
+ </Fragment>
911
+ );
912
+ },
913
+ )}
914
+ {label && (
915
+ <Label
916
+ config={label}
917
+ iosStyle={{
918
+ isIOS,
919
+ left: halfChartWidth + marginLeft,
920
+ top: halfChartHeight + marginTop,
921
+ }}
922
+ arcs={_arcs}
923
+ judge={judgeData}
924
+ />
925
+ )}
926
+ {current && (
927
+ <g
928
+ fillOpacity={y}
929
+ style={{ transform: "rotate(" + -rotate_ + "deg)" }}
930
+ >
931
+ <Current
932
+ config={current}
933
+ width={chartWidth}
934
+ height={chartHeight}
935
+ iosStyle={{
936
+ marginLeft,
937
+ marginTop,
938
+ isIOS,
939
+ }}
940
+ data={_arcs}
941
+ judge={judgeData}
942
+ currentIndex={+currentIndex}
943
+ />
944
+ </g>
945
+ )}
946
+ </g>
947
+ </ChartContainer>
948
+ {decorate && (
949
+ <ConicalGradient
950
+ width={width}
951
+ height={height}
952
+ centerX={halfChartWidth + marginLeft}
953
+ centerY={halfChartHeight + marginTop}
954
+ config={decorate}
955
+ arcs={_arcs}
956
+ radius={radius}
957
+ />
958
+ )}
959
+
960
+ <Legend
961
+ {...legend}
962
+ height={chartHeight}
963
+ componentWidth={width}
964
+ marginLeft={marginLeft}
965
+ marginRight={marginRight}
966
+ isPieChart
967
+ data={data}
968
+ columnsSeries={columnsSeries}
969
+ series={_arcs.map((arc) => ({
970
+ ...arc,
971
+ percent: arc.percent.toFixed(legendPrecision),
972
+ }))}
973
+ pieClick={onClick}
974
+ formatter={formatter}
975
+ judge={judgeData}
976
+ />
977
+ {tooltip &&
978
+ mousePos &&
979
+ mousePos.x != 0 &&
980
+ mousePos.y != 0 &&
981
+ tooltip.manual && (
982
+ <div
983
+ style={{
984
+ position: "absolute",
985
+ pointerEvents: "none",
986
+ }}
987
+ >
988
+ <PieTooltip
989
+ series={series}
990
+ domRef={domRef}
991
+ data={hoverData}
992
+ config={tooltip}
993
+ pieCenter={{
994
+ x: halfChartWidth,
995
+ y: maxRadius + marginTop,
996
+ }}
997
+ mousePos={mousePos}
998
+ />
999
+ </div>
1000
+ )}
1001
+ </div>
1002
+ );
1003
+ },
1004
+ );
1005
+
1006
+ const Current = ({
1007
+ config: {
1008
+ show,
1009
+ gap,
1010
+ name: {
1011
+ show: showName,
1012
+ sameColor: nameColor,
1013
+ font: nameFont,
1014
+ translate = { x: 0, y: 0 },
1015
+ maxWidth,
1016
+ textOverflow,
1017
+ speed,
1018
+ },
1019
+ percent: {
1020
+ show: showPercent,
1021
+ sameColor: percentColor,
1022
+ font: percentFont,
1023
+ precision,
1024
+ translate: { x: translatePercentX, y: translatePercentY },
1025
+ },
1026
+ value: {
1027
+ show: showValue,
1028
+ sameColor: valueColor,
1029
+ font: valueFont,
1030
+ translate: { x: translateValueX, y: translateValueY },
1031
+ suffix: {
1032
+ show: showSuffix,
1033
+ fontSize,
1034
+ text,
1035
+ translate: { x: translateSuffixX, y: translateSuffixY },
1036
+ },
1037
+ },
1038
+ },
1039
+ iosStyle: { isIOS, marginLeft, marginTop },
1040
+ width,
1041
+ height,
1042
+ data,
1043
+ judge,
1044
+ currentIndex,
1045
+ }) => {
1046
+ const _data = useMemo(() => {
1047
+ const legendDataWithPercent = getDataWithPercent(data, precision);
1048
+ return sortPie(legendDataWithPercent, "");
1049
+ }, [data, precision]);
1050
+
1051
+ //数据容错,当data都为零那么需要进行以下容错
1052
+ if (judge == 0) {
1053
+ _data.forEach((d) => {
1054
+ ((d.percent = 0), (d.value = 0));
1055
+ });
1056
+ }
1057
+
1058
+ const currentData = _data[currentIndex];
1059
+
1060
+ if (!currentData) return null;
1061
+
1062
+ const { displayName, fieldName, value, percent } = currentData;
1063
+ let nameTemp = displayName ? displayName : fieldName; //类目名
1064
+
1065
+ let foreignStyle = {
1066
+ //foreignObject标签样式,
1067
+ width,
1068
+ height,
1069
+ position: "relative",
1070
+ overflow: "visible",
1071
+ pointerEvents: "none",
1072
+ },
1073
+ boxStyle = {
1074
+ //弹性盒子样式,用于当前值的上下居中对齐等
1075
+ width,
1076
+ height,
1077
+ position: "absolute",
1078
+ display: "flex",
1079
+ flexDirection: "column",
1080
+ justifyContent: "center",
1081
+ alignItems: "center",
1082
+ transform: isIOS
1083
+ ? `translate(${marginLeft}px,${marginTop}px)`
1084
+ : `translate(-${width / 2}px,-${height / 2}px)`,
1085
+ };
1086
+ let seriesColor = currentData.series.color;
1087
+ seriesColor =
1088
+ seriesColor.type == "pure"
1089
+ ? seriesColor.pure
1090
+ : seriesColor.linear.stops[0].color;
1091
+ return (
1092
+ show && (
1093
+ <foreignObject style={foreignStyle}>
1094
+ <div style={boxStyle}>
1095
+ {showName && (
1096
+ <TextOverflow
1097
+ type={textOverflow}
1098
+ value={nameTemp}
1099
+ speed={speed}
1100
+ style={{
1101
+ width: "100%",
1102
+ maxWidth,
1103
+ textAlign: "center",
1104
+ display: textOverflow == "marquee" ? "flex" : "bolck",
1105
+ justifyContent: "center",
1106
+ ...getFontStyle(nameFont),
1107
+ margin: gap / 2 + "px 0",
1108
+ color: nameColor ? seriesColor : nameFont.color,
1109
+ transform: `translate(${translate.x}px,${translate.y}px)`,
1110
+ }}
1111
+ ></TextOverflow>
1112
+ )}
1113
+ {
1114
+ //真实值
1115
+ showValue && (
1116
+ <span
1117
+ style={{
1118
+ ...getFontStyle(valueFont),
1119
+ transform:
1120
+ "translate(" +
1121
+ translateValueX +
1122
+ "px," +
1123
+ translateValueY +
1124
+ "px)",
1125
+ margin: gap / 2 + "px 0",
1126
+ color: valueColor ? seriesColor : valueFont.color,
1127
+ }}
1128
+ >
1129
+ {value}
1130
+ {showSuffix && text && (
1131
+ <span
1132
+ style={{
1133
+ display: "inline-block",
1134
+ transform:
1135
+ "translate(" +
1136
+ translateSuffixX +
1137
+ "px," +
1138
+ translateSuffixY +
1139
+ "px)",
1140
+ fontSize: fontSize,
1141
+ }}
1142
+ >
1143
+ {text}
1144
+ </span>
1145
+ )}
1146
+ </span>
1147
+ )
1148
+ }
1149
+ {
1150
+ //百分比值
1151
+ showPercent && (
1152
+ <span
1153
+ style={{
1154
+ transform:
1155
+ "translate(" +
1156
+ translatePercentX +
1157
+ "px," +
1158
+ translatePercentY +
1159
+ "px)",
1160
+ ...getFontStyle(percentFont),
1161
+ margin: gap / 2 + "px 0",
1162
+ color: percentColor ? seriesColor : percentFont.color,
1163
+ }}
1164
+ >
1165
+ {percent + "%"}
1166
+ </span>
1167
+ )
1168
+ }
1169
+ </div>
1170
+ </foreignObject>
1171
+ )
1172
+ );
1173
+ };
1174
+
1175
+ const Label = ({
1176
+ config: {
1177
+ maxRadius = 0,
1178
+ lineLength,
1179
+ lineColor,
1180
+ distance,
1181
+ mode,
1182
+ align,
1183
+ show,
1184
+ translate: { x: translateX, y: translateY },
1185
+ name: {
1186
+ show: showName,
1187
+ font: nameFont,
1188
+ maxWidth,
1189
+ textOverflow,
1190
+ speed,
1191
+ translate: NameTranslate,
1192
+ },
1193
+ value: {
1194
+ show: showValue,
1195
+ font: valueFont,
1196
+ sameColor: valueSameColor,
1197
+ suffix: {
1198
+ show: showSuffix,
1199
+ text,
1200
+ fontSize: suffixFontSize,
1201
+ translate: { x: suffixTranslateX, y: suffixTranslateY },
1202
+ },
1203
+ translate: ValueTranslate,
1204
+ },
1205
+ percent: {
1206
+ show: showPercent,
1207
+ sameColor: percentSameColor,
1208
+ font: percentFont,
1209
+ precision,
1210
+ translate: PercentTranslate,
1211
+ },
1212
+ },
1213
+ iosStyle: { isIOS, left, top },
1214
+ arcs,
1215
+ judge,
1216
+ animation,
1217
+ }) => {
1218
+ const _arcs = useMemo(
1219
+ () => getDataWithPercent(arcs, precision),
1220
+ [arcs, precision],
1221
+ );
1222
+ //数据做出容错
1223
+ if (judge == 0) {
1224
+ _arcs.forEach((d) => {
1225
+ d.percent = 0;
1226
+ });
1227
+ }
1228
+ return (
1229
+ <g>
1230
+ {_arcs.map(
1231
+ (
1232
+ {
1233
+ series: {
1234
+ color: {
1235
+ type,
1236
+ pure,
1237
+ linear: { stops },
1238
+ },
1239
+ },
1240
+ data,
1241
+ displayName,
1242
+ value,
1243
+ percent,
1244
+ arc,
1245
+ outerRadius,
1246
+ index: actualIndex,
1247
+ },
1248
+ index,
1249
+ ) => {
1250
+ const [x, y] = arc.centroid();
1251
+ const midAngle = Math.atan2(y, x);
1252
+
1253
+ const [x1, y1] = getCoord(
1254
+ midAngle,
1255
+ maxRadius ? maxRadius : outerRadius,
1256
+ );
1257
+
1258
+ const radius = (maxRadius ? maxRadius : outerRadius) + distance;
1259
+ const [x2, y2] = getCoord(midAngle, radius);
1260
+
1261
+ const direction = x2 < 0 ? -1 : 1;
1262
+ const x3 = x2 + lineLength * direction;
1263
+
1264
+ const _x = x3 + (translateX + 6) * direction;
1265
+
1266
+ const _showName = showName && displayName;
1267
+ const _showValue = showValue && (value || showSuffix);
1268
+ const nameStyle = getFontStyle(nameFont);
1269
+ return (
1270
+ show &&
1271
+ (_showName || showPercent || showValue) && (
1272
+ <g key={index}>
1273
+ <path
1274
+ className={animation ? ringCss["label-line"] : ""}
1275
+ style={{
1276
+ animationDelay: `${
1277
+ animation
1278
+ ? (actualIndex + 1) * ringDuration - labelDuration
1279
+ : 0
1280
+ }ms`,
1281
+ }}
1282
+ d={
1283
+ "M" +
1284
+ x1 +
1285
+ ", " +
1286
+ y1 +
1287
+ "L" +
1288
+ x2 +
1289
+ ", " +
1290
+ y2 +
1291
+ "L" +
1292
+ x3 +
1293
+ ", " +
1294
+ y2
1295
+ }
1296
+ stroke={
1297
+ lineColor
1298
+ ? lineColor
1299
+ : type == "pure"
1300
+ ? pure
1301
+ : stops[0].color
1302
+ }
1303
+ fill="none"
1304
+ />
1305
+ <foreignObject
1306
+ width="1"
1307
+ height="1"
1308
+ x={_x}
1309
+ y={y2 + translateY}
1310
+ style={{ overflow: "visible", position: "relative" }}
1311
+ >
1312
+ <div
1313
+ className={animation ? ringCss["label-text"] : ""}
1314
+ style={{
1315
+ position: isIOS ? "absolute" : "relative",
1316
+ transform: isIOS
1317
+ ? `translate(calc(${x3 < 0 ? "-100%" : "0px"} + ${
1318
+ left + _x
1319
+ }px),calc(-50% + ${top + y2 + translateY}px))`
1320
+ : "translate(0,-50%)",
1321
+ whiteSpace: "nowrap",
1322
+ float: x3 >= 0 ? "left" : "right",
1323
+ width: "max-content",
1324
+ display: "flex",
1325
+ flexDirection: mode == "horizontal" ? "row" : "column",
1326
+ alignItems:
1327
+ align == "left"
1328
+ ? "flex-start"
1329
+ : align == "center"
1330
+ ? "center"
1331
+ : "flex-end",
1332
+ justifyContent: "center",
1333
+ }}
1334
+ >
1335
+ {_showName && (
1336
+ <TextOverflow
1337
+ type={textOverflow}
1338
+ value={
1339
+ displayName + (showValue || showPercent ? ":" : "")
1340
+ }
1341
+ speed={speed}
1342
+ style={{
1343
+ maxWidth,
1344
+ ...nameStyle,
1345
+ float: mode == "horizontal" ? "left" : "none",
1346
+ transform: `translate(${NameTranslate.x}px, ${NameTranslate.y}px)`,
1347
+ }}
1348
+ ></TextOverflow>
1349
+ )}
1350
+ {showValue && (
1351
+ <span
1352
+ style={{
1353
+ ...getFontStyle(valueFont),
1354
+ color: valueSameColor ? pure : valueFont.color,
1355
+ transform: `translate(${ValueTranslate.x}px, ${ValueTranslate.y}px)`,
1356
+ }}
1357
+ >
1358
+ {data.y}
1359
+ {showSuffix && (
1360
+ <span
1361
+ style={{
1362
+ position: "relative",
1363
+ fontSize: suffixFontSize,
1364
+ marginLeft: suffixTranslateX,
1365
+ top: suffixTranslateY,
1366
+ }}
1367
+ >
1368
+ {text}
1369
+ </span>
1370
+ )}
1371
+ </span>
1372
+ )}
1373
+ {showPercent && (
1374
+ <span
1375
+ style={{
1376
+ ...getFontStyle(percentFont),
1377
+ color: percentSameColor ? pure : percentFont.color,
1378
+ transform: `translate(${PercentTranslate.x}px, ${PercentTranslate.y}px)`,
1379
+ }}
1380
+ >
1381
+ {(_showValue ? "(" : "") +
1382
+ percent +
1383
+ "%" +
1384
+ (_showValue ? ")" : "")}
1385
+ </span>
1386
+ )}
1387
+ </div>
1388
+ </foreignObject>
1389
+ </g>
1390
+ )
1391
+ );
1392
+ },
1393
+ )}
1394
+ </g>
1395
+ );
1396
+ };
1397
+
1398
+ function getAlign(align, reverse) {
1399
+ if (align == "center") return "center";
1400
+ if (align == "left") return reverse ? "flex-end" : "flex-start";
1401
+ return reverse ? "flex-start" : "flex-end";
1402
+ }
1403
+ function getTranslate(translate, reverse) {
1404
+ const { x, y } = translate;
1405
+ return `translate(${reverse ? -x : x}px, ${y}px)`;
1406
+ }
1407
+ const RingLabel = ({
1408
+ config: {
1409
+ ringDuration,
1410
+ labelDuration,
1411
+ maxRadius = 0,
1412
+ lineLength,
1413
+ lineColor,
1414
+ distance,
1415
+ mode,
1416
+ align = "center",
1417
+ show,
1418
+ translate: { x: translateX, y: translateY },
1419
+ name: {
1420
+ show: showName,
1421
+ font: nameFont,
1422
+ maxWidth,
1423
+ textOverflow,
1424
+ speed,
1425
+ translate: nameTranslate,
1426
+ },
1427
+ value: {
1428
+ show: showValue,
1429
+ font: valueFont,
1430
+ sameColor: valueSameColor,
1431
+ suffix: {
1432
+ show: showSuffix,
1433
+ text,
1434
+ fontSize: suffixFontSize,
1435
+ translate: { x: suffixTranslateX, y: suffixTranslateY },
1436
+ },
1437
+ translate: valueTranslate,
1438
+ },
1439
+ percent: {
1440
+ show: showPercent,
1441
+ sameColor: percentSameColor,
1442
+ font: percentFont,
1443
+ precision,
1444
+ translate: percentTranslate,
1445
+ },
1446
+ },
1447
+ iosStyle: { isIOS, left, top },
1448
+ judge,
1449
+ arcs,
1450
+ }) => {
1451
+ const _arcs = useMemo(
1452
+ () => getDataWithPercent(arcs, precision),
1453
+ [arcs, precision],
1454
+ );
1455
+
1456
+ //数据做出容错
1457
+ if (judge == 0) {
1458
+ _arcs.forEach((d) => {
1459
+ d.percent = 0;
1460
+ });
1461
+ }
1462
+
1463
+ return (
1464
+ <g>
1465
+ {_arcs.map(
1466
+ (
1467
+ {
1468
+ series: {
1469
+ color: {
1470
+ type,
1471
+ pure,
1472
+ linear: { stops },
1473
+ },
1474
+ },
1475
+ data: realData,
1476
+ displayName,
1477
+ value,
1478
+ percent,
1479
+ arc,
1480
+ outerRadius,
1481
+ index: actualIndex,
1482
+ },
1483
+ index,
1484
+ ) => {
1485
+ const [x, y] = arc.centroid();
1486
+ const midAngle = Math.atan2(y, x);
1487
+
1488
+ const [x1, y1] = getCoord(
1489
+ midAngle,
1490
+ maxRadius ? maxRadius : outerRadius,
1491
+ );
1492
+
1493
+ const radius = (maxRadius ? maxRadius : outerRadius) + distance;
1494
+ const [x2, y2] = getCoord(midAngle, radius);
1495
+
1496
+ const directionX = x2 < 0 ? -1 : 1;
1497
+ const directionY = y2 < 0 ? -1 : 1;
1498
+ const x3 = x2 + lineLength * directionX;
1499
+ const _x = x3 + (translateX + 6) * directionX;
1500
+
1501
+ const _showName = showName && displayName;
1502
+ const _showValue = showValue && (value || showSuffix);
1503
+
1504
+ return (
1505
+ show &&
1506
+ (_showName || showPercent || _showValue) && (
1507
+ <g key={index}>
1508
+ <path
1509
+ className={ringCss["label-line"]}
1510
+ style={{
1511
+ animationDelay: `${
1512
+ (actualIndex + 1) * ringDuration - labelDuration
1513
+ }ms`,
1514
+ }}
1515
+ d={
1516
+ "M" +
1517
+ x1 +
1518
+ ", " +
1519
+ y1 +
1520
+ "L" +
1521
+ x2 +
1522
+ ", " +
1523
+ y2 +
1524
+ "L" +
1525
+ x3 +
1526
+ ", " +
1527
+ y2
1528
+ }
1529
+ stroke={
1530
+ lineColor
1531
+ ? lineColor
1532
+ : type == "pure"
1533
+ ? pure
1534
+ : stops[0].color
1535
+ }
1536
+ fill="none"
1537
+ />
1538
+ <foreignObject
1539
+ width="1"
1540
+ height="1"
1541
+ x={_x}
1542
+ y={y2 + translateY}
1543
+ style={{ overflow: "visible", position: "relative" }}
1544
+ >
1545
+ <div
1546
+ className={ringCss["label-text"]}
1547
+ style={{
1548
+ position: isIOS ? "absolute" : "relative",
1549
+ transform: isIOS
1550
+ ? `translate(calc(${x3 < 0 ? "-100%" : "0px"} + ${
1551
+ left + _x
1552
+ }px),calc(-50% + ${top + y2 + translateY}px))`
1553
+ : "translate(0,-50%)",
1554
+ whiteSpace: "nowrap",
1555
+ float: x3 >= 0 ? "left" : "right",
1556
+ width: "max-content",
1557
+ animationDelay: `${
1558
+ (actualIndex + 1) * ringDuration - labelDuration
1559
+ }ms`,
1560
+ display: "flex",
1561
+ flexDirection: mode == "horizontal" ? "row" : "column",
1562
+ alignItems: getAlign(align, x3 >= 0),
1563
+ justifyContent: "center",
1564
+ }}
1565
+ >
1566
+ {_showName && (
1567
+ <TextOverflow
1568
+ type={textOverflow}
1569
+ value={
1570
+ displayName + (showValue || showPercent ? ":" : "")
1571
+ }
1572
+ speed={speed}
1573
+ style={{
1574
+ maxWidth,
1575
+ ...getFontStyle(nameFont),
1576
+ float: mode == "horizontal" ? "left" : "none",
1577
+ transform: getTranslate(nameTranslate, x3 >= 0),
1578
+ }}
1579
+ ></TextOverflow>
1580
+ )}
1581
+ {showValue && (
1582
+ <span
1583
+ style={{
1584
+ ...getFontStyle(valueFont),
1585
+ transform: getTranslate(valueTranslate, x3 >= 0),
1586
+ color: valueSameColor ? pure : valueFont.color,
1587
+ }}
1588
+ >
1589
+ {realData.y}
1590
+ {showSuffix && (
1591
+ <span
1592
+ style={{
1593
+ position: "relative",
1594
+ fontSize: suffixFontSize,
1595
+ marginLeft: suffixTranslateX,
1596
+ top: suffixTranslateY,
1597
+ }}
1598
+ >
1599
+ {text}
1600
+ </span>
1601
+ )}
1602
+ </span>
1603
+ )}
1604
+ {showPercent && (
1605
+ <span
1606
+ style={{
1607
+ ...getFontStyle(percentFont),
1608
+ transform: getTranslate(percentTranslate, x3 >= 0),
1609
+ color: percentSameColor ? pure : percentFont.color,
1610
+ }}
1611
+ >
1612
+ {(_showValue ? "(" : "") +
1613
+ percent +
1614
+ "%" +
1615
+ (_showValue ? ")" : "")}
1616
+ </span>
1617
+ )}
1618
+ </div>
1619
+ </foreignObject>
1620
+ </g>
1621
+ )
1622
+ );
1623
+ },
1624
+ )}
1625
+ </g>
1626
+ );
1627
+ };
1628
+
1629
+ export default Mapping(Carousel(Component));