Axes

Now that we can make all these different shapes, let's use them to create something actually useful. One way we can do that is by using lines and text to create graph axes. It would be tedious though, so D3 makes our lives easier with axis generators. They take care of drawing a line, putting on some ticks, adding labels, evenly spacing them, and so on.

A D3 axis is just a combination of path generators configured for awesomeness. All we have to do for a simple linear axis is create a scale and tell the axis to use it.

In D3, it's worth remembering that a scale is a function that maps an input range to an output domain, whereas an axis is merely a visual representation of a scale.

Also, because we will be using scales a lot from now on, let's do that Scott Murray exercise from Chapter 1, Getting Started with D3, ES2017, and Node.js, again: Input! Domain! Output! Range! Input! Domain! Output! Range!

Honestly, it really never gets old.

For a more customized axis, we might have to define the desired number of ticks and specify the labels, perhaps with something even more interesting. There are even ways to make circular axes.

Let's clear our screens by removing the yayPaths(); line at the bottom of chapter3/index.js and replacing it with the following:

export function axisDemos() { 
const chart = chartFactory({
margin: { top: 30, bottom: 10, left: 50, right: 50 },
});

const amount = 200;
}
axisDemos();

Here, we override the default margins with some bigger ones to help you see these easier. We also have a maximum value assigned to a constant.

We also need a linear scale:

  const x = d3.scaleLinear() 
.domain([0, amount])
.range([
0,
chart.width - chart.margin.right - chart.margin.left - 20,
]);

We set the maximum output value to the width of our chart (which, if you recall, is the entire width of the window), minus our two 10-pixel margins, minus another 20, because a horizontal axis needs about 20 pixels of padding on the right side to accommodate a three-digit final label.

Our axis will use the following to translate data points (domain) to coordinates (range):

  const axis = d3.axisBottom() 
.scale(x);

chart.container.append('g')
.data(d3.range(0, amount))
.call(axis);

We told the d3.axis() generator to use our x scale. Then, we simply created a new grouping element, joined some data, and called the axis. It's very important to call the axis generator on all of the data at once so that it can handle appending its own element. It now looks like this:

In D3 v3, you had to style the axes yourself before they looked even borderline decent. D3 v4 now includes a reasonable set of defaults for axes, so you don't have to worry about them as much. In V4, these defaults are known as style attributes, so you'll need to use !important if you want to override them in CSS.

If you play around with the amount variable, you'll note that axes are smart enough to always pick the perfect amount of ticks to fit the space. This is an incredibly useful feature that allows us to do some cool stuff with animation later on.

Let's compare what the different settings do to these axes. We will loop through several axes and render the same data.

Replace your call to chart.container.append('g') with the following:

  const axes = [ 
d3.axisBottom().scale(x),
d3.axisTop().scale(x).ticks(5),
];

axes.forEach((axis, i) =>
chart.container.append('g')
.data(d3.range(0, amount))
.attr('transform',
`translate(0,${(i * 50) + chart.margin.top})`)
.call(axis)
);

Let's limit it to just two axes for now: one is the plain vanilla version and the other will render with exactly 5 ticks:


It worked! The axis generator figured out which ticks are best left off and relabeled everything without us doing too much.

Let's add more axes to the array and see what happens:

d3.axisBottom().scale(x).tickSize(10, 5, 10),

With .tickSize() we can make the minor ticks smaller. The arguments are major, minor, and end tick size:


For our final trick, let's define some custom ticks and place them above the axis. We'll add another axis to the array:

d3.axisTop().scale(x).tickValues([0, 20, 50, 70, 100])
.tickFormat((d, i) => ['a', 'e', 'i', 'o', 'u'][i]),

Two things happen here: .tickValues() exactly defines which values should have a tick, and .tickFormat() specifies how to render the labels. We set it as axisTop to render it above the line.

In D3 v3, you had to set the axis direction using the axis.orient() method during creation time of the axis. In D3 v4, you choose d3.axisTop, d3.axisBottom, d3.axisLeft, or d3.axisRight to create an axis oriented in a particular way. This emphasizes the fact an axis' orientation can't be changed after it's initially drawn.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset