Pie animation in React Native using SVG

Ori Harel
4 min readApr 25, 2018

--

SVG is the the most adopted markup language for vector graphics out there, mostly thanks to its vast support of web development. The interoperability within web browsers as well as the ease of debugging it simply via your favorite dev tools just made it second to none when it comes to the creation of beautiful graphics.

It’s only natural that web developers, when approaching React Native, will look for the same set of tools when trying to implement their designer’s wild ideas, which most certainly will come in SVG form.

React Native is delivered with a built-in SVG-like library, called ART. What it does is basically map React components to native classes in iOS and Android that draw on screen using the platform components (Core Graphics in iOS and android.graphics on Android). However I’d like to recommend using a great package called react-native-svg which is both available in Expo and hosted in react-native-community. The advantages using react-native-svg are:

  1. Writing SVG exactly as you would for web (same syntax)
  2. Support of touch events for every SVG element (Component)

So without further a due, let’s dive into how to implement a nice pie chart and eventually we’ll also animate it using React Native Animated library. Here’s what the end result will look like:

App.js

import SVG, {G} from 'react-native-svg';
...
import Slice from "./Slice";
const AnimatedSlice = Animated.createAnimatedComponent(Slice);
const demoData = [
{
number: 60,
color: '#0d2f51'
},
{
number: 20,
color: '#28BD8B'
},
{
number: 20,
color: '#F66A6A'
}
];
export default class App extends Component<Props> {

constructor(props) {
super(props);
this.state = {
animValue: new Animated.Value(0.1),
};

}

resetPie = ()=>{
this.state.animValue.setValue(0.1);
};

animate = ()=>{

Animated.timing(
this.state.animValue,
{
toValue: 2,
duration: 500,
easing: Easing.inOut(Easing.quad)
}
).start(()=>{
setTimeout(this.resetPie, 2000);
});
};

render() {
let endAngle = Animated.multiply(this.state.animValue, Math.PI);
return (
<View style={styles.container}>
<Svg
width={200}
style={styles.pieSVG}
height={200}
viewBox={`-100 -100 200 200`}
>
<G>
{
demoData.map( (item, index) =>{
return (
<AnimatedSlice
index={index}
endAngle={endAngle}
color={item.color}
data={demoData}
key={'pie_shape_' + index}
/>
)
})
}
</G>
</Svg>
<View style={{marginTop: 20}}>
<Button onPress={this.animate}/>
</View>

</View>
);
}
}

In App.js, we’re taking sample data (demoData) and drawing it onto a pie. To do so we’re using few react-native-svg components (SVG, G and Path). The pie will be constructed out of path elements (a path element for each pie slice). We wrap the <Path> component in a custom element, called Slice and creating an animated component out of it, using createAnimatedComponent.

Here’s the code:

Slice.js

import React, {Component} from 'react';
import {Path} from 'react-native-svg';
import * as shape from 'd3-shape';
const d3 = {shape};

export default class Slice extends Component {
constructor(props) {
super(props);
this.state = {};
this.arcGenerator = d3.shape.arc()
.outerRadius(100)
.padAngle(0)
.innerRadius(0);
}

createPieArc = (index, endAngle, data) => {

const arcs = d3.shape.pie()
.value((item)=>item.number)
.startAngle(0)
.endAngle(endAngle)
(data);

let arcData = arcs[index];

return this.arcGenerator(arcData);
};


render() {

const {
endAngle,
color,
index,
data
} = this.props;
let val = data[index].number;

return (
<Path
onPress={()=>alert('value is: '+val)}
d={this.createPieArc(index, endAngle, data)}
fill={color}
/>
)

}
}

You can see, that in order to calculate a pie slice, we are using the d3 library, which as of V4 became usable for React Native and other non-browser environments (e.g., the new “d3-shape” module).

The animation of the pie will be done by simply drawing it from angle 0 all the way to angle 360 in a nice animated way. Now, we’d like to use React Native’s Animated library as much as possible as it’s the official and documented way of doing animations in RN.

Ideally, we would have just use the <Path> element and provide it with a d property, pointing to an Animated.Value. However, this will not work as the way Path.js is implemented in react-native-svg makes it impossible for values to be parsed from Animated.Value to primitive.

So this is why we wrapped the <Path> component with our <Slice> component, and by creating an animated component out of it, we are now able to pass down an animated value.

Using this technique, one can easily animate any shape, making designers craziest ideas come true!

--

--

Ori Harel

Engineering Manager, love startups, love NBA Basketball but mostly procrastinate. Work @ Taranis