Animated Charts in React Native using D3 and ART

David Vacca
Oct 24, 2016 · 6 min read

A few months ago, React Native included D3 and ART libraries as part of its distribution but so far there is not much documentation about how to use them. In this post, I will share my learnings in creating different kinds of animated charts using only D3 and ART. I will try to keep it short, but I’m including links to references and source code for more information.

My example application looks like this:

Image for post
Image for post
Animated Pie and AreaSplines charts

If you just want to jump into the final code, you can access it from here: https://github.com/mdvacca/rn-d3-art-charts.

How D3 and ART works in React Native

Introduction

ART is a vector drawing API that knows how to render multiple vector outputs (canvas, svg, vml, etc). The react-native implementation of ART is not 100% complete yet (e.g. we can’t attach events to surfaces yet) but it can be used to generate powerful charts, like animated Pie charts, AreaSplines, Line, etc…

The full D3 API reference can be found here and the actual implementation of ART in react-native here.

Let’s render a Pie chart

First, you need to import ART and D3 libraries into our project:

npm install art —-save
npm install d3 —-save
npm install d3-shape —-save

If you are using iOS, you also need to link the ART library into your project (you can follow the steps described here), this is required because ART is part of react native but it is linked by default to improve performance of project not using ART.

2. Use D3 to generate coordinates of our Pie Chart:

To create a Pie chart, we are going to use the library d3-shape that provides a variety of shape generators to create simple and complex graphs (like symbols, arcs, lines, areas, rounded annular sectors and centripetal splines). Each shape generator exposes accessors that control how the input data is mapped to a visual representation. The output of the shape generator is the path attribute of a svg shape object.

To render a Pie chart, we will need two kind of shapes: Pie Generator and an Arc Generator:

The pie generator does not produce a shape directly, but instead computes the necessary angles to represent a tabular dataset as a pie or donut chart; later these angles can be passed to an arc generator to generate the svg path of the shape.

For example if we have a dataset like this:

// data represents the distribution of spendings in a month
data = [
{"number": 8, "name": 'Fun activities'},
{"number": 7, "name": 'Dog'},
{"number": 16, "name": 'Food'},
{"number": 23, "name": 'Car'},
{"number": 42, "name": 'Rent'},
{"number": 4, "name": 'Misc'},
];

We can create a Pie using the d3-shape pie generator like this:

var arcs = d3.shape.pie()
.value(this._value)
(this.props.data);

This code will generate the corresponding angles (startAngle and endAngle) for each section of the pie, for example:

[{“data”:
{“number”:8,
”name”:”Fun activities”},
”index”:3,
”value”:8,
startAngle”:5.0893800988154645,
endAngle”:5.592034923389831,
”padAngle”:0
},
....
{"data":{"number":4,"name":"Misc"},"index":5,"value":4,"startAngle":6.031857894892402,"endAngle":6.283185307179586,"padAngle":0}]

The format of the data returned by the pie generator matches the one expected by the d3-shape arc generator.

The arc generator will take each of the angles of the arcs and it will generate the svg path that will be used by ART to render the pie later. This shape also provides parameters to customize the rendering of the shape. For example we can use different outer radius to highlight one section of the Pie.

// calculate the path for the pie's arc number [i]
var path = d3.shape.arc()
.outerRadius(this.props.pieWidth/2) // Radius of the pie
.padAngle(.05) // Angle between sections
.innerRadius(30) // Inner radius: to create a donut or pie
(arcs[i]);

Using the example data for the first item of the array, this code will return just a svg path:

M-68.9646319937036,-29.476762610114324A75,75,0,0,1,-49.345310456503256,-56.48044206582762L-20.635195356782273,-21.775874553905552A30,30,0,0,0,-27.086713440010442,-12.896121704557451Z

3. Render the D3 output using ART:

In the Step 2 we learned how to generate each of the svg paths of the Pie, now we will use ART to render them.

Before rendering anything, we need to create the root Surface container (“kind of <canvas> of html5”) that is going to be used by ART to render the shapes, lines and points of our graph. Since the Pie contains several sections and we want to group them, we will use the Group ART element.

Finally, we need to render the sections of the Pie. For this we are going to use the Shape class, this class not only renders svg path (using the prop d), but it also provides properties to customize how the graph will look. For example, we can change the stroke property to define the color of the shape and strokeWidth to change the width of the lines. Let’s see how to render the path generated in Step 2:

<Surface width={this.props.width} height={this.props.height}>
<Group x={x} y={y}>
<Shape
d={"M-68.9646319937036,-29.476762610114324A75,75,0,0,1,-49.345310456503256,-56.48044206582762L-20.635195356782273,-21.775874553905552A30,30,0,0,0,-27.086713440010442,-12.896121704557451Z"}
stroke={"#2ca02c"} // green line
strokeWidth={3}
/>
</Group>
</Surface>

This code will render the first section of the pie:

Image for post
Image for post

We are almost there, we just need to render all the svg paths calculated in Step 2 and we will have our first Pie chart:

<Surface width={this.props.width} height={this.props.height}>
<Group x={x} y={y}>
{
// pieChart has all the svg paths calculated in step 2)
pieChart.paths.map( (item, index) =>
(<Shape
key={'pie_shape_' + index}
fill={_getcolor(index)}
stroke={_getcolor(index)}

d={item.path}
/>))
}
</Group>
</Surface>

This code will render:

Image for post
Image for post

You can take a look to the full code of my project to see how to render labels and highlight sections.

Note that ART exposes more classes: LinearGradient, RadialGradient, Pattern, Transform, Path, Surface, Group, ClippingRectangle, Shape and Text.

Animated Shapes

import Morph from 'art/morph/path';...// Start of the animation:
this.setState({
path: Morph.Tween(
pathFrom, // OLD SVG path
pathTo, // NEW SVG path
),...
// Each step of the animation:
delta = (timestamp - start) / AnimationDurationMs;
this.state.path.tween(delta);

With this approach we can animate one Shape element, but in this case, we want to add multiple animations that needs to run at the same time for different kind of Shapes. That’s why I extended the same concept of creating a component called AnimShape. This component is a wrapper on top of Shape that animates the change of the state of a Shape

AnimShape contains the same props as Shape, but the property d expects a function (instead of a string). This function will be used by AnimShape to create the path of the Shape.

Now we can include animations to our Pie chart just replacing Shape by AnimShape like this:

<Group x={x} y={y}>
{
this.props.data.map( (item, index) =>
(<AnimShape
key={'pie_shape_' + index}
color={this._color(index)}
createPath={ () => this._createPieChart(index) }
/>)
)
}

This approach make it very simple to add animations for any kind of shapes. It is also a way to centralize the management of the animations. On the other hand, this way “might” have some performance issues when rendering many shapes because it is rendering multiple independent animations at the same time.

I hope this helps anyone trying to render native charts into React Native using D3 and ART.

The React Native Log

All things React Native — tutorials, experiments, tips &…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store