Given a set of random points in 2 dimensional space, how could one go about animating the points appearing in the window? Currently the code I have will create all the nodes from the given point set, and then after a short delay all of the nodes will appear at once, not in one-by-one in order. I have found a way using an html canvas and some D3 code, but this may limit my use of D3 as I proceed further into the project.
The end goal is to be able to animate the construction of geometric objects like a t-spanner in the window using D3. I would also like to be able to animate objects like edges, bounding boxes, and more progressively using D3. The reason I chose to use D3 for this project is because I thought the animation process was fairly simple and smooth, I still hope this is the case. Please let me know the best way to animate the construction of a given object. If I can get the nodes animated properly the other objects should be more simple.
Below is the current code, it will animate the nodes but not one-by-one instead all animation happens at once.
function drawVertexSet(s = pointSet) {
d3.selectAll("g").remove();
var nodes = svg.append("g");
for (let i = 0; i < pointSet.length; i++) {
var circles = nodes.append("circle")
.attr("cx", pointSet[i][0])
.attr("cy", pointSet[i][1])
.attr("r", 3)
.style("fill", "white")
}
for (let i = 0; i < pointSet.length; i++) {
vertex = document.getElementsByTagName("circle")[i].id = "vertex_" + i;
setTimeout(() => {
d3.select("#vertex_" + i)
.transition()
.delay(200)
.duration(1)
.style("fill", "black");
}, 200);
}
}
Here's an example:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://d3js.org/d3.v7.js"></script>
</head>
<body>
<button id="replay">Replay</button>
<div id="chart"></div>
<script>
// set up
const margin = { top: 5, bottom: 5, left: 5, right: 5 };
const width = 200 - margin.left - margin.right;
const height = 200 - margin.top - margin.bottom;
const svg = d3.select('#chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// connect button
d3.select('#replay')
.on('click', () => drawVertexSet(getRandomData()));
// random data generator
function getRandomData() {
const randX = d3.randomInt(0, width);
const randY = d3.randomInt(0, height);
const numPoints = d3.randomInt(10, 30)();
return d3.range(numPoints).map(() => [randX(), randY()]);
}
function drawVertexSet(pointSet) {
g.selectAll('circle')
.data(pointSet)
.join('circle')
.attr('cx', ([x, y]) => x)
.attr('cy', ([x, y]) => y)
.attr('r', 3)
.attr('fill', 'black')
// start the circle as invisible
.attr('opacity', 0)
.transition()
// how long it takes each circle to fade in
.duration(100)
// how long to wait before transition the circle
.delay((d, i) => i * 100)
// make the circle visible
.attr('opacity', 1);
}
drawVertexSet(getRandomData());
</script>
</body>
</html>