使用x范围和多个堆栈的高图表时间线图表

时间:2018-10-27 15:10:21

标签: javascript highcharts

我正在使用Highcharts创建时间线图,该图显示了随着时间的推移不同“状态”的流向。当前的实现位于http://jsfiddle.net/hq1kdpmo/8/,看起来像state chart

当前代码如下:

Highcharts.chart('container', {
  "chart": {
    "type": "xrange"
  },
  "title": {
    "text": "State Periods"
  },
  "xAxis": {
    "type": "datetime"
  },
  "yAxis": [{
    "title": {
      "text": "Factions"
    },
    "categories": ["A", "B", "C", "D"],
    "reversed": true
  }],
  "plotOptions": {
    "xrange": {
      "borderRadius": 0,
      "borderWidth": 0,
      "grouping": false,
      "dataLabels": {
        "align": "center",
        "enabled": true,
        "format": "{point.name}"
      },
      "colorByPoint": false
    }
  },
  "tooltip": {
    "headerFormat": "<span style=\"font-size: 0.85em\">{point.x} - {point.x2}</span><br/>",
    "pointFormat": "<span style=\"color:{series.color}\">●</span> {series.name}: <b>{point.yCategory}</b><br/>"
  },
  "series": [{
    "name": "State A",
    "pointWidth": 20,
    "data": [{
      "x": 1540430613000,
      "x2": 1540633768100,
      "y": 0
    }, {
      "x": 1540191009000,
      "x2": 1540633768100,
      "y": 1
    }, {
      "x": 1540191009000,
      "x2": 1540530613000,
      "y": 2
    }, {
      "x": 1540530613000,
      "x2": 1540633768100,
      "y": 3
    }]
  }, {
    "name": "State B",
    "pointWidth": 20,
    "data": [{
      "x": 1540191009000,
      "x2": 1540430613000,
      "y": 0
    }, {
      "x": 1540530613000,
      "x2": 1540633768100,
      "y": 2
    }, {
      "x": 1540191009000,
      "x2": 1540330613000,
      "y": 3
    }]
  }, {
    "name": "State C",
    "pointWidth": 20,
    "data": [{
      "x": 1540330613000,
      "x2": 1540530613000,
      "y": 3
    }]
  }],
  "exporting": {
    "enabled": true,
    "sourceWidth": 1200
  }
});

现在,我期盼的是创造这种东西(请原谅我的绘画技能)chart to be

这里,类别A到D是y上的唯一轴。但是我想对可变范围的并行范围进行分组。用例是在任何时间点可以有多个状态,并且在任何时间点的状态数都是可变的。我该怎么做呢?

3 个答案:

答案 0 :(得分:1)

要创建这样的图表,您将必须添加12 yAxis(0-11)并设置适当的刻度和标签,以便仅绘制A-D类别。此外,调整plotOptions.pointPaddingplotOptions.groupPadding属性以自动设置点的宽度(然后应先定义series.pointWidth)。

yAxis选项:

  yAxis: [{
    title: {
      text: "Factions"
    },
    categories: ["A", "B", "C", "D"],
    tickPositions: [-1, 2, 5, 8, 11],
    lineWidth: 0,
    labels: {
      y: -20,
      formatter: function() {
        var chart = this.chart,
          axis = this.axis,
          label;

        if (!chart.yaxisLabelIndex) {
          chart.yaxisLabelIndex = 0;
        }

        if (this.value !== -1) {

          label = axis.categories[chart.yaxisLabelIndex];
          chart.yaxisLabelIndex++;

          if (chart.yaxisLabelIndex === 4) {
            chart.yaxisLabelIndex = 0;
          }

          return label;
        }
      },
    },
    reversed: true
  }]

演示: https://jsfiddle.net/wchmiel/s9qefg7t/1/

答案 1 :(得分:1)

