Skip to content

Commit

Permalink
feat: Circle support conic color (#253)
Browse files Browse the repository at this point in the history
* docs: update demo

* chore: basic color

* feat: support conic

* feat: support conic

* test: update snapshot
  • Loading branch information
zombieJ authored Aug 24, 2023
1 parent 8c3750f commit db4a823
Show file tree
Hide file tree
Showing 11 changed files with 466 additions and 148 deletions.
28 changes: 26 additions & 2 deletions docs/examples/gap.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { Circle, ProgressProps } from 'rc-progress';
import { Circle, type ProgressProps } from 'rc-progress';

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

Expand All @@ -11,7 +11,7 @@ class Example extends React.Component<ProgressProps, any> {
constructor(props) {
super(props);
this.state = {
percent: 30,
percent: 100,
colorIndex: 0,
subPathsCount: 3,
};
Expand Down Expand Up @@ -103,6 +103,30 @@ class Example extends React.Component<ProgressProps, any> {
strokeColor={color}
/>
</div>
<div style={circleContainerStyle}>
<Circle
percent={percent}
gapDegree={70}
strokeWidth={6}
strokeColor={{
'0%': 'red',
'100%': 'blue',
}}
/>
</div>
<div style={circleContainerStyle}>
<Circle
percent={percent}
gapDegree={70}
strokeWidth={6}
strokeColor={{
conic: true,
'0%': 'red',
'99%': 'blue',
'100%': 'green',
}}
/>
</div>
</div>
);
}
Expand Down
15 changes: 15 additions & 0 deletions docs/examples/gradient-circle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const Example = () => {
}}
/>
</div>

<h3>Circle With Success Percent {65}%</h3>
<div style={circleContainerStyle}>
<Circle
Expand All @@ -49,6 +50,20 @@ const Example = () => {
]}
/>
</div>

<h3>Circle colors</h3>
<div style={circleContainerStyle}>
<Circle
percent={100}
strokeWidth={6}
strokeColor={{
conic: true,
'0%': 'green',
'99%': 'red',
'100%': 'blue',
}}
/>
</div>
</div>
);
};
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"rc-util": "^5.16.1"
},
"devDependencies": {
"@testing-library/react": "^12.1.5",
"@types/classnames": "^2.2.9",
"@types/jest": "^27.5.0",
"@types/keyv": "3.1.4",
Expand Down
26 changes: 26 additions & 0 deletions src/Circle/ColorGradient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as React from 'react';

function stripPercentToNumber(percent: string) {
return +percent.replace('%', '');
}

export interface ColorGradientProps {
gradientId: string;
gradient?: Record<string, string>;
}

export default function ColorGradient(props: ColorGradientProps) {
const { gradientId, gradient } = props;

return (
<defs>
<linearGradient id={gradientId} x1="100%" y1="0%" x2="0%" y2="0%">
{Object.keys(gradient)
.sort((a, b) => stripPercentToNumber(a) - stripPercentToNumber(b))
.map((key, index) => (
<stop key={index} offset={key} stopColor={gradient[key]} />
))}
</linearGradient>
</defs>
);
}
102 changes: 102 additions & 0 deletions src/Circle/PtgCircle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import * as React from 'react';
import type { ProgressProps } from '..';
import type { StrokeColorType } from '../interface';

export interface ColorGradientProps {
prefixCls: string;
gradientId: string;
style: React.CSSProperties;
ptg: number;
radius: number;
strokeLinecap: ProgressProps['strokeLinecap'];
strokeWidth: ProgressProps['strokeWidth'];
size: number;
color: StrokeColorType;
conic: boolean;
gapDegree: number;
}

const PtgCircle = React.forwardRef<SVGCircleElement, ColorGradientProps>((props, ref) => {
const {
prefixCls,
color,
gradientId,
radius,
style: circleStyleForStack,
ptg,
strokeLinecap,
strokeWidth,
size,
conic,
gapDegree,
} = props;

const isGradient = color && typeof color === 'object';

const stroke = React.useMemo(() => {
if (conic) {
return '#FFF';
}

return isGradient ? `url(#${gradientId})` : undefined;
}, [gradientId, isGradient, conic]);

// ========================== Circle ==========================
const halfSize = size / 2;

const circleNode = (
<circle
className={`${prefixCls}-circle-path`}
r={radius}
cx={halfSize}
cy={halfSize}
stroke={stroke}
strokeLinecap={strokeLinecap}
strokeWidth={strokeWidth}
opacity={ptg === 0 ? 0 : 1}
style={circleStyleForStack}
ref={ref}
/>
);

// ========================== Render ==========================
if (!conic) {
return circleNode;
}

const maskId = `${gradientId}-conic`;
const conicColorKeys = Object.keys(color).filter((key) => key !== 'conic');

const fromDeg = gapDegree ? `${180 + gapDegree / 2}deg` : '0deg';

const conicColors = conicColorKeys.map((key) => {
const parsedKey = parseFloat(key);
const ptgKey = `${gapDegree ? Math.floor((parsedKey * (360 - gapDegree)) / 360) : parsedKey}%`;

return `${color[key]} ${ptgKey}`;
});

const conicColorBg = `conic-gradient(from ${fromDeg}, ${conicColors.join(', ')})`;

return (
<>
<mask id={maskId}>{circleNode}</mask>

<foreignObject x={0} y={0} width={size} height={size} mask={`url(#${maskId})`}>
<div
style={{
width: '100%',
height: '100%',
background: conicColorBg,
}}
/>
</foreignObject>
</>
);
});

if (process.env.NODE_ENV !== 'production') {
PtgCircle.displayName = 'PtgCircle';
}

export default PtgCircle;
Loading

0 comments on commit db4a823

Please sign in to comment.