防止d3.js时间轴中的重叠水平条

时间:2016-12-07 17:11:28

标签: d3.js

我正在使用d3.js创建事件的时间轴。一行中的某些事件相互重叠。即,下一个在前一个结束之前开始。 e.g:

enter image description here

对于每个栏,如果它与前一个栏重叠,我想将其移动:

enter image description here

我该怎么做? Here's a JSfiddle that generates the first example.

以下是一些相关部分:

var spanX = function(d, i) { return xScale(d['begin']); },
    spanY = function(d) { return 0; },
    spanW = function(d, i) {
      return xScale(d['end']) - xScale(d['begin']);
    },
    spanH = 10; 

var span = chart.selectAll('.chart-span')
                    .data(data) 
                    .enter().append('rect')
                      .classed('chart-span', true)
                      .attr('x', spanX)
                      .attr('y', spanY)
                      .attr('width', spanW)
                      .attr('height', spanH);

(还有其他问题,例如在同一个图表上显示更多时间轴,并在左侧显示描述每个时间轴的标签......但一次只出现一个问题!)

更新:对于以下人士,请查看完成的图表:http://spelunker.moma.org/departments/(2017-02-08)

1 个答案:

答案 0 :(得分:1)

最好的解决方案是创建一个算法来检查前一个柱的结束,并根据之前的柱结束设置下一个柱的y位置(这样的算法将是您下两个问题的基础, ...在同一图表上相互显示更多时间轴,并在左侧显示描述每个时间轴的标签“)。

但是,由于简单(和懒惰),如果您的数据不是动态的或您的图表不是非常复杂,您可以设置(硬编码)数据中的位置:

var data = [
      {'begin': 2001, 'end': 2005, 'pos': 1},
      {'begin': 2006, 'end': 2010, 'pos': 1},
      {'begin': 2008, 'end': 2012, 'pos': 2},
      {'begin': 2011, 'end': 2015, 'pos': 3}
]; 

然后定义您的y比例和spanY

yScale.domain(data.map(d=> d.pos))
    .range([0, height]);

spanY = function(d) { return yScale(d.pos); }

这是一个演示:

var data = [
				  {'begin': 2001, 'end': 2005, 'pos': 1},
				  {'begin': 2006, 'end': 2010, 'pos': 1},
				  {'begin': 2008, 'end': 2012, 'pos': 2},
				  {'begin': 2011, 'end': 2015, 'pos': 3}
				];

      var margin = {top: 20, right: 30, bottom: 50, left: 40},
          width = 500 - margin.left - margin.right,
          height = 120 - margin.top - margin.bottom,
          xScale = d3.scaleBand(),
					yScale = d3.scaleBand(),
          xAxis = d3.axisBottom(xScale),
          minX = d3.min(data, function(d) { return d['begin']; }),
          maxX = d3.max(data, function(d) { return d['end']; });

      xScale.domain(d3.range(minX, maxX+1));

      xScale.rangeRound([0, width]);
			
			yScale.domain(data.map(d=> d.pos))
				.range([0, height]);

      var spanX = function(d, i) { return xScale(d['begin']); },
          spanY = function(d) { return yScale(d.pos); },
          spanW = function(d, i) {
            return xScale(d['end']) - xScale(d['begin']);
          },
          spanH = 10; 

      var chart = d3.select('.chart')
            .attr('width', width + margin.left + margin.right)
            .attr('height', height + margin.left + margin.right)
          .append('g')
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

			// Add x axis
      chart.append("g")
              .attr("class", "axis axis-x");

      chart.select('.axis-x')
              .attr("transform", "translate(0," + height + ")")
              .call(xAxis);

			// Add spans
      var span = chart.selectAll('.chart-span')
                        .data(data) 
                        .enter().append('rect')
                          .classed('chart-span', true)
                          .attr('x', spanX)
                          .attr('y', spanY)
                          .attr('width', spanW)
                          .attr('height', spanH);
.chart-span {
      fill: #333;
      opacity: 0.5;
    }
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg class="chart"></svg>

PS:同样,这个硬编码解决方案对于您接下来的两个问题(“...在同一个图表上显示更多时间轴,并显示描述左侧“”上每个时间轴的标签。当您发布这些问题时,请更详细地描述数据的方式,以便人们可以考虑使用一种好的算法来计算条形位置。