感谢Highcharts本身的支持,我设法解决了这个问题。这个想法是在load事件上设置刻度位置,并使用labels.formatter来格式化每个标签。

events: {
  load() {
    let labelGroup = document.querySelectorAll('.highcharts-yaxis-labels');
    // nodeValue is distance from top
    let ticks = document.querySelectorAll('.highcharts-yaxis-grid');
    let tickPositions = Array.from(ticks[0].childNodes).map(
        function(node){
            return +node.attributes.d.nodeValue.split(" ")[2];
        }
    );
    let labelPositions = [];
    for(let i =1 ;i<tickPositions.length;i++){
        labelPositions.push((tickPositions[i] + tickPositions[i-1])/2);
    }
    labelGroup[0].childNodes[0].attributes.y.nodeValue = labelPositions[0] + parseFloat(labelGroup[0].childNodes[0].style["font-size"], 10) / 2;
    labelGroup[0].childNodes[1].attributes.y.nodeValue = labelPositions[1] + parseFloat(labelGroup[0].childNodes[1].style["font-size"], 10) / 2;
    labelGroup[0].childNodes[2].attributes.y.nodeValue = labelPositions[2] + parseFloat(labelGroup[0].childNodes[2].style["font-size"], 10) / 2;
    labelGroup[0].childNodes[3].attributes.y.nodeValue = labelPositions[3] + parseFloat(labelGroup[0].childNodes[3].style["font-size"], 10) / 2;
    labelGroup[0].childNodes[4].attributes.y.nodeValue = labelPositions[4] + parseFloat(labelGroup[0].childNodes[4].style["font-size"], 10) / 2;
  }
}

标签的格式为:

labels: {
  formatter: function() {
    var chart = this.chart,
      axis = this.axis,
      label;

    if (!chart.yaxisLabelIndex) {
      chart.yaxisLabelIndex = 0;
    }
    if (this.value !== -1) {

      label = axis.categories[chart.yaxisLabelIndex];
      chart.yaxisLabelIndex++;

      if (chart.yaxisLabelIndex === groups.length) {
        chart.yaxisLabelIndex = 0;
      }

      return label;
    }
  },
}

拨弄https://jsfiddle.net/yvnp4su0/42/

答案 2 :(得分:1)

正如我在上面的注释中所建议的那样,使用Highcharts renderer并添加自定义标签比使用上一个答案中的操作Dom元素更好,因为它是一种更干净的解决方案。

禁用默认标签:

  yAxis: [{
    title: {
      text: "Factions",
      margin: 35
    },
    categories: ["A", "B", "C", "D", "E"],
    tickPositions: tickPositions,
    lineWidth: 0,
    labels: {
      enabled: false
    },
    reversed: true
  }]

使用渲染器在适当位置添加自定义标签:

  chart: {
    type: 'xrange',
    height: 500,
    marginLeft: 60,
    events: {
        load: function() {
        this.customLabels = [];
      },
      render: function() {
        var chart = this,
          yAxis = chart.yAxis[0],
          categories = yAxis.categories,
          xOffset = 15,
          yOffset = 20,
          xPos = yAxis.left - xOffset,
          tickPositions = yAxis.tickPositions,
          text,
          label,
          yPos,
          tick1Y,
          tick2Y,
          i;

        for (i = 0; i < tickPositions.length - 1; i++) {
            if (chart.customLabels[i]) {
            chart.customLabels[i].destroy();
          }

          tick1Y = yAxis.toPixels(tickPositions[i]);
          tick2Y = yAxis.toPixels(tickPositions[i + 1]);
          yPos = (tick1Y + tick2Y) / 2 + yOffset;
          text = categories[i];

          label = chart.renderer.text(text, xPos, yPos)
            .css({
              color: '#ccc',
              fontSize: '14px'
            })
            .add();

          chart.customLabels[i] = label;
        }
      }
    }
  }

演示:https://jsfiddle.net/wchmiel/vkz7o1hw/