Highcharts: Der Flex-Container passt die Größe von Diagrammen nicht richtig an, wenn Sie mehrere Diagramme in einem Flex-Container haben.

Erstellt am 2. März 2017  ·  18Kommentare  ·  Quelle: highcharts/highcharts

Erwartetes Verhalten

Wenn Sie einen Flex-Container mit CSS haben
Anzeige: flexibel;
Flex-Richtung: Reihe;

und 4 Kinder mit
Flex-Grow: 1;
Flex- Basis: 25 %;
Sie erwarten eine Zeile mit 4 Diagrammen, jede Größe von 25 % der Eltern.

Tatsächliches Verhalten

Das tatsächliche Verhalten hängt von der Größe des Elternteils ab, das es sein könnte
4 Items in einer Reihe mit 25 % (erwartetes Verhalten)
3 kleine Artikel in einer Reihe und 100% Artikel in der nächsten Reihe.
4 Reihen mit 100 % darin enthaltenen Artikeln.

Live-Demo mit Schritten zum Reproduzieren

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

Betroffene(r) Browser

Chrom
Feuerfuchs

Undecided

Hilfreichster Kommentar

@KacperMadej
Nun, svg ist eine Vektorgrafik. Das bedeutet, dass das Setzen von SVG-Breite und -Höhe auf 100% keine negativen Auswirkungen auf das Rendering haben sollte. Korrigieren Sie mich, wenn ich falsch liege.

Ich habe es geschafft, die innere svg Elementbreite CSS zu überschreiben:

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

Es funktioniert jetzt einigermaßen gut.

Alle 18 Kommentare

In Ihrer Demo habe ich ein Snippet hinzugefügt, um chart.reflow() auszuführen, nachdem die Größe der Flexbox geändert wurde. Dies ist notwendig, da die Diagramme nur dann automatisch umfließen, wenn das _Fenster_ die Größe ändert.

Außerdem stellt das Hinzufügen von overflow: hidden zu den Diagrammcontainern sicher, dass der Container kleiner als das Diagramm sein kann, sodass sie beim Verkleinern nicht zu breit werden.

Behebt dies Ihr Problem? http://jsfiddle.net/xLz8tcrc/2/

Danke für deine Antwort.
Die Verwendung von chart.reflow() sieht eher nach einer Problemumgehung als nach einer Behebung des ursprünglichen Problems aus. Es wird viel einfacher sein, wenn HighCharts die Größe entsprechend den CSS-Regeln des übergeordneten Elements richtig ändert.

Ich habe nur ein Beispiel gepostet, in einem echten Anwendungsfall möchten wir, dass Diagramme bei der Größenänderung des Fensters oder der Größe des übergeordneten Elements geändert werden. Daher ist es nicht so einfach, Logik zu implementieren, bei der Sie chart.reflow() aufrufen können. Bei der Fenstergrößenänderung ist es weniger einfach, nur das Fenstergrößenänderungsereignis zu verwenden.
Für andere Komponenten müssen wir eine größenveränderbare Schnittstelle für eine übergeordnete Komponente implementieren, damit wir bei Größenänderung chart.reflow aufrufen können, was in einigen Fällen funktionieren könnte, aber nicht in allen Fällen sicher ist.

Kannst du das Ticket bitte vorerst nicht schließen, ich werde versuchen deinen Ansatz umzusetzen oder vielleicht hat jemand noch andere Ideen.

Was Sie beschreiben, ist im Grunde das gleiche Problem, das wir intern in Highcharts haben. Wir führen derzeit automatische Reflows bei der Größenänderung von Fenstern durch. Idealerweise möchten wir auch automatische Reflows bei der Größenänderung von enthaltenden Elementen durchführen, aber es gibt derzeit keine effektive Möglichkeit, auf Änderungen der DOM-Elementgröße zu hören.

Siehe http://stackoverflow.com/questions/6492683/how-to-detect-divs-dimension-changed für eine Diskussion, und das gleiche Thema wurde auch zuvor in diesem Forum diskutiert. Wie im Thread erwähnt, scheint die einzige Möglichkeit derzeit die Verwendung von setTimeout oder requestAnimationFrame , die beide CPU verschwenden.

