|
<!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> |
|
|