block by emeeks fcb0e3dad2e10a8c5987

Radial Timeline

Full Screen

An example of d3.layout.timeline that takes advantage of d3.svg.arc and a radial scale to draw timelines in a radial manner.

index.html

<html xmlns="//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="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.min.js" charset="utf-8" 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>

d3.layout.timeline.js

(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;
}
})();