D3 + React Intro #4 | Animated Cryptocurrency Line + Area Chart
As part 4 of the D3 intro I wanted to get into some line charting with animations because, in my experience, it is something a lot of front-end developers are interested in doing, especially when you are working with financial data or crypto data.
Unfortunately, D3 can be intimidating for new developers and the fact that there are a lot of examples floating out there using deprecated versions of D3 doesn’t help people who are trying to learn the library. In addition, while the documentation on GitHub is extensive it lacks examples, which forces you to search the web for code snippets to copy and play around with. This can be tedious, especially in light of the fact that you run into deprecated examples quite a bit.
But its all good! The whole point of doing this series is to help you guys/girls out by combining explanations with actual working code you can jump into. (Shoutout to the devs at CodeSandbox.io for building arguably the best browser-based editor experience.)
Ok let’s get into it!… ps: link to full editor example is at the bottom of this post.
We’ll start off by importing React and D3, as well as writing out our constructor and setting our state to contain a property called data. I’m using a stateful component here because I’m going to be importing live data for the chart.
Next, let’s import some random cryptocurrency data that we can use as the basic for our chart. As is normally recommended, you’ll want to write your ajax http request in the ‘componentDidMount()’ lifecycle method. This will allow you to call the setState() method to build your chart once the data is retrieved.
Note: I’m pulling in Bitcoin data from Cryptocompare’s public Api. You can find the documentation here.
In case you’re confused why I’m using ‘res.Data’, check out the object that the http request returns:
As you can see, our actual array of data is contained in the “Data” property, so we’ll need to extract it.
Now that we have our data, we have to think about how to translate it into our SVG and figure out how to draw a line that traces the data with respect to the window size that we want. This is where D3’s ‘linearScale()’ function comes in.
linearScale() takes in a ‘domain’ and a ‘range’ and what it does is very straightforward. You could even build your own scaling function quickly, but I prefer to use D3’s because its intuitive once you understand it.
linearScale().domain()
The domain basically asks you “Hey man, what is the minimum and maximum value that I should expect from your dataset?”. So, in order to make the domain function happy we do just that (PS: I coded the process of finding the min/max in detail. If you want to minimize your code lines, check out d3.extent(). You pass your data and it automagically creates an array with the min and max values for you!)
linearScale().range()
Now here is where the real magic happens. This function basically asks “Ok cool, in the domain you told me what the input data looks like. Now I need to know how big your SVG/display element is so that I can take the input data and give you its relative value with respect to the size of that element!”.
Here’s a simple mental example:
Let’s say you have 5 data points in an array ([1,2,3,4,5]) that you want to use for your X-Axis on your chart. You decided to make the chart 300px wide, so now you need to translate your array to the corresponding pixels on your screen. That’s where linearScale() comes in. It will say “Ok cool, your width is 300px, so the value of 1 should be located at (300px / 5) * 1 = 60px and the value of 2 should be at (300px/5) * 2 = 120px and so on … ”
Ok let’s move on…
If you know how SVG’s work you know that a line is created via a <path> element. And this <path> element requires an attribute “d”. This attribute is the actual combination of coordinates that the SVG will then use to connect with lines. It’s kinda cool.
If this sounds confusing, look at some examples of how SVG lines work here.
D3, because its awesome, comes with out-of-the-box functions to create the mumbo-jumbo looking coordinate combination that you will pass into the <path> element to draw a line.
The code above may look a little confusing at first but here’s how it goes:
(Note: we will have to invoke our created line variable with our data and pass that to the path element like so: <path d={line(this.state.data)} />. This is helpful in understanding the code above, because now you know that it will have access to our crypto data we are storing in our state).
- Hey, i’m the d3 line method() and I have to two functions (.x() and .y()) that I will need you to use to return me data points that have been scaled to proportion using your previously built linearScale() functions.
- In .x() I will be passing you the data value-by-value (remember, our data is an array), so you’ll have to give me a function that takes that data and shoots back the scaled X-coordinates for each input value.
- In .y() I will also be passing you the data you gave me, and I will need you to give me a function that will take that data and return me your scaled Y-coordinates for each input value.
By the way, the process for the d3.area() function is identical, however, it does require one additional part: Because we aren’t just drawing a line but an area, d3 will need both a y0 (your base) and a y1 (your y-coordinate) to calculate the space from the bottom of your chart to the data point. Makes sense, right ?!
If your head is hurting by now, that’s ok. We’re almost at the finish line :)
We now have everything to render our elements, so between our <svg> tag, we write the following:
As you can see, we call the line() and area() functions inside the <path> element, and that will automatically create the lines for us because D3 converts our data into x and y coordinates and draws the lines between those points.
Lastly, let’s look at the transition portion:
Because we are fetching our data in the componentDidMount() lifecycle hook, I decided to do all the animations inside the componentDidUpdate() hook (If you’re new to React and this doesn’t make a lot of sense, don’t worry about it this for now. It will make sense down the road for you.)
To understand this code, you have to understand how d3 transitions think, so let’s do that:
- As a first step, d3 will want to know what you’re trying to animate, so you’ll have to find the DOM-element using the d3.selectAll() function.
- Now that you have the element you set its starting attributes (i.e. how is the element supposed to look BEFORE you animate it).
- Next, you call the transition() and duration() functions to tell your code “hey, i’m going to now give you some attributes to transition to and I want you to transition between my start and end attributes over this particular time / duration)
- Lastly, you pass the attributes that you want the element to have AT THE END of the transition.
BOOOOM! There you have it!
You just created a cool cryptocurrency, animated, awesome line + area chart! Congrats!
If you enjoyed this article and found it helpful and want to say thanks, I’d appreciate a few claps and/or a follow. I’ll be posting more stuff and I wouldn’t want you to miss it!
Also, if you have questions, just throw them in the comment section and I’ll get back to you, hopefully, with a solution.