Animating SVG in React Native
November 22, 2019
So this started when i wanted to create placeholder components using SVG.. Here's the complete code:
code
// Placeholder.tsx
import React, { useEffect } from 'react'
import { Animated, View } from 'react-native'
import Svg, {
SvgProps,
Circle,
CircleProps,
Rect,
RectProps,
CommonPathProps
} from 'react-native-svg'
type Props = Readonly<{
loading?: boolean
shape?: 'circle' | 'rect'
size: number
height: number
width: number
}>
// set a value to be animated
const currentColor = new Animated.Value(0)
// interpolate color value
const changeColor = currentColor.interpolate({
inputRange: [0, 1, 2], // the values that the animation will transition from
outputRange: ['gainsboro', 'whitesmoke', 'gainsboro'] // values that are animating
})
// define the looping animation
const animateColor = ()=> {
Animated.loop(
Animated.sequence([
Animated.timing(currentColor, {
toValue: 2, // the value in interpolated output range that you want to go to
duration: 2000 // ms
}),
])
).start()
}
export default function Placeholder({
loading = true,
size = 50,
height = 50,
width = 50,
radius = 8,
shape = 'rect',
fill = 'gainsboro',
...rest
}: Props &
SvgProps &
CircleProps &
RectProps &
CommonPathProps &
any) {
useEffect(() => {
// start the animation to change background color
animateColor()
})
const AnimatedSvg = Animated.createAnimatedComponent(Svg)
return (
<View
loading={loading}
style={{margin: 8}}
{...rest}
>
{shape === 'circle' && (
<AnimatedSvg
size={size}
height={size}
width={size}
viewBox={`0 0 ${size * 2} ${size * 2}`}
fill={loading ? changeColor : fill}
>
<Circle
cx={size}
cy={size}
r={size}
/>
</AnimatedSvg>
)}
{shape === 'rect' && (
<AnimatedSvg
height={height}
width={width}
viewBox={`0 0 ${width} ${height}`}
fill={loading ? changeColor : fill}
>
<Rect
x="0"
y="0"
rx={radius}
width={width}
height={height}
/>
</AnimatedSvg>
)}
</View>
)
}
Referencing the component:
code
// App.tsx
import React from "react";
import { StyleSheet, View } from "react-native";
import Placeholder from "./Placeholder";
export default function App() {
return (
<View style={styles.container}>
<Placeholder />
<Placeholder loading={false} />
<Placeholder shape="circle" size={150} />
<Placeholder shape="rect" width={200} />
<Placeholder loading={false} shape="circle" size={100} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
});
Notes
- SVG is not supported in react Native, using
react-native-svgfor that - Since SVG is not supported, it is also not supported by React Native
Animated. So made the SVG a custom Animated component usingcreateAnimatedComponent() - For example, to animate an SVG path, you'd make it an Animated component like so:
code
import { Path } from "react-native-svg";
const AnimatedPath = Animated.createAnimatedComponent(Path);
- The prop types are there because i'm using Typescript
- The
<Placeholder>component can take- a
shapeprop, the two options arecircleorrect - a
sizeforcircle heightandwidthforrectloading: booleanto decide whether to animate the background color or not- Default values have been added as fallbacks (
rx,height,width,loading,size,shapeandfill)
- a
- I originally had
heightandwidthforcircleas well, withsizeas a complimentary value. But i ended up getting rid of those since it is a circle.. Common usage will always have the sameheightandwidth