Hast du dir den ResizeSensor angesehen, der in dem StackOverflow-Beitrag erwähnt wurde, auf den du verlinkt hast (https://github.com/marcj/css-element-queries)? Es verwendet ein paar zusätzliche DOM-Elemente und einen scroll Ereignis-Listener, um Größenänderungen zu erkennen. Es verwendet nur requestAnimationFrame , um zu begrenzen, wie viele Größenänderungsereignisse gesendet werden (maximal 1 pro Frame).

Es gibt auch http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/, in dem beschrieben wird, wie dies mithilfe eines object Tags erreicht wird löst zufällig ein resize Ereignis aus, wenn sich seine Größe ändert.

In Zukunft werden ResizeObserver in den Browsern implementiert sein, aber das ist derzeit nicht machbar. Es gibt mehrere Polyfills dafür, die unter https://github.com/WICG/ResizeObserver/issues/3 aufgeführt sind, aber sie scheinen sich auf MutationObeserver und Polling oder andere verschwenderische Ansätze zu verlassen.

Meiner Meinung nach sollten Highcharts solche Tricks nicht im Kern implementieren. Der Fenster-Resize-Listener deckt die meisten Fälle ab, dann haben wir den chart.reflow Hook, um es Implementierern zu ermöglichen, andere Fälle abzudecken, wenn ein Element ohne eine Fenstergröße geändert wird. Wenn Sie also einen der oben genannten Ansätze getestet haben und er für Ihren Fall funktioniert, schlage ich vor, dass Sie ihn zusammen mit chart.reflow .

+1 Wir sind gerade auf dieses Problem gestoßen und haben ewig gebraucht, um zu erkennen, dass es sich um Flexbox handelt.

Einer der Gründe, warum ich aufhöre, Flex zu verwenden ... die Größenänderung funktioniert nie wie erwartet, der gleiche Scheiß für das Display-Raster

Inline-Block anzeigen ist viel besser

Einer der Gründe, warum ich aufhöre, Flex zu verwenden ... die Größenänderung funktioniert nie wie erwartet, der gleiche Scheiß für das Display-Raster

Dies ist nur ein weiterer Workaround.

Gibt es hierzu Updates? Gibt es Pläne, dieses @TorsteinHonsi zu reparieren ?

Ich fange an, Highcharts in Angular-Projekten zu verwenden, und bin auch darauf gestoßen.
Was ich nicht verstehe, ist, dass das Diagramm tatsächlich SVG . Es sollte also kein Problem sein, es auf 100% Größe zu bringen. Stattdessen sieht es so aus, als ob Breite und Höhe explizit direkt auf svg gesetzt sind.

capture

Hallo @GeorgeKnap

Könnten Sie eine Live-Demo mit Highcharts und einer Nicht-Highcharts-SVG bereitstellen, um zu zeigen, was Sie im Sinn haben? Wie wird sich dies auf die Skalierung und die Pixelgenauigkeit des scharfen Renderings auswirken?

@KacperMadej
Nun, svg ist eine Vektorgrafik. Das bedeutet, dass das Setzen von SVG-Breite und -Höhe auf 100% keine negativen Auswirkungen auf das Rendering haben sollte. Korrigieren Sie mich, wenn ich falsch liege.

Ich habe es geschafft, die innere svg Elementbreite CSS zu überschreiben:

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

Es funktioniert jetzt einigermaßen gut.

Vielen Dank für das Teilen der Lösung.

Das Problem bei der Skalierung von SVG besteht darin, dass dies zu verschwommenem Rendering führen kann, da die Pixelgenauigkeit verloren gehen könnte. Ein weiteres Problem für eine allgemeine Highcharts-Lösung ist die Unterstützung von Legacy-Browsern (es ist kein Blocker, wir müssen nur sicherstellen, dass wir ein richtiges Polyfill haben).

Ich habe mich eingehend mit diesem Problem befasst und drei Probleme gefunden:

  1. Bei der Bestimmung der Containergröße verwendet Highcharts scrollWidth , das aufgerundet wird, falls die tatsächliche Breite Subpixel beträgt. In der ursprünglichen Geige führt dies dazu, dass das erste Diagramm einen Bruchteil eines Pixels zu breit gezeichnet wird und das Flex-Layout von 4 Spalten auf 3 Spalten springt. Dies wird im obigen Commit behoben, indem Math.floor und element.getBoundingClientRect .

  2. Beim Verkleinern der Breite der Seite oder des Containers wären die Diagramme breiter als die ihnen zugewiesenen 25 %, wodurch das Layout zerstört würde. Dies wird im obigen Commit behoben, indem overflow:hidden zum Diagrammcontainer der obersten Ebene hinzugefügt wird.

  3. Beim Hoch- oder Herunterskalieren des Flex-Containers _ohne_ die Fenstergröße zu ändern, werden die Diagramme nicht skaliert. Sie werden es immer noch nicht, aber zumindest brechen sie jetzt nicht das Layout. Die Lösung von @GeorgeKnap mag für einige funktionieren, aber wie Kacper sagt, kann dies zu verschwommenen Diagrammen führen, außerdem wird die Höhe geändert, die Schriftgröße usw. missachtet . Siehe chart.reflow Aufruf: http://jsfiddle.net/highcharts/xLz8tcrc/221/. Die gewünschte Lösung wäre, auf ein Ereignis zu hören, wenn die Größe des Container-Divs geändert wird, aber im Moment ist das einzige native Ereignis dieser Art das ResizeObserver Chrome.

Meine oben genannte CSS-Override-Lösung verursachte große Probleme mit der Position des Mauszeigers (Tooltip). Um das Problem zu beheben, habe ich die CSS-Überschreibung komplett entfernt und ResizeObserver implementiert. Es beobachtet die Größe des übergeordneten Elements und ändert die Eigenschaften von width und height direkt in den Diagrammoptionen:

Dies ist die Angular-Lösung, daher wird ElementRef auf das native DOM-Element zuzugreifen, und ChangeDetectorRef , um die Änderungsprüfung auszulösen:

    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]);

