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> |