Skip to content

Commit

Permalink
fix: remove progress circle padding
Browse files Browse the repository at this point in the history
- fixed padding issue in progress indicator #53
- added new prop to disable font scaling #54
  • Loading branch information
nithinpp69 committed Apr 18, 2022
1 parent 5814516 commit 71437a2
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 22 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# react-native-circular-progress-indicator

[![license](https://img.shields.io/github/license/mashape/apistatus.svg)]()
![platforms](https://img.shields.io/badge/platforms-Android%20%7C%20iOS%20%7C%20Web-brightgreen.svg?style=flat&colorB=191A17)
[![Version](https://img.shields.io/npm/v/react-native-circular-progress-indicator.svg)](https://www.npmjs.com/package/react-native-circular-progress-indicator)
[![npm](https://img.shields.io/npm/dt/react-native-circular-progress-indicator.svg)](https://www.npmjs.com/package/react-native-circular-progress-indicator)
[![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=for-the-badge)]()
![platforms](https://img.shields.io/badge/platforms-Android%20%7C%20iOS%20%7C%20Web-brightgreen.svg?style=for-the-badge&colorB=191A17)
[![Version](https://img.shields.io/npm/v/react-native-circular-progress-indicator.svg?style=for-the-badge)](https://www.npmjs.com/package/react-native-circular-progress-indicator)
[![npm](https://img.shields.io/npm/dt/react-native-circular-progress-indicator.svg?style=for-the-badge)](https://www.npmjs.com/package/react-native-circular-progress-indicator)

A simple and customizable React Native circular progress indicator component.

Expand Down Expand Up @@ -310,6 +310,7 @@ Make sure to mark this function as a worklet function. Read more about worklets
| showProgressValue | show or hide the progress text value | Bool | true | false |
| clockwise | show ring progress clockwise or anti-clockwise. pass false to enable anti clock-wise | Bool | true | false |
| progressFormatter | function to format the progress value. Make sure to define it as a worklet function. | Function | (v)=> Math.round(v) | false |
| allowFontScaling | specifies whether fonts should scale to respect Text Size accessibility settings. | Bool | true | false |

## License
This project is licenced under the MIT License.
6 changes: 6 additions & 0 deletions src/circularProgress/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const CircularProgress: React.FC<CircularProgressProps> = ({

return Math.round(v);
},
allowFontScaling = true,
}: CircularProgressProps) => {
const {
animatedCircleProps,
Expand All @@ -60,6 +61,8 @@ const CircularProgress: React.FC<CircularProgressProps> = ({
value,
duration,
onAnimationComplete,
activeStrokeWidth,
inActiveStrokeWidth,
valuePrefix,
progressFormatter,
valueSuffix,
Expand Down Expand Up @@ -148,12 +151,14 @@ const CircularProgress: React.FC<CircularProgressProps> = ({
styles(styleProps).fromProps,
]}
animatedProps={animatedTextProps}
allowFontScaling={allowFontScaling}
/>
)}
{title && title !== '' ? (
<Text
style={[styles(styleProps).title, titleStyle]}
numberOfLines={1}
allowFontScaling={allowFontScaling}
>
{title}
</Text>
Expand All @@ -166,6 +171,7 @@ const CircularProgress: React.FC<CircularProgressProps> = ({
subtitleStyle,
]}
numberOfLines={1}
allowFontScaling={allowFontScaling}
>
{subtitle}
</Text>
Expand Down
4 changes: 4 additions & 0 deletions src/circularProgress/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ export interface CircularProgressProps {
* https://docs.swmansion.com/react-native-reanimated/docs/2.2.0/worklets/
*/
progressFormatter?: (v: number) => number | string;
/**
* specifies whether fonts should scale to respect Text Size accessibility settings.
*/
allowFontScaling?: boolean;
}
declare const CircularProgress: React.FC<CircularProgressProps>;
export default CircularProgress;
Expand Down
2 changes: 2 additions & 0 deletions src/circularProgressBase/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const CircularProgressBase: React.FC<CircularProgressBaseProps> = ({
value,
duration,
onAnimationComplete,
activeStrokeWidth,
inActiveStrokeWidth,
});

const styleProps = useMemo(
Expand Down
21 changes: 21 additions & 0 deletions src/components/circleGradient/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { Defs, LinearGradient, Stop } from 'react-native-svg';
import { CircleGradientProps } from './types';

const CircleGradient: React.FC<CircleGradientProps> = ({
activeStrokeSecondaryColor,
activeStrokeColor,
}) => {
if (activeStrokeSecondaryColor)
return (
<Defs>
<LinearGradient id="grad" x1="0%" y1="0%" x2="0%" y2="100%">
<Stop offset="0%" stopColor={activeStrokeSecondaryColor} />
<Stop offset="100%" stopColor={activeStrokeColor} />
</LinearGradient>
</Defs>
);
return null;
};

export default React.memo(CircleGradient);
15 changes: 15 additions & 0 deletions src/components/circleGradient/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

export interface CircleGradientProps {
/**
* active progress circle color
*/
activeStrokeColor?: string;
/**
* active progress secondary color. Use this to provide a gradient effect
*/
activeStrokeSecondaryColor?: string | null;
}
declare const CircleGradient: React.FC<CircleGradientProps>;
export default CircleGradient;
// # sourceMappingURL=index.d.ts.map
32 changes: 19 additions & 13 deletions src/components/progressCircle/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { useMemo } from 'react';
import Svg, { G, Circle, Defs, LinearGradient, Stop } from 'react-native-svg';
import Svg, { G, Circle } from 'react-native-svg';
import Animated from 'react-native-reanimated';
import useCircleValues from '../../hooks/useCircleValues';
import COLORS from '../../utils/colors';
import CircleGradient from '../circleGradient';
import styles from './styles';
import { ProgressCircleProps } from './types';

Expand All @@ -21,9 +23,17 @@ const ProgressCircle: React.FC<ProgressCircleProps> = ({
}: ProgressCircleProps) => {
const viewBox = useMemo(
() => radius + Math.max(activeStrokeWidth, inActiveStrokeWidth),
[radius, activeStrokeWidth, inActiveStrokeWidth],
[radius, activeStrokeWidth, inActiveStrokeWidth]
);
const circleCircumference = useMemo(() => 2 * Math.PI * radius, [radius]);
const {
inactiveCircleRadius,
activeCircleRadius,
circleCircumference,
} = useCircleValues({
radius,
activeStrokeWidth,
inActiveStrokeWidth,
});

return (
<Svg
Expand All @@ -32,21 +42,17 @@ const ProgressCircle: React.FC<ProgressCircleProps> = ({
viewBox={`0 0 ${viewBox * 2} ${viewBox * 2}`}
style={styles.svg}
>
{activeStrokeSecondaryColor ? (
<Defs>
<LinearGradient id="grad" x1="0%" y1="0%" x2="0%" y2="100%">
<Stop offset="0%" stopColor={activeStrokeSecondaryColor} />
<Stop offset="100%" stopColor={activeStrokeColor} />
</LinearGradient>
</Defs>
) : null}
<CircleGradient
activeStrokeColor={activeStrokeColor}
activeStrokeSecondaryColor={activeStrokeSecondaryColor}
/>
<G origin={`${viewBox}, ${viewBox}`}>
<Circle
cx="50%"
cy="50%"
stroke={inActiveStrokeColor}
strokeWidth={inActiveStrokeWidth}
r={radius}
r={inactiveCircleRadius}
fill={circleBackgroundColor}
strokeOpacity={inActiveStrokeOpacity}
/>
Expand All @@ -55,7 +61,7 @@ const ProgressCircle: React.FC<ProgressCircleProps> = ({
cy="50%"
stroke={activeStrokeSecondaryColor ? 'url(#grad)' : activeStrokeColor}
strokeWidth={activeStrokeWidth}
r={radius}
r={activeCircleRadius}
fill={COLORS.TRANSPARENT}
strokeDasharray={circleCircumference}
strokeLinecap={strokeLinecap}
Expand Down
19 changes: 14 additions & 5 deletions src/hooks/useAnimatedValue.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo } from 'react';
import { useEffect } from 'react';
import {
Easing,
runOnJS,
Expand All @@ -8,6 +8,7 @@ import {
withDelay,
withTiming,
} from 'react-native-reanimated';
import useCircleValues from './useCircleValues';

export interface UseAnimatedValueProps {
value: number;
Expand All @@ -17,6 +18,8 @@ export interface UseAnimatedValueProps {
delay?: number;
maxValue?: number;
onAnimationComplete?: () => void;
activeStrokeWidth?: number;
inActiveStrokeWidth?: number;
clockwise?: boolean;
valueSuffix?: string;
valuePrefix?: string;
Expand All @@ -32,6 +35,8 @@ export default function useAnimatedValue({
value,
duration,
onAnimationComplete = () => null,
activeStrokeWidth = 10,
inActiveStrokeWidth = 10,
valuePrefix = '',
progressFormatter = (v: number) => {
'worklet';
Expand All @@ -41,7 +46,11 @@ export default function useAnimatedValue({
valueSuffix = '',
}: UseAnimatedValueProps) {
const animatedValue = useSharedValue(initialValue);
const circleCircumference = useMemo(() => 2 * Math.PI * radius, [radius]);
const { circleCircumference } = useCircleValues({
radius,
activeStrokeWidth,
inActiveStrokeWidth,
});

const animatedCircleProps = useAnimatedProps(() => {
let biggestValue = Math.max(initialValue, maxValue);
Expand All @@ -58,17 +67,17 @@ export default function useAnimatedValue({
useEffect(() => {
animatedValue.value = withDelay(
delay,
withTiming(value, { duration, easing: Easing.linear }, isFinished => {
withTiming(value, { duration, easing: Easing.linear }, (isFinished) => {
if (isFinished) {
runOnJS(onAnimationComplete)?.();
}
}),
})
);
}, [value]);

const progressValue = useDerivedValue(() => {
return `${valuePrefix}${progressFormatter(
animatedValue.value,
animatedValue.value
)}${valueSuffix}`;
});

Expand Down
52 changes: 52 additions & 0 deletions src/hooks/useCircleValues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useCallback, useMemo } from 'react';

export interface UseCircleValuesProps {
radius?: number;
activeStrokeWidth?: number;
inActiveStrokeWidth?: number;
}

export default function useCircleValues({
radius = 60,
activeStrokeWidth = 10,
inActiveStrokeWidth = 10,
}: UseCircleValuesProps) {
const isSameStrokeWidth = useMemo(
() => activeStrokeWidth === inActiveStrokeWidth,
[activeStrokeWidth, inActiveStrokeWidth]
);

const isActiveStrokeBigger = useMemo(() => {
return activeStrokeWidth > inActiveStrokeWidth;
}, [activeStrokeWidth, inActiveStrokeWidth]);

const findRadius = useCallback(() => {
if (isSameStrokeWidth) {
return radius + inActiveStrokeWidth / 2;
}
if (isActiveStrokeBigger) {
return radius + activeStrokeWidth / 2;
}
return radius + inActiveStrokeWidth / 2;
}, [
isSameStrokeWidth,
isActiveStrokeBigger,
radius,
inActiveStrokeWidth,
activeStrokeWidth,
]);

const inactiveCircleRadius = useMemo(() => findRadius(), [findRadius]);

const activeCircleRadius = useMemo(() => findRadius(), [findRadius]);

const circleCircumference = useMemo(() => 2 * Math.PI * activeCircleRadius, [
activeCircleRadius,
]);

return {
inactiveCircleRadius,
activeCircleRadius,
circleCircumference,
};
}

0 comments on commit 71437a2

Please sign in to comment.