TASIOMIND.DEV — OPERATIONAL▸▸▸FULL STACK DEVELOPER @ GWQ SERVICEPLUS AG▸▸▸FOUNDER — K8SGPT.AI▸▸▸OPEN SOURCE: ACTIVE▸▸▸DISTRIBUTED SYSTEMS / KUBERNETES / AI▸▸▸RUST + GO + PYTHON▸▸▸FIELD TESTED / STATUS — NOMINAL▸▸▸LOCATION: EUROPE/BERLIN▸▸▸TASIOMIND.DEV — OPERATIONAL▸▸▸FULL STACK DEVELOPER @ GWQ SERVICEPLUS AG▸▸▸FOUNDER — K8SGPT.AI▸▸▸OPEN SOURCE: ACTIVE▸▸▸DISTRIBUTED SYSTEMS / KUBERNETES / AI▸▸▸RUST + GO + PYTHON▸▸▸FIELD TESTED / STATUS — NOMINAL▸▸▸LOCATION: EUROPE/BERLIN▸▸▸

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-svg for that
  • Since SVG is not supported, it is also not supported by React Native Animated. So made the SVG a custom Animated component using createAnimatedComponent()
  • 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 &lt;Placeholder&gt; component can take
    • a shape prop, the two options are circle or rect
    • a size for circle
    • height and width for rect
    • loading: boolean to decide whether to animate the background color or not
    • Default values have been added as fallbacks (rx, height, width, loading, size, shape and fill)
  • I originally had height and width for circle as well, with size as a complimentary value. But i ended up getting rid of those since it is a circle.. Common usage will always have the same height and width