Built with blockbuilder.org
xxxxxxxxxx
<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; } */
.axis {font-size: 12;}
svg {}
</style>
</head>
<body>
<script>
const margins = {
top: 20,
bottom: 50,
left: 50,
right: 50
};
const width = 900 - margins.left - margins.right;
const height = 400 - margins.top - margins.bottom;
var year = 1800;
var svg = d3.select("body").append("svg")
.attr("width", width + margins.left + margins.right)
.attr("height", height + margins.top + margins.bottom)
.append("g")
.attr("transform", "translate(" + margins.left + ", " + margins.top + ")");
var circleG = svg.append('g').attr('class', 'circles');
const findMax = (max, cur) => Math.max(max, cur);
const findMin = (min, cur) => Math.min(min, cur);
d3.json("nations.json", (data) => {
// Load and preprocess data
data = data.map(mapCountry);
// Find the global maxima for income/pop/lifeExp
const minInc = data.map(d => d.minIncome)
.reduce(findMin);
const maxInc = data.map(d => d.maxIncome)
.reduce(findMax);
const minPop = data.map(d => d.minPop)
.reduce(findMin);
const maxPop = data.map(d => d.maxPop)
.reduce(findMax);
const maxLife = data.map(d => d.maxLife)
.reduce(findMax);
const minLife = data.map(d => d.minLife)
.reduce(findMin);
const xExtent = d3.extent(data, (d) => d.maxIncome);
const yExtent = d3.extent(data, (d) => d.maxLife);
const rExtent = d3.extent(data, (d) => d.maxPop);
const colorDomain = Array.from(new Set(data.map(d => d.region)));
console.log(colorDomain);
const xScale = d3.scaleLog()
.domain([minInc, maxInc])
.range([margins.left, width + margins.left]).clamp(true);
const yScale = d3.scaleLinear()
.domain([0, maxLife])
.range([height + margins.top, margins.top]);
const rScale = d3.scaleSqrt()
.domain([minPop, maxPop]).range([1, 100]);
const colorScale = d3.scaleOrdinal().domain(colorDomain).range(d3.schemeCategory10);
const axes = getAxes(xScale, yScale);
const scales = {
x: xScale,
y: yScale,
r: rScale,
color: colorScale
};
svg.append('g')
.attr("class", "axis")
.attr("transform", "translate(0," + (height + margins.top) + ")")
.call(axes.x);
svg.append('g')
.attr("class", "axis")
.attr("transform", "translate(" + margins.left + ",0)")
.call(axes.y);
svg.append('text')
.text('year')
.attr("class", "current-year")
.attr("x", "100")
.attr("y", "40")
var interval = setInterval(
function () {
updateBubbles(circleG, data, year, scales);
year += 1;
if (year > 2009) {clearInterval(interval);}
}, 250);
})
function getAxes(xScale, yScale) {
return {
x: d3.axisBottom().scale(xScale),
y: d3.axisLeft().scale(yScale)
}
}
function updateBubbles(svg, data, year, scales) {
// var bubbles = svg.selectAll("circle")
var bubbles = svg.selectAll(".country")
.data(data, country => country.name)
var enter = bubbles.enter().append("g")
.attr("id", d => d.name)
.attr("class", "country");
enter.append("circle")
enter.append("text").attr("fill", "dimgray").attr("opacity", 0.8);
// bubbles.merge(bubbles.enter().append("circle"))
// .attr("id", d => d.name)
// .attr("class", "country")
var selection = bubbles.merge(enter);
var circles = selection.select("circle")
.transition(250)
.ease(d3.easeBounceInOut)
.attr("cx", d => {d.incVal = d.income[year] || d.incVal || 0; return scales.x(d.incVal);})
.attr("cy", d => {d.lifeVal = d.lifeExpectancy[year] || d.lifeVal || 0; return scales.y(d.lifeVal);})
.attr("r", d => d.r = scales.r(d.population[year]) || d.r || 0)
.attr("region", d => d.region)
.attr("fill", function(d, i) { return scales.color(d.region) })
.attr("opacity", 0.6);
selection.select("text")
.transition(250)
.ease(d3.easeLinear)
.attr("x", d => scales.x(d.incVal))
.attr("y", d => scales.y(d.lifeVal))
.attr("font-family", "sans-serif")
.attr("font-size", d => d.r / 2.0 + "px")
.attr("text-anchor", "middle")
.text(d => d.name);
d3.select('.current-year')
.text(year);
return bubbles;
}
function mapCountry(countryData) {
var newData = {
name: countryData.name,
region: countryData.region,
income: {},
population: {},
lifeExpectancy: {},
maxIncome: 0,
maxPop: 0
};
countryData.income.forEach((d, i) => {
newData.income[d[0]] = d[1];
newData.maxIncome = Math.max(newData.maxIncome || d[1], d[1]);
newData.minIncome = Math.min(newData.maxIncome || d[1], d[1]);
});
countryData.population.forEach((d, i) => {
newData.population[d[0]] = d[1];
newData.maxPop = Math.max(newData.maxPop || d[1], d[1]);
newData.minPop = Math.min(newData.minPop || d[1], d[1]);
});
countryData.lifeExpectancy.forEach((d, i) => {
newData.lifeExpectancy[d[0]] = d[1];
newData.minLife = Math.min(newData.minLife || d[1], d[1]);
newData.maxLife = Math.max(newData.maxLife || d[1], d[1]);
});
return newData;
}
</script>
</body>
https://d3js.org/d3.v4.min.js