chartAnimate = {
function getFilteredDataByYear(year) {
const filteredData = elodata.map(d => d[0])
.filter(d => parseInt(d.year) === parseInt(year))
.filter(d => parseInt(d.rating) >= min_elo)
.sort((a, b) => parseInt(b.rating) - parseInt(a.rating))
.filter((d, i) => i < 10);
return filteredData;
}
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
svg.append("text")
.attr("x", width / 2)
.attr("y", margin.top / 3)
.attr("text-anchor", "middle")
.style("font-size", "20px")
.style("text-decoration", "bold")
.text("Chess Elo Over The Past 20 Years");
svg.append('g')
.attr('transform', `translate(0, ${margin.top})`)
.call(d3.axisTop(x))
.append('text')
.attr('text-anchor', 'middle')
.attr('fill', 'black')
.attr('font-size', '16px')
.attr('x', width / 2)
.attr('y', -30)
.text('Elo Rating');
// y-axis
svg.append('g')
.attr('transform', `translate(${margin.left}, ${barHeight / 2})`)
.call(d3.axisLeft(y))
.append('text')
.attr('transform', `translate(-30, ${height / 2}) rotate(-90)`)
.attr('text-anchor', 'end')
.attr('fill', 'black')
.attr('font-size', '16px')
.text('Player Ranking');
svg.append('line') // janky fix for y-axis style
.style("stroke", "black")
.style("stroke-width", 1)
.attr("x1", margin.left)
.attr("y1", margin.top)
.attr("x2", margin.left)
.attr("y2", height);
// year label - bottom right
const yearLabel = svg.append('text')
.attr('class', 'year')
.attr('x', width - 200)
.attr('y', height - margin.bottom)
.attr('fill', '#ccc')
.attr('font-family', 'Helvetica Neue, Arial')
.attr('font-weight', 500)
.attr('font-size', 80)
.text(years[0]);
// bars
let players = svg
.selectAll('rect')
.data(getFilteredDataByYear(years[0]))
.enter().append('rect')
.attr('class', 'player')
.attr('opacity', 0.85)
.attr('fill', d => color(d.country))
.attr('y', (d, i) => y(i + 1))
.attr('x', margin.left)
.attr('width', d => x(parseInt(d.rating) - 8))
.attr('height', barHeight)
.style('font-size', 'small');
// player name label for each bar chart
let names = svg
.selectAll(".names")
.data(getFilteredDataByYear(years[0]))
.join("text")
.attr('text-anchor', 'end')
.attr('fill', '#ffffff')
.attr('font-size', 16)
.attr('font-family', 'Helvetica Neue, Arial')
.attr('y', (d, i) => y(i + 1) + 20)
.attr('x', d => margin.left + x(parseInt(d.rating) - 8) - 3)
.text(d => d.name);
// elo rating label for each bar chart
let ratings = svg
.selectAll(".ratings")
.data(getFilteredDataByYear(years[0]))
.join("text")
.attr('text-anchor', 'start')
.attr('fill', '#aaaaaa')
.attr('font-size', 16)
.attr('font-family', 'Helvetica Neue, Arial')
.attr('y', (d, i) => y(i + 1) + 20)
.attr('x', d => margin.left + x(parseInt(d.rating) - 8) + 3)
.text(d => d.rating);
updateInfo(years[0]);
function updateInfo(year){
players.selectAll('title').remove();
players.data(getFilteredDataByYear(year))
.append('title')
.text((d) => "Name: " + d.name + "\nCountry : " + d.country + "\nRating: " + d.rating);
players
.on('mouseover', function() {
d3.select(this).attr('stroke', '#333').attr('stroke-width', 2);
})
.on('mouseout', function() {
d3.select(this).attr('stroke', null);
});
}
function setYear(year) {
yearLabel.text(year);
players = players.data(getFilteredDataByYear(year), d => d.name)
.join(
enter => enter.append('rect')
.attr('class', 'player')
.attr('opacity', 0.0)
.attr('y', (d, i) => y(i + 1))
.attr('x', margin.left)
.attr('width', d => x(parseInt(d.rating) - 8))
.attr('height', barHeight),
update => update,
exit => exit.remove()
);
players.transition().duration(1000).ease(d3.easeCubic)
.attr('opacity', 0.75)
.attr('fill', d => color(d.country))
.attr('y', (d, i) => y(i + 1))
.attr('x', margin.left)
.attr('width', d => x(parseInt(d.rating) - 8))
.attr('height', barHeight)
.style('font-size', 'small');
names = names.data(getFilteredDataByYear(year), d => d.name)
.join(
enter => enter.append("text")
.attr('text-anchor', 'end')
.attr('fill', '#ffffff')
.attr('font-size', 16)
.attr('font-family', 'Helvetica Neue, Arial')
.attr('y', (d, i) => y(i + 1) + 20)
.attr('x', d => margin.left + x(parseInt(d.rating) - 8) - 3)
.text(d => d.name),
update => update,
exit => exit.remove()
);
names.transition().duration(1000).ease(d3.easeCubic)
.attr('text-anchor', 'end')
.attr('fill', '#ffffff')
.attr('font-size', 16)
.attr('font-family', 'Helvetica Neue, Arial')
.attr('y', (d, i) => y(i + 1) + 20)
.attr('x', d => margin.left + x(parseInt(d.rating) - 8) - 3)
.text(d => d.name);
ratings = ratings.data(getFilteredDataByYear(year), d => d.name)
.join(
enter => enter.append("text")
.attr('text-anchor', 'start')
.attr('fill', '#aaaaaa')
.attr('font-size', 16)
.attr('font-family', 'Helvetica Neue, Arial')
.attr('y', (d, i) => y(i + 1) + 20)
.attr('x', d => margin.left + x(parseInt(d.rating) - 8) + 3)
.text(d => d.rating),
update => update,
exit => exit.remove()
);
ratings.transition().duration(1000).ease(d3.easeCubic)
.attr('text-anchor', 'start')
.attr('fill', '#aaaaaa')
.attr('font-size', 16)
.attr('font-family', 'Helvetica Neue, Arial')
.attr('y', (d, i) => y(i + 1) + 20)
.attr('x', d => margin.left + x(parseInt(d.rating) - 8) + 3)
.text(d => d.rating);
updateInfo(year);
}
return Object.assign(svg.node(), { setYear });
}