Layout with D3.js

Rendering postion is a key point to draw a graph.
D3.js has its own abstruction layer translating stastical values into SVG pixel postions.

Render data to visual coordinates

For basic graph area rendering, d3 provides a set of d3-scale sub-modules.
The API reference shows example:

var x = d3.scaleLinear()
    .domain([10, 130])
    .range([0, 960]);
  • d3-scale creates a function
    • Resulting x can be used as x(val), converting val into x-coordinatte.
    • Basically each graph should have x and y functions generatid with d3-scale.
  • Map with shape, input and output
    • scaleLinear() above is a shape with the most basic scale 1 by 1. There’re other shapes like scalePow(), scaleLog.
    • domain() specifies the range of input values. The basic parameter is array of [min, max].
    • range() specifies the range of output values. For rendering, it typically means [min, max] of SVG pixels.

For popular vertical coordinate like Y-axis, output range() parameter should be reversed like range([500, 0]).
SVG pixels start from 0 at top-left that corresponds with max input value.

With scale functions like this, we can draw SVG circle as a data point of scatter plots as following:

d3.select('svg').selectAll('circle')
    .data(data).enter()
    .append('circle')
    .attr('cx', function(d) { return x(d.val_x) })
    .attr('cy', function(d) { return y(d.val_y) });

cx means the position in SVG x-coordinate, ranging from 0px to 960px by range() parameter of x().

Split graph area into sub-blocks

If you want to separate coordinates in several categories, you can use Band scales.
It calculates positions from domain counts, optionally with padding. For a simple example, bar graphs have such a categorical domains.

var x = d3.scaleBand()
    .domain(["category1", "category2", "category3"])
    .range([0, 960]);

Parts composition

For composite rendering that have multiple sub parts, you can create each part with SVG g element.

var base = d3.select('svg');
var sub_area = base.append('g')
        .attr('id', 'sub_area')
        .attr('width', 300)
        .attr('height', 200)
        .attr("transform", `translate(100, 100)`);

id, width, height, transform are just HTML attributes or CSS properties.
CSS transform can move the left-top position with its translate() function.

Now sub_area has its own coordinates, you can just add any element inside range(300, 200) starting from [0, 0] position.

sub_area.data(data).enter()
    .append('circle')
    .attr('cx', function(d) { return x(d.val_x) })
    .attr('cy', function(d) { return y(d.val_y) });
⁋ Jan 4, 2023↻ Sep 12, 2024
中馬崇尋
Chuma Takahiro