Skip to content

Commit db4a823

Browse files
authoredAug 24, 2023
feat: Circle support conic color (#253)
* docs: update demo * chore: basic color * feat: support conic * feat: support conic * test: update snapshot
1 parent 8c3750f commit db4a823

11 files changed

+466
-148
lines changed
 

‎docs/examples/gap.tsx

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { Circle, ProgressProps } from 'rc-progress';
2+
import { Circle, type ProgressProps } from 'rc-progress';
33

44
const colorMap = ['#3FC7FA', '#85D262', '#FE8C6A', '#FF5959', '#BC3FFA'];
55

@@ -11,7 +11,7 @@ class Example extends React.Component<ProgressProps, any> {
1111
constructor(props) {
1212
super(props);
1313
this.state = {
14-
percent: 30,
14+
percent: 100,
1515
colorIndex: 0,
1616
subPathsCount: 3,
1717
};
@@ -103,6 +103,30 @@ class Example extends React.Component<ProgressProps, any> {
103103
strokeColor={color}
104104
/>
105105
</div>
106+
<div style={circleContainerStyle}>
107+
<Circle
108+
percent={percent}
109+
gapDegree={70}
110+
strokeWidth={6}
111+
strokeColor={{
112+
'0%': 'red',
113+
'100%': 'blue',
114+
}}
115+
/>
116+
</div>
117+
<div style={circleContainerStyle}>
118+
<Circle
119+
percent={percent}
120+
gapDegree={70}
121+
strokeWidth={6}
122+
strokeColor={{
123+
conic: true,
124+
'0%': 'red',
125+
'99%': 'blue',
126+
'100%': 'green',
127+
}}
128+
/>
129+
</div>
106130
</div>
107131
);
108132
}

‎docs/examples/gradient-circle.tsx

+15
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const Example = () => {
3434
}}
3535
/>
3636
</div>
37+
3738
<h3>Circle With Success Percent {65}%</h3>
3839
<div style={circleContainerStyle}>
3940
<Circle
@@ -49,6 +50,20 @@ const Example = () => {
4950
]}
5051
/>
5152
</div>
53+
54+
<h3>Circle colors</h3>
55+
<div style={circleContainerStyle}>
56+
<Circle
57+
percent={100}
58+
strokeWidth={6}
59+
strokeColor={{
60+
conic: true,
61+
'0%': 'green',
62+
'99%': 'red',
63+
'100%': 'blue',
64+
}}
65+
/>
66+
</div>
5267
</div>
5368
);
5469
};

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"rc-util": "^5.16.1"
4848
},
4949
"devDependencies": {
50+
"@testing-library/react": "^12.1.5",
5051
"@types/classnames": "^2.2.9",
5152
"@types/jest": "^27.5.0",
5253
"@types/keyv": "3.1.4",

‎src/Circle/ColorGradient.tsx

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import * as React from 'react';
2+
3+
function stripPercentToNumber(percent: string) {
4+
return +percent.replace('%', '');
5+
}
6+
7+
export interface ColorGradientProps {
8+
gradientId: string;
9+
gradient?: Record<string, string>;
10+
}
11+
12+
export default function ColorGradient(props: ColorGradientProps) {
13+
const { gradientId, gradient } = props;
14+
15+
return (
16+
<defs>
17+
<linearGradient id={gradientId} x1="100%" y1="0%" x2="0%" y2="0%">
18+
{Object.keys(gradient)
19+
.sort((a, b) => stripPercentToNumber(a) - stripPercentToNumber(b))
20+
.map((key, index) => (
21+
<stop key={index} offset={key} stopColor={gradient[key]} />
22+
))}
23+
</linearGradient>
24+
</defs>
25+
);
26+
}

‎src/Circle/PtgCircle.tsx

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import * as React from 'react';
2+
import type { ProgressProps } from '..';
3+
import type { StrokeColorType } from '../interface';
4+
5+
export interface ColorGradientProps {
6+
prefixCls: string;
7+
gradientId: string;
8+
style: React.CSSProperties;
9+
ptg: number;
10+
radius: number;
11+
strokeLinecap: ProgressProps['strokeLinecap'];
12+
strokeWidth: ProgressProps['strokeWidth'];
13+
size: number;
14+
color: StrokeColorType;
15+
conic: boolean;
16+
gapDegree: number;
17+
}
18+
19+
const PtgCircle = React.forwardRef<SVGCircleElement, ColorGradientProps>((props, ref) => {
20+
const {
21+
prefixCls,
22+
color,
23+
gradientId,
24+
radius,
25+
style: circleStyleForStack,
26+
ptg,
27+
strokeLinecap,
28+
strokeWidth,
29+
size,
30+
conic,
31+
gapDegree,
32+
} = props;
33+
34+
const isGradient = color && typeof color === 'object';
35+
36+
const stroke = React.useMemo(() => {
37+
if (conic) {
38+
return '#FFF';
39+
}
40+
41+
return isGradient ? `url(#${gradientId})` : undefined;
42+
}, [gradientId, isGradient, conic]);
43+
44+
// ========================== Circle ==========================
45+
const halfSize = size / 2;
46+
47+
const circleNode = (
48+
<circle
49+
className={`${prefixCls}-circle-path`}
50+
r={radius}
51+
cx={halfSize}
52+
cy={halfSize}
53+
stroke={stroke}
54+
strokeLinecap={strokeLinecap}
55+
strokeWidth={strokeWidth}
56+
opacity={ptg === 0 ? 0 : 1}
57+
style={circleStyleForStack}
58+
ref={ref}
59+
/>
60+
);
61+
62+
// ========================== Render ==========================
63+
if (!conic) {
64+
return circleNode;
65+
}
66+
67+
const maskId = `${gradientId}-conic`;
68+
const conicColorKeys = Object.keys(color).filter((key) => key !== 'conic');
69+
70+
const fromDeg = gapDegree ? `${180 + gapDegree / 2}deg` : '0deg';
71+
72+
const conicColors = conicColorKeys.map((key) => {
73+
const parsedKey = parseFloat(key);
74+
const ptgKey = `${gapDegree ? Math.floor((parsedKey * (360 - gapDegree)) / 360) : parsedKey}%`;
75+
76+
return `${color[key]} ${ptgKey}`;
77+
});
78+
79+
const conicColorBg = `conic-gradient(from ${fromDeg}, ${conicColors.join(', ')})`;
80+
81+
return (
82+
<>
83+
<mask id={maskId}>{circleNode}</mask>
84+
85+
<foreignObject x={0} y={0} width={size} height={size} mask={`url(#${maskId})`}>
86+
<div
87+
style={{
88+
width: '100%',
89+
height: '100%',
90+
background: conicColorBg,
91+
}}
92+
/>
93+
</foreignObject>
94+
</>
95+
);
96+
});
97+
98+
if (process.env.NODE_ENV !== 'production') {
99+
PtgCircle.displayName = 'PtgCircle';
100+
}
101+
102+
export default PtgCircle;

‎src/Circle.tsx ‎src/Circle/index.tsx

+41-89
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,17 @@
11
import * as React from 'react';
22
import classNames from 'classnames';
3-
import { defaultProps, useTransitionDuration } from './common';
4-
import type { ProgressProps } from './interface';
5-
import useId from './hooks/useId';
6-
7-
function stripPercentToNumber(percent: string) {
8-
return +percent.replace('%', '');
9-
}
3+
import { defaultProps, useTransitionDuration } from '../common';
4+
import type { ProgressProps } from '../interface';
5+
import useId from '../hooks/useId';
6+
import ColorGradient from './ColorGradient';
7+
import PtgCircle from './PtgCircle';
8+
import { VIEW_BOX_SIZE, getCircleStyle, isConicColor } from './util';
109

1110
function toArray<T>(value: T | T[]): T[] {
1211
const mergedValue = value ?? [];
1312
return Array.isArray(mergedValue) ? mergedValue : [mergedValue];
1413
}
1514

16-
const VIEW_BOX_SIZE = 100;
17-
18-
const getCircleStyle = (
19-
perimeter: number,
20-
perimeterWithoutGap: number,
21-
offset: number,
22-
percent: number,
23-
rotateDeg: number,
24-
gapDegree,
25-
gapPosition: ProgressProps['gapPosition'] | undefined,
26-
strokeColor: string | Record<string, string>,
27-
strokeLinecap: ProgressProps['strokeLinecap'],
28-
strokeWidth,
29-
stepSpace = 0,
30-
) => {
31-
const offsetDeg = (offset / 100) * 360 * ((360 - gapDegree) / 360);
32-
const positionDeg =
33-
gapDegree === 0
34-
? 0
35-
: {
36-
bottom: 0,
37-
top: 180,
38-
left: 90,
39-
right: -90,
40-
}[gapPosition];
41-
42-
let strokeDashoffset = ((100 - percent) / 100) * perimeterWithoutGap;
43-
// Fix percent accuracy when strokeLinecap is round
44-
// https://github.com/ant-design/ant-design/issues/35009
45-
if (strokeLinecap === 'round' && percent !== 100) {
46-
strokeDashoffset += strokeWidth / 2;
47-
// when percent is small enough (<= 1%), keep smallest value to avoid it's disappearance
48-
if (strokeDashoffset >= perimeterWithoutGap) {
49-
strokeDashoffset = perimeterWithoutGap - 0.01;
50-
}
51-
}
52-
53-
return {
54-
stroke: typeof strokeColor === 'string' ? strokeColor : undefined,
55-
strokeDasharray: `${perimeterWithoutGap}px ${perimeter}`,
56-
strokeDashoffset: strokeDashoffset + stepSpace,
57-
transform: `rotate(${rotateDeg + offsetDeg + positionDeg}deg)`,
58-
transformOrigin: '0 0',
59-
transition:
60-
'stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s',
61-
fillOpacity: 0,
62-
};
63-
};
64-
6515
const Circle: React.FC<ProgressProps> = (props) => {
6616
const {
6717
id,
@@ -83,15 +33,26 @@ const Circle: React.FC<ProgressProps> = (props) => {
8333
...props,
8434
};
8535

36+
const halfSize = VIEW_BOX_SIZE / 2;
37+
8638
const mergedId = useId(id);
8739
const gradientId = `${mergedId}-gradient`;
88-
const radius = VIEW_BOX_SIZE / 2 - strokeWidth / 2;
40+
const radius = halfSize - strokeWidth / 2;
8941
const perimeter = Math.PI * 2 * radius;
9042
const rotateDeg = gapDegree > 0 ? 90 + gapDegree / 2 : -90;
9143
const perimeterWithoutGap = perimeter * ((360 - gapDegree) / 360);
9244
const { count: stepCount, space: stepSpace } =
9345
typeof steps === 'object' ? steps : { count: steps, space: 2 };
9446

47+
const percentList = toArray(percent);
48+
const strokeColorList = toArray(strokeColor);
49+
const gradient = strokeColorList.find((color) => color && typeof color === 'object') as Record<
50+
string,
51+
string
52+
>;
53+
const isConicGradient = isConicColor(gradient);
54+
const mergedStrokeLinecap = isConicGradient ? 'butt' : strokeLinecap;
55+
9556
const circleStyle = getCircleStyle(
9657
perimeter,
9758
perimeterWithoutGap,
@@ -101,12 +62,9 @@ const Circle: React.FC<ProgressProps> = (props) => {
10162
gapDegree,
10263
gapPosition,
10364
trailColor,
104-
strokeLinecap,
65+
mergedStrokeLinecap,
10566
strokeWidth,
10667
);
107-
const percentList = toArray(percent);
108-
const strokeColorList = toArray(strokeColor);
109-
const gradient = strokeColorList.find((color) => color && typeof color === 'object');
11068

11169
const paths = useTransitionDuration();
11270

@@ -115,7 +73,6 @@ const Circle: React.FC<ProgressProps> = (props) => {
11573
return percentList
11674
.map((ptg, index) => {
11775
const color = strokeColorList[index] || strokeColorList[strokeColorList.length - 1];
118-
const stroke = color && typeof color === 'object' ? `url(#${gradientId})` : undefined;
11976
const circleStyleForStack = getCircleStyle(
12077
perimeter,
12178
perimeterWithoutGap,
@@ -125,22 +82,24 @@ const Circle: React.FC<ProgressProps> = (props) => {
12582
gapDegree,
12683
gapPosition,
12784
color,
128-
strokeLinecap,
85+
mergedStrokeLinecap,
12986
strokeWidth,
13087
);
13188
stackPtg += ptg;
89+
13290
return (
133-
<circle
91+
<PtgCircle
13492
key={index}
135-
className={`${prefixCls}-circle-path`}
136-
r={radius}
137-
cx={0}
138-
cy={0}
139-
stroke={stroke}
140-
strokeLinecap={strokeLinecap}
141-
strokeWidth={strokeWidth}
142-
opacity={ptg === 0 ? 0 : 1}
93+
color={color}
94+
ptg={ptg}
95+
radius={radius}
96+
prefixCls={prefixCls}
97+
gradientId={gradientId}
14398
style={circleStyleForStack}
99+
strokeLinecap={mergedStrokeLinecap}
100+
strokeWidth={strokeWidth}
101+
conic={isConicGradient}
102+
gapDegree={gapDegree}
144103
ref={(elem) => {
145104
// https://reactjs.org/docs/refs-and-the-dom.html#callback-refs
146105
// React will call the ref callback with the DOM element when the component mounts,
@@ -149,6 +108,7 @@ const Circle: React.FC<ProgressProps> = (props) => {
149108

150109
paths[index] = elem;
151110
}}
111+
size={VIEW_BOX_SIZE}
152112
/>
153113
);
154114
})
@@ -186,10 +146,9 @@ const Circle: React.FC<ProgressProps> = (props) => {
186146
key={index}
187147
className={`${prefixCls}-circle-path`}
188148
r={radius}
189-
cx={0}
190-
cy={0}
149+
cx={halfSize}
150+
cy={halfSize}
191151
stroke={stroke}
192-
// strokeLinecap={strokeLinecap}
193152
strokeWidth={strokeWidth}
194153
opacity={1}
195154
style={circleStyleForStack}
@@ -204,31 +163,24 @@ const Circle: React.FC<ProgressProps> = (props) => {
204163
return (
205164
<svg
206165
className={classNames(`${prefixCls}-circle`, className)}
207-
viewBox={`${-VIEW_BOX_SIZE / 2} ${-VIEW_BOX_SIZE / 2} ${VIEW_BOX_SIZE} ${VIEW_BOX_SIZE}`}
166+
viewBox={`0 0 ${VIEW_BOX_SIZE} ${VIEW_BOX_SIZE}`}
208167
style={style}
209168
id={id}
210169
role="presentation"
211170
{...restProps}
212171
>
213-
{gradient && (
214-
<defs>
215-
<linearGradient id={gradientId} x1="100%" y1="0%" x2="0%" y2="0%">
216-
{Object.keys(gradient)
217-
.sort((a, b) => stripPercentToNumber(a) - stripPercentToNumber(b))
218-
.map((key, index) => (
219-
<stop key={index} offset={key} stopColor={gradient[key]} />
220-
))}
221-
</linearGradient>
222-
</defs>
172+
{/* Line Gradient */}
173+
{gradient && !isConicGradient && (
174+
<ColorGradient gradientId={gradientId} gradient={gradient} />
223175
)}
224176
{!stepCount && (
225177
<circle
226178
className={`${prefixCls}-circle-trail`}
227179
r={radius}
228-
cx={0}
229-
cy={0}
180+
cx={halfSize}
181+
cy={halfSize}
230182
stroke={trailColor}
231-
strokeLinecap={strokeLinecap}
183+
strokeLinecap={mergedStrokeLinecap}
232184
strokeWidth={trailWidth || strokeWidth}
233185
style={circleStyle}
234186
/>

‎src/Circle/util.ts

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import type { StrokeColorObject, StrokeColorType } from '../interface';
2+
import type { ProgressProps } from '..';
3+
4+
export const VIEW_BOX_SIZE = 100;
5+
6+
export function isConicColor(gradient: StrokeColorObject) {
7+
return gradient && gradient.conic;
8+
}
9+
10+
export const getCircleStyle = (
11+
perimeter: number,
12+
perimeterWithoutGap: number,
13+
offset: number,
14+
percent: number,
15+
rotateDeg: number,
16+
gapDegree,
17+
gapPosition: ProgressProps['gapPosition'] | undefined,
18+
strokeColor: StrokeColorType,
19+
strokeLinecap: ProgressProps['strokeLinecap'],
20+
strokeWidth,
21+
stepSpace = 0,
22+
) => {
23+
const offsetDeg = (offset / 100) * 360 * ((360 - gapDegree) / 360);
24+
const positionDeg =
25+
gapDegree === 0
26+
? 0
27+
: {
28+
bottom: 0,
29+
top: 180,
30+
left: 90,
31+
right: -90,
32+
}[gapPosition];
33+
34+
let strokeDashoffset = ((100 - percent) / 100) * perimeterWithoutGap;
35+
// Fix percent accuracy when strokeLinecap is round
36+
// https://github.com/ant-design/ant-design/issues/35009
37+
if (strokeLinecap === 'round' && percent !== 100) {
38+
strokeDashoffset += strokeWidth / 2;
39+
// when percent is small enough (<= 1%), keep smallest value to avoid it's disappearance
40+
if (strokeDashoffset >= perimeterWithoutGap) {
41+
strokeDashoffset = perimeterWithoutGap - 0.01;
42+
}
43+
}
44+
45+
const halfSize = VIEW_BOX_SIZE / 2;
46+
47+
return {
48+
stroke: typeof strokeColor === 'string' ? strokeColor : undefined,
49+
strokeDasharray: `${perimeterWithoutGap}px ${perimeter}`,
50+
strokeDashoffset: strokeDashoffset + stepSpace,
51+
transform: `rotate(${rotateDeg + offsetDeg + positionDeg}deg)`,
52+
transformOrigin: `${halfSize}px ${halfSize}px`,
53+
transition:
54+
'stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s',
55+
fillOpacity: 0,
56+
};
57+
};

‎src/interface.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ export interface ProgressProps {
1616
steps?: number | { count: number; space: number };
1717
}
1818

19-
export type BaseStrokeColorType = string | Record<string, string>;
19+
export type StrokeColorObject = Partial<Record<`${number}%` | 'from' | 'to', string>> & {
20+
conic?: boolean;
21+
};
22+
23+
export type BaseStrokeColorType = string | StrokeColorObject;
2024

2125
export type StrokeColorType = BaseStrokeColorType | BaseStrokeColorType[];
2226

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Circle.conic gapDegree 1`] = `
4+
<DocumentFragment>
5+
<svg
6+
class="rc-progress-circle"
7+
role="presentation"
8+
viewBox="0 0 100 100"
9+
>
10+
<circle
11+
class="rc-progress-circle-trail"
12+
cx="50"
13+
cy="50"
14+
r="47"
15+
stroke="#D9D9D9"
16+
stroke-linecap="butt"
17+
stroke-width="1"
18+
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(125deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
19+
/>
20+
<mask
21+
id="rc_progress_TEST_OR_SSR-gradient-conic"
22+
>
23+
<circle
24+
class="rc-progress-circle-path"
25+
cx="50"
26+
cy="50"
27+
opacity="1"
28+
r="47"
29+
stroke="#FFF"
30+
stroke-linecap="butt"
31+
stroke-width="6"
32+
style="stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(125deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
33+
/>
34+
</mask>
35+
<foreignobject
36+
height="100"
37+
mask="url(#rc_progress_TEST_OR_SSR-gradient-conic)"
38+
width="100"
39+
x="0"
40+
y="0"
41+
>
42+
<div
43+
style="width: 100%; height: 100%;"
44+
/>
45+
</foreignobject>
46+
</svg>
47+
</DocumentFragment>
48+
`;
49+
50+
exports[`Circle.conic should work 1`] = `
51+
<DocumentFragment>
52+
<svg
53+
class="rc-progress-circle"
54+
role="presentation"
55+
viewBox="0 0 100 100"
56+
>
57+
<circle
58+
class="rc-progress-circle-trail"
59+
cx="50"
60+
cy="50"
61+
r="47"
62+
stroke="#D9D9D9"
63+
stroke-linecap="butt"
64+
stroke-width="1"
65+
style="stroke: #D9D9D9; stroke-dasharray: 295.3097094374406px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(-90deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
66+
/>
67+
<mask
68+
id="rc_progress_TEST_OR_SSR-gradient-conic"
69+
>
70+
<circle
71+
class="rc-progress-circle-path"
72+
cx="50"
73+
cy="50"
74+
opacity="1"
75+
r="47"
76+
stroke="#FFF"
77+
stroke-linecap="butt"
78+
stroke-width="6"
79+
style="stroke-dasharray: 295.3097094374406px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(-90deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
80+
/>
81+
</mask>
82+
<foreignobject
83+
height="100"
84+
mask="url(#rc_progress_TEST_OR_SSR-gradient-conic)"
85+
width="100"
86+
x="0"
87+
y="0"
88+
>
89+
<div
90+
style="width: 100%; height: 100%;"
91+
/>
92+
</foreignobject>
93+
</svg>
94+
</DocumentFragment>
95+
`;

‎tests/__snapshots__/index.spec.js.snap

+56-56
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ exports[`Progress Circle should gradient works and circles have different gradie
8787
<svg
8888
class="rc-progress-circle"
8989
role="presentation"
90-
viewBox="-50 -50 100 100"
90+
viewBox="0 0 100 100"
9191
>
9292
<defs>
9393
<linearGradient
@@ -109,30 +109,30 @@ exports[`Progress Circle should gradient works and circles have different gradie
109109
</defs>
110110
<circle
111111
class="rc-progress-circle-trail"
112-
cx="0"
113-
cy="0"
112+
cx="50"
113+
cy="50"
114114
r="47"
115115
stroke="#D9D9D9"
116116
stroke-linecap="round"
117117
stroke-width="1"
118-
style="stroke: #D9D9D9; stroke-dasharray: 295.3097094374406px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(-90deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
118+
style="stroke: #D9D9D9; stroke-dasharray: 295.3097094374406px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(-90deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
119119
/>
120120
<circle
121121
class="rc-progress-circle-path"
122-
cx="0"
123-
cy="0"
122+
cx="50"
123+
cy="50"
124124
opacity="1"
125125
r="47"
126126
stroke="url(#rc_progress_TEST_OR_SSR-gradient)"
127127
stroke-linecap="round"
128128
stroke-width="6"
129-
style="stroke-dasharray: 295.3097094374406px 295.3097094374406; stroke-dashoffset: 32.53097094374406; transform: rotate(-90deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
129+
style="stroke-dasharray: 295.3097094374406px 295.3097094374406; stroke-dashoffset: 32.53097094374406; transform: rotate(-90deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
130130
/>
131131
</svg>
132132
<svg
133133
class="rc-progress-circle"
134134
role="presentation"
135-
viewBox="-50 -50 100 100"
135+
viewBox="0 0 100 100"
136136
>
137137
<defs>
138138
<linearGradient
@@ -154,24 +154,24 @@ exports[`Progress Circle should gradient works and circles have different gradie
154154
</defs>
155155
<circle
156156
class="rc-progress-circle-trail"
157-
cx="0"
158-
cy="0"
157+
cx="50"
158+
cy="50"
159159
r="47"
160160
stroke="#D9D9D9"
161161
stroke-linecap="round"
162162
stroke-width="1"
163-
style="stroke: #D9D9D9; stroke-dasharray: 295.3097094374406px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(-90deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
163+
style="stroke: #D9D9D9; stroke-dasharray: 295.3097094374406px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(-90deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
164164
/>
165165
<circle
166166
class="rc-progress-circle-path"
167-
cx="0"
168-
cy="0"
167+
cx="50"
168+
cy="50"
169169
opacity="1"
170170
r="47"
171171
stroke="url(#rc_progress_TEST_OR_SSR-gradient)"
172172
stroke-linecap="round"
173173
stroke-width="6"
174-
style="stroke-dasharray: 295.3097094374406px 295.3097094374406; stroke-dashoffset: 32.53097094374406; transform: rotate(-90deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
174+
style="stroke-dasharray: 295.3097094374406px 295.3097094374406; stroke-dashoffset: 32.53097094374406; transform: rotate(-90deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
175175
/>
176176
</svg>
177177
</div>
@@ -182,157 +182,157 @@ exports[`Progress Circle should show right gapPosition 1`] = `
182182
<svg
183183
class="rc-progress-circle"
184184
role="presentation"
185-
viewBox="-50 -50 100 100"
185+
viewBox="0 0 100 100"
186186
>
187187
<circle
188188
class="rc-progress-circle-trail"
189-
cx="0"
190-
cy="0"
189+
cx="50"
190+
cy="50"
191191
r="47"
192192
stroke="#D9D9D9"
193193
stroke-linecap="square"
194194
stroke-width="1"
195-
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(305deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
195+
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(305deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
196196
/>
197197
<circle
198198
class="rc-progress-circle-path"
199-
cx="0"
200-
cy="0"
199+
cx="50"
200+
cy="50"
201201
opacity="1"
202202
r="47"
203203
stroke-linecap="square"
204204
stroke-width="6"
205-
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 166.52186393277898; transform: rotate(305deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
205+
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 166.52186393277898; transform: rotate(305deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
206206
/>
207207
</svg>
208208
<svg
209209
class="rc-progress-circle"
210210
role="presentation"
211-
viewBox="-50 -50 100 100"
211+
viewBox="0 0 100 100"
212212
>
213213
<circle
214214
class="rc-progress-circle-trail"
215-
cx="0"
216-
cy="0"
215+
cx="50"
216+
cy="50"
217217
r="47"
218218
stroke="#D9D9D9"
219219
stroke-linecap="square"
220220
stroke-width="1"
221-
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(125deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
221+
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(125deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
222222
/>
223223
<circle
224224
class="rc-progress-circle-path"
225-
cx="0"
226-
cy="0"
225+
cx="50"
226+
cy="50"
227227
opacity="1"
228228
r="47"
229229
stroke-linecap="square"
230230
stroke-width="6"
231-
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 166.52186393277898; transform: rotate(125deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
231+
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 166.52186393277898; transform: rotate(125deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
232232
/>
233233
</svg>
234234
<svg
235235
class="rc-progress-circle"
236236
role="presentation"
237-
viewBox="-50 -50 100 100"
237+
viewBox="0 0 100 100"
238238
>
239239
<circle
240240
class="rc-progress-circle-trail"
241-
cx="0"
242-
cy="0"
241+
cx="50"
242+
cy="50"
243243
r="47"
244244
stroke="#D9D9D9"
245245
stroke-linecap="square"
246246
stroke-width="1"
247-
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(215deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
247+
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(215deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
248248
/>
249249
<circle
250250
class="rc-progress-circle-path"
251-
cx="0"
252-
cy="0"
251+
cx="50"
252+
cy="50"
253253
opacity="1"
254254
r="47"
255255
stroke-linecap="square"
256256
stroke-width="6"
257-
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 166.52186393277898; transform: rotate(215deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
257+
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 166.52186393277898; transform: rotate(215deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
258258
/>
259259
</svg>
260260
<svg
261261
class="rc-progress-circle"
262262
role="presentation"
263-
viewBox="-50 -50 100 100"
263+
viewBox="0 0 100 100"
264264
>
265265
<circle
266266
class="rc-progress-circle-trail"
267-
cx="0"
268-
cy="0"
267+
cx="50"
268+
cy="50"
269269
r="47"
270270
stroke="#D9D9D9"
271271
stroke-linecap="square"
272272
stroke-width="1"
273-
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(35deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
273+
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(35deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
274274
/>
275275
<circle
276276
class="rc-progress-circle-path"
277-
cx="0"
278-
cy="0"
277+
cx="50"
278+
cy="50"
279279
opacity="1"
280280
r="47"
281281
stroke-linecap="square"
282282
stroke-width="6"
283-
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 166.52186393277898; transform: rotate(35deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
283+
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 166.52186393277898; transform: rotate(35deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
284284
/>
285285
</svg>
286286
<svg
287287
class="rc-progress-circle"
288288
role="presentation"
289-
viewBox="-50 -50 100 100"
289+
viewBox="0 0 100 100"
290290
>
291291
<circle
292292
class="rc-progress-circle-trail"
293-
cx="0"
294-
cy="0"
293+
cx="50"
294+
cy="50"
295295
r="47"
296296
stroke="#D9D9D9"
297297
stroke-linecap="round"
298298
stroke-width="1"
299-
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(305deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
299+
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(305deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
300300
/>
301301
<circle
302302
class="rc-progress-circle-path"
303-
cx="0"
304-
cy="0"
303+
cx="50"
304+
cy="50"
305305
opacity="1"
306306
r="47"
307307
stroke-linecap="round"
308308
stroke-width="6"
309-
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 169.52186393277898; transform: rotate(305deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
309+
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 169.52186393277898; transform: rotate(305deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
310310
/>
311311
</svg>
312312
<svg
313313
class="rc-progress-circle"
314314
role="presentation"
315-
viewBox="-50 -50 100 100"
315+
viewBox="0 0 100 100"
316316
>
317317
<circle
318318
class="rc-progress-circle-trail"
319-
cx="0"
320-
cy="0"
319+
cx="50"
320+
cy="50"
321321
r="47"
322322
stroke="#D9D9D9"
323323
stroke-linecap="round"
324324
stroke-width="1"
325-
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(305deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
325+
style="stroke: #D9D9D9; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 0; transform: rotate(305deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0;"
326326
/>
327327
<circle
328328
class="rc-progress-circle-path"
329-
cx="0"
330-
cy="0"
329+
cx="50"
330+
cy="50"
331331
opacity="1"
332332
r="47"
333333
stroke-linecap="round"
334334
stroke-width="6"
335-
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 169.52186393277898; transform: rotate(305deg); transform-origin: 0 0; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
335+
style="stroke: #2db7f5; stroke-dasharray: 237.88837704682715px 295.3097094374406; stroke-dashoffset: 169.52186393277898; transform: rotate(305deg); transform-origin: 50px 50px; transition: stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s; fill-opacity: 0; transition-duration: 0s, 0s;"
336336
/>
337337
</svg>
338338
</div>

‎tests/conic.spec.tsx

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* eslint-disable react/no-render-return-value */
2+
// eslint-disable-next-line max-classes-per-file
3+
import React from 'react';
4+
import { render } from '@testing-library/react';
5+
import { Circle } from '../src';
6+
7+
describe('Circle.conic', () => {
8+
it('should work', () => {
9+
const { asFragment } = render(
10+
<Circle
11+
percent={100}
12+
strokeWidth={6}
13+
strokeColor={{
14+
conic: true,
15+
'0%': 'green',
16+
'99%': 'red',
17+
'100%': 'blue',
18+
}}
19+
/>,
20+
);
21+
22+
expect(asFragment()).toMatchSnapshot();
23+
});
24+
25+
it('gapDegree', () => {
26+
const { asFragment } = render(
27+
<Circle
28+
percent={100}
29+
gapDegree={70}
30+
strokeWidth={6}
31+
strokeColor={{
32+
conic: true,
33+
'0%': 'green',
34+
'99%': 'red',
35+
'100%': 'blue',
36+
}}
37+
/>,
38+
);
39+
40+
expect(asFragment()).toMatchSnapshot();
41+
});
42+
});

0 commit comments

Comments
 (0)
Please sign in to comment.