ProgressBar
Animated progress bar with line and wavy variants. The wavy variant uses SVG Bezier curves for a smooth, organic wave effect.
9:41
signal_cellular_altwifibattery_full
Line Variant
Wavy Variant
Progress50%
Drag the slider to adjust progress. Both variants animate smoothly.
Installation
$
Usage
Example.tsx
import { ProgressBar } from './components/ProgressBar';export default function App() {const [progress, setProgress] = useState(50);return (<View style={{ padding: 16 }}>{/* Line variant */}<ProgressBar progress={progress} variant="line" />{/* Wavy variant */}<ProgressBarprogress={progress}variant="wavy"wavelength={25}amplitude={0.6}color="#cf350b"/></View>);}
Full Component Code
ProgressBar.tsx
1import React, { useEffect } from 'react';2import { LayoutChangeEvent, StyleSheet, View, ViewStyle } from 'react-native';3import Animated, {4 useAnimatedStyle,5 useSharedValue,6 withSpring,7} from 'react-native-reanimated';8import Svg, { Defs, Mask, Path, Rect } from 'react-native-svg';910export type ProgressBarVariant = 'line' | 'wavy';1112interface ProgressBarProps {13 progress: number;14 variant?: ProgressBarVariant;15 height?: number;16 color?: string;17 trackColor?: string;18 strokeWidth?: number;19 gapSize?: number;20 amplitude?: number;21 wavelength?: number;22 style?: ViewStyle;23}2425export const ProgressBar: React.FC<ProgressBarProps> = ({26 progress,27 variant = 'line',28 height: propHeight,29 color = '#cf350b',30 trackColor = '#E5E7EB',31 strokeWidth = 4,32 gapSize = 8,33 amplitude = 0.6,34 wavelength = 50,35 style,36}) => {37 const minWavyHeight = strokeWidth * 9;38 const height = propHeight ?? (variant === 'wavy' ? minWavyHeight : 8);39 const effectiveHeight = variant === 'wavy' ? Math.max(height, minWavyHeight) : height;4041 const [width, setWidth] = React.useState(0);42 const progressValue = useSharedValue(0);4344 useEffect(() => {45 progressValue.value = withSpring(Math.min(Math.max(progress, 0), 100), {46 damping: 15,47 stiffness: 100,48 });49 }, [progress]);5051 const onLayout = (event: LayoutChangeEvent) => {52 setWidth(event.nativeEvent.layout.width);53 };5455 if (variant === 'wavy') {56 const canvasHeight = effectiveHeight;57 return (58 <View onLayout={onLayout} style={[{ width: '100%', height: canvasHeight }, style]}>59 {width > 0 && (60 <WavyBar61 progress={progressValue}62 width={width}63 height={effectiveHeight}64 canvasHeight={canvasHeight}65 color={color}66 trackColor={trackColor}67 strokeWidth={strokeWidth}68 gapSize={gapSize}69 amplitude={amplitude}70 wavelength={wavelength}71 />72 )}73 </View>74 );75 }7677 const animatedStyle = useAnimatedStyle(() => ({78 width: `${progressValue.value}%`,79 }));8081 return (82 <View onLayout={onLayout} style={[styles.container, styles.rounded, { height: effectiveHeight, backgroundColor: trackColor }, style]}>83 <Animated.View style={[styles.fill, styles.rounded, { backgroundColor: color }, animatedStyle]} />84 </View>85 );86};8788// WavyBar component uses SVG with Bezier curves89const WavyBar = ({ /* props */ }) => {90 // See full implementation for SVG wave rendering91};9293const styles = StyleSheet.create({94 container: { width: '100%', overflow: 'hidden' },95 rounded: { borderRadius: 9999 },96 fill: { height: '100%' },97});9899export default ProgressBar;
Props
| Name | Type | Default | Description |
|---|---|---|---|
| progress | number | 0 | Progress value from 0 to 100 |
| variant | 'line' | 'wavy' | 'line' | Visual style of progress bar |
| height | number | 8 (line) / 36 (wavy) | Height of the progress bar |
| color | string | '#cf350b' | Fill color for progress |
| trackColor | string | '#E5E7EB' | Track background color |
| strokeWidth | number | 4 | Stroke width for wavy variant |
| gapSize | number | 8 | Gap between fill and track (wavy) |
| amplitude | number | 0.6 | Wave amplitude (0-1) |
| wavelength | number | 50 | Wave length in pixels |
