Skip to content

Instantly share code, notes, and snippets.

@4096void
Created April 15, 2019 09:54
Show Gist options
  • Select an option

  • Save 4096void/1f618cc5c03d5b1e73cbe267b214ed84 to your computer and use it in GitHub Desktop.

Select an option

Save 4096void/1f618cc5c03d5b1e73cbe267b214ed84 to your computer and use it in GitHub Desktop.
multiple lines in one linechart
license: mit

Multiple lines in one linechart.

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<script>
/**
const d3 = require('d3');
*/
/**
linechart D3 version.
dependency
d3
todos
add axises DONE
add labels DONE
add path DONE
add gridline DONE
multiple lines in one chart DONE
nice color scheme DONE
mousemove -> tooltip.
https://observablehq.com/@elishaterada/simple-area-chart-with-tooltip
https://observablehq.com/@jianan-li/basic-tooltip
grouped categories.
more customization parameters.
expose update method.
transition.
reverse yAxis and xAxis.
animations.
http://bl.ocks.org/markmarkoh/8700606
*/
/* utils */
const roll = Math.random;
const rolln = n => Math.random() * n;
const rollBetween = (n, m) => Math.random() * (m - n) + n;
const max = Math.max;
const k = k => c => c[k];
const mk = k => c => c.map(i => i[k]);
const arrayMax = i => Math.max.apply(null, i);
function line(
container = 'body', // selector or pure DOM node
width = 500, // default to square
height = width,
margin = Math.max(width, height) / 5,
word = Math.max(width, height) / 30,
isSmooth = false,
yLabel = 'Profit(USD).',
xLabel = 'Time(YEAR).',
) {
let kv = [];
let kv1 = [];
let kv2 = [];
let kv3 = [];
/* walden theme */
const colors = [
'#3fb1e3',
'#6be6c1',
'#626c91',
'#a0a7e6',
'#c4ebad',
'#96dee8',
];
for (let i = 2000; i < 2008; i++) {
kv.push({ key: i, value: rollBetween(1000, 2000) });
kv1.push({ key: i, value: rollBetween(3000, 4000) });
kv2.push({ key: i, value: rollBetween(5000, 6000) });
kv3.push({ key: i, value: rollBetween(8000, 9000) });
}
let kvs = [kv, kv1, kv2, kv3];
window.kv = kv;
/* scales */
const xScale = d3
.scalePoint()
.domain(d3.set(kv.map(k('key'))).values()) // fixme
.rangeRound([0, width]);
const yScale = d3.scaleLinear()
.domain([0, arrayMax(kvs.map(mk('value')).map(arrayMax)) * 1.3])
.range([height, 0]);
const lineGenerator = d3
.line()
.x(d => xScale(d.key))
.y(d => yScale(d.value))
.curve(d3.curveMonotoneX); // smoothline
/* root */
const svg = d3
.select(container)
.append('svg')
.attr('width', width + margin * 2)
.attr('height', height + margin * 2)
.style('margin', '1em')
.style('border', '1px solid #e0e0e0');
/* y gridlines */
function make_y_gridlines() {
return d3.axisLeft(yScale)
.ticks(5);
}
/* x axis */
svg.append('g')
.attr('transform', `translate(${margin}, ${height + margin})`)
.attr('color', '#333')
.style('font-size', '14px')
.call(d3.axisBottom(xScale));
svg
.append('text')
.attr('transform', `translate(${width + margin - word}, ${height + 1.5 * margin})`)
.text(xLabel);
/* y axis */
svg.append('g')
.attr('transform', `translate(${margin}, ${margin})`)
.attr('color', '#333')
.style('font-size', '14px')
.call(d3.axisLeft(yScale));
svg
.append('text')
.attr('x', margin - 2 * word)
.attr('y', margin - 2 * word)
.text(yLabel);
svg
.append('g')
.attr('transform', `translate(${margin}, ${margin})`)
.style('color', '#ddd')
.call(make_y_gridlines()
.tickSize(-width)
.tickFormat(''));
for (let i = 0; i < kvs.length; i++) {
let kv = kvs[i];
svg.append('path')
.attr('transform', `translate(${margin}, ${margin})`)
.datum(kv)
.style('stroke', colors[i])
.style('stroke-width', 2)
.style('fill', 'none')
.attr('d', lineGenerator);
svg.selectAll(`circle._${i}`)
.data(kv)
.enter()
.append('circle')
.style('fill', '#fff')
.style('stroke', colors[i])
.style('stroke-width', 1)
.attr('transform', `translate(${margin}, ${margin})`)
.attr('cx', d => xScale(d.key))
.attr('cy', d => yScale(d.value))
.attr('r', 3);
}
/* path animation */
/* todo mousemove handler */
const mousemoveHandler = () => {
d3.event.preventDefault();
console.log('I\'m moving ...');
};
/* listen mousemove to add tooltip */
svg.on('mousemove', mousemoveHandler);
}
line(
'body',
320,
);
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment