Skip to content

Instantly share code, notes, and snippets.

@ramnathv
Last active August 29, 2015 14:14
Show Gist options
  • Save ramnathv/d748ae9e9eb62b1eff67 to your computer and use it in GitHub Desktop.
Save ramnathv/d748ae9e9eb62b1eff67 to your computer and use it in GitHub Desktop.
Arc Tween
{"description":"Arc Tween","endpoint":"","display":"svg","public":true,"require":[],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"fullscreen":false,"play":false,"loop":false,"restart":false,"autoinit":true,"pause":true,"loop_type":"pingpong","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01,"ajax-caching":true,"thumbnail":"http://i.imgur.com/FJYlJfC.png"}
var cell = 86
var width = cell,
height = cell,
tau = 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(cell*0.35)
.outerRadius(cell*0.45)
.startAngle(0.3*tau)
var margin = {top: 20, left: 25, bottom: 20, right: 20}
// 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("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + (margin.left + width / 2) + "," + (margin.right + height / 2) + ")")
// Add the background arc, from 0 to 100% (τ).
var background = svg.append("path")
.datum({endAngle: tau})
.style("fill", "#ddd")
.attr("d", arc);
// Add the foreground arc in orange, currently showing 12.7%.
var foreground = svg.append("path")
.datum({endAngle: 0.3*tau})
.style("fill", "darkred")
.attr("d", arc);
foreground
.transition()
.duration(750)
.call(arcTween, 0.5*tau)
svg.append("text")
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("dy", 0)
.text(20)
// Every so often, start a transition to a new random angle. Use transition.call
// (identical to selection.call) so that we can encapsulate the logic for
// tweening the arc in a separate function below.
/*
setInterval(function() {
foreground.transition()
.duration(750)
.call(arcTween, Math.random() * tau);
}, 1500);
*/
// 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);
};
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment