Vue.js and D3: A Chart Waiting To Happen

Vue.js and D3: A Chart Waiting To Happen

In this article I will show you how combining D3 and Vue can make your quest for the perfect data visualization a whole lot easier.

In this article I will show you how combining D3 and Vue can make your quest for the perfect data visualization a whole lot easier.

This article is the summary of a talk I gave at the Vue.js Antwerp meetup.

For a while now, D3.js has been the go-to JavaScript library for creating custom data visualizations. However, it’s sometimes perceived as difficult to get started with or unsuitable for small projects.

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.js

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.

Before diving into some code, let me quickly give you an overview of what D3 exactly is. D3 is short for Data Driven Documents and calls itself “a JavaScript library for manipulating documents based on data”. D3 doesn’t include any pre-built visualizations, but provides you with a lot of useful utilities. This list of utilities might look a little intimidating at first, but we will only need a few.

D3 has a jQuery-like syntax when it comes to defining templates:

// Add a <g> element for every data point
const leaf = svg.selectAll('g').data(circles)

// Append a styled <circle> to every <g> element
leaf
  .append('circle')
  .attr('id', d => d.data.id)
  .attr('r', d => d.r)
  .attr('fill-opacity', 0.7)
  .attr('fill', d => d.data.color)

d3-snippet.js hosted with ❤ by GitHub

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.

<template>
  <svg width="500" height="500">
  </svg>
</template>

<script>
export default {
  data() {
    return {
      flowers: [
        {
          name: 'Roses',
          amount: 25,
          color: '#cc2936'
        },
        {
          name: 'Tulips',
          amount: 40,
          color: '#f2c640'
        },
        {
          name: 'Daisies',
          amount: 15,
          color: '#2a93d4'
        },
        {
          name: 'Narcissuses',
          amount: 9,
          color: '#F7AD0A'
        }
      ]
    }
  }
}
</script>

blank-component.vue hosted with ❤ by GitHub

We now need to find out the best way to:

  1. Render a circle for every type of flower
  2. Size the circles according to the amount of flowers
  3. Give each circle the right color
  4. 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:

<template>
  <svg width="500" height="500">
  </svg>
</template>

<script>
export default {
  data() {
    return {
      flowers: [
        {
          name: 'Roses',
          amount: 25,
          color: '#cc2936'
        },
        {
          name: 'Tulips',
          amount: 40,
          color: '#f2c640'
        },
        {
          name: 'Daisies',
          amount: 15,
          color: '#2a93d4'
        },
        {
          name: 'Narcissuses',
          amount: 9,
          color: '#F7AD0A'
        }
      ]
    }
  },
  computed: {
    transformedFlowerData() {
      return {
        name: 'Top Level',
        children: this.flowers.map(flower => ({
          ...flower,
          parent: 'Top Level'
        }))
      }
    }
  }
}
</script>

transform-data.vue hosted with ❤ by GitHub

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.

<template>
  <svg width="500" height="500">
  </svg>
</template>

<script>
import { hierarchy, pack } from 'd3-hierarchy'
export default {
  data() {
    return {
      flowers: [
        {
          name: 'Roses',
          amount: 25,
          color: '#cc2936'
        },
        {
          name: 'Tulips',
          amount: 40,
          color: '#f2c640'
        },
        {
          name: 'Daisies',
          amount: 15,
          color: '#2a93d4'
        },
        {
          name: 'Narcissuses',
          amount: 9,
          color: '#F7AD0A'
        }
      ]
    }
  },
  computed: {
    transformedFlowerData() {
      return {
        name: 'Top Level',
        children: this.flowers.map(flower => ({
          ...flower,
          parent: 'Top Level'
        }))
      }
    },
    
    layoutData() {
      // Generate a D3 hierarchy
      const rootHierarchy = 
        hierarchy(this.transformedFlowerData)
        .sum(d => d.size)
        .sort((a, b) => {
          return b.value - a.value
        })
      // Pack the circles inside the viewbox
      return pack()
        .size([500, 500])
        .padding(10)(rootHierarchy)
    }
  }
}
</script>

d3-calculate.vue hosted with ❤ by GitHub

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.

<template>
  <svg width="500" height="500">
    <g
      class="flower"
      v-for="flower in layoutData.children"
      :key="flower.data.name"
      :style="{
        transform: `translate(${flower.x}px, ${flower.y}px)`
      }"
    >
      <circle
        class=“flower__circle"
        :r=“flower.r"
        :fill=“flower.data.color"
      />
      <text class=“flower__label”>
        {{ flower.data.name }}
      </text>
    </g>
  </svg>
</template>

vue-template.vue hosted with ❤ by GitHub

Adding a simple CSS transition will make value changes animate smoothly:

.flower {
  transition: transform 0.1s ease-in-out;
}

.flower__circle {
  transition: r 0.1s ease-in-out;
}

transitions.css hosted with ❤ by GitHub

<template>
<div>
<svg width="500" height="500">
<g
class="flower"
v-for="flower in layoutData.children"
:key="flower.data.name"
:style="{
transform: `translate(${flower.x}px, ${flower.y}px)`
}"
>
<circle class="flower__circle" :r="flower.r" :fill="flower.data.color"></circle>
<text class="flower__label">{{ flower.data.name }}</text>
</g>
</svg>
<div class="controls">
<div class="control" v-for="flower in flowers" :key="flower.name">
<label>{{ flower.name }}</label>
<input type="number" v-model="flower.amount" step="10" min="10">
</div>
</div>
</div>
</template>

<script>
import { hierarchy, pack } from 'd3-hierarchy'
export default {
data() {
return {
flowers: [
{
name: 'Roses',
amount: 25,
color: '#cc2936'
},
{
name: 'Tulips',
amount: 40,
color: '#00a03e'
},
{
name: 'Daisies',
amount: 15,
color: '#2a93d4'
},
{
name: 'Narcissuses',
amount: 9,
color: '#F7AD0A'
}
]
}
},

computed: {
transformedFlowerData() {
return {
name: 'Top Level',
children: this.flowers.map(flower => ({
...flower,
size: flower.amount,
parent: 'Top Level'
}))
}
},

layoutData() {
// Generate a D3 hierarchy
const rootHierarchy = hierarchy(this.transformedFlowerData)
.sum(d => d.size)
.sort((a, b) => {
return b.value - a.value
})

// Pack the circles inside the viewbox
return pack()
.size([500, 500])
.padding(10)(rootHierarchy)
}
}
}
</script>

<style>
body {
font: 16px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
Arial, sans-serif;
}

svg {
display: block;
margin: 0 auto;
}

.flower {
transition: transform 0.2s ease-in-out;
text-anchor: middle;
}

.flower__circle {
transition: r 0.2s ease-in-out;
}

.flower__label {
fill: #fff;
font-weight: bold;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
}

.controls {
display: flex;
justify-content: center;
margin-top: 20px;
}

.control {
display: inline-flex;
flex-direction: column;
margin: 0 4px;
}

.control label {
font-size: 14px;
font-weight: bold;
margin-bottom: 4px;
}

.control input {
display: block;
font: inherit;
width: 100px;
}
</style>



Conclusion

Nothing is perfect of course, and there are three caveats to this technique you should know about.

  1. Render a circle for every type of flower
  2. Size the circles according to the amount of flowers
  3. Give each circle the right color
  4. Find the best position for each circle

Luckily, this technique also has a lot of advantages:

  1. Render a circle for every type of flower
  2. Size the circles according to the amount of flowers
  3. Give each circle the right color
  4. Find the best position for each circle

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.

Top Vue.js Developers in USA

Top Vue.js Developers in USA

Vue.js is an extensively popular JavaScript framework with which you can create powerful as well as interactive interfaces. Vue.js is the best framework when it comes to building a single web and mobile apps.

We, at HireFullStackDeveloperIndia, implement the right strategic approach to offer a wide variety through customized Vue.js development services to suit your requirements at most competitive prices.

Vue.js is an open-source JavaScript framework that is incredibly progressive and adoptive and majorly used to build a breathtaking user interface. Vue.js is efficient to create advanced web page applications.

Vue.js gets its strength from the flexible JavaScript library to build an enthralling user interface. As the core of Vue.js is concentrated which provides a variety of interactive components for the web and gives real-time implementation. It gives freedom to developers by giving fluidity and eases the integration process with existing projects and other libraries that enables to structure of a highly customizable application.

Vue.js is a scalable framework with a robust in-build stack that can extend itself to operate apps of any proportion. Moreover, vue.js is the best framework to seamlessly create astonishing single-page applications.

Our Vue.js developers have gained tremendous expertise by delivering services to clients worldwide over multiple industries in the area of front-end development. Our adept developers are experts in Vue development and can provide the best value-added user interfaces and web apps.

We assure our clients to have a prime user interface that reaches end-users and target the audience with the exceptional user experience across a variety of devices and platforms. Our expert team of developers serves your business to move ahead on the path of success, where your enterprise can have an advantage over others.

Here are some key benefits that you can avail when you decide to hire vue.js developers in USA from HireFullStackDeveloperIndia:

  • A team of Vue.js developers of your choice
  • 100% guaranteed client satisfaction
  • Integrity and Transparency
  • Free no-obligation quote
  • Portal development solutions
  • Interactive Dashboards over a wide array of devices
  • Vue.js music and video streaming apps
  • Flexible engagement model
  • A free project manager with your team
  • 24*7 communication with your preferred means

If you are looking to hire React Native developers in USA, then choosing HireFullStackDeveloperIndia would be the best as we offer some of the best talents when it comes to Vue.js.