diff --git a/README.md b/README.md index 54e972d..2e8b162 100644 --- a/README.md +++ b/README.md @@ -9,21 +9,6 @@ React Native component for creating natively animated, circular progress wheel. ![image](preview.gif) -## Why use this component - -This implementation is 100% JS, meaning you do not need to use any additional libraries such as 'react-native-svg' and you don't need to do any linking. 😱 -This component also sets `useNativeDriver: true`, meaning that all animation is done smoothly on the native side.💖 - -This package is also SUPER-LIGHTWEIGHT. - -Let's compare: - -react-native-progress-wheel: Unpacked size: 8.74 kB (this library) - -react-native-circular-progress: Unpacked size: 3.38 MB (other popular library) - -![image](performance_compare.gif) - ## Installation yarn add react-native-progress-wheel @@ -52,6 +37,23 @@ The following example will animate from 0% to 45% at a duration of 3 seconds. /> ``` + +The progress wheel can display progess as titles in the center of the circle. + +![image](titles.gif) + +```js + +``` + The progress wheel can be updated using state variables. ```js @@ -72,15 +74,38 @@ size | number | 200 | Width a width | number | 25 | Thickness of the progress line color | string | white | Color of the progress line backgroundColor | string | gray | Color of the background progress line -progress | number (0, 100) | 0 | Angle from which the progress starts from -duration | number | 600 | Duration at which to animate the progress. +progress | number | 0 | Angle from which the progress starts from +max | number | 100 | Max value for the progress wheel +rotation | string | 0deg | Set starting angle of progress +duration | number | 600 | Duration at which to animate the progress rounded | boolean | false | Rounds edges -animateFromValue | number (0, 100) | -1 | Starting value to animate to progres when component is mounted +animateFromValue | number | -1 | Starting value to animate to progres when component is mounted containerColor | string | null | Container color delay | number | 0 | Delay for animation easing | EasingFunction | null | Easing for animation +showProgressLabel | boolean | false | Show the progress as text in the circle +labelStyle | TextStyle | {} | Style object for progress label +subtitle | string | null | Text displayed directly below progress label +subtitleStyle | TextStyle | {} | Style object for subtitle +showPercentageSymbol| boolean | false | Show the progress as a percentage onAnimationComplete | function | null | Called when animation finishes + +## Why use this component + +This implementation is 100% base react-native, meaning you do not need to use any additional libraries such as 'react-native-svg' or 'react-native-reanimated'. +This component also sets `useNativeDriver: true`, meaning that all animation is done smoothly on the native side.💖 + +This package is also SUPER-LIGHTWEIGHT. + +Let's compare: + +react-native-progress-wheel: Unpacked size: 8.74 kB (this library) + +react-native-circular-progress: Unpacked size: 3.38 MB (other popular library) + +![image](performance_compare.gif) + ## FAQ Q: Does it work in Expo? A: Yes it does. diff --git a/ReactNativeProgressWheel/AnimatedProgressWheel/index.tsx b/ReactNativeProgressWheel/AnimatedProgressWheel/index.tsx index 5ef3939..039baf5 100644 --- a/ReactNativeProgressWheel/AnimatedProgressWheel/index.tsx +++ b/ReactNativeProgressWheel/AnimatedProgressWheel/index.tsx @@ -1,5 +1,13 @@ -import React, {Fragment, useEffect, useMemo} from 'react'; -import {Animated, EasingFunction, StyleSheet, View} from 'react-native'; +import React, {Fragment, useEffect, useMemo, useState} from 'react'; +import { + Animated, + EasingFunction, + StyleSheet, + Text, + TextStyle, + View, + ViewStyle, +} from 'react-native'; interface Props { color: string; @@ -11,7 +19,14 @@ interface Props { animateFromValue?: number; containerColor?: string; rounded?: boolean; + showProgressLabel?: boolean; + showPercentageSymbol?: boolean; delay?: number; + max?: number; + subtitle?: string; + rotation?: string; + labelStyle?: ViewStyle | TextStyle; + subtitleStyle?: ViewStyle | TextStyle; easing?: EasingFunction; onAnimationComplete?: (status: any) => void; } @@ -24,14 +39,33 @@ const AnimatedProgressWheel = ({ size, width, color = 'white', + rotation = '0deg', + subtitle = '', delay = 0, + max = 100, rounded = false, + showProgressLabel = false, + showPercentageSymbol = false, backgroundColor = 'grey', + labelStyle = {}, + subtitleStyle = {}, easing, containerColor, }: Props) => { + const [labelValue, setLabelValue] = useState(0); const animatedVal = useMemo(() => new Animated.Value(0), []); + useEffect(() => { + if (showProgressLabel) { + animatedVal.addListener(({value}) => + setLabelValue(Math.floor((value / 100) * max)), + ); + } + return () => { + animatedVal.removeAllListeners(); + }; + }, [animatedVal, showProgressLabel, max]); + const styles = useMemo( () => generateStyles({ @@ -48,8 +82,8 @@ const AnimatedProgressWheel = ({ if (animateFromValue >= 0) { animatedVal.setValue(animateFromValue); } - animateTo(progress); - }, [animateFromValue, progress]); + animateTo((progress / max) * 100); + }, [animateFromValue, progress, max]); const interpolateAnimVal = ( inputRange: number[], @@ -144,7 +178,24 @@ const AnimatedProgressWheel = ({ ); - return {renderLoader()}; + return ( + + {renderLoader()} + {showProgressLabel && ( + + {labelValue !== null && ( + + {labelValue} + {showPercentageSymbol ? '%' : ''} + + )} + {!!subtitle && ( + {subtitle} + )} + + )} + + ); }; const generateStyles = ({ @@ -216,6 +267,18 @@ const generateStyles = ({ width: size, height: size, }, + labelContainer: { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + alignItems: 'center', + justifyContent: 'center', + }, + label: { + fontSize: 20, + }, }); export default AnimatedProgressWheel; diff --git a/ReactNativeProgressWheel/App.tsx b/ReactNativeProgressWheel/App.tsx index 6805096..ecf98ac 100644 --- a/ReactNativeProgressWheel/App.tsx +++ b/ReactNativeProgressWheel/App.tsx @@ -3,37 +3,26 @@ import {StyleSheet, View} from 'react-native'; import AnimatedProgressWheel from './AnimatedProgressWheel'; function App(): JSX.Element { - const size = 300; - const progress = 100; + const size = 240; + const progress = 36; const duration = 2500; - const width = 24; + const width = 10; const rounded = true; + const color = '#49ccf9'; + const backgroundColor = '#000079'; return ( - - - - - - ); } @@ -49,6 +38,15 @@ const styles = StyleSheet.create({ absolute: { position: 'absolute', }, + progressLabel: { + color: '#49ccf9', + fontWeight: 'bold', + fontSize: 56, + }, + subtitle: { + fontSize: 14, + color: '#49ccf9', + }, }); export default App; diff --git a/npm/README.md b/npm/README.md index 54e972d..2e8b162 100644 --- a/npm/README.md +++ b/npm/README.md @@ -9,21 +9,6 @@ React Native component for creating natively animated, circular progress wheel. ![image](preview.gif) -## Why use this component - -This implementation is 100% JS, meaning you do not need to use any additional libraries such as 'react-native-svg' and you don't need to do any linking. 😱 -This component also sets `useNativeDriver: true`, meaning that all animation is done smoothly on the native side.💖 - -This package is also SUPER-LIGHTWEIGHT. - -Let's compare: - -react-native-progress-wheel: Unpacked size: 8.74 kB (this library) - -react-native-circular-progress: Unpacked size: 3.38 MB (other popular library) - -![image](performance_compare.gif) - ## Installation yarn add react-native-progress-wheel @@ -52,6 +37,23 @@ The following example will animate from 0% to 45% at a duration of 3 seconds. /> ``` + +The progress wheel can display progess as titles in the center of the circle. + +![image](titles.gif) + +```js + +``` + The progress wheel can be updated using state variables. ```js @@ -72,15 +74,38 @@ size | number | 200 | Width a width | number | 25 | Thickness of the progress line color | string | white | Color of the progress line backgroundColor | string | gray | Color of the background progress line -progress | number (0, 100) | 0 | Angle from which the progress starts from -duration | number | 600 | Duration at which to animate the progress. +progress | number | 0 | Angle from which the progress starts from +max | number | 100 | Max value for the progress wheel +rotation | string | 0deg | Set starting angle of progress +duration | number | 600 | Duration at which to animate the progress rounded | boolean | false | Rounds edges -animateFromValue | number (0, 100) | -1 | Starting value to animate to progres when component is mounted +animateFromValue | number | -1 | Starting value to animate to progres when component is mounted containerColor | string | null | Container color delay | number | 0 | Delay for animation easing | EasingFunction | null | Easing for animation +showProgressLabel | boolean | false | Show the progress as text in the circle +labelStyle | TextStyle | {} | Style object for progress label +subtitle | string | null | Text displayed directly below progress label +subtitleStyle | TextStyle | {} | Style object for subtitle +showPercentageSymbol| boolean | false | Show the progress as a percentage onAnimationComplete | function | null | Called when animation finishes + +## Why use this component + +This implementation is 100% base react-native, meaning you do not need to use any additional libraries such as 'react-native-svg' or 'react-native-reanimated'. +This component also sets `useNativeDriver: true`, meaning that all animation is done smoothly on the native side.💖 + +This package is also SUPER-LIGHTWEIGHT. + +Let's compare: + +react-native-progress-wheel: Unpacked size: 8.74 kB (this library) + +react-native-circular-progress: Unpacked size: 3.38 MB (other popular library) + +![image](performance_compare.gif) + ## FAQ Q: Does it work in Expo? A: Yes it does. diff --git a/npm/dist/index.d.ts b/npm/dist/index.d.ts index 163def1..a9ec236 100644 --- a/npm/dist/index.d.ts +++ b/npm/dist/index.d.ts @@ -1,5 +1,5 @@ import React from 'react'; -import { EasingFunction } from 'react-native'; +import { EasingFunction, TextStyle, ViewStyle } from 'react-native'; interface Props { color: string; backgroundColor: string; @@ -7,12 +7,19 @@ interface Props { width: number; progress?: number; duration?: number; - rounded?: boolean; animateFromValue?: number; containerColor?: string; + rounded?: boolean; + showProgressLabel?: boolean; + showPercentageSymbol?: boolean; delay?: number; + max?: number; + subtitle?: string; + rotation?: string; + labelStyle?: ViewStyle | TextStyle; + subtitleStyle?: ViewStyle | TextStyle; easing?: EasingFunction; onAnimationComplete?: (status: any) => void; } -declare const AnimatedProgressWheel: ({ animateFromValue, progress, duration, onAnimationComplete, size, width, color, delay, rounded, backgroundColor, easing, containerColor, }: Props) => React.JSX.Element; +declare const AnimatedProgressWheel: ({ animateFromValue, progress, duration, onAnimationComplete, size, width, color, rotation, subtitle, delay, max, rounded, showProgressLabel, showPercentageSymbol, backgroundColor, labelStyle, subtitleStyle, easing, containerColor, }: Props) => React.JSX.Element; export default AnimatedProgressWheel; diff --git a/npm/dist/index.js b/npm/dist/index.js index 27faa04..ac381ef 100644 --- a/npm/dist/index.js +++ b/npm/dist/index.js @@ -26,8 +26,20 @@ Object.defineProperty(exports, "__esModule", { value: true }); var react_1 = __importStar(require("react")); var react_native_1 = require("react-native"); var AnimatedProgressWheel = function (_a) { - var _b = _a.animateFromValue, animateFromValue = _b === void 0 ? -1 : _b, _c = _a.progress, progress = _c === void 0 ? 75 : _c, _d = _a.duration, duration = _d === void 0 ? 600 : _d, onAnimationComplete = _a.onAnimationComplete, size = _a.size, width = _a.width, _e = _a.color, color = _e === void 0 ? 'white' : _e, _f = _a.delay, delay = _f === void 0 ? 0 : _f, _g = _a.rounded, rounded = _g === void 0 ? false : _g, _h = _a.backgroundColor, backgroundColor = _h === void 0 ? 'grey' : _h, easing = _a.easing, containerColor = _a.containerColor; + var _b = _a.animateFromValue, animateFromValue = _b === void 0 ? -1 : _b, _c = _a.progress, progress = _c === void 0 ? 75 : _c, _d = _a.duration, duration = _d === void 0 ? 600 : _d, onAnimationComplete = _a.onAnimationComplete, size = _a.size, width = _a.width, _e = _a.color, color = _e === void 0 ? 'white' : _e, _f = _a.rotation, rotation = _f === void 0 ? '0deg' : _f, _g = _a.subtitle, subtitle = _g === void 0 ? '' : _g, _h = _a.delay, delay = _h === void 0 ? 0 : _h, _j = _a.max, max = _j === void 0 ? 100 : _j, _k = _a.rounded, rounded = _k === void 0 ? false : _k, _l = _a.showProgressLabel, showProgressLabel = _l === void 0 ? false : _l, _m = _a.showPercentageSymbol, showPercentageSymbol = _m === void 0 ? false : _m, _o = _a.backgroundColor, backgroundColor = _o === void 0 ? 'grey' : _o, _p = _a.labelStyle, labelStyle = _p === void 0 ? {} : _p, _q = _a.subtitleStyle, subtitleStyle = _q === void 0 ? {} : _q, easing = _a.easing, containerColor = _a.containerColor; + var _r = (0, react_1.useState)(0), labelValue = _r[0], setLabelValue = _r[1]; var animatedVal = (0, react_1.useMemo)(function () { return new react_native_1.Animated.Value(0); }, []); + (0, react_1.useEffect)(function () { + if (showProgressLabel) { + animatedVal.addListener(function (_a) { + var value = _a.value; + return setLabelValue(Math.floor((value / 100) * max)); + }); + } + return function () { + animatedVal.removeAllListeners(); + }; + }, [animatedVal, showProgressLabel, max]); var styles = (0, react_1.useMemo)(function () { return generateStyles({ size: size, @@ -41,8 +53,8 @@ var AnimatedProgressWheel = function (_a) { if (animateFromValue >= 0) { animatedVal.setValue(animateFromValue); } - animateTo(progress); - }, [animateFromValue, progress]); + animateTo((progress / max) * 100); + }, [animateFromValue, progress, max]); var interpolateAnimVal = function (inputRange, outputRange) { return animatedVal.interpolate({ inputRange: inputRange, @@ -117,7 +129,16 @@ var AnimatedProgressWheel = function (_a) { )} ); }; - return {renderLoader()}; + return ( + {renderLoader()} + {showProgressLabel && ( + {labelValue !== null && ( + {labelValue} + {showPercentageSymbol ? '%' : ''} + )} + {!!subtitle && ({subtitle})} + )} + ); }; var generateStyles = function (_a) { var size = _a.size, width = _a.width, color = _a.color, backgroundColor = _a.backgroundColor, containerColor = _a.containerColor; @@ -183,6 +204,18 @@ var generateStyles = function (_a) { width: size, height: size, }, + labelContainer: { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + alignItems: 'center', + justifyContent: 'center', + }, + label: { + fontSize: 20, + }, }); }; exports.default = AnimatedProgressWheel; diff --git a/npm/package.json b/npm/package.json index 647b27e..3585383 100644 --- a/npm/package.json +++ b/npm/package.json @@ -1,6 +1,6 @@ { "name": "react-native-progress-wheel", - "version": "2.0.3", + "version": "2.1.0", "description": "A better way to show animated progress in a circle.", "main": "dist/index.js", "scripts": { diff --git a/titles.gif b/titles.gif new file mode 100644 index 0000000..51fc94b Binary files /dev/null and b/titles.gif differ