Simple interpolated line-chart with legend and mouseover.
Built with blockbuilder.org
license: mit |
Simple interpolated line-chart with legend and mouseover.
Built with blockbuilder.org
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
g.tick line { | |
opacity: .1; | |
} | |
path.domain { | |
opacity:0; | |
} | |
</style> | |
</head> | |
<body> | |
<script src="linechart.js"></script> | |
</body> |
'use strict' | |
d3.csv('tweetdata.csv', linechart); | |
function linechart(data) { | |
console.log(data); | |
const colDict = {retweets: 'skyblue', favorites: 'coral' , tweets: 'olive'} | |
const margin = {top: 50, right: 25, bottom: 25, left: 25}; | |
const width = 700 - margin.right - margin.left; | |
const height = 500 - margin.top - margin.bottom; | |
const svg = d3.select('body').append('svg') | |
.attr('width', width + margin.right + margin.left) | |
.attr('height', height + margin.top + margin.bottom) | |
.append('g') | |
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); | |
// define scales | |
const xScale = d3.scaleLinear().domain([1, 10]).range([20, width]); | |
const yScale = d3.scaleLinear().domain([0, 35]).range([height, 20]); | |
// define axes | |
const xAxis = d3.axisBottom() | |
.scale(xScale) | |
.tickSize(height + 5) | |
.tickValues([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); | |
const yAxis = d3.axisRight() | |
.scale(yScale) | |
.tickSize(width + 10) | |
.ticks(10); | |
// draw axies | |
svg.append('g').attr('id', 'xAxisG').call(xAxis); | |
svg.append('g').attr('id', 'yAxisG').call(yAxis); | |
// Add circles | |
svg.selectAll('circle.tweets') | |
.data(data) | |
.enter() | |
.append('circle') | |
.attr('class', 'tweets') | |
.attr('r', 5) | |
.attr('cx', d => xScale(d.day)) | |
.attr('cy', d => yScale(d.tweets)) | |
.style('fill', 'olive'); | |
svg.selectAll('circle.favorites') | |
.data(data) | |
.enter() | |
.append('circle') | |
.attr('class', 'favorites') | |
.attr('r', 5) | |
.attr('cx', d => xScale(d.day)) | |
.attr('cy', d => yScale(d.favorites)) | |
.style('fill', 'coral'); | |
svg.selectAll('circle.retweets') | |
.data(data) | |
.enter() | |
.append('circle') | |
.attr('class', 'retweets') | |
.attr('r', 5) | |
.attr('cx', d => xScale(d.day)) | |
.attr('cy', d => yScale(d.retweets)) | |
.style('fill', 'skyblue'); | |
// add lines | |
const tweetLine = d3.line() | |
.x(d => xScale(d.day)) | |
.y(d => yScale(d.tweets)) | |
.curve(d3.curveBasis); | |
svg.append('path') | |
.attr('class', 'tweets') | |
.attr('d', tweetLine(data)) | |
.attr('fill', 'none') | |
.attr('stroke', 'olive') | |
.attr('stroke-width', 5) | |
const favoriteLine = d3.line() | |
.x(d => xScale(d.day)) | |
.y(d => yScale(d.favorites)) | |
.curve(d3.curveCardinal); | |
svg.append('path') | |
.attr('class', 'favorites') | |
.attr('d', favoriteLine(data)) | |
.attr('fill', 'none') | |
.attr('stroke', 'coral') | |
.attr('stroke-width', 5) | |
const retweetLine = d3.line() | |
.x(d => xScale(d.day)) | |
.y(d => yScale(d.retweets)) | |
.curve(d3.curveStep); | |
svg.select('path') | |
.attr('class', 'retweets') | |
.attr('d', retweetLine(data)) | |
.attr('fill', 'none') | |
.attr('stroke', 'skyblue') | |
.attr('stroke-width', 5) | |
// interactivity | |
d3.selectAll('path') | |
.on('mouseover', highlightLine); | |
d3.selectAll('path') | |
.on('mouseout', deHighlightLine); | |
function highlightLine(d) { | |
let col = d3.select(this).attr('stroke'); | |
let sw = d3.select(this).attr('stroke-width'); | |
d3.select(this).attr('stroke', d3.rgb(col).brighter(.2)); | |
d3.select(this).attr('stroke-width', 10); | |
}; | |
function deHighlightLine(d) { | |
let col = d3.select(this).attr('class'); | |
let sw = d3.select(this).attr('stroke-width'); | |
d3.select(this).attr('stroke', colDict[col]); | |
d3.select(this).attr('stroke-width', 5); | |
}; | |
// legend | |
var legend_keys = ["retweets", "favorites", "tweets"] | |
var lineLegend = svg.selectAll(".lineLegend").data(legend_keys) | |
.enter().append("g") | |
.attr("class","lineLegend") | |
.attr("transform", function (d,i) { | |
return "translate(" + (margin.left) + "," + (i*20)+")"; | |
}); | |
lineLegend.append("text").text(function (d) {return d;}) | |
.attr("transform", "translate(15, 6)"); //align texts with boxes | |
lineLegend.append("rect") | |
.attr("fill", d => colDict[d]) | |
.attr("width", 12).attr('height', 5); | |
// title | |
d3.select('svg') | |
.append('text') | |
.html('Tweets Line-Chart') | |
.attr('x', width / 2 - margin.right) | |
.attr('y', margin.top / 2) | |
.attr('font', 'Georgia') | |
.style('font-size', 20); | |
}; |
day | tweets | retweets | favorites | |
---|---|---|---|---|
1 | 1 | 2 | 5 | |
2 | 6 | 11 | 3 | |
3 | 3 | 8 | 1 | |
4 | 5 | 14 | 6 | |
5 | 10 | 29 | 16 | |
6 | 4 | 22 | 10 | |
7 | 3 | 14 | 1 | |
8 | 5 | 18 | 7 | |
9 | 1 | 30 | 22 | |
10 | 4 | 16 | 15 |