Danke für das Teilen! Hier ist ein allgemeines Plugin, das auf die Option chart.reflow reagiert und den integrierten window.onresize Handler aufhebt, wenn ResizeObserver unterstützt wird:

// 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);
    }
});

Sehen Sie es sich live auf jsFiddle an .

Chart1
chart2
Hallo,

Ich bin neu in der Verwendung von Highcharts, in den beigefügten Screenshots konnte man deutlich sehen, dass das Diagramm die Breite des Containers überschreitet. Ich habe viele Diagramme auf derselben Seite, die sich öffnen, wenn ich auf das Akkordeon klicke. Bei jedem anfänglichen Laden überschreitet die Diagrammbreite die Containerbreite.
gibt es eine Lösung für dieses Problem?

Unten ist der Code bezüglich des Akkordeons.

Funktion initsectionclicks(Container) {
$('.fe-section-accordion-header', container).click(function () {
var parentcontainer = $(this).parent();
var Akkordeoncontainer = $(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

Bitte fügen Sie eine Live-Demo für das Problem hinzu, damit wir das Problem neu erstellen, debuggen und mögliche Lösungen und Problemumgehungen testen können. Bitte überprüfen Sie auch die vorgeschlagene Problemumgehung in diesem Kommentar: https://github.com/highcharts/highcharts/issues/6427#issuecomment -438212322

Weder der Aufruf von reflow() bei der Größenänderung des Fensters noch die Verwendung des Plugins lösten das Problem für mich; diese Antwort von SO hat es jedoch getan.

Ich verwende Highcharts in einem Bootstrap-4-Flex-Container. Das Diagramm wird vergrößert (breiter), aber nicht verkleinert, es sei denn, ich mache den Container absolut:

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

mit dem CSS:

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

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

Ich habe das Diagramm an #chart mit renderTo: 'chart' angehängt.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen