This example demonstrates usage of d3-component to create a spinner component, and render it conditionally while something is loading. After loading finishes, the spinner goes away and is replaced by another component.
How often have you seen a data visualization show nothing but a blank screen while the data is loading? Showing a spinner with D3 and hiding it after data is loaded is not as easy as it seems. Here's a solution for doing this, feel free to use it in your projects!
This demonstrates the following patterns supported by d3-component:
Built with blockbuilder.org
forked from curran's block: Posts with D3 Component
forked from curran's block: Spinny Loading Icon
forked from curran's block: Spinner with d3-component
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/d3@4"></script>
<script src="https://unpkg.com/d3-component@1"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
// This function simulates fetching with a 2 second delay.
// You can replace stuff here with e.g. d3.csv.
function fetchData(callback){
setTimeout(function (){
callback([6, 5, 4, 3, 2, 1]);
}, 2000);
}
// This function visualizes the data.
function visualize(selection, data){
var rects = selection
.selectAll("rect")
.data(data);
rects.exit().remove();
rects
.enter().append("rect")
.attr("x", function (d, i){ return i * 100 + 182; })
.attr("y", function (d){ return 400; })
.attr("width", 50)
.attr("height", 0)
.merge(rects)
.transition().duration(1000).ease(d3.easeBounce)
.delay(function (d, i){ return i * 500; })
.attr("y", function (d){ return 400 - d * 50; })
.attr("height", function (d){ return d * 50; });
}
// The stuff below uses d3-component to display a spinner
// while the data loads, then render the visualization after loading.
// This stateless component renders a static "wheel" made of circles,
// and rotates it depending on the value of props.angle.
var wheel = d3.component("g")
.enter(function (){
var minRadius = 4,
maxRadius = 10,
numDots = 10,
wheelRadius = 40,
rotation = 0,
rotationIncrement = 3,
radius = d3.scaleLinear()
.domain([0, numDots - 1])
.range([maxRadius, minRadius]),
angle = d3.scaleLinear()
.domain([0, numDots])
.range([0, Math.PI * 2]);
d3.select(this)
.selectAll("circle").data(d3.range(numDots))
.enter().append("circle")
.attr("cx", function (d){ return Math.sin(angle(d)) * wheelRadius; })
.attr("cy", function (d){ return Math.cos(angle(d)) * wheelRadius; })
.attr("r", radius);
})
.update(function (d){
d3.select(this).attr("transform", "rotate(" + d + ")");
});
// This component with a local timer makes the wheel spin.
var spinner = (function (){
var timer = d3.local();
return d3.component("g")
.enter(function (d){
timer.set(this, d3.timer(function (elapsed){
d3.select(this).call(wheel, elapsed * d.speed);
}.bind(this)));
})
.update(function (d){
d3.select(this).attr("transform", "translate(" + d.x + "," + d.y + ")");
})
.exit(function(d){
timer.get(this).stop();
return d3.select(this)
.attr("fill-opacity", 1)
.transition().duration(3000)
.attr("transform", "translate(" + d.x + "," + d.y + ") scale(10)")
.attr("fill-opacity", 0);
});
}());
// This component displays the visualization.
var visualization = d3.component("g")
.update(function (d){
d3.select(this).call(visualize, d.data);
});
// This component manages an svg element, and
// either displays a spinner or text,
// depending on the value of the `loading` state.
var app = d3.component("g")
.update(function (d){
d3.select(this)
.call(spinner, !d.loading ? [] : {
x: d.width / 2,
y: d.height / 2,
speed: 0.2
})
.call(visualization, d.loading ? [] : d);
});
// Kick off the app.
function main(){
var svg = d3.select("svg"),
width = svg.attr("width"),
height = svg.attr("height");
// Initialize the app to be "loading".
svg.call(app, {
width: width,
height: height,
loading: true
});
// Invoke the data fetching logic.
fetchData(function (data){
svg.call(app, {
width: width,
height: height,
loading: false,
data: data
});
});
}
main();
</script>
</body>
https://unpkg.com/d3@4
https://unpkg.com/d3-component@1