An example of d3.layout.timeline that takes advantage of d3.svg.arc and a radial scale to draw timelines in a radial manner.
-
-
Save sivartravis/d6ce7d02887712e6fa7e to your computer and use it in GitHub Desktop.
Radial Timeline
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
d3.layout.timeline = function() { | |
var timelines = []; | |
var dateAccessor = function (d) {return new Date(d)}; | |
var processedTimelines = []; | |
var startAccessor = function (d) {return d.start}; | |
var endAccessor = function (d) {return d.end}; | |
var size = [500,100]; | |
var timelineExtent = [-Infinity, Infinity]; | |
var setExtent = []; | |
var displayScale = d3.scale.linear(); | |
var swimlanes = []; | |
var padding = 0; | |
var fixedExtent = false; | |
var maximumHeight = Infinity; | |
function processTimelines() { | |
timelines.forEach(function (band) { | |
var projectedBand = {}; | |
for (var x in band) { | |
if (band.hasOwnProperty(x)) { | |
projectedBand[x] = band[x]; | |
} | |
} | |
projectedBand.start = dateAccessor(startAccessor(band)); | |
projectedBand.end = dateAccessor(endAccessor(band)); | |
projectedBand.lane = 0; | |
processedTimelines.push(projectedBand); | |
}); | |
} | |
function projectTimelines() { | |
if (fixedExtent === false) { | |
var minStart = d3.min(processedTimelines, function (d) {return d.start}); | |
var maxEnd = d3.max(processedTimelines, function (d) {return d.end}); | |
timelineExtent = [minStart,maxEnd]; | |
} | |
else { | |
timelineExtent = [dateAccessor(setExtent[0]), dateAccessor(setExtent[1])]; | |
} | |
displayScale.domain(timelineExtent).range([0,size[0]]); | |
processedTimelines.forEach(function (band) { | |
band.originalStart = band.start; | |
band.originalEnd = band.end; | |
band.start = displayScale(band.start); | |
band.end = displayScale(band.end); | |
}); | |
} | |
function fitsIn(lane, band) { | |
if (lane.end < band.start || lane.start > band.end) { | |
return true; | |
} | |
var filteredLane = lane.filter(function (d) {return d.start <= band.end && d.end >= band.start}); | |
if (filteredLane.length === 0) { | |
return true; | |
} | |
return false; | |
} | |
function findlane(band) { | |
//make the first array | |
if (swimlanes[0] === undefined) { | |
swimlanes[0] = [band]; | |
return; | |
} | |
var l = swimlanes.length - 1; | |
var x = 0; | |
while (x <= l) { | |
if (fitsIn(swimlanes[x], band)) { | |
swimlanes[x].push(band); | |
return; | |
} | |
x++; | |
} | |
swimlanes[x] = [band]; | |
return; | |
} | |
function timeline(data) { | |
if (!arguments.length) return timeline; | |
timelines = data; | |
processedTimelines = []; | |
swimlanes = []; | |
processTimelines(); | |
projectTimelines(); | |
processedTimelines.forEach(function (band) { | |
findlane(band); | |
}); | |
var height = size[1] / swimlanes.length; | |
height = Math.min(height, maximumHeight); | |
swimlanes.forEach(function (lane, i) { | |
lane.forEach(function (band) { | |
band.y = i * (height); | |
band.dy = height - padding; | |
band.lane = i; | |
}); | |
}); | |
return processedTimelines; | |
} | |
timeline.dateFormat = function (_x) { | |
if (!arguments.length) return dateAccessor; | |
dateAccessor = _x; | |
return timeline; | |
} | |
timeline.bandStart = function (_x) { | |
if (!arguments.length) return startAccessor; | |
startAccessor = _x; | |
return timeline; | |
} | |
timeline.bandEnd = function (_x) { | |
if (!arguments.length) return endAccessor; | |
endAccessor = _x; | |
return timeline; | |
} | |
timeline.size = function (_x) { | |
if (!arguments.length) return size; | |
size = _x; | |
return timeline; | |
} | |
timeline.padding = function (_x) { | |
if (!arguments.length) return padding; | |
padding = _x; | |
return timeline; | |
} | |
timeline.extent = function (_x) { | |
if (!arguments.length) return timelineExtent; | |
fixedExtent = true; | |
setExtent = _x; | |
if (_x.length === 0) { | |
fixedExtent = false; | |
} | |
return timeline; | |
} | |
timeline.maxBandHeight = function (_x) { | |
if (!arguments.length) return maximumHeight; | |
maximumHeight = _x; | |
return timeline; | |
} | |
return timeline; | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title>Radial Timeline with Procedurally Generated Data</title> | |
<meta charset="utf-8" /> | |
<style type="text/css"> | |
svg { | |
height: 1100px; | |
width: 1100px; | |
} | |
div.viz { | |
height: 1000px; | |
width: 1000px; | |
} | |
</style> | |
</head> | |
<script src="http://d3js.org/d3.v3.min.js" type="text/JavaScript"></script> | |
<script src="d3.layout.timeline.js" charset="utf-8" type="text/javascript"></script> | |
<script> | |
var timelineData = []; | |
var timeline = d3.layout.timeline() | |
.size([1000,300]) | |
.bandStart(function (d) {return d.s}) | |
.bandEnd(function (d) {return d.e}) | |
.dateFormat(function (d) {return parseInt(d)}) | |
.padding(5) | |
.extent([0,40]) | |
.maxBandHeight(20); | |
addData() | |
function addData() { | |
var arc = d3.svg.arc(); | |
rando = Math.floor(Math.random() * 40); | |
rando2 = Math.floor(Math.random() * 20); | |
timelineData.push({s: rando, e: Math.min(40, rando + rando2)}); | |
timelineBands = timeline(timelineData); | |
angleScale = d3.scale.linear().domain([0,1000]).range([0,(2 * Math.PI)]); | |
timelineBands.forEach(function (d) { | |
d.startAngle = angleScale(d.start); | |
d.endAngle = angleScale(d.end); | |
d.y = d.y + 50; | |
}) | |
d3.select("svg").selectAll("path") | |
.data(timelineBands) | |
.enter() | |
.append("path") | |
.attr("transform", "translate(500,250)") | |
.style("fill-opacity", 0) | |
.attr("d", function (d) {return arc.innerRadius(10).outerRadius(d.dy + 10)(d)}) | |
var size = timelineBands.length; | |
d3.selectAll("path") | |
.transition() | |
.duration(400) | |
.attr("d", function (d) {return arc.innerRadius(d.y).outerRadius(d.y + d.dy)(d)}) | |
.attr("x", function (d) {return d.start}) | |
.attr("y", function (d) {return d.y}) | |
.attr("height", function (d) {return d.dy}) | |
.attr("width", function (d) {return d.end - d.start}) | |
.style("fill", "#b0909d") | |
.style("fill-opacity", function (d, i) {return Math.max(0.05, 1 - ((size - i) * .01))}); | |
if (size < 100) | |
{ | |
setTimeout(addData, 500); | |
} | |
} | |
</script> | |
<body> | |
<div id="viz"> | |
<svg style="background:white;" height=1100 width=1100> | |
</svg> | |
</div> | |
<footer> | |
</footer> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment