Highcharts: 1つのフレックスコンテナ内に複数のチャートがある場合、フレックスコンテナはチャートのサイズを適切に変更しません。

作成日 2017年03月02日  ·  18コメント  ·  ソース: highcharts/highcharts

期待される動作

cssを使用したフレックスコンテナがある場合
表示:フレックス;
フレックス方向:行;

と4人の子供
フレックスグロー:1;
フレックスベース:25%;
1つの行に4つのグラフがあり、各サイズは親の25%であると想定しています。

実際の動作

実際の動作は、親のサイズによって異なります。
25%の1行に4つのアイテム(予想される動作)
小さいサイズの3つのアイテムが1つの行にあり、100%のアイテムが次の行にあります。
100%のアイテムが含まれる4行。

再現する手順を含むライブデモ

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

影響を受けるブラウザ

クロム
Firefox

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を参照してsetTimeoutまたはrequestAnimationFrame 、どちらもCPUを浪費しています。

リンクしたStackOverflowの投稿(https://github.com/marcj/css-element-queries)に記載されているResizeSensorをご覧になりましたか? いくつかの追加のDOM要素とscrollイベントリスナーを使用して、サイズ変更を検出します。 requestAnimationFrameのみを使用して、送信されるサイズ変更イベントの数を制限します(フレームごとに最大1つ)。

objectタグを使用してこれを実現する方法を説明するhttp://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/もありresizeイベントが発生します。

将来的には、ブラウザにResizeObserver実装される予定ですが、現時点では実現可能ではありません。 https://github.com/WICG/ResizeObserver/issues/3にリストされているものにはいくつかのポリフィルがありMutationObeserverとポーリングまたは他の無駄なアプローチに依存しているようです。

私の意見では、Highchartsはこのようなトリックをコアに実装するべきではありません。 ウィンドウサイズ変更リスナーはほとんどの場合をカバーし、要素がウィンドウサイズ変更なしでサイズ変更されたときに、実装者が他のケースをカバーできるようにするchart.reflowフックがあります。 したがって、上記のアプローチの1つをテストし、それが自分のケースで機能する場合は、それをchart.reflowと一緒に使用することをお勧めします。

+1私たちはちょうどこの問題にぶつかり、問題がフレックスボックスであることに気付くのに永遠にかかりました。

私がフレックスの使用をやめる理由の1つ...サイズ変更が期待どおりに機能しない、ディスプレイグリッドの同じたわごと

インラインブロックの表示がはるかに優れています

私がフレックスの使用をやめる理由の1つ...サイズ変更が期待どおりに機能しない、ディスプレイグリッドの同じたわごと

これは別の回避策です。

これに関する更新はありますか? この@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ソリューションのもう1つの問題は、レガシーブラウザのサポートです(これはブロッカーではありません。適切なポリフィルがあることを確認する必要があります)。

私はこの問題を深く掘り下げて、3つの問題を見つけました:

  1. コンテナのサイズを決定するとき、HighchartsはscrollWidth 。これは、実際の幅がサブピクセルの場合に切り上げられます。 元のフィドルでは、これにより、最初のグラフが1ピクセルの何分の1か幅で描画され、フレックスレイアウトが4列から3列にジャンプします。 これは、上記のコミットでMath.floorelement.getBoundingClientRectを使用して修正されています。

  2. ページまたはコンテナの幅を縮小すると、グラフは割り当てられた25%よりも幅が広くなり、レイアウトが崩れます。 これは、トップレベルのチャートコンテナにoverflow:hiddenを追加することにより、上記のコミットで修正されています。

  3. ウィンドウのサイズを変更せずにフレックスコンテナを拡大または縮小すると、グラフは拡大縮小されません。 それでもそうはなりませんが、少なくとも今ではレイアウトを壊すことはありません。 @GeorgeKnapのソリューションは、いくつかのために働くかもしれないが、カツペルは、それがぼやけチャートをもたらす可能性が言うように、それに加えても、高さ、それだろう無礼フォントサイズなどを参照してください。サイズが変更されますhttp://jsfiddle.net/highcharts/xLz8tcrc/219を/および明示的なchart.reflow呼び出しを使用した同じセットアップ: httpResizeObserverです。

上記のCSSオーバーライドソリューションにより、マウスオーバー(ツールチップ)の位置に大きな問題が発生しました。 これを修正するために、CSSオーバーライドを完全に削除し、 ResizeObserverを実装しました。 親要素のサイズを監視し、チャートオプションでwidthおよびheightプロパティを直接変更します。

これはAngularソリューションであるため、ネイティブDOM要素にアクセスするためにElementRefが使用され、変更チェックをトリガーするために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オプションに応答し、 ResizeObserverがサポートされている場合は、組み込みのwindow.onresizeハンドラーのバインドを解除する一般的なプラグインを次に示します。

// 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
こんにちは、

ハイチャートを使用するのは初めてです。添付の​​スクリーンショットでは、チャートがコンテナの幅を超えていることがはっきりとわかります。 同じページにアコーディオンをクリックすると開くチャートがたくさんあります。 すべての初期ロードで、チャートの幅がコンテナの幅を超えています。
この問題の回避策はありますか?

以下はアコーディオンに関するコードです。

関数initsectionclicks(container){
$( '。fe-section-accordion-header'、container).click(function(){
var parentcontainer = $(this).parent();
var accordioncontainer = $(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からのこの回答はそうしました。

ブートストラップ4フレックスコンテナで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 評価