AnimatedContours = {
const data = [];
let prevData = data.slice(0);
const height = width;
const svg = d3
.select(DOM.svg(width, height))
.attr("fill", "none")
.attr("stroke", "blue")
.attr("stroke-linejoin", "round");
const sigma = width * 0.2;
const randomX = d3.randomNormal(width / 2, sigma);
const randomY = d3.randomNormal(height / 2, sigma);
function updateData() {
prevData = data.slice(0);
for (var i = 0; i < 50; i++) {
data[i] = {
x: randomX(),
y: randomY()
};
}
}
function updateGraph() {
var circles = svg.selectAll("circle").data(data);
circles
.enter()
.append("circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 2)
.attr("stroke", "none")
.attr("fill", "red");
circles
.transition()
.duration(1000)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
const thresholds = [0.001];
const contourFunc = d3
.contourDensity()
.thresholds(thresholds)
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
})
.size([width, height])
.bandwidth(70);
const entering = data.map(d => {
return {
...d,
type: "entering"
};
});
const exiting = prevData.map(d => {
return {
...d,
type: "exiting"
};
});
const allData = entering.concat(exiting);
d3.transition()
.duration(1000)
.tween("contours", d => {
return tweenValue => {
const inverse = 1 - tweenValue;
contourFunc.weight(d => {
return d.type === "entering" ? tweenValue : inverse;
});
const contours = contourFunc(allData);
const contourPaths = svg.selectAll("path").data(contours);
contourPaths.exit().remove();
contourPaths
.enter()
.append("path")
.style("opacity", 1);
contourPaths.attr("d", d3.geoPath());
};
});
}
updateData();
updateGraph();
setInterval(function() {
updateData();
updateGraph();
}, 2000);
return svg.node();
}