AngularJS 标签中的 Highcharts 动态(重新)调整大小

Highcharts dynamic (re-)sizing in AngularJS tabs

我的应用程序在各种选项卡中显示 HighCharts 图表(使用 AngularJS)。请注意,图表会在运行时重新生成 每次选择选项卡时 (意味着每次 Angular "removes or recreates a portion of the DOM tree")。

我的问题是图表的大小是正确的只是第一次我单击了一个选项卡。当我切换选项卡时,创建的图表大大超过了它们容器的大小。令人惊讶的是,图表在 window 调整大小后(即调用 chart.reflow() 时)正确调整大小。

所以我尝试了以下方法,但没有帮助:

$(element).highcharts({
    chart: {
      type: 'scatter',
      zoomType: 'xy',
      events: {
        load: function () {
          this.reflow();
        }
      }
    },
    ...

最后,下面的方法确实奏效了,但感觉像是 hack。

$(element).highcharts({
    chart: {
      type: 'scatter',
      zoomType: 'xy',
      events: {
        load: function () {
          var chart = this;
          setTimeout(function () { chart.reflow(); }, 0);
        }
      }
    },
    ...

知道问题出在哪里吗?我在这上面花了好几个小时,这真令人沮丧...

编辑:我还有另一个相关问题:默认情况下,我的图表具有给定的大小,但我有一个 "enlarge" 按钮可以让它们占据完整的 space。该按钮只需切换 CSS class 即可。但是,这样做 不会 触发调整大小事件,因此不会触发重排。所以我有同样的问题,图表没有填满他们的容器。

这里有一个 fiddle 来演示这个问题: http://jsfiddle.net/swaek268/3/

再次,我找到了解决问题的方法,但感觉更像是一个更大的 hack。

var width = 0;
var planreflow = function(){
  setTimeout(function(){
      if(width!==$('#container').width()){
          $('#container').highcharts().reflow(); 
          console.log('reflow!')
      }
      width = $('#container').width();
      planreflow();
  }, 10);
};
planreflow();

所以,其实你有两个问题:

1) 第二次点击时尺寸不正确。这很可能是由于隐藏 (display: none) 容器的宽度和高度错误造成的。在这种情况下,浏览器会报告不正确的 width/height(如 0px)。同样的问题here and here.

2) 为容器设置 class 不会触发图表的 resize(或者 reflow)事件。这意味着您需要自己调用 chart.reflow()。我建议在您更改 class.

的同一位置执行此操作

我想我找到了两个问题的解决方案:

  1. 图表完成加载时尺寸错误:

问题不是出在Highcharts,而是出在图表创建的时间上。我直接从 Angular 指令的 link() 函数中调用了 .highcharts()。 JS 看起来基本上是这样的:

app.directive('dynamicView', function() {
  return {
    link: function(scope, element, attrs){
      $(element).highcharts({...});
    },    
  };
});

由于我的元素也有一个 ng-if 指令,具体取决于选项卡是否被选中,我想当该条件变为 true 时,angular 调用 link() 函数在确定元素的尺寸之前(我想 HTML 必须准备好才能确定元素的大小)。所以我认为问题是我在文档准备好之前调用了 .highcharts()。由于某些我仍然不明白的原因,元素的大小在第一次 ng-if 变为 true 时是正确的,但下一次就不正确了。

无论如何,一个解决方案(仍然不是很优雅 - 但比调用 reflow() 更好)是延迟对 .highcharts() 本身的调用,以确保 HTML 已准备就绪创建图表时:

link: function(scope, element, attrs){
  setTimeout(function () {
    $(element).highcharts({...});
  }, 0);
}

感谢 @Pavel Fus.highcharts()setTimeout().

行为的评论
  1. 切换 class
  2. 时图表未调整大小

对于这个问题,我利用了 Angular 的事件系统。

在我控制全屏按钮的控制器中:

$scope.toggleFullScreen = function(e){
  $scope.$broadcast('fullscreen');
}

此事件沿层次结构向下传递,最终到达包含图表的我的指令。在创建我的图表时,我所要做的就是监听这个事件,并相应地触发一个 reflow()

scope.$on('fullscreen', function(){
  setTimeout(function () { 
    $(element).highcharts().reflow(); 
  }, 0);
});

请注意 --again-- 这个技巧只有在我使用 setTimeout() 延迟回流时才有效。不知道为什么。

希望对您有所帮助!在这上面花了很多时间...