在甘特图d3.js上绘制多条线

时间:2017-06-21 16:10:19

标签: javascript css d3.js

我正在构建这个甘特图,我在块上找到了一个例子:

https://bl.ocks.org/arpitnarechania/4b4aa79b04d2e79f30765674b4c24ace

效果很好。我只想在“任务”之上添加一条垂直线,以指示在任务进行过程中发生的其他事件。我们的想法是在矩形上绘制一条垂直线: enter image description here

我唯一不确定的是如何处理从时间戳数组中绘制多条线。我知道目前我正在使用d.synched[0]因此只有一行,但我如何更改它以绘制与时间戳一样多的行。

这是我的JSBIN:https://jsbin.com/layecehidu/edit?js,console,output

任何,所有的帮助将不胜感激!

1 个答案:

答案 0 :(得分:1)

最简单的解决方案,即在不更改代码的情况下,为data中的每个对象创建一个组:

var lineGroup = svg.selectAll(null)
    .data(data)
    .enter()
    .append("g");

然后根据这些群组的数据创建行:

var lines = lineGroup.selectAll(null)
    .data(function(d) {
        return d.synched
    })
    .enter()
    .append("line")
    .style("stroke", "red")
    .attr("x1", function(d) {
        console.log(d);
        return x(parseDate(d));
    })
    .attr("y1", function(d) {
        var parentDatum = d3.select(this.parentNode).datum();
        return y(parentDatum.category) - 5;
    })
    .attr("x2", function(d) {
        return x(parseDate(d));
    })
    .attr("y2", function(d) {
        var parentDatum = d3.select(this.parentNode).datum();
        return y(parentDatum.category) + y.rangeBand() + 5;
    });

以下是更新后的代码:

var parseDate = d3.time.format("%d-%b-%y").parse;

var data = [{
  "category": "Task 1",
  "from": "1-Jan-17",
  "to": "15-Jan-17",
  "progress": 100,
  "synched": ["5-Jan-17", "7-Jan-17"]
}, {
  "category": "Task 2",
  "from": "13-Jan-17",
  "to": "1-Feb-17",
  "progress": 60,
  "synched": ["15-Jan-17"]
}, {
  "category": "Task 1",
  "from": "1-Feb-17",
  "to": "11-Mar-17",
  "progress": 90,
  "synched": ["2-Feb-17", "4-Feb-17"]
}]

var types_of_statuses = ["Completed", "Remaining"];
var statuses_color = ["#2ecc71", "#e74c3c"];

data.forEach(function(d) {
  d.from = parseDate(d.from);
  d.to = parseDate(d.to);
});
var margin = {
    top: 50,
    right: 50,
    bottom: 50,
    left: 100
  },
  width = 900 - margin.left - margin.right,
  height = 500 - margin.top - margin.bottom;

var y = d3.scale.ordinal()
  .rangeRoundBands([0, height], 0.2);

var x = d3.time.scale().range([0, width]);

y.domain(data.map(function(d) {
  return d.category;
}));
x.domain([d3.min(data, function(d) {
  return d.from;
}), d3.max(data, function(d) {
  return d.to;
})]);

var xAxis = d3.svg.axis()
  .scale(x)
  .orient("bottom")
  .ticks(15)
  .tickFormat(d3.time.format("%d%b"));

var yAxis = d3.svg.axis()
  .scale(y)
  .orient("left");

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

svg.selectAll(".bar")
  .data(data)
  .enter().append("rect")
  .attr("class", "bar")
  .attr("y", function(d) {
    return y(d.category);
  })
  .attr("height", y.rangeBand())
  .attr("x", function(d) {
    return x(d.from);
  })
  .attr("width", function(d) {
    return x(d.to) - x(d.from);
  });

var lineGroup = svg.selectAll(null)
  .data(data)
  .enter()
  .append("g");

var lines = lineGroup.selectAll(null)
  .data(function(d) {
    return d.synched
  })
  .enter()
  .append("line")
  .style("stroke", "red")
  .attr("x1", function(d) {
    return x(parseDate(d));
  })
  .attr("y1", function(d) {
    var parentDatum = d3.select(this.parentNode).datum();
    return y(parentDatum.category) - 5;
  })
  .attr("x2", function(d) {
    return x(parseDate(d));
  })
  .attr("y2", function(d) {
    var parentDatum = d3.select(this.parentNode).datum();
    return y(parentDatum.category) + y.rangeBand() + 5;
  });


svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + height + ")")
  .call(xAxis)
  .append("text")
  .attr("x", width - margin.right)
  .attr("dx", ".71em")
  .attr("dy", "-0.2em")
  .text("Date");

svg.append("g")
  .attr("class", "y axis")
  .call(yAxis);
.pending {
        fill: #e74c3c;
    }

    .bar {
        fill: #2ecc71;
    }

/*     .bar:hover, .pending:hover {
      fill: #3498db;
    } */

    .axis path,
    .axis line {
      fill: none;
      stroke: #000;
      shape-rendering: crispEdges;
    }

    .legend {
        padding: 5px;
        font: 16px sans-serif;
        background: yellow;
        box-shadow: 2px 2px 1px #888;
    }

    .tooltip {
        background: #eee;
        box-shadow: 0 0 5px #999999;
        color: #333;
        font-size: 12px;
        left: 130px;
        padding: 10px;
        position: absolute;
        text-align: center;
        top: 95px;
        z-index: 10;
        display: block;
        opacity: 0;
    }
.axis,
.frame {
    shape-rendering: crispEdges;
}

    .axis path {
        fill: none;
        stroke: grey;
        shape-rendering: crispEdges;
    }

    .axis text {
        font-family: Arial;
        font-size: 10px;
    }

    .axis line {
        fill: none;
        stroke: grey;
        stroke-width: 1;
        shape-rendering: crispEdges;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

请注意,为了获取每一行的category,我们必须访问行选择中的父数据(即组)。

如果需要,另一个选项是使用父级(组)数据填充行的数据,如下所示:

var lines = lineGroup.selectAll(null)
    .data(function(d) {
        return d.synched.map(function(e) {
            return {
                syn: e,
                cat: d.category
            };
        })
    })
    .enter()
    .append("line")
    .style("stroke", "red")
    .attr("x1", function(d) {
        return x(parseDate(d.syn));
    })
    .attr("y1", function(d) {
        return y(d.cat) - 5;
    })
    .attr("x2", function(d) {
        return x(parseDate(d.syn));
    })
    .attr("y2", function(d) {
        return y(d.cat) + y.rangeBand() + 5;
    });