Published
Edited
Feb 14, 2018
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
globalState = ({
animationStartTime: 0,
animationCurrentTime: 0,
completed: 0,
scrap: 0,
timeouts: []
})
Insert cell
updatePartsCompleted = () => {
//Update global state
globalState.completed += 1
//Use global state to update DOM element
const text = d3.select("#completedCount")
return text.transition()
.duration(20000)
.text(globalState.completed)
}
Insert cell
resetPartsCompleted = () => {
//Update global state
globalState.completed = 0
//Use global state to update DOM element
const text = d3.select("#completedCount")
return text.transition()
.duration(20000)
.text(globalState.completed)
}
Insert cell
transitionMaster = (transition, pathIndex) => {
//Calculate the time in the world of the animation and update the global state
globalState.animationCurrentTime = (d3.now() - globalState.animationStartTime);
transition
.delay(function(d) {
return pathIndex < d.path.length ? d.path[pathIndex-1].rest
: null;
})
.attrTween("cx", function(d) {
return pathIndex < d.path.length ? d3.interpolateNumber(this.getAttribute("cx"), d.path[pathIndex].x) : null;
})
.attrTween("cy", function(d) {
return pathIndex < d.path.length ? d3.interpolateNumber(this.getAttribute("cy"), d.path[pathIndex].y) : null;
})
.ease(d3.easeLinear)
.duration(function(d) {
const rateLocal =
pathIndex < d.path.length ?
(
d.path[pathIndex].pathType === "robot" ?
0.1 :
0.2
)
: null;
return pathIndex < d.path.length ?
(
distance(d.path[pathIndex-1].x, d.path[pathIndex-1].y, d.path[pathIndex].x,
d.path[pathIndex].y)/rateLocal
)
: null;
})
.on('end', function(d) {
d.pathIndex = pathIndex
})
//Fade out and remove when last path completed
.transition()
.styleTween("opacity", function(d) {
return pathIndex >= d.path.length ? d3.interpolate(0.8, 0)
: null;
})
.duration(1000)
.on('end', function(d) {
return pathIndex >= d.path.length ?
(
//The length of scrap path. Probably better way to do this.
d.path.length === 4 ? globalState.scrap += 1
: updatePartsCompleted()
)
: null;
})
//Why does this work?
.remove()
}
Insert cell
Insert cell
Insert cell
transitionMap = (g) => {
pathIndexArray.forEach((x) => {
g = g.transition().call(transitionMaster, x)
})
}
Insert cell
Insert cell
startSingleAnimation = (data) => {
const svg = d3.select("#lineMap")

//Select Field and place the players
const circles = svg.selectAll("circle")
.data(data, function(d) { return d ? d.name : this.id; })

return circles
.enter().append("circle")
.attr("class", "agent")
.attr("cx", function(d) { return d.path[0].x; })
.attr("cy", function(d) { return d.path[0].y; })
.attr("r", 5)
.attr("id", function(d) { return d.name; })
.style("fill", function(d) { return d.color; })
.style("opacity", 0)
.transition()
.duration(300)
.style("opacity", 0.8)
.transition().call(transitionMap);
}
Insert cell
setStartTime = (data) => {
var t = d3.timeout(
function() {
startSingleAnimation([data]);
}, data.start)
return t
}
Insert cell
//If you restart before all agents have finished it adds the remaining to your completed parts count
startAnimation = () => {
globalState.animationStartTime = d3.now();
globalState.timeouts = data.map((d) => {
return setStartTime(d)
})
return globalState.timeouts
}
Insert cell
restartAnimation = (data) => {
const selection = d3.selectAll(".agent")
resetPartsCompleted()
globalState.scrap = 0;
globalState.timeouts.forEach((timeout) => timeout.stop())
selection.interrupt();
selection.remove();
return startAnimation();
}
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more