|
<!DOCTYPE html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<script src="https://unpkg.com/d3@4"></script> |
|
<script src="https://unpkg.com/d3-component@3.0.0"></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") |
|
.create(function (selection){ |
|
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]); |
|
selection |
|
.selectAll("circle").data(d3.range(numDots)) |
|
.enter().append("circle") |
|
.attr("cx", d => Math.round(Math.sin(angle(d)) * wheelRadius)) |
|
.attr("cy", d => Math.round(Math.cos(angle(d)) * wheelRadius)) |
|
.attr("r", d => Math.round(radius(d))); |
|
}) |
|
.render(function (selection, d){ |
|
selection.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") |
|
.create(function (selection, d){ |
|
timer.set(selection.node(), d3.timer(function (elapsed){ |
|
selection.call(wheel, elapsed * d.speed); |
|
})); |
|
}) |
|
.render(function (selection, d){ |
|
selection.attr("transform", "translate(" + d.x + "," + d.y + ")"); |
|
}) |
|
.destroy(function(selection, d){ |
|
timer.get(selection.node()).stop(); |
|
return selection |
|
.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") |
|
.render(function (selection, d){ |
|
selection.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") |
|
.render(function (selection, d){ |
|
selection |
|
.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> |