Highcharts - how can I center labels on a datetime

2019-02-07 15:11发布

I was having a hard time trying to figure out how to center labels on a datetime x-axis in Highcharts without using categories and tickPlacement (since tickPlacement only works on categories).

My axis was dynamically created so I could not simply set an x-offset or padding, as this would cause axes of different intervals to look strange.

After messing around with the config options I think I may have found a solution using the x-axis formatter and some css / jquery noodling in the Highcharts callback. See my answer below.

2条回答
贪生不怕死
2楼-- · 2019-02-07 15:28

Based on the existing answer, there is a much simpler solution that also works when resizing the browser window (or otherwise forcing the chart to redraw), even when the tick count changes: http://jsfiddle.net/McNetic/eyyom2qg/3/

It works by attaching the same event handler to both the load and the redraw events:

$('#container').highcharts({
  chart: {
    events: {
      load: fixLabels,
      redraw: fixLabels
    }
  },
[...]

The handler itself looks like this:

  var fixLabels = function() {
  var labels = $('div.highcharts-xaxis-labels span', this.container).sort(function(a, b) {
    return +parseInt($(a).css('left')) - +parseInt($(b).css('left'));
  });
  labels.css('margin-left', 
    (parseInt($(labels.get(1)).css('left')) - parseInt($(labels.get(0)).css('left'))) / 2
  );
  $(labels.get(this.xAxis[0].tickPositions.length - 1)).remove();
};

As you see, the extra wrapping of labels is unnecessary (at least if you do not have more than one xAxis). Basically, it works like this:

  1. Get all existing labels (when redrawn, this includes newly added ones). 2. Sort by css property 'left' (they are not sorted this way after some redrawing)
  2. Calculate offset between the first two labels (the offset is the same for all labels)
  3. Set half of the offset as margin-left of all labels, effectively shifting them half the offset to the right.
  4. Remove the rightmost label (moved outside of chart, by sometimes partly visible).
查看更多
The star\"
3楼-- · 2019-02-07 15:40

The trick is to use the x-axis labels object like this:

xAxis: {
  type: 'datetime',
  labels: {
    useHTML: true,
    align: 'center',
    formatter: function () {
      //using a specific class for the labels helps to ensure no other labels are moved 
      return '<span class="timeline_label">' + Highcharts.dateFormat(this.dateTimeLabelFormat, this.value) + '</span>';
    }
}

You can see that the formatter will keep whatever dateTimeLabelFormat has been set by the user or default.

Then have a callback that does something like this:

function (chart) {
  var $container = $(chart.container);
  var $labels = $container.find('.highcharts-axis-labels .timeline_label');
  var $thisLabel, $nextLabel, thisXPos, nextXPos, delta, newXPos;

  $labels.each(function () {
    $thisLabel = $(this).parent('span');
    thisXPos = parseInt($thisLabel.css('left'));

    $nextLabel = $thisLabel.next();
    nextXPos = $nextLabel.length ? parseInt($nextLabel.css('left')) : chart.axes[0].left + chart.axes[0].width;
    delta = (nextXPos - thisXPos) / 2.0;
    newXPos = thisXPos + delta;

    if ($nextLabel.length || $(this).width() + newXPos < nextXPos) {
      $thisLabel.css('left', newXPos + 'px');
    } else {
      $thisLabel.remove();
    }
  });
});

In short, this will go through each label and determine how much it should be moved over (using css) by calculating the distance between itself and the next label. When it reaches the the last label, it either moves it over using the end of the axis for the calculation or removes it if it won't fit. This last part is just the decision I decided to make, you can probably choose to do something else like word wrap, etc.

You can see the jsfiddle here

Hope this helps some people. Also, if there are any improvements it would be great to see them here.

查看更多
登录 后发表回答