【问题标题】:Create a gantt chart using d3.js [closed]使用 d3.js 创建甘特图 [关闭]
【发布时间】:2016-09-26 17:44:18
【问题描述】:

这是我拥有的数据类型:

[
  {'TotalTime': 10, 'Hour': 0, 'Name': 'Sam'}, 
  {'TotalTime': 15, 'Hour': 1, 'Name': 'Bob'}, 
  {'TotalTime': 300, 'Hour': 2, 'Name': 'Tom'},
  ... and so on till, 
  {'TotalTime': 124, 'Hour': 23, 'Name': 'Jon'}
]

一天中每个小时的数据。我希望从中创建一个甘特图,其中条的大小基于 TotalTime。

y 轴上的名称和 x 上的小时。

是否可以在d3.js上制作没有开始时间和结束时间的甘特图?

【问题讨论】:

标签: javascript d3.js


【解决方案1】:

这是可能的,但如果您使用的是 D3.js,则需要自己绘制。因此,如果您以前制作过条形图,沿着这些线,设置一些轴,将它们添加到 SVG 并使用它们将数据转换为您将放在图表上的矩形,然后用数据中的名称标记矩形. D3.js 不包含此布局。如果您还没有这样做,请查看tutorialsLet’s Make a Bar Chart, Parts IIIIII,然后继续查看custom time axis examplerelatedAP Is.

还有很多其他的基于 D3.js 的库,比如 C3,它们提供了预制图表(比如 D3 的布局),但我不知道有哪个库可以提供甘特图。那里有one example Gantt chart(随机将任务添加到从 1 小时到 1 周的各种缩放时间视图中),但我发现它比下面我自己的时间块更令人困惑。 YMMV。

我用 d3js 制作了一个更像日历的图表,您可以在此处阅读:https://github.com/dlamblin/timeblocks。您有不同的输入数据格式,但您可以在紧要关头进行调整并交换轴以进行旋转。假设你愿意尽快做这件事。

为了使上述内容更易于阅读和查看,我将其拆分为JSfiddle example

这里只是嵌入到答案中的 JavaScript(这不是甘特图,它是每周 7 天计划时间块的垂直布局):

    var timeFmt = d3.time.format.utc('%H.%M'),
      weekdaydef = 'Mon Tue Wed Thu Fri Sat Sun'.split(' '),
      weekdayseq = 'Mon Tue Wed Thu Fri Sat Sun'.split(' '),
      axes = [null, null, null, null];

    function hm(i) {
      var s = i.toFixed(2);
      return timeFmt.parse((s.length < 4) ? '0' + s : s);
    }
    var timeData = [
        {key: "m1","wday": 0,"begin": hm(6.00),"end": hm(7.00),
        label: "Rising, dress etc\n\retc"},
        {key: "m2","wday": 0,"begin": hm(7.00),"end": hm(7.30),
        label: "Prep Sophie"},
        {key: "m3","wday": 0,"begin": hm(7.30),"end": hm(8.00),
        label: "Transit to School"
      }, {
        key: "t1",
        "wday": 1,
        "begin": hm(6.00),
        "end": hm(7.00),
        label: "Rising, dress etc"
      }, {
        key: "t2",
        "wday": 1,
        "begin": hm(17.00),
        "end": hm(18.00),
        label: "call"
      },

      {
        key: "w1",
        "wday": 2,
        "begin": hm(6.00),
        "end": hm(7.00),
        'color': 0,
        label: "Rising, dress etc"
      }, {
        key: "w2",
        "wday": 2,
        "begin": hm(7.00),
        "end": hm(7.30),
        'color': 0,
        label: "Prep Sophie"
      }, {
        key: "w3",
        "wday": 2,
        "begin": hm(7.30),
        "end": hm(8.00),
        'color': 1,
        label: "Transit to School"
      }, {
        key: "w4",
        "wday": 2,
        "begin": hm(8.00),
        "end": hm(9.00),
        'color': 2,
        label: "Read Emails"
      }, {
        key: "w5",
        "wday": 2,
        "begin": hm(9.00),
        "end": hm(10.00),
        'color': 2,
        label: "Write Emails"
      }, {
        key: "w6",
        "wday": 2,
        "begin": hm(10.00),
        "end": hm(13.00),
        'color': 3,
        label: "Job"
      }, {
        key: "w7",
        "wday": 2,
        "begin": hm(13.00),
        "end": hm(14.00),
        'color': 4,
        label: "Lunch & Meditation"
      }, {
        key: "w8",
        "wday": 2,
        "begin": hm(14.00),
        "end": hm(15.00),
        'color': 5,
        label: "Pick Sophie & Home"
      }, {
        key: "w9",
        "wday": 2,
        "begin": hm(15.00),
        "end": hm(18.00),
        'color': 0,
        label: "Clean"
      }, {
        key: "wa",
        "wday": 2,
        "begin": hm(18.00),
        "end": hm(19.00),
        'color': 0,
        label: "Plan"
      }, {
        key: "wb",
        "wday": 2,
        "begin": hm(19.00),
        "end": hm(20.00),
        'color': 0,
        label: "Wrap: Read Email & Clean"
      },

      {
        key: "r1",
        "wday": 3,
        "begin": hm(6.00),
        "end": hm(7.00),
        label: "Rising, dress etc"
      },

      {
        key: "f1",
        "wday": 4,
        "begin": hm(6.00),
        "end": hm(7.00),
        label: "Rising, dress etc"
      }
    ];
    timeData = timeData.sort(function(a, b) {
      var o = d3.ascending(a.wday, b.wday);
      return o === 0 ? d3.ascending(a.begin, b.begin) : o;
    });
    // Spacing out times by 5 minutes... see display
    // var timeDataMap = d3.map(timeData, function(d) {return d.key;});
    // timeDataMap.forEach(function(k,v) {v.end.setMinutes(v.end.getMinutes()-5);});
    // timeData = timeDataMap.values();
    var scale, colors = d3.scale.category10();
    colors.range(d3.range(10).map(
      function(i) {
        return d3.rgb(colors(i)).brighter(1.25).toString();
      }));

    function d3UpdateScales() {
      var svg = d3.select('#timeblock')[0][0],
        margin = {
          top: 25,
          right: 80,
          bottom: 25,
          left: 80
        },
        width = svg.clientWidth - margin.left - margin.right,
        height = svg.clientHeight - margin.top - margin.bottom;
      return scale = {
        margin: margin,
        width: width,
        height: height,
        time: d3.time.scale.utc() // not d3.scale.linear()
          .domain([d3.min(timeData, function(d) {
              return d.begin
            }),
            d3.max(timeData, function(d) {
              return d.end
            })
          ])
          .rangeRound([0, height]),
        days: d3.scale.ordinal()
          .domain(weekdayseq)
          .rangePoints([0, width]),
        week: d3.scale.ordinal()
          .domain(weekdayseq)
          .rangeRoundBands([0, width], 0.05),
      }
    }

    function d3Update() {
      var scale = d3UpdateScales();

      // Update…
      var svg = d3.select('#timeblock');
      if (svg.select('g.view')[0][0] == null) {
        svg.append('g').attr('class', 'view').attr('transform', 'translate(' + scale.margin.left + ',' + scale.margin.top + ')');
      }
      var g = svg.select('g.view').selectAll('g.data')
        .data(timeData);

      // Enter…
      var ge = g.enter()
        .append("g")
        .attr('class', 'data');

      ge.append("rect")
        .attr("x", function(d) {
          return scale.week(weekdaydef[d.wday]) + (scale.week.rangeBand() / 2)
        })
        .attr("y", function(d) {
          var e = new Date(d.end);
          e.setMinutes(e.getMinutes() - 5);
          return scale.time(d.begin) + ((scale.time(e) - scale.time(d.begin)) / 2)
        })
        .attr("width", 0)
        .attr("height", 0)
        .attr("style", function(d) {
          return ("color" in d) ? "fill:" + colors(d.color) : null
        })
      ge.append("text")
        .attr("dy", "1.1em")
        .text(function(d) {
          return d.label;
        });

      // Exit…
      g.exit().remove();

      // Update…
      g.select("rect")
        .transition()
        .attr("x", function(d) {
          return scale.week(weekdaydef[d.wday])
        })
        .attr("y", function(d) {
          return scale.time(d.begin)
        })
        .attr("width", function(d) {
          return scale.week.rangeBand()
        })
        .attr("height", function(d) {
          var e = new Date(d.end);
          e.setMinutes(e.getMinutes() - 5);
          return (scale.time(e) - scale.time(d.begin))
        })
      g.select("text")
        .transition()
        .attr("x", function(d) {
          return scale.week(weekdaydef[d.wday]) + 5
        })
        .attr("y", function(d) {
          return scale.time(d.begin)
        });

      axesAddOrUpdate(svg);
    }

    function axesAddOrUpdate(svg) {
      var xaxis_t = d3.svg.axis().scale(scale.week).tickSize(13).orient('top'),
        yaxis_r = d3.svg.axis().scale(scale.time).tickSize(7).orient('right'),
        xaxis_b = d3.svg.axis().scale(scale.week).tickSize(13),
        yaxis_l = d3.svg.axis().scale(scale.time).tickSize(7).orient('left');
      // global axes array contains top, right, bottom, left axis.
      if (null == axes[0]) {
        axes[0] = svg.append("g").attr('class', 'axis')
          .attr('transform', 'translate(' + String(scale.margin.left) + ',' + String(scale.margin.top) + ')')
          .call(xaxis_t);
      } else {
        axes[0].transition().call(xaxis_t);
      }
      if (null == axes[2]) {
        axes[2] = svg.append("g").attr('class', 'axis')
          .attr('transform', 'translate(' + String(scale.margin.left) + ',' + String(scale.height + scale.margin.top) + ')')
          .call(xaxis_b);
      } else {
        axes[2].transition().call(xaxis_b);
      }
      if (null == axes[3]) {
        axes[3] = svg.append("g").attr('class', 'axis')
          .attr('transform', 'translate(' + String(scale.margin.left - 5) + ',' + String(scale.margin.top) + ')')
          .call(yaxis_l);
      } else {
        axes[3].transition().call(yaxis_l);
      }
      if (null == axes[1]) {
        axes[1] = svg.append("g").attr('class', 'axis')
          .attr('transform', 'translate(' + String(scale.margin.left + scale.width + 5) + ',' + String(scale.margin.top) + ')')
          .call(yaxis_r);
      } else {
        axes[1].transition().call(yaxis_r);
      }
    }

    window.onload = d3Update;

    d3.select('#b_rotate').on("click", function(d, i, t) {
      weekdayseq.push(weekdayseq.shift());
      d3Update();
    });

【讨论】:

  • 嘿!谢谢!我正在做这件事。同时,你有没有机会知道这个问题的答案?stackoverflow.com/questions/37498470/…
  • @glitterati 我不使用 MongoDB,所以我不知道你的问题的答案。不过,其他人可能已经制作了映射库。我添加了一些文档链接和一个示例甘特图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-28
  • 1970-01-01
  • 1970-01-01
  • 2013-08-06
  • 1970-01-01
相关资源
最近更新 更多