Vue.js and D3: A Chart Waiting To Happen
This article is the summary of a talk I gave at the Vue.js Antwerp meetup.
Right now, I’m working on a project called uman.ai, together with ML6, a Ghent-based company specialized in Machine Learning. Uman.ai explores new ways of gaining insight in talents and skills within organizations with the help of Artificial Intelligence. I took on the challenge to find a good interactive visualization for this model.
After making some first rough sketches, I started exploring well-known existing libraries like Chart.js and Highcharts. However, none of them turned out to be a good fit for this very specific situation. And this is where D3.js got in and I first got the idea for this talk.
D3 had always felt kind of unfeasable for me. Most of the demo projects I saw were impressive, but they also looked pretty hard to recreate. For a long time I was convinced D3 was only suited for large and complex projects. I turned out to be wrong.
D3 has a jQuery-like syntax when it comes to defining templates:
This might work well most of the time, but it feels a little counter-intuitive when you’re already using Vue.js in your project. With Vue.js, you’re probably used to template code that has a close connection the actual HTML result. In the next part of this article, I will show you how to replace the rendering part in the D3 workflow with Vue’s templating system we’re already using.
Let’s write some code
For the sake of simplicity, I will use the example of a flower shop here. Let’s start with Vue component with nothing more than an empty SVG element and some base data to start from.
We now need to find out the best way to:
- Render a circle for every type of flower
- Size the circles according to the amount of flowers
- Give each circle the right color
- Find the best position for each circle
This last one is the trickiest one, since we will need some kind of algorithm to calculate the most optimal positions. The algorithm we need is called Circle Packing. One of the layout utilities D3 offers is the pack layout. It takes a data set (which is called a hierarchy here) and outputs a set of packed circles. Exactly what we need.
However, for D3 to correctly parse our flower data, we have to pass it through in a specific format. Let’s use a computed property to transform our original state:
Right now, we have everything in place to start using some of D3’s magic. Let’s import only the parts we need and let D3 do its calculations.
Finally, we can use the layoutData property to compose a template like we would in any other Vue component. Here we use the calculated layout values to add some labels, colors, transforms and sizes.
Adding a simple CSS transition will make value changes animate smoothly:
The chart now looks just as we intended. You can now expand your component functionality as you like, by adding some controls to alter the state for example:
Nothing is perfect of course, and there are three caveats to this technique you should know about.
- For big sets of data, it gets more and more difficult to keep a good performance. Especially when using a lot of animations. However, this also applies when you’re using D3 without Vue.js.
- For simple and well-known chart formats, it might still be easier to just use something like Chart.js or Highcharts.
- Some more complex features of D3, like gravity-based animations, spring values or drag and drop-interactions might be a little harder to integrate this way. However, most of them will just work fine.
Luckily, this technique also has a lot of advantages:
- Your code stays very close to the actual result.
- This makes experimenting easy and fast.
- Your code will blend very nicely with your existing Vue.js code.
- Your code will be very approachable. Even someone who only knows HTML and CSS will be able to make adjustments.
- Since you can also use Vue for mobile apps (with for example NativeScript or Weex) you can use D3 on virtually any platform that supports Vue.
I really hope next time your project needs some kind of custom out-of-the-box chart, you’ll think of this talk and give D3 a chance. The rest will be up to your imagination.