NOTE: This is a fork of the original Arc Tween example from Mike Bostock.
The goal is not to enhance the original example in any way, but to use its code as a base to create a prototype for a widget that can be used in a dashboard.
NOTE: This is a fork of the original Arc Tween example from Mike Bostock.
The goal is not to enhance the original example in any way, but to use its code as a base to create a prototype for a widget that can be used in a dashboard.
This file is just used to control the name of the gist. |
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<body> | |
<span id="arc1"></span> | |
<span id="arc2"></span> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<script> | |
var width = 478, | |
height = 500, | |
τ = 2 * Math.PI; // http://tauday.com/tau-manifesto | |
// An arc function with all values bound except the endAngle. So, to compute an | |
// SVG path string for a given angle, we pass an object with an endAngle | |
// property to the `arc` function, and it will return the corresponding string. | |
var arc = d3.svg.arc() | |
.innerRadius(110) | |
.outerRadius(120) | |
.startAngle(0); | |
// Creates a tween on the specified transition's "d" attribute, transitioning | |
// any selected arcs from their current angle to the specified new angle. | |
function arcTween(transition, newAngle) { | |
// The function passed to attrTween is invoked for each selected element when | |
// the transition starts, and for each element returns the interpolator to use | |
// over the course of transition. This function is thus responsible for | |
// determining the starting angle of the transition (which is pulled from the | |
// element's bound datum, d.endAngle), and the ending angle (simply the | |
// newAngle argument to the enclosing function). | |
transition.attrTween("d", function(d) { | |
// To interpolate between the two angles, we use the default d3.interpolate. | |
// (Internally, this maps to d3.interpolateNumber, since both of the | |
// arguments to d3.interpolate are numbers.) The returned function takes a | |
// single argument t and returns a number between the starting angle and the | |
// ending angle. When t = 0, it returns d.endAngle; when t = 1, it returns | |
// newAngle; and for 0 < t < 1 it returns an angle in-between. | |
var interpolate = d3.interpolate(d.endAngle, newAngle); | |
// The return value of the attrTween is also a function: the function that | |
// we want to run for each tick of the transition. Because we used | |
// attrTween("d"), the return value of this last function will be set to the | |
// "d" attribute at every tick. (It's also possible to use transition.tween | |
// to run arbitrary code for every tick, say if you want to set multiple | |
// attributes from a single function.) The argument t ranges from 0, at the | |
// start of the transition, to 1, at the end. | |
return function(t) { | |
// Calculate the current arc angle based on the transition time, t. Since | |
// the t for the transition and the t for the interpolate both range from | |
// 0 to 1, we can pass t directly to the interpolator. | |
// | |
// Note that the interpolated angle is written into the element's bound | |
// data object! This is important: it means that if the transition were | |
// interrupted, the data bound to the element would still be consistent | |
// with its appearance. Whenever we start a new arc transition, the | |
// correct starting angle can be inferred from the data. | |
d.endAngle = interpolate(t); | |
// Lastly, compute the arc path given the updated data! In effect, this | |
// transition uses data-space interpolation: the data is interpolated | |
// (that is, the end angle) rather than the path string itself. | |
// Interpolating the angles in polar coordinates, rather than the raw path | |
// string, produces valid intermediate arcs during the transition. | |
return arc(d); | |
}; | |
}); | |
} | |
function arcWidget(id, value, primaryText, secondaryText) { | |
// Create the SVG container, and apply a transform such that the origin is the | |
// center of the canvas. This way, we don't need to position arcs individually. | |
var svg = d3.select(id).append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.append("g") | |
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") | |
// Add the background arc, from 0 to 100% (τ). | |
var background = svg.append("path") | |
.datum({endAngle: τ}) | |
.style("fill", "#ddd") | |
.attr("d", arc); | |
// Add the foreground arc in LightSkyBlue, currently showing 0%. | |
var foreground = svg.append("path") | |
.datum({endAngle: 0}) | |
.style("fill", "LightSkyBlue") | |
.attr("d", arc) | |
.transition() | |
.duration(1000) | |
.call(arcTween, value * τ); | |
// Text | |
if (primaryText !== undefined) { | |
svg.append("text") | |
.attr("text-anchor", "middle") | |
.style("dominant-baseline", "middle") | |
.style("font-size", "36px") | |
.text(primaryText); | |
} | |
if (secondaryText !== undefined) { | |
svg.append("text") | |
.attr("y", "40px") | |
.attr("text-anchor", "middle") | |
.style("font-size", "16px") | |
.style("fill", "#666") | |
.text(secondaryText); | |
} | |
} | |
arcWidget("#arc1", 1/3, "1 of 3"); | |
arcWidget("#arc2", 0.62, "62%", "31MB of 50MB"); | |
</script> |