xxxxxxxxxx
<meta charset="utf-8">
<style>
text { fill: #7ec5e1; }
text.year {
font-family: Heiti TC;
pointer-events: none;
}
#controls {
position: absolute;
top: 15px; right: 15px;
}
.guide {
pointer-events: none;
font-size: 14px;
font-weight: 600;
}
.popover {
pointer-events: none;
}
.legendCircle {
stroke-width:1;
stroke:#999999;
stroke-dasharray:2 2;
fill:none;
}
.legendLine {
stroke-width: 1;
stroke: #D1D1D1;
shape-rendering: crispEdges;
}
.legendTitle {
fill: #1A1A1A;
color: #1A1A1A;
text-anchor: middle;
font-size: 10px;
}
.legendText {
fill: #949494;
text-anchor: start;
font-size: 9px;
}
@media (min-width: 500px) {
.col-sm-3, .col-sm-9 {
float: left;
}
.col-sm-9 {
width: 75%;
}
.col-sm-3 {
width: 25%;
}
}
</style>
<body>
<div id="controls">
<button id="explode">Exploser</button>
<button id="collapse">Regrouper</button>
</div>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var years = d3.range(1950, 2015 + 1)
var interval = 1000
var blurStable = '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 10 -7'
var blurIn = '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 37 -11'
var blurOut = '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 17 -7'
var margin = { top: 15, right: 15, bottom: 45, left: 55 }
var width = 960 - margin.left - margin.right
var height = 500 - margin.top - margin.bottom
var svg = d3.select('body')
.append('svg')
.attr('width', width + margin.left + margin.top)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
var x = d3.scaleLinear().range([0, width])
var y = d3.scaleLinear().range([height, 0])
var r = d3.scaleSqrt().range([0, 50])
var color = d3.scaleOrdinal()
.range(['#22cc99', '#f76c02', '#6a65ae', '#d4679f', '#fdd844', '#136071'])
var xAxis = d3.axisBottom().scale(x)
var yAxis = d3.axisLeft().scale(y)
var yearLabel = svg.append('text')
.attr('class', 'year')
.attr('x', width / 2)
.attr('y', height / 2)
.attr('dy', '.28em')
.style('font-size', width / 3)
.style('text-anchor', 'middle')
.style('font-weight', 'bold')
.style('opacity', 0.2)
.text('1950')
d3.csv('data.csv', function (d) {
return {
country: d.country,
year: d.year,
life_expectancy: +d.life_expectancy,
total_fertility: +d.total_fertility,
population: +d.population,
continent: d.continent
}
}, initialize)
function initialize(error, data) {
if (error) { throw error }
x.domain([0, d3.max(data, function (d) { return d.total_fertility })]).nice()
y.domain([0, d3.max(data, function (d) { return d.life_expectancy })]).nice()
r.domain([0, d3.max(data, function (d) { return d.population })])
data = d3.nest()
.key(function (d) { return d.year })
.key(function (d) { return d.continent })
.entries(data)
data.forEach(function (d) {
d.values.forEach(function (e) {
e.population = d3.sum(e.values, function (f) { return f.population })
e.total_fertility = d3.sum(e.values, function (f) {
return f.population * f.total_fertility
}) / e.population
e.life_expectancy = d3.sum(e.values, function (f) {
return f.population * f.life_expectancy
}) / e.population
e.values.forEach(function (f) { f.parent = e })
})
})
var uniqueContinents = data[0].values.map(function (d) { return d.key })
// from https://www.visualcinnamon.com/2016/06/fun-data-visualizations-svg-gooey-effect.html
// modified to create a filter for each continent group, which can be individually transitioned
var filters = svg.append('defs')
.selectAll('filter')
.data(uniqueContinents)
.enter().append('filter')
.attr('id', function (d) { return 'gooeyCodeFilter-' + d.replace(' ', '-') })
filters.append('feGaussianBlur')
.attr('in', 'SourceGraphic')
.attr('stdDeviation', '10')
.attr('color-interpolation-filters', 'sRGB')
.attr('result', 'blur')
var blurValues = filters.append('feColorMatrix')
.attr('class', 'blurValues')
.attr('in', 'blur')
.attr('mode', 'matrix')
.attr('values', blurStable)
.attr('result', 'gooey')
filters.append('feBlend')
.attr('in', 'SourceGraphic')
.attr('in2', 'gooey')
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis)
.append('text')
.attr('x', width)
.attr('y', 30)
.style('text-anchor', 'end')
.style('font-weight', 'bold')
.text ('Natalité (naissances par femme)')
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('x', 0)
.attr('y', -30)
.style('text-anchor', 'end')
.style('font-weight', 'bold')
.text('Esperance vie (années)')
var colorLegendG = svg.append('g')
.attr('class', 'color-legend');
var yearIndex = 0
var year = '' + years[yearIndex]
var exploded = d3.set()
var blurTransition = d3.set()
d3.select('#explode').on('click', function () {
uniqueContinents.forEach(function (d) {
if (!exploded.has(d)) {
exploded.add(d)
blurTransition.add(d)
}
})
})
d3.select('#collapse').on('click', function () {
uniqueContinents.forEach(function (d) {
if (exploded.has(d)) {
exploded.remove(d)
blurTransition.add(d)
}
})
})
var continents = svg.selectAll('.continent')
.data(data[0].values)
.enter().append('g')
.attr('class', 'continent')
.style('filter', function (d) { return 'url(#gooeyCodeFilter-' + d.key.replace(' ', '-') + ')' })
.on("mouseover", function(){d3.select(this).attr("r",10);})
continents.append('circle')
.attr('class', 'aggregate')
.attr('cx', width / 2)
.attr('cy', height / 2)
.style('fill', function (d) { return color(d.key) })
.on('mouseover', function (d){ return d.continent })
.on('click', function (d) { exploded.add(d.key); blurTransition.add(d.continent) })
.append('title', color).text(function (d) { return d.key })
colorLegendG
.attr('transform', `translate(${innerWidth - 100}, 50)`);
update()
d3.interval(incrementYear, interval)
const colorLegend = d3.legendColor()
.scale(colorScale)
.shape('circle');
function incrementYear() {
year = '' + years[++yearIndex >= years.length ? yearIndex = 0 : yearIndex]
update()
}
function update() {
yearLabel.transition().duration(0).delay(interval / 2).text(year)
continents = continents.data(
data.find(function (d) { return d.key === year }).values,
function (d) { return d.key }
)
var countries = continents.selectAll('.country')
.data(function (d) { return d.values }, function (d) { return d.country })
countries.exit().remove()
var enterCountries = countries.enter().insert('circle', '.aggregate')
.attr('class', 'country')
.attr('cx', width / 2)
.attr('cy', height / 2)
.style('fill', function (d) { return color(d.continent) })
.on('click', function (d) { exploded.remove(d.continent); blurTransition.add(d.continent) })
enterCountries.append('title').text(function (d) { return d.country })
countries = countries.merge(enterCountries)
var t = d3.transition()
.ease(d3.easeLinear)
.duration(interval)
continents.select('.aggregate')
.transition(t)
.attr('r', function (d) { return exploded.has(d.key) ? 0 : r(d.population) })
.attr('cx', function (d) { return x(d.total_fertility) })
.attr('cy', function (d) { return y(d.life_expectancy) })
countries
.transition(t)
.attr('r', function (d) { return r(d.population) })
.attr('cx', function (d) { return x((exploded.has(d.continent) ? d : d.parent).total_fertility) })
.attr('cy', function (d) { return y((exploded.has(d.continent) ? d : d.parent).life_expectancy) })
blurValues
.transition(t)
.attrTween('values', function (d) {
if (blurTransition.has(d)) {
blurTransition.remove(d)
if (exploded.has(d)) {
return d3.interpolateString(blurIn, blurOut)
} else {
return d3.interpolateString(blurOut, blurIn)
}
}
return function () { return blurStable }
})
}
}
</script>
</body>
https://d3js.org/d3.v4.min.js