@easyv/charts 1.10.20 → 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,1569 +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
- const [y, setY] = useState(1);
305
- const radius = (Math.min(chartWidth, chartHeight) / 2) * outerRadius;
306
-
307
- const arcsFunc = useMemo(() => {
308
- const { startAngle = 0, endAngle = 360 } = getAngle(angle);
309
- const arcsFunc = pie()
310
- .startAngle((startAngle * PI) / 180)
311
- .endAngle((endAngle * PI) / 180)
312
- .value((d) => d.y);
313
- return arcsFunc;
314
- }, [angle]);
315
- //此处创建arcsFuncTwo的原因是为了兼容数据全为零
316
- const arcsFuncTwo = useMemo(() => {
317
- const { startAngle = 0, endAngle = 360 } = getAngle(angle);
318
- const arcsFunc = pie()
319
- .startAngle((startAngle * PI) / 180)
320
- .endAngle((endAngle * PI) / 180)
321
- .value((d) => (d.y == 0 ? 1 : d.y));
322
- return arcsFunc;
323
- }, [angle]);
324
- let judgeData = 0; //此处声明全局变量是为了父子组件传递值来判断数据是否都为零
325
- const arcs = useMemo(() => {
326
- const _chart = Object.assign(defaultChart, chart);
327
- const {
328
- innerRadius,
329
- outerRadius,
330
- rose,
331
- cornerRadius,
332
- padAngle,
333
- roseType,
334
- } = _chart;
335
- const _padAngle = (padAngle * Math.PI) / 180;
336
-
337
- switch (order) {
338
- case "":
339
- arcsFunc.sort(null);
340
- break;
341
- case "desc":
342
- arcsFunc.sort((a, b) => b.y - a.y);
343
- break;
344
- case "asc":
345
- arcsFunc.sort((a, b) => a.y - b.y);
346
- break;
347
- }
348
-
349
- //此处判断data中的y是否都为零,方便饼图都为零时展示
350
-
351
- let arcs = 0;
352
- data.forEach(function (item) {
353
- judgeData += item.y;
354
- });
355
- if (judgeData == 0) {
356
- arcs = arcsFuncTwo(data);
357
- } else {
358
- arcs = arcsFunc(data);
359
- }
360
-
361
- //const arcs = arcsFunc(data); 此处是原本的传输饼图data流程
362
- const legendDataWithPercent = getDataWithPercent(arcs, legendPrecision);
363
- const _legendDataWithPercent = sortPie(legendDataWithPercent, order);
364
-
365
- if (rose) {
366
- const domain = extent(_legendDataWithPercent, (d) => d.value);
367
- const roseRadius = getRoseRadius(_chart);
368
- const scaler = scaleLinear().domain(domain).range([roseRadius, 1]);
369
-
370
- const angle = (PI * 2) / _legendDataWithPercent.length;
371
- return _legendDataWithPercent.map(
372
- ({ startAngle, endAngle, ...arc }, index) => ({
373
- ...arc,
374
- id: id + "_linear_" + index,
375
- startAngle: roseType == "area" ? angle * index : startAngle,
376
- endAngle: roseType == "area" ? angle * (index + 1) : endAngle,
377
- cornerRadius,
378
- padAngle: _padAngle,
379
- innerRadius,
380
- outerRadius: scaler(arc.value),
381
- }),
382
- );
383
- }
384
- return _legendDataWithPercent.map((arc, index) => ({
385
- ...arc,
386
- id: id + "_linear_" + index,
387
- cornerRadius,
388
- padAngle: _padAngle,
389
- innerRadius,
390
- outerRadius,
391
- }));
392
- }, [data, arcsFunc, arcsFuncTwo, chart, legendPrecision, order]);
393
-
394
- const _arcs = useMemo(() => {
395
- const seriesLength = series.size;
396
- if (!seriesLength) return [];
397
- const _series = [...series.values()];
398
- if (_series.length < arcs.length)
399
- console.warn("请检查数据中是否存在相同的s");
400
- return arcs.map((arc, index) => getArc(radius, arc, _series, index));
401
- }, [series, arcs, radius]);
402
-
403
- const onClick = useCallback(
404
- (e) => {
405
- const _data = arcs[+e.currentTarget.dataset.index].data;
406
- triggerOnRelative("onClick", _data);
407
- onEmit("onClick", _data);
408
- onEvent({
409
- currentIndex: +e.currentTarget.dataset.index,
410
- type: "onClick",
411
- });
412
- },
413
- [onEvent],
414
- );
415
-
416
- const onMouseEnter = useCallback(
417
- (e) => {
418
- const _data = arcs[+e.currentTarget.dataset.index].data;
419
- triggerOnRelative("mousehover", _data);
420
- onEmit("mousehover", _data);
421
- onEvent({
422
- currentIndex: +e.currentTarget.dataset.index,
423
- type: "onMouseEnter",
424
- });
425
- },
426
- [onEvent, triggerOnRelative, onEmit],
427
- );
428
-
429
- const onMouseLeave = useCallback(
430
- (e) => {
431
- setMousePos({
432
- x: 0,
433
- y: 0,
434
- });
435
- onEvent({
436
- currentIndex: +e.currentTarget.dataset.index,
437
- type: "onMouseLeave",
438
- });
439
- },
440
- [onEvent],
441
- );
442
-
443
- useLayoutEffect(() => {
444
- let animation;
445
- if (!!on) {
446
- animation = animate({
447
- from: 0,
448
- to: 1,
449
- duration: 500,
450
- ease: linear,
451
- onPlay: () => {},
452
- onUpdate: (v) => {
453
- setY(v);
454
- },
455
- onComplete: () => {
456
- const _data = arcs[+currentIndex] ? arcs[+currentIndex].data : {};
457
- triggerOnRelative("carousel", _data);
458
- onEmit("carousel", _data);
459
- },
460
- });
461
- } else {
462
- if (currentIndex !== null && trigger === "onClick") {
463
- const _data = arcs[+currentIndex] ? arcs[+currentIndex].data : {};
464
- triggerOnRelative("click", _data);
465
- onEmit("click", _data);
466
- }
467
- }
468
- return () => {
469
- prevIndex.current = currentIndex;
470
- animation && animation.stop();
471
- animation = null;
472
- };
473
- }, [
474
- JSON.stringify(arcs),
475
- on,
476
- currentIndex,
477
- trigger,
478
- onEmit,
479
- triggerOnRelative,
480
- ]);
481
- const aiData = useAiDataOfPie(_arcs, legend);
482
- useEffect(() => {
483
- if (aiData.length) {
484
- if (!window.aiData) {
485
- window.aiData = {};
486
- }
487
- window.aiData[id] = {
488
- getAI: () => {
489
- return aiData;
490
- },
491
- };
492
- }
493
- return () => {
494
- window.aiData && window.aiData[id] && delete window.aiData[id];
495
- };
496
- }, [JSON.stringify(aiData), id]);
497
-
498
- const halfChartWidth = chartWidth / 2;
499
- const halfChartHeight = chartHeight / 2;
500
-
501
- const rotate_ = decorate2
502
- ? (-(arcs[+currentIndex].startAngle + arcs[+currentIndex].endAngle) *
503
- 90) /
504
- Math.PI +
505
- rotate
506
- : 0;
507
- let maxRadius = 0;
508
- _arcs.map((d) => {
509
- maxRadius = Math.max(maxRadius, d.outerRadius);
510
- });
511
- let centerRadius = 0.5 * maxRadius + 0.5 * _arcs[0].innerRadius;
512
- const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
513
- const [hoverData, setHoverData] = useState(null);
514
- const pieWarpEl = useRef(null);
515
- const domRef = useRef();
516
- return outerDecorate ? (
517
- <div ref={domRef}>
518
- <ChartContainer //用于生成甜甜圈图,判断依据是外环装饰这个配置项(outerDecorate)
519
- width={width}
520
- height={height}
521
- marginLeft={marginLeft}
522
- marginTop={marginTop}
523
- ref={pieWarpEl}
524
- >
525
- <g
526
- style={{
527
- "--labelDuration": labelDuration + "ms",
528
- "--ringDuration": ringDuration + "ms",
529
- transition: "transform ease-in-out 0.3s",
530
- transform:
531
- "translate(" +
532
- halfChartWidth +
533
- "px, " +
534
- halfChartHeight +
535
- "px) rotate(" +
536
- rotate_ +
537
- "deg)",
538
- }}
539
- >
540
- {
541
- //用于生成外环装饰的刻度
542
- outerDecorate.tick.show &&
543
- getCircleScale(outerDecorate.tick, maxRadius)
544
- }
545
- <circle //外环装饰
546
- cx="0"
547
- cy="0"
548
- r={maxRadius + 2}
549
- fill="none"
550
- stroke={outerDecorate.color}
551
- strokeWidth={outerDecorate.width}
552
- />
553
- {_arcs.map(
554
- ({ id, value, series, arc, innerRadius, outerRadius }, index) => {
555
- const arcWidth = outerRadius - innerRadius;
556
- const path = arc
557
- .innerRadius(centerRadius)
558
- .outerRadius(centerRadius)(value);
559
- const dashLength = Math.ceil(
560
- (Math.PI * centerRadius * 2) / _arcs.length,
561
- );
562
- const pie = getColorList(series.color);
563
- return (
564
- <Fragment key={index}>
565
- <path
566
- className={ringCss["inner-arc"]}
567
- style={{
568
- strokeDasharray: `${dashLength},${2 * dashLength}`,
569
- strokeDashoffset: dashLength,
570
- animationDelay: `${index * ringDuration}ms`,
571
- }}
572
- data-index={index}
573
- onClick={onClick}
574
- onMouseEnter={onMouseEnter}
575
- onMouseLeave={onMouseLeave}
576
- onMouseMove={(e) => {
577
- const _data = arcs[+e.currentTarget.dataset.index];
578
- const warpBoxPos = {
579
- x: pieWarpEl.current.getBoundingClientRect().x,
580
- y: pieWarpEl.current.getBoundingClientRect().y,
581
- };
582
- setMousePos({
583
- x: e.clientX - warpBoxPos.x,
584
- y: e.clientY - warpBoxPos.y,
585
- });
586
- setHoverData(_data);
587
- }}
588
- d={path.split("L")[0]}
589
- stroke={"url(#" + id + ")"}
590
- strokeWidth={arcWidth}
591
- fill="none"
592
- />
593
- <defs>
594
- <LinearGradient
595
- id={id}
596
- colors={pie}
597
- rotate={series.color.linear.angle + 180}
598
- // gradientUnits='objectBoundingBox'
599
- />
600
- </defs>
601
- </Fragment>
602
- );
603
- },
604
- )}
605
- {label && (
606
- <RingLabel
607
- config={{
608
- ...label,
609
- maxRadius: maxRadius + 2,
610
- ringDuration,
611
- labelDuration,
612
- }}
613
- iosStyle={{
614
- isIOS,
615
- left: halfChartWidth + marginLeft,
616
- top: halfChartHeight + marginTop,
617
- }}
618
- arcs={_arcs}
619
- judge={judgeData}
620
- />
621
- )}
622
- </g>
623
- </ChartContainer>
624
-
625
- <Legend
626
- {...legend}
627
- height={chartHeight}
628
- componentWidth={width}
629
- marginLeft={marginLeft}
630
- marginRight={marginRight}
631
- isPieChart
632
- columnsSeries={columnsSeries}
633
- data={data}
634
- series={_arcs.map((arc) => ({
635
- ...arc,
636
- percent: arc.percent.toFixed(legendPrecision),
637
- }))}
638
- pieClick={onClick}
639
- formatter={formatter}
640
- judge={judgeData}
641
- />
642
- {tooltip &&
643
- mousePos &&
644
- mousePos.x != 0 &&
645
- mousePos.y != 0 &&
646
- tooltip.manual && (
647
- <div
648
- style={{
649
- position: "absolute",
650
- pointerEvents: "none",
651
- }}
652
- >
653
- <PieTooltip
654
- series={series}
655
- domRef={domRef}
656
- data={hoverData}
657
- config={tooltip}
658
- pieCenter={{
659
- x: halfChartWidth,
660
- y: maxRadius + marginTop,
661
- }}
662
- mousePos={mousePos}
663
- />
664
- </div>
665
- )}
666
- </div>
667
- ) : (
668
- <div ref={domRef}>
669
- <ChartContainer
670
- width={width}
671
- height={height}
672
- marginLeft={marginLeft}
673
- marginTop={marginTop}
674
- onMouseEnter={() => {
675
- hoverEvent(true);
676
- }}
677
- onMouseLeave={() => {
678
- hoverEvent(false);
679
- }}
680
- ref={pieWarpEl}
681
- >
682
- <g
683
- style={{
684
- transition: "transform ease-in-out 0.3s",
685
- transform:
686
- "translate(" +
687
- halfChartWidth +
688
- "px, " +
689
- halfChartHeight +
690
- "px) rotate(" +
691
- rotate_ +
692
- "deg)",
693
- }}
694
- >
695
- {_arcs.map(
696
- (
697
- {
698
- id,
699
- value,
700
- series,
701
- arc,
702
- innerRadius,
703
- outerRadius,
704
- index: dataIndex,
705
- },
706
- index,
707
- ) => {
708
- const current = index == currentIndex;
709
- const prev = index == prevIndex.current;
710
- const offset = current ? y : prev ? 1 - y : 0;
711
-
712
- const fillOpacity = animateColor
713
- ? 1
714
- : current
715
- ? opacity / 100
716
- : 1;
717
- const deltaWidthen = offset * widthen;
718
- const deltaHeighten = offset * heighten;
719
- const path = arc
720
- .innerRadius(innerRadius + deltaWidthen)
721
- .outerRadius(outerRadius + deltaHeighten + deltaWidthen)(
722
- value,
723
- );
724
- const pie = getColorList(series.color);
725
- const currentPie = animateColor
726
- ? getColorList(animateColor)
727
- : getColorList(series.color);
728
- let textPath = "",
729
- categoryTextStyle = {};
730
- if (categoryText && categoryText.show) {
731
- //如果有类目文本,则需要计算文字路径
732
- //let offsetWidth=decorate2.radiusWidth/2 + radiusWidthAdd/2; //当前文字需生成在装饰物内,故而半径需要减小
733
- let textArc = arc
734
- .innerRadius(
735
- outerRadius + (current ? gap : categoryText.gap),
736
- )
737
- .outerRadius(
738
- outerRadius + (current ? gap : categoryText.gap),
739
- )(value);
740
- let lastA = textArc.lastIndexOf("A");
741
- textPath = textArc.slice(
742
- 0,
743
- lastA > 0 ? lastA : textArc.length,
744
- ); //文字路径
745
- categoryTextStyle = current
746
- ? animateCTS
747
- : categoryText.textStyle; //这里把textstyle拿出来
748
- }
749
-
750
- return (
751
- <Fragment key={index}>
752
- <path
753
- data-index={index}
754
- onClick={onClick}
755
- onMouseEnter={onMouseEnter}
756
- onMouseLeave={onMouseLeave}
757
- onMouseMove={(e) => {
758
- const _data = arcs[+e.currentTarget.dataset.index];
759
- const warpBoxPos = {
760
- x: pieWarpEl.current.getBoundingClientRect().x,
761
- y: pieWarpEl.current.getBoundingClientRect().y,
762
- };
763
- setMousePos({
764
- x: e.clientX - warpBoxPos.x,
765
- y: e.clientY - warpBoxPos.y,
766
- });
767
- setHoverData(_data);
768
- }}
769
- d={path}
770
- stroke={show ? color : "none"}
771
- strokeWidth={show ? strokeWidth : "0"}
772
- fill={"url(#" + id + ")"}
773
- fillOpacity={fillOpacity}
774
- />
775
- {
776
- //装饰物2,产生于每个弧的外部
777
- decorate2 && decorate2.show && (
778
- <path
779
- data-index={index}
780
- onClick={onClick}
781
- onMouseEnter={onMouseEnter}
782
- onMouseLeave={onMouseLeave}
783
- d={arc
784
- .innerRadius(outerRadius)
785
- .outerRadius(
786
- outerRadius +
787
- decorate2.radiusWidth +
788
- (current ? radiusWidthAdd : 0),
789
- )(value)}
790
- stroke={show ? color : "none"}
791
- strokeWidth={show ? strokeWidth : "0"}
792
- fill={"url(#" + id + ")"}
793
- fillOpacity={decorate2.opacity / 100}
794
- />
795
- )
796
- }
797
- {
798
- //类目文本
799
- value && categoryText && categoryText.show && (
800
- <g>
801
- <path
802
- onClick={onClick}
803
- onMouseEnter={onMouseEnter}
804
- onMouseLeave={onMouseLeave}
805
- id={id + "_text_" + index}
806
- d={textPath}
807
- fill="none"
808
- stroke="none"
809
- />
810
- <text
811
- textAnchor="middle"
812
- style={{
813
- ...categoryTextStyle,
814
- fontWeight: categoryTextStyle.bold
815
- ? "bold"
816
- : "normal",
817
- fontStyle: categoryTextStyle.italic
818
- ? "italic"
819
- : "normal",
820
- pointerEvents: "none",
821
- }}
822
- fill={categoryTextStyle.color}
823
- >
824
- <textPath
825
- startOffset="50%"
826
- href={"#" + id + "_text_" + index}
827
- >
828
- {_arcs[index].displayName ||
829
- _arcs[index].fieldName}
830
- </textPath>
831
- </text>
832
- </g>
833
- )
834
- }
835
- <defs>
836
- {/* 此处是环的发生地 */}
837
- <LinearGradient
838
- id={id}
839
- colors={current ? currentPie : pie}
840
- rotate={
841
- current
842
- ? animateColor
843
- ? animateColor.linear.angle + 180
844
- : series.color.linear.angle + 180
845
- : series.color.linear.angle + 180
846
- }
847
- // gradientUnits='objectBoundingBox'
848
- />
849
- </defs>
850
- </Fragment>
851
- );
852
- },
853
- )}
854
- {label && (
855
- <Label
856
- config={label}
857
- iosStyle={{
858
- isIOS,
859
- left: halfChartWidth + marginLeft,
860
- top: halfChartHeight + marginTop,
861
- }}
862
- arcs={_arcs}
863
- judge={judgeData}
864
- />
865
- )}
866
- {current && (
867
- <g
868
- fillOpacity={y}
869
- style={{ transform: "rotate(" + -rotate_ + "deg)" }}
870
- >
871
- <Current
872
- config={current}
873
- width={chartWidth}
874
- height={chartHeight}
875
- iosStyle={{
876
- marginLeft,
877
- marginTop,
878
- isIOS,
879
- }}
880
- data={_arcs}
881
- judge={judgeData}
882
- currentIndex={+currentIndex}
883
- />
884
- </g>
885
- )}
886
- </g>
887
- </ChartContainer>
888
- {decorate && (
889
- <ConicalGradient
890
- width={width}
891
- height={height}
892
- centerX={halfChartWidth + marginLeft}
893
- centerY={halfChartHeight + marginTop}
894
- config={decorate}
895
- arcs={_arcs}
896
- radius={radius}
897
- />
898
- )}
899
-
900
- <Legend
901
- {...legend}
902
- height={chartHeight}
903
- componentWidth={width}
904
- marginLeft={marginLeft}
905
- marginRight={marginRight}
906
- isPieChart
907
- data={data}
908
- columnsSeries={columnsSeries}
909
- series={_arcs.map((arc) => ({
910
- ...arc,
911
- percent: arc.percent.toFixed(legendPrecision),
912
- }))}
913
- pieClick={onClick}
914
- formatter={formatter}
915
- judge={judgeData}
916
- />
917
- {tooltip &&
918
- mousePos &&
919
- mousePos.x != 0 &&
920
- mousePos.y != 0 &&
921
- tooltip.manual && (
922
- <div
923
- style={{
924
- position: "absolute",
925
- pointerEvents: "none",
926
- }}
927
- >
928
- <PieTooltip
929
- series={series}
930
- domRef={domRef}
931
- data={hoverData}
932
- config={tooltip}
933
- pieCenter={{
934
- x: halfChartWidth,
935
- y: maxRadius + marginTop,
936
- }}
937
- mousePos={mousePos}
938
- />
939
- </div>
940
- )}
941
- </div>
942
- );
943
- },
944
- );
945
-
946
- const Current = ({
947
- config: {
948
- show,
949
- gap,
950
- name: {
951
- show: showName,
952
- sameColor: nameColor,
953
- font: nameFont,
954
- translate = { x: 0, y: 0 },
955
- maxWidth,
956
- textOverflow,
957
- speed,
958
- },
959
- percent: {
960
- show: showPercent,
961
- sameColor: percentColor,
962
- font: percentFont,
963
- precision,
964
- translate: { x: translatePercentX, y: translatePercentY },
965
- },
966
- value: {
967
- show: showValue,
968
- sameColor: valueColor,
969
- font: valueFont,
970
- translate: { x: translateValueX, y: translateValueY },
971
- suffix: {
972
- show: showSuffix,
973
- fontSize,
974
- text,
975
- translate: { x: translateSuffixX, y: translateSuffixY },
976
- },
977
- },
978
- },
979
- iosStyle: { isIOS, marginLeft, marginTop },
980
- width,
981
- height,
982
- data,
983
- judge,
984
- currentIndex,
985
- }) => {
986
- const _data = useMemo(() => {
987
- const legendDataWithPercent = getDataWithPercent(data, precision);
988
- return sortPie(legendDataWithPercent, "");
989
- }, [data, precision]);
990
-
991
- //数据容错,当data都为零那么需要进行以下容错
992
- if (judge == 0) {
993
- _data.forEach((d) => {
994
- ((d.percent = 0), (d.value = 0));
995
- });
996
- }
997
-
998
- const currentData = _data[currentIndex];
999
-
1000
- if (!currentData) return null;
1001
-
1002
- const { displayName, fieldName, value, percent } = currentData;
1003
- let nameTemp = displayName ? displayName : fieldName; //类目名
1004
-
1005
- let foreignStyle = {
1006
- //foreignObject标签样式,
1007
- width,
1008
- height,
1009
- position: "relative",
1010
- overflow: "visible",
1011
- pointerEvents: "none",
1012
- },
1013
- boxStyle = {
1014
- //弹性盒子样式,用于当前值的上下居中对齐等
1015
- width,
1016
- height,
1017
- position: "absolute",
1018
- display: "flex",
1019
- flexDirection: "column",
1020
- justifyContent: "center",
1021
- alignItems: "center",
1022
- transform: isIOS
1023
- ? `translate(${marginLeft}px,${marginTop}px)`
1024
- : `translate(-${width / 2}px,-${height / 2}px)`,
1025
- };
1026
- let seriesColor = currentData.series.color;
1027
- seriesColor =
1028
- seriesColor.type == "pure"
1029
- ? seriesColor.pure
1030
- : seriesColor.linear.stops[0].color;
1031
- return (
1032
- show && (
1033
- <foreignObject style={foreignStyle}>
1034
- <div style={boxStyle}>
1035
- {showName && (
1036
- <TextOverflow
1037
- type={textOverflow}
1038
- value={nameTemp}
1039
- speed={speed}
1040
- style={{
1041
- width: "100%",
1042
- maxWidth,
1043
- textAlign: "center",
1044
- display: textOverflow == "marquee" ? "flex" : "bolck",
1045
- justifyContent: "center",
1046
- ...getFontStyle(nameFont),
1047
- margin: gap / 2 + "px 0",
1048
- color: nameColor ? seriesColor : nameFont.color,
1049
- transform: `translate(${translate.x}px,${translate.y}px)`,
1050
- }}
1051
- ></TextOverflow>
1052
- )}
1053
- {
1054
- //真实值
1055
- showValue && (
1056
- <span
1057
- style={{
1058
- ...getFontStyle(valueFont),
1059
- transform:
1060
- "translate(" +
1061
- translateValueX +
1062
- "px," +
1063
- translateValueY +
1064
- "px)",
1065
- margin: gap / 2 + "px 0",
1066
- color: valueColor ? seriesColor : valueFont.color,
1067
- }}
1068
- >
1069
- {value}
1070
- {showSuffix && text && (
1071
- <span
1072
- style={{
1073
- display: "inline-block",
1074
- transform:
1075
- "translate(" +
1076
- translateSuffixX +
1077
- "px," +
1078
- translateSuffixY +
1079
- "px)",
1080
- fontSize: fontSize,
1081
- }}
1082
- >
1083
- {text}
1084
- </span>
1085
- )}
1086
- </span>
1087
- )
1088
- }
1089
- {
1090
- //百分比值
1091
- showPercent && (
1092
- <span
1093
- style={{
1094
- transform:
1095
- "translate(" +
1096
- translatePercentX +
1097
- "px," +
1098
- translatePercentY +
1099
- "px)",
1100
- ...getFontStyle(percentFont),
1101
- margin: gap / 2 + "px 0",
1102
- color: percentColor ? seriesColor : percentFont.color,
1103
- }}
1104
- >
1105
- {percent + "%"}
1106
- </span>
1107
- )
1108
- }
1109
- </div>
1110
- </foreignObject>
1111
- )
1112
- );
1113
- };
1114
-
1115
- const Label = ({
1116
- config: {
1117
- maxRadius = 0,
1118
- lineLength,
1119
- lineColor,
1120
- distance,
1121
- mode,
1122
- align,
1123
- show,
1124
- translate: { x: translateX, y: translateY },
1125
- name: {
1126
- show: showName,
1127
- font: nameFont,
1128
- maxWidth,
1129
- textOverflow,
1130
- speed,
1131
- translate: NameTranslate,
1132
- },
1133
- value: {
1134
- show: showValue,
1135
- font: valueFont,
1136
- sameColor: valueSameColor,
1137
- suffix: {
1138
- show: showSuffix,
1139
- text,
1140
- fontSize: suffixFontSize,
1141
- translate: { x: suffixTranslateX, y: suffixTranslateY },
1142
- },
1143
- translate: ValueTranslate,
1144
- },
1145
- percent: {
1146
- show: showPercent,
1147
- sameColor: percentSameColor,
1148
- font: percentFont,
1149
- precision,
1150
- translate: PercentTranslate,
1151
- },
1152
- },
1153
- iosStyle: { isIOS, left, top },
1154
- arcs,
1155
- judge,
1156
- animation,
1157
- }) => {
1158
- const _arcs = useMemo(
1159
- () => getDataWithPercent(arcs, precision),
1160
- [arcs, precision],
1161
- );
1162
- //数据做出容错
1163
- if (judge == 0) {
1164
- _arcs.forEach((d) => {
1165
- d.percent = 0;
1166
- });
1167
- }
1168
- return (
1169
- <g>
1170
- {_arcs.map(
1171
- (
1172
- {
1173
- series: {
1174
- color: {
1175
- type,
1176
- pure,
1177
- linear: { stops },
1178
- },
1179
- },
1180
- data,
1181
- displayName,
1182
- value,
1183
- percent,
1184
- arc,
1185
- outerRadius,
1186
- index: actualIndex,
1187
- },
1188
- index,
1189
- ) => {
1190
- const [x, y] = arc.centroid();
1191
- const midAngle = Math.atan2(y, x);
1192
-
1193
- const [x1, y1] = getCoord(
1194
- midAngle,
1195
- maxRadius ? maxRadius : outerRadius,
1196
- );
1197
-
1198
- const radius = (maxRadius ? maxRadius : outerRadius) + distance;
1199
- const [x2, y2] = getCoord(midAngle, radius);
1200
-
1201
- const direction = x2 < 0 ? -1 : 1;
1202
- const x3 = x2 + lineLength * direction;
1203
-
1204
- const _x = x3 + (translateX + 6) * direction;
1205
-
1206
- const _showName = showName && displayName;
1207
- const _showValue = showValue && (value || showSuffix);
1208
- const nameStyle = getFontStyle(nameFont);
1209
- return (
1210
- show &&
1211
- (_showName || showPercent || showValue) && (
1212
- <g key={index}>
1213
- <path
1214
- className={animation ? ringCss["label-line"] : ""}
1215
- style={{
1216
- animationDelay: `${
1217
- animation
1218
- ? (actualIndex + 1) * ringDuration - labelDuration
1219
- : 0
1220
- }ms`,
1221
- }}
1222
- d={
1223
- "M" +
1224
- x1 +
1225
- ", " +
1226
- y1 +
1227
- "L" +
1228
- x2 +
1229
- ", " +
1230
- y2 +
1231
- "L" +
1232
- x3 +
1233
- ", " +
1234
- y2
1235
- }
1236
- stroke={
1237
- lineColor
1238
- ? lineColor
1239
- : type == "pure"
1240
- ? pure
1241
- : stops[0].color
1242
- }
1243
- fill="none"
1244
- />
1245
- <foreignObject
1246
- width="1"
1247
- height="1"
1248
- x={_x}
1249
- y={y2 + translateY}
1250
- style={{ overflow: "visible", position: "relative" }}
1251
- >
1252
- <div
1253
- className={animation ? ringCss["label-text"] : ""}
1254
- style={{
1255
- position: isIOS ? "absolute" : "relative",
1256
- transform: isIOS
1257
- ? `translate(calc(${x3 < 0 ? "-100%" : "0px"} + ${
1258
- left + _x
1259
- }px),calc(-50% + ${top + y2 + translateY}px))`
1260
- : "translate(0,-50%)",
1261
- whiteSpace: "nowrap",
1262
- float: x3 >= 0 ? "left" : "right",
1263
- width: "max-content",
1264
- display: "flex",
1265
- flexDirection: mode == "horizontal" ? "row" : "column",
1266
- alignItems:
1267
- align == "left"
1268
- ? "flex-start"
1269
- : align == "center"
1270
- ? "center"
1271
- : "flex-end",
1272
- justifyContent: "center",
1273
- }}
1274
- >
1275
- {_showName && (
1276
- <TextOverflow
1277
- type={textOverflow}
1278
- value={
1279
- displayName + (showValue || showPercent ? ":" : "")
1280
- }
1281
- speed={speed}
1282
- style={{
1283
- maxWidth,
1284
- ...nameStyle,
1285
- float: mode == "horizontal" ? "left" : "none",
1286
- transform: `translate(${NameTranslate.x}px, ${NameTranslate.y}px)`,
1287
- }}
1288
- ></TextOverflow>
1289
- )}
1290
- {showValue && (
1291
- <span
1292
- style={{
1293
- ...getFontStyle(valueFont),
1294
- color: valueSameColor ? pure : valueFont.color,
1295
- transform: `translate(${ValueTranslate.x}px, ${ValueTranslate.y}px)`,
1296
- }}
1297
- >
1298
- {data.y}
1299
- {showSuffix && (
1300
- <span
1301
- style={{
1302
- position: "relative",
1303
- fontSize: suffixFontSize,
1304
- marginLeft: suffixTranslateX,
1305
- top: suffixTranslateY,
1306
- }}
1307
- >
1308
- {text}
1309
- </span>
1310
- )}
1311
- </span>
1312
- )}
1313
- {showPercent && (
1314
- <span
1315
- style={{
1316
- ...getFontStyle(percentFont),
1317
- color: percentSameColor ? pure : percentFont.color,
1318
- transform: `translate(${PercentTranslate.x}px, ${PercentTranslate.y}px)`,
1319
- }}
1320
- >
1321
- {(_showValue ? "(" : "") +
1322
- percent +
1323
- "%" +
1324
- (_showValue ? ")" : "")}
1325
- </span>
1326
- )}
1327
- </div>
1328
- </foreignObject>
1329
- </g>
1330
- )
1331
- );
1332
- },
1333
- )}
1334
- </g>
1335
- );
1336
- };
1337
-
1338
- function getAlign(align, reverse) {
1339
- if (align == "center") return "center";
1340
- if (align == "left") return reverse ? "flex-end" : "flex-start";
1341
- return reverse ? "flex-start" : "flex-end";
1342
- }
1343
- function getTranslate(translate, reverse) {
1344
- const { x, y } = translate;
1345
- return `translate(${reverse ? -x : x}px, ${y}px)`;
1346
- }
1347
- const RingLabel = ({
1348
- config: {
1349
- ringDuration,
1350
- labelDuration,
1351
- maxRadius = 0,
1352
- lineLength,
1353
- lineColor,
1354
- distance,
1355
- mode,
1356
- align = "center",
1357
- show,
1358
- translate: { x: translateX, y: translateY },
1359
- name: {
1360
- show: showName,
1361
- font: nameFont,
1362
- maxWidth,
1363
- textOverflow,
1364
- speed,
1365
- translate: nameTranslate,
1366
- },
1367
- value: {
1368
- show: showValue,
1369
- font: valueFont,
1370
- sameColor: valueSameColor,
1371
- suffix: {
1372
- show: showSuffix,
1373
- text,
1374
- fontSize: suffixFontSize,
1375
- translate: { x: suffixTranslateX, y: suffixTranslateY },
1376
- },
1377
- translate: valueTranslate,
1378
- },
1379
- percent: {
1380
- show: showPercent,
1381
- sameColor: percentSameColor,
1382
- font: percentFont,
1383
- precision,
1384
- translate: percentTranslate,
1385
- },
1386
- },
1387
- iosStyle: { isIOS, left, top },
1388
- judge,
1389
- arcs,
1390
- }) => {
1391
- const _arcs = useMemo(
1392
- () => getDataWithPercent(arcs, precision),
1393
- [arcs, precision],
1394
- );
1395
-
1396
- //数据做出容错
1397
- if (judge == 0) {
1398
- _arcs.forEach((d) => {
1399
- d.percent = 0;
1400
- });
1401
- }
1402
-
1403
- return (
1404
- <g>
1405
- {_arcs.map(
1406
- (
1407
- {
1408
- series: {
1409
- color: {
1410
- type,
1411
- pure,
1412
- linear: { stops },
1413
- },
1414
- },
1415
- data: realData,
1416
- displayName,
1417
- value,
1418
- percent,
1419
- arc,
1420
- outerRadius,
1421
- index: actualIndex,
1422
- },
1423
- index,
1424
- ) => {
1425
- const [x, y] = arc.centroid();
1426
- const midAngle = Math.atan2(y, x);
1427
-
1428
- const [x1, y1] = getCoord(
1429
- midAngle,
1430
- maxRadius ? maxRadius : outerRadius,
1431
- );
1432
-
1433
- const radius = (maxRadius ? maxRadius : outerRadius) + distance;
1434
- const [x2, y2] = getCoord(midAngle, radius);
1435
-
1436
- const directionX = x2 < 0 ? -1 : 1;
1437
- const directionY = y2 < 0 ? -1 : 1;
1438
- const x3 = x2 + lineLength * directionX;
1439
- const _x = x3 + (translateX + 6) * directionX;
1440
-
1441
- const _showName = showName && displayName;
1442
- const _showValue = showValue && (value || showSuffix);
1443
-
1444
- return (
1445
- show &&
1446
- (_showName || showPercent || _showValue) && (
1447
- <g key={index}>
1448
- <path
1449
- className={ringCss["label-line"]}
1450
- style={{
1451
- animationDelay: `${
1452
- (actualIndex + 1) * ringDuration - labelDuration
1453
- }ms`,
1454
- }}
1455
- d={
1456
- "M" +
1457
- x1 +
1458
- ", " +
1459
- y1 +
1460
- "L" +
1461
- x2 +
1462
- ", " +
1463
- y2 +
1464
- "L" +
1465
- x3 +
1466
- ", " +
1467
- y2
1468
- }
1469
- stroke={
1470
- lineColor
1471
- ? lineColor
1472
- : type == "pure"
1473
- ? pure
1474
- : stops[0].color
1475
- }
1476
- fill="none"
1477
- />
1478
- <foreignObject
1479
- width="1"
1480
- height="1"
1481
- x={_x}
1482
- y={y2 + translateY}
1483
- style={{ overflow: "visible", position: "relative" }}
1484
- >
1485
- <div
1486
- className={ringCss["label-text"]}
1487
- style={{
1488
- position: isIOS ? "absolute" : "relative",
1489
- transform: isIOS
1490
- ? `translate(calc(${x3 < 0 ? "-100%" : "0px"} + ${
1491
- left + _x
1492
- }px),calc(-50% + ${top + y2 + translateY}px))`
1493
- : "translate(0,-50%)",
1494
- whiteSpace: "nowrap",
1495
- float: x3 >= 0 ? "left" : "right",
1496
- width: "max-content",
1497
- animationDelay: `${
1498
- (actualIndex + 1) * ringDuration - labelDuration
1499
- }ms`,
1500
- display: "flex",
1501
- flexDirection: mode == "horizontal" ? "row" : "column",
1502
- alignItems: getAlign(align, x3 >= 0),
1503
- justifyContent: "center",
1504
- }}
1505
- >
1506
- {_showName && (
1507
- <TextOverflow
1508
- type={textOverflow}
1509
- value={
1510
- displayName + (showValue || showPercent ? ":" : "")
1511
- }
1512
- speed={speed}
1513
- style={{
1514
- maxWidth,
1515
- ...getFontStyle(nameFont),
1516
- float: mode == "horizontal" ? "left" : "none",
1517
- transform: getTranslate(nameTranslate, x3 >= 0),
1518
- }}
1519
- ></TextOverflow>
1520
- )}
1521
- {showValue && (
1522
- <span
1523
- style={{
1524
- ...getFontStyle(valueFont),
1525
- transform: getTranslate(valueTranslate, x3 >= 0),
1526
- color: valueSameColor ? pure : valueFont.color,
1527
- }}
1528
- >
1529
- {realData.y}
1530
- {showSuffix && (
1531
- <span
1532
- style={{
1533
- position: "relative",
1534
- fontSize: suffixFontSize,
1535
- marginLeft: suffixTranslateX,
1536
- top: suffixTranslateY,
1537
- }}
1538
- >
1539
- {text}
1540
- </span>
1541
- )}
1542
- </span>
1543
- )}
1544
- {showPercent && (
1545
- <span
1546
- style={{
1547
- ...getFontStyle(percentFont),
1548
- transform: getTranslate(percentTranslate, x3 >= 0),
1549
- color: percentSameColor ? pure : percentFont.color,
1550
- }}
1551
- >
1552
- {(_showValue ? "(" : "") +
1553
- percent +
1554
- "%" +
1555
- (_showValue ? "" : "")}
1556
- </span>
1557
- )}
1558
- </div>
1559
- </foreignObject>
1560
- </g>
1561
- )
1562
- );
1563
- },
1564
- )}
1565
- </g>
1566
- );
1567
- };
1568
-
1569
- 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));