D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
naoyak
Full window
Github gist
class 4 lab (now w/ labels)
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; } */ .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