Skip to content

Instantly share code, notes, and snippets.

@satirama
Last active May 29, 2020 23:58
Show Gist options
  • Save satirama/102e981563164e36136ac1f891522e6d to your computer and use it in GitHub Desktop.
Save satirama/102e981563164e36136ac1f891522e6d to your computer and use it in GitHub Desktop.
Vertical scatter-ish chart
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vertical Plot</title>
<meta name="author" content="Angelica Miranda - https://satirama.github.io" />
<style>
#home {
width: 100%;
height: 100vh;
font-family: Arial, Helvetica, sans-serif;
}
#chart {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="home">
<p id="tooltip"></p>
<div id="chart"></div>
</div>
</body>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
/*
What have the rock & alternative bands of the Rolling Stone top 100 albums in the 2000 been up to in the last decade.
New LP or EP but no live sessions or live albums
Radiohead
Interpol
The Strokes
Arctic Monkeys
Franz Ferdinand
The Black Keys
Phoenix
Arcade Fire
Beck
MGMT
LCD Soundsystem
Wilco
The Flaming Lips
Yeah Yeah Yeahs
PJ Harvey
Tv on the radio
My Morning Jacket
No new albums:
The White Stripes
System of a Down
*/
var artistsColor = {
"Radiohead": "#1f77b4",
"Interpol": "#ff7f0e",
"The Strokes": "#2ca02c",
"Arctic Monkeys": "#d62728",
"Franz Ferdinand": "#9467bd",
"The Black Keys": "#8c564b",
"Phoenix": "#e377c2",
"Arcade Fire": "#7f7f7f",
"Beck": "#bcbd22",
"MGMT": "#17becf",
"LCD Soundsystem": "#7fc97f",
"Wilco": "#beaed4",
"The Flaming Lips": "#fdc086",
"Yeah Yeah Yeahs": "#ffff99",
"PJ Harvey": "#386cb0",
"TV on the Radio": "#f0027f",
"My Morning Jacket": "#bf5b17"
}
function drawChart(data) {
const margin = {
top: 50,
right: 50,
bottom: 50,
left: 50
};
const years = Object.keys(data);
const svgWidth = document.getElementById('chart').clientWidth;
const svgHeight = document.getElementById('chart').clientHeight;
const chartWidth = svgWidth - margin.right - margin.left;
const chartHeight = svgHeight - margin.top - margin.bottom;
const paddingX = chartWidth / (years.length - 1);
const startY = chartHeight;
const paddingY = window.innerHeight / 14; // y spacing between circles, should be optimized for each case by the max number of items within a year
const radius = chartWidth > 500 ? 6 : 4;
const largeRadius = 80;
const svg
= d3.select("#chart")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight);
const g
= svg.append("g")
.attr("transform",`translate(${margin.left},${margin.top})`);
const line
= g.selectAll('.line')
.data(years)
.enter()
.append('g')
.attr('id', d => 'year-' + d);
line.append('line')
.attr('x1', (d, i) => i * paddingX)
.attr('y1', 0)
.attr('x2', (d, i) => i * paddingX)
.attr('y2', chartHeight)
.attr('year', d => d)
// accent lines every 5 years
.attr('stroke-width', d => d % 5 == 0 ? 2 : 1)
.attr('stroke', d => d % 5 == 0 ? '#b8b8b8' : '#e6e6e6');
line.append('text')
.attr('x', d => chartHeight * -0.3 )
.attr('y', function (d, i) { return parseInt(d3.select(this.parentNode).select('line').attr('x1')) - radius * 2; })
.attr("text-anchor","end")
.attr("dominant-baseline","baseline")
.style("font-size", 18)
.attr('transform', "rotate(-90)")
// add labels every 5 years
.text(d => d % 5 == 0 ? d : '');
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
this.parentNode.appendChild(this);
});
};
const circles = g.selectAll('.circles')
.data(years)
.enter()
.append('g')
.attr('class', 'circles')
.attr('data-year', d => d);
circles.selectAll('circle')
.data(d => {data[d].year = d; return data[d]})
.enter()
.append('circle')
.attr('class', 'circle')
.attr('cx', function (d) {
const year = this.parentNode.dataset.year;
return d3.select('#year-' + year).select('line').attr('x1');
})
.attr('cy', function (d, i) {
const year = this.parentNode.dataset.year;
const qtyPerYear = data[year].length;
const spacing = i * paddingY;
const centered = (chartHeight - (qtyPerYear - 1) * paddingY )/ 2;
return spacing + centered;
})
.attr('r', radius)
.attr('fill', d => artistsColor[d.artist])
.on("mouseover", function(d, i) {
d3.select(this).moveToFront();
d3.select(this.parentNode).moveToFront();
d3.select(this)
.attr('cx', () => {
// fix position if large circle would go outside of svg
if (d3.select(this).attr('cx') < largeRadius) return largeRadius - 10;
if (Number.parseFloat(d3.select(this).attr('cx')) + largeRadius > chartWidth) return (chartWidth - largeRadius + 10);
return d3.select(this).attr('cx');
})
.attr('r', largeRadius)
.attr("class", "circle active")
.style('cursor','pointer');
const tooltip = d3.select(this.parentNode)
.append("text")
.attr('id', () => "t" + i)
.attr('x', () => d3.select(this).attr('cx') - 52)
.attr('y', () => d3.select(this).attr('cy') - 24)
.style('fill','white')
.style('font-size','13px')
.style('font-weight', '700')
.style('pointer-events','none')
.text(() => d.artist);
})
.on("mouseout", function(d, i) {
d3.select("#t" + i)
.transition()
.duration(50)
.remove();
d3.select(this)
.transition()
.duration(200)
.attr('r', radius)
.attr('cx', function (d) {
const year = this.parentNode.dataset.year;
return d3.select('#year-' + year).select('line').attr('x1');
})
.style('cursor', null);
});
}
getData = async () => await d3.json('musicData.json');
drawChart(getData());
</script>
</html>
{
"2010": [
{
"artist": "Interpol",
"album": "Try it on"
},
{
"artist": "The Strokes",
"album": "Angles"
},
{
"artist": "Arcade Fire",
"album": "The suburbs"
},
{
"artist": "MGMT",
"album": "Congratulations"
},
{
"artist": "LCD Soundsystem",
"album": "This is happening"
}
],
"2011": [
{
"artist": "Radiohead",
"album": "The king of limbs"
},
{
"artist": "Arctic Monkeys",
"album": "Suck it and see"
},
{
"artist": "The Black Keys",
"album": "El camino"
},
{
"artist": "Wilco",
"album": "The Whole Love"
},
{
"artist": "The Flaming Lips",
"album": "24 hours song skull"
},
{
"artist": "PJ Harvey",
"album": "Let England Shake"
},
{
"artist": "TV on the Radio",
"album": "Nine types of light"
},
{
"artist": "My Morning Jacket",
"album": "Circuital"
}
],
"2012": [
{
"artist": "The Flaming Lips",
"album": "The Flaming Lips and heavy fewnds"
}
],
"2013": [
{
"artist": "The Strokes",
"album": "Comedown machine"
},
{
"artist": "Arctic Monkeys",
"album": "AM"
},
{
"artist": "Franz Ferdinand",
"album": "Right thoughts, right words, right actions"
},
{
"artist": "Phoenix",
"album": "Bankrupt!"
},
{
"artist": "Arcade Fire",
"album": "Reflektor"
},
{
"artist": "MGMT",
"album": "MGMT"
},
{
"artist": "The Flaming Lips",
"album": "The Time Has Come to Shoot You Down... What a Sound"
},
{
"artist": "Yeah Yeah Yeahs",
"album": "Mosquito"
}
],
"2014": [
{
"artist": "Interpol",
"album": "El pintor"
},
{
"artist": "The Black Keys",
"album": "Turn blue"
},
{
"artist": "Beck",
"album": "Morning Phase"
},
{
"artist": "The Flaming Lips",
"album": "With a Little Help from My Fwends"
},
{
"artist": "The Flaming Lips",
"album": "Atlas Eets Christmas"
},
{
"artist": "TV on the Radio",
"album": "Seeds"
}
],
"2015": [
{
"artist": "Wilco",
"album": "Star Wars"
},
{
"artist": "My Morning Jacket",
"album": "The waterfall"
}
],
"2016": [
{
"artist": "Radiohead",
"album": "A moon shaped pool"
},
{
"artist": "The Strokes",
"album": "Future Present Past"
},
{
"artist": "Wilco",
"album": "Schmilco"
},
{
"artist": "PJ Harvey",
"album": "The Hope Six Demolition Project"
}
],
"2017": [
{
"artist": "Phoenix",
"album": "Ti amo"
},
{
"artist": "Arcade Fire",
"album": "Everything Now"
},
{
"artist": "Beck",
"album": "Colors"
},
{
"artist": "LCD Soundsystem",
"album": "American Dream"
},
{
"artist": "The Flaming Lips",
"album": "Oczy Mlody"
}
],
"2018": [
{
"artist": "Interpol",
"album": "Marauder"
},
{
"artist": "Arctic Monkeys",
"album": "Tranquility base hotel & casino"
},
{
"artist": "Franz Ferdinand",
"album": "Always ascending"
},
{
"artist": "MGMT",
"album": "Little Dark Age"
}
],
"2019": [
{
"artist": "Interpol",
"album": "A fine mess"
},
{
"artist":"The Black Keys",
"album": "Let's rock"
},
{
"artist": "Beck",
"album": "Hyperspace"
},
{
"artist": "Wilco",
"album": "Ode to joy"
},
{
"artist": "The Flaming Lips",
"album": "King's mouth"
}
],
"2020": [
{
"artist": "The Strokes",
"album": "The new abnormal"
},
{
"artist": "The Flaming Lips",
"album": "Deap Lips"
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment