Data visualizations with Vue and D3: counting stars (part 2)
Tags: vue, javascript
Published: 2019-10-21 13:51:43

You can find the first part of this article [here](https://ultimatecourses.com/blog/create-data-visualization-chart-vuejs-d3).

If you skipped part 1, [here](https://github.com/NataliaTepluhina/vue-d3-example/tree/basic-star) is the code to start with 😉

## What are we going to build?

In this article, we will improve our star example with D3 data scaling. Scale functions are JavaScript functions that:

- take an input (usually a number, date or category) and
- return a value (such as a coordinate, a color, a length or a radius).

They’re typically used to transform (or `map`) data values into visual variables (such as position, length, and color). This way, we can quickly 'draw' any set of initial data as SVG.

So, we will map our star 'visuals' to specific data values. At the end of the article, our star will look this way:

![Finished sample](https://media.giphy.com/media/Zd5X28z9znil7vmvXI/giphy.gif)

## Scaling star radius

The first obvious question is: why do we need to scale the radius? Can't we use the value without scaling, as we did for rays in the [previous article](https://ultimatecourses.com/blog/create-data-visualization-chart-vuejs-d3#Customizing_star_rays_amount_with_Vue)?

The reason is we want to limit our star outer radius while input data value can vary in a vast range. D3 will calculate the ratio and scale our star radius proportionally.

For the first iteration, let's say our input value will scale from 10 to 1000, but we want to display a star with a max radius of 250px. To perform the calculation, we're going to use D3 `scaleLinear` method.

> `scaleLinear` constructs a new continuous scale with the specified domain and range. Each range value y can be expressed as a function of the domain value _x: y = mx + b_. ([D3 Wiki](https://github.com/d3/d3-scale/blob/v2.2.2/README.md#scaleLinear))

First, let's remove our hardcoded `outerRadius` from `data` and let's create a new `computed` property to calculate it. Instead, we will add a `radius` property to data; it will serve as our input value.

```js
data() {
  return {
    radius: 300,
    rays: 8,
  };
},
computed: {
  outerRadius() {
    return this.radius
  },
  ...
},
```

So, for now `outerRadius` just returns what we put in `radius`. Now, let's add some scaling to it! First, we need to import `scaleLinear` method from D3:

```js
import { lineRadial } from 'd3-shape';
import { scaleLinear } from 'd3-scale';
```

We need to define a scaling function using `scaleLinear` method and _then_ we need to call this function with our input `radius` as a parameter:

```js
computed: {
  outerRadius() {
    const scale = scaleLinear()
      .domain([10, 1000])
      .range([5, 250]);

    return this.radius
  },
  ...
},
```

What's happening here? We provided a `domain` to `scaleLinear`, and it's the range in which our input value is going to change. As for `range`, it's what we have as an `output`: we expect our `outerRadius` to change in the range between 5px and 250px.

Now we can pass `radius` to the `scale` function:

```js
computed: {
  outerRadius() {
  const scale = scaleLinear()
    .domain([10, 1000])
    .range([5, 250]);

  return scale(this.radius)
  },
  ...
},
```

As a result, we will have something like this:

![Star with scaled radius](https://i.imgur.com/Gued9Uk.png).

Try to change the `radius` property to 500 or 1000, and you will see how the star grows in size.

Now, similarly to rays, let's add an input to change star radius dynamically. In our `template`, let's add

```html
<div class="range-input">
  <label for="radius">Radius</label>
  <input name="radius" type="range" min="10" max="1000" v-model="radius" />
</div>
```

This way, we have bound our `radius` data property to the input field value.

![Scaling radius gif](https://media.giphy.com/media/McVQmbbeiHastoJyCK/giphy.gif).

## Styling the app

Now we can change star radius and rays amount, but it still keeps this dull green color. What if we can improve it? If our star were a real star, it would change its color [proportionally to its temperature](https://www.enchantedlearning.com/subjects/astronomy/stars/startypes.shtml), from red to blue. Can we create a scale that will do the same? With D3, the answer is obvious 😊.

But before we start working on it, let's style our application a bit. First, let's move sliders to the right side of our image. To do so, we need to change the template slightly:

```html
<template>
  <section class="wrapper">
    <svg width="500" height="500">
      <path
        class="radial"
        :d="radialData"
        transform="translate(250, 250)"
        fill="green"
      ></path>
    </svg>
    <aside>
      <div class="range-input">
        <input name="rays" type="range" min="4" max="60" v-model="rays" />
        <label for="rays">Rays</label>
      </div>
      <div class="range-input">
        <input
          name="radius"
          type="range"
          min="10"
          max="1000"
          v-model="radius"
        />
        <label for="radius">Radius</label>
      </div>
    </aside>
  </section>
</template>
```

And let's add some styles to the `style` section:

```html
<style lang="scss">
  .wrapper {
    display: flex;
    align-items: center;
  }
  aside {
    padding-left: 20px;

    .range-input {
      display: flex;
      align-items: center;
      margin-bottom: 10px;
      input {
        margin-right: 10px;
      }
    }
  }
</style>
```

Now our app looks more structured:

![Styled app](https://i.imgur.com/akxAjVA.png).

Also, we want our star to be lighter, like... well, like real stars! Let's give it a dark background with a deep blue rectangle and let's change star color to white for now.

```html
<svg width="500" height="500">
  <rect width="100%" height="100%" fill="#0e0432" />
  <path
    class="radial"
    :d="radialData"
    transform="translate(250, 250)"
    fill="white"
  ></path>
</svg>
```

![Star on dark background](https://i.imgur.com/m3WYiKM.png)

Now we can play with changing star color!

## Creating a custom scale for star color

While D3 has [built-in color interpolators](https://bl.ocks.org/d3indepth/89ced137bece23b908cf51580d5e082d), we want to build our simple one. We will take an input value from 0 to 100, and we will define a color for 0, 25, 50, 75 and 100 marks. D3 is smart enough to interpolate the color for in-between values.

As a first step, let's create a new `data` property - `heat` and set it to be 5:

```js
data() {
  return {
    radius: 300,
    rays: 8,
    heat: 5,
  };
},
```

Next, let's create a new `computed` property for star color calculation:

```js
starColor() {
  return this.heat
},
```

And finally we're ready to create a new scale:

```js
starColor() {
  const myColor = scaleLinear()
    .domain([0, 25, 50, 75, 100])
    .range(['#ff7665', '#ffb469', '#ffe876', '#fff', '#99cdff']);
  return myColor(this.heat);
},
```

Now, we have a star color ready to be added to our SVG `path` `fill` property instead of static `white` color. Let's create a dynamic binding there:

```html
<path
  class="radial"
  :d="radialData"
  transform="translate(250, 250)"
  :fill="starColor"
></path>
```

Our star has changed the color!

![Red star](https://i.imgur.com/nQqId18.png).

Let's try to change the `heat` data property to 90, and a star will become light blue. To make it look nicer, we can create a gradient from white to current star color:

```html
<svg width="500" height="500">
  <defs>
    <radialGradient id="starGradient">
      <stop offset="2%" stop-color="white" />
      <stop offset="95%" :stop-color="starColor" />
    </radialGradient>
  </defs>
  ...
</svg>
```

And now we can fill our star with this gradient instead of solid color:

```html
<svg width="500" height="500">
  <defs>
    <radialGradient id="starGradient">
      <stop offset="2%" stop-color="white" />
      <stop offset="95%" :stop-color="starColor" />
    </radialGradient>
  </defs>
  <rect width="100%" height="100%" fill="#0e0432" />
  <path
    class="radial"
    :d="radialData"
    transform="translate(250, 250)"
    fill="url(#starGradient)"
  ></path>
</svg>
```

Now our star is really nice-looking:

![Gradient star](https://i.imgur.com/6NmdRHM.png)

And the last thing left is to add the same input slider as we did previously for rays and radius:

```html
<div class="range-input">
  <input name="heat" type="range" min="0" max="100" v-model="heat" />
  <label for="heat">Heat</label>
</div>
```

And we're ready to go! You can find the final code of the Chart component [here](https://github.com/NataliaTepluhina/vue-d3-example/blob/star-generator/src/components/Chart.vue).

## What's next?

In part 3, we're going to create not a single star but the whole constellation! And we will visualize a real dataset of Vue repositories:

![Vue constellation](https://media.giphy.com/media/cl20GJQAHUPZnqigVG/giphy.gif)