Highcharts: 如果一个 Flex 容器中有多个图表,则 Flex 容器无法正确调整图表大小。

创建于 2017-03-02  ·  18评论  ·  资料来源: highcharts/highcharts

预期行为

如果你有一个带有 css 的 flex 容器
显示:弹性;
弹性方向:行;

和 4 个孩子
弹性增长:1;
弹性基础:25%;
您希望有一行包含 4 个图表,每个图表大小为父级的 25%。

实际行为

实际行为取决于它可能是的父级的大小
一行 4 项,占 25%(预期行为)
一行 3 件小号商品,下一行 100% 商品。
4 行,里面有 100% 的项目。

带有重现步骤的现场演示

http://jsfiddle.net/xLz8tcrc/1/

受影响的浏览器

铬合金
火狐

Undecided

最有用的评论

@KacperMadej
好吧,svg 是矢量图形。 这意味着将 SVG 宽度和高度设置为 100% 应该不会对渲染产生任何不良影响,如果我错了,请纠正我。

我设法覆盖了内部svg元素宽度 CSS:

highcharts-chart::ng-deep {
  .highcharts-container,
  .highcharts-container svg {
     width: 100% !important;
     height: 100% !important;
  }
}

它现在运行得相当好。

所有18条评论

在您的演示中,我添加了一个片段来在调整弹性框大小后运行chart.reflow() 。 这是必要的,因为图表只会在 _window_ 调整大小时自动回流。

此外,向图表容器添加overflow: hidden可确保容器可以小于图表,从而防止它们在缩小时过宽。

这能解决您的问题吗? http://jsfiddle.net/xLz8tcrc/2/

感谢您的回复。
使用 chart.reflow() 看起来更像是一种解决方法,而不是对原始问题的修复。 如果 HighCharts 能够根据父元素的 CSS 规则正确调整大小,将会简单得多。

我只发布了一个示例,在实际用例中,我们希望图表在窗口调整大小或父元素调整大小时调整大小,因此实现可以调用 chart.reflow() 的逻辑并不那么容易。 对于窗口调整大小,它更不那么直接,只需使用窗口调整大小事件。
对于其他组件,我们需要为父组件实现某种可调整大小的接口,因此我们可以在调整大小时调用chart.reflow,这在某些情况下可能有效,但不确定所有情况。

能否请您暂时不要关闭票证,我会尝试实施您的方法,或者也许有人有其他想法。

您所描述的问题与我们在 Highcharts 内部遇到的问题基本相同。 我们目前在调整窗口大小时进行自动重排。 理想情况下,我们也希望对包含元素调整大小进行自动重排,但目前没有有效的方法来监听 DOM 元素大小的变化。

请参阅http://stackoverflow.com/questions/6492683/how-to-detect-divs-dimension-changed进行讨论,之前在本论坛中也讨论过相同的主题。 正如线程中提到的,目前唯一可能的方法似乎是使用setTimeoutrequestAnimationFrame ,这两者都在浪费 CPU。

您是否查看了链接到的 StackOverflow 帖子中提到的 ResizeSensor(https://github.com/marcj/css-element-queries)? 它使用几个额外的 DOM 元素和一个scroll事件侦听器来检测调整大小。 它仅使用requestAnimationFrame来限制发送的调整大小事件的数量(每帧最多 1 个)。

还有http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/描述了如何使用object标签来实现这一点,其中巧合的是当它的大小改变时触发resize事件。

将来,浏览器中将实现ResizeObserver ,但现在不可行。 在https://github.com/WICG/ResizeObserver/issues/3上列出了几个 polyfill,但它们似乎依赖于MutationObeserver和轮询或其他浪费的方法。

在我看来,Highcharts 不应该在核心中实现这样的技巧。 窗口调整大小侦听器涵盖了大多数情况,然后我们有chart.reflow钩子来允许实现者覆盖其他情况,当元素调整大小而不调整窗口大小时。 因此,如果您测试了上述方法之一并且它适用于您的情况,我建议您将其与chart.reflow一起使用。

+1 我们刚刚遇到这个问题,花了很长时间才意识到问题是 flexbox。

我停止使用 flex 的原因之一......调整大小从来没有按预期工作,显示网格同样糟糕

显示内联块要好得多

我停止使用 flex 的原因之一......调整大小从来没有按预期工作,显示网格同样糟糕

这只是另一种解决方法。

是否有任何更新? 有没有计划修复这个@TorsteinHonsi

我开始在 Angular 项目中使用 Highcharts,我也遇到了这个问题。
我不明白的是图表实际上是SVG 。 因此,将其设为 100% 大小应该没有任何问题。 相反,它看起来像宽度和高度被直接设置为 svg。

capture

@GeorgeKnap

你能提供一个带有 Highcharts 和非 Highcharts SVG 的现场演示来展示你的想法吗? 这将如何影响锐化渲染的缩放和像素精度?

@KacperMadej
好吧,svg 是矢量图形。 这意味着将 SVG 宽度和高度设置为 100% 应该不会对渲染产生任何不良影响,如果我错了,请纠正我。

我设法覆盖了内部svg元素宽度 CSS:

highcharts-chart::ng-deep {
  .highcharts-container,
  .highcharts-container svg {
     width: 100% !important;
     height: 100% !important;
  }
}

它现在运行得相当好。

感谢您分享解决方案。

缩放 SVG 的问题在于,这可能会导致渲染模糊,因为像素精度可能会丢失。 通用 Highcharts 解决方案的另一个问题是对旧浏览器的支持(它不是阻止程序,我们只需要确保我们有适当的 polyfill)。

我对这个问题进行了深入研究,发现了三个问题:

  1. 在确定容器的大小时,Highcharts 使用scrollWidth ,如果实际宽度是亚像素,则会四舍五入。 在原始小提琴中,这导致第一个图表被绘制的像素太宽,并且 flex 布局从 4 列跳到 3 列。 这是通过使用Math.floorelement.getBoundingClientRect在上面的提交中修复的。

  2. 当缩小页面或容器的宽度时,图表将比分配给它们的 25% 更宽,从而破坏布局。 这在上面的提交中通过将overflow:hidden到顶级图表容器来修复。

  3. 当缩放 flex 容器 _without_ 调整窗口大小时,图表不会缩放。 他们仍然不会,但至少现在他们不会破坏布局。 @GeorgeKnap的解决方案可能适用于某些人,但正如 Kacper 所说,它可能会导致图表模糊,而且还会调整高度,它会不尊重字体大小等。请参阅http://jsfiddle.net/highcharts/xLz8tcrc/219 /以及带有显式chart.reflow调用的相同设置: http : ResizeObserver

我上面的 CSS 覆盖解决方案导致鼠标悬停(工具提示)位置出现重大问题。 为了修复它,我完全删除了 CSS 覆盖并实现了ResizeObserver 。 它观察父元素的大小并直接在图表选项中更改widthheight属性:

这是 Angular 解决方案,因此使用ElementRef来访问本机 DOM 元素并使用ChangeDetectorRef来触发更改检查:

    const resizeObserver = new ResizeObserver(debounce((data: Array<ResizeObserverEntry>) => {
      if (this.chartOptions) {
        const options = { ...this.chartOptions } as Highcharts.Options;
        options.chart!.height = data[0].contentRect.height;
        options.chart!.width = data[0].contentRect.width;
        this.chartOptions = options;
        this.cd.markForCheck();
      }
    }, 200));
    resizeObserver.observe(this.elementRef.nativeElement.children[0]);

感谢分享! 这是一个普通的插件响应该chart.reflow选项,并取消绑定内置window.onresize处理程序,如果ResizeObserver支持:

// Generic plugin that adds support for listening to container resize rather than
// window resize. As of 2018 only Chrome supports ResizeObserver.
Highcharts.wrap(Highcharts.Chart.prototype, 'setReflow', function(proceed, reflow) {

    var chart = this;

    proceed.call(this, reflow);

    if (reflow !== false && typeof ResizeObserver === 'function') {

        // Unbind window.onresize handler so we don't do double redraws
        if (this.unbindReflow) {
            this.unbindReflow();
        }

        var ro = new ResizeObserver(function () {
            chart.reflow();
        });

        ro.observe(this.renderTo);
    }
});

在 jsFiddle 上实时查看。

Chart1
chart2
你好,

我是使用 highcharts 的新手,在那些附加的屏幕截图中,您可以清楚地看到图表超出了容器的宽度。 我在同一页面中有许多图表,当我单击手风琴时会打开这些图表。 在每次初始加载时,图表宽度都会超过其容器宽度。
这个问题有什么解决办法吗?

下面是关于手风琴的代码。

函数initsectionclicks(容器){
$('.fe-section-accordion-header', container).click(function () {
var parentcontainer = $(this).parent();
var 手风琴容器 = $(parentcontainer).find('.fe-section-accordion-container');

        if (accordioncontainer != null)
        {
            var accordioncontainerOpen = accordioncontainer.is(':visible');

            accordioncontainer[accordioncontainerOpen ? 'slideUp' : 'slideDown'](400, "swing", function() {
                ToolkitUI.Common.ResizeIFrame();
                var highChartsWrapper = $('.fe-section-chart-wrap', accordioncontainer);
                if (highChartsWrapper.length) {
                    $.each(highChartsWrapper,
                        function(index, obj) {
                            var hcObj = $(obj).highcharts();
                            if (hcObj)
                                hcObj.reflow();
                        });
                }
            });

            if (!accordioncontainerOpen)
            {
                $(accordioncontainer).ShowLoadingInsideContainer();
            }

            $(parentcontainer).find('.fe-expandcollapse').toggleClass("fe-icon-expand", accordioncontainerOpen).toggleClass("fe-icon-collapse", !accordioncontainerOpen);
            $('.fe-section-accordion-header', parentcontainer).toggleClass('highlight', !accordioncontainerOpen);
        }

        $(window).resize();
    });

    //we have to unbind any click event handlers before attaching a new one, as without unbind we will be attaching the same handler multiple times.
    //this is because we are binding the data from the start for each tab when it is clicked, which rebinds the same method again and again.
    $('.fe-accordion-expandcollapse-all', container).unbind('click').click(function () {
        var isallopen = $(this).hasClass('fe-accordion-collapse');
        var counter = 1;
        $(this).toggleClass('fe-accordion-expand', isallopen).toggleClass('fe-accordion-collapse', !isallopen);
        $('.fe-expandcollapse', container).toggleClass('fe-icon-expand', isallopen).toggleClass('fe-icon-collapse', !isallopen);
        $('.fe-section-accordion-header', container).toggleClass('highlight', !isallopen);
        $('.fe-section-accordion-container')[isallopen ? 'slideUp' : 'slideDown'](400, "swing", function () {
            if (counter == $('.fe-section-accordion-container').length) {
                ToolkitUI.Common.ResizeIFrame();
            }
            counter++;
        });

        if (!isallopen)
        {
            $('.fe-section-accordion-container').ShowLoadingInsideContainer();

        }
        $(window).resize();
    });
};

@Nagaraj2106

请为问题添加实时演示,以便我们重新创建问题、调试并测试可能的解决方案和变通方法。 另外,请查看此评论中建议的解决方法: https :

在窗口调整大小时调用 reflow() 或使用插件都没有为我解决问题; 然而,这个来自 SO 的回答确实如此。

我在 bootstrap 4 flex 容器中使用 highcharts。 图表会向上调整(更宽)但不会缩小,除非我将容器设为绝对:

<div id="chart-container">
    <div id="chart"></div>
</div>

使用 CSS:

#chart-container {
    position: relative;
    width: 100%;
    height: 280px;
}

#chart {
    position: absolute;
    width: 100%;
}

我已经用renderTo: 'chart'将图表附加到 #chart 。

此页面是否有帮助?
0 / 5 - 0 等级