Skip to content

Instantly share code, notes, and snippets.

@GitNoise
Last active March 10, 2020 23:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GitNoise/ccadeabb947bb0f92e0e3922d7c5695b to your computer and use it in GitHub Desktop.
Save GitNoise/ccadeabb947bb0f92e0e3922d7c5695b to your computer and use it in GitHub Desktop.
Multiline slope chart
license: mit

D3 v4 Line Chart Example

This is a example for basic line chart using D3. We are using the newest version of D3, version 4. As for all visualizations, we can break down this work into a checklist.

Line Chart Checklist
  1. Add an SVG to draw our line chart on
  2. Use the D3 standard margin convetion
  3. Create an x axis
  4. Create a y axis
  5. Create an x scale
  6. Create a y scale
  7. Create a line generator
  8. Create a random dataset
  9. Create a path object for the line
  10. Bind the data to the path object
  11. Call the line generator on the data-bound path object
  12. Add circles to show each datapoint
  13. Add some basic styling to the chart so its easier on the eyes

Read through the code below to see where each part of the checklist is completed.

forked from pstuffa's block: D3 v4 Line Chart

forked from gordlea's block: D3 v5 Line Chart

<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
svg {
font-family: Arial;
font-size: 12px;
}
.data .dataLine {
fill: none;
stroke: black;
stroke-width: 1;
stroke-opacity: 0.2;
}
.data path.dataLine.highlight {
stroke: orange;
stroke-width: 2;
stroke-opacity: 1;
}
.data text {
fill-opacity: 0.2;
}
.data text.highlight {
fill: orange;
fill-opacity: 1;
}
.ticks text {
text-anchor: end;
}
.dotted {
stroke: black;
stroke-opacity: 0.2;
stroke-width: 1px;
stroke-dasharray: 5,5;
}
.solid {
stroke: black;
stroke-opacity: 0.7;
stroke-width: 1px;
}
.dates {
text-anchor: middle;
}
</style>
<body>
</body>
<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
const margin = {top: 50, right: 100, bottom: 50, left: 50}
, width = 600 - margin.left - margin.right // Use the window's width
, height = 400 - margin.top - margin.bottom; // Use the window's height
// The number of datapoints
const n = 6;
const xScale = d3.scaleLinear()
.domain([0, n-1]) // input
.range([0, width]); // output
const yScale = d3.scaleLinear()
.domain([0, 1]) // input
.range([height, 0]); // output
const line = d3.line()
.x((d, i) => xScale(i))
.y(d => yScale(d.y))
.curve(d3.curveMonotoneX)
const dataset = [
{ name: 'Danmark', values: d3.range(n).map(() => ({"y": d3.randomUniform(1)() }))},
{ name: 'Finland', values: d3.range(n).map(() => ({"y": d3.randomUniform(1)() }))},
{ name: 'Sverige', values: d3.range(n).map(() => ({"y": d3.randomUniform(1)() }))},
{ name: 'Norge', values: d3.range(n).map(() => ({"y": d3.randomUniform(1)() }))},
];
dataset[0].values[0].y = 0;
dataset[1].values[0].y = 0;
dataset[1].values[1].y = 0;
dataset[2].values[0].y = 0;
dataset[2].values[1].y = 0;
dataset[3].values[0].y = 0;
dataset[3].values[1].y = 0;
dataset[3].values[2].y = 0;
const svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
const dataGroup = svg.append('g').classed('data', true);
dataGroup.selectAll("path")
.data(dataset)
.join('path')
.classed('dataLine', true)
.classed('highlight', d => d.name === 'Sverige')
.attr("d", d => line(d.values));
dataGroup.selectAll(".name")
.data(dataset)
.join('text')
.classed('name', true)
.classed('highlight', d => d.name === 'Sverige')
.attr('x', xScale.range()[1] + 8)
.attr('y', d => yScale(d.values[d.values.length - 1].y))
.text(d =>
`${d.name} ${new Intl.NumberFormat('sv-SE').format(d.values[d.values.length - 1].y)}`)
/********* ticks *********/
const ticksGroup = svg.append('g').classed('ticks', true)
// bottom line
ticksGroup.append('line').classed('dotted', true)
.attr('x1', 0)
.attr('x2', xScale.range()[1])
.attr('y1', yScale.range()[0])
.attr('y2', yScale.range()[0])
ticksGroup.append("text")
.attr('x', -8)
.attr('y', yScale.range()[0])
.attr('dy', '0.33em')
.text('0')
// middle line
ticksGroup.append('line').classed('dotted', true)
.attr('x1', 0)
.attr('x2', xScale.range()[1])
.attr('y1', yScale.range()[0]/2)
.attr('y2', yScale.range()[0]/2)
ticksGroup.append("text")
.attr('x', -8)
.attr('y', yScale.range()[0]/2)
.attr('dy', '0.33em')
.text(Math.round(yScale.domain()[1]) / 2)
// top line
ticksGroup.append('line').classed('dotted', true)
.attr('x1', 0)
.attr('x2', xScale.range()[1])
.attr('y1', yScale.range()[1])
.attr('y2', yScale.range()[1])
ticksGroup.append("text")
.attr('x', -8)
.attr('y', yScale.range()[1])
.attr('dy', '0.33em')
.text(Math.round(yScale.domain()[1]))
/********* dates *********/
const datesGroup = svg.append('g').classed('dates', true)
// left line
datesGroup.append('line').classed('solid', true)
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', yScale.range()[0] + 8)
.attr('y2', yScale.range()[1] - 8)
datesGroup.append("text")
.attr('x', 0)
.attr('y', yScale.range()[1] - 8)
.attr('dy', '-1em')
.text('2020-01-01')
// right line
datesGroup.append('line').classed('solid', true)
.attr('x1', xScale.range()[1])
.attr('x2', xScale.range()[1])
.attr('y1', yScale.range()[0] + 8)
.attr('y2', yScale.range()[1] - 8)
datesGroup.append("text")
.attr('x', xScale.range()[1])
.attr('y', yScale.range()[1] - 8)
.attr('dy', '-1em')
.text('Idag')
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment