Highcharts: O flex container não redimensiona os gráficos corretamente, se você tiver vários gráficos dentro de um flex container.

Criado em 2 mar. 2017  ·  18Comentários  ·  Fonte: highcharts/highcharts

Comportamento esperado

Se você tiver um flex container com css
display: flex;
direção flexível: fileira;

e 4 filhos com
flex-grow: 1;
base flexível
Você espera ter uma linha com 4 gráficos, cada um com 25% do tamanho do pai.

Comportamento real

O comportamento real depende do tamanho do pai que pode ser
4 itens em uma linha com 25% (comportamento esperado)
3 itens de tamanho pequeno em uma linha e 100% item na próxima linha.
4 linhas com 100% de itens dentro dela.

Demonstração ao vivo com etapas para reproduzir

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

Navegadores afetados

cromada
Raposa de fogo

Undecided

Comentários muito úteis

@KacperMadej
bem, svg é um gráfico vetorial. Isso significa que colocar a largura e a altura do SVG em 100% não deve ter nenhum efeito negativo na renderização, corrija-me se eu estiver errado.

Consegui substituir o CSS interno de largura do elemento svg :

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

Funciona razoavelmente bem agora.

Todos 18 comentários

Em sua demonstração, adicionei um snippet para executar chart.reflow() após redimensionar a caixa flexível. Isso é necessário, pois os gráficos só refluem automaticamente quando a _janela_ for redimensionada.

Além disso, adicionar overflow: hidden aos contêineres do gráfico garante que o contêiner possa ser menor do que o gráfico, o que evita que fiquem muito largos ao serem reduzidos.

Isso resolve o seu problema? http://jsfiddle.net/xLz8tcrc/2/

Obrigado pela sua resposta.
Usar chart.reflow () parece mais uma solução alternativa do que uma correção para o problema original. Será muito mais simples se HighCharts for redimensionado corretamente de acordo com as regras CSS do elemento pai.

Eu postei apenas um exemplo, no caso de uso real, queremos que os gráficos sejam redimensionados no redimensionamento da janela ou do elemento pai, então não é tão fácil implementar a lógica onde você pode chamar chart.reflow (). Para o redimensionamento de janela é menos direto, apenas usando o evento de redimensionamento de janela.
Para outros componentes, precisamos implementar algum tipo de interface redimensionável para um componente pai, para que possamos chamar chart.reflow no redimensionamento, que pode funcionar em alguns casos, mas não temos certeza sobre todos eles.

Por favor, não feche o tíquete por enquanto, tentarei implementar sua abordagem ou talvez alguém tenha outras idéias.

O que você está descrevendo é basicamente o mesmo problema que temos internamente em Highcharts. Atualmente, fazemos refluxos automáticos no redimensionamento da janela. Idealmente, gostaríamos de fazer refluxos automáticos ao conter o redimensionamento do elemento também, mas atualmente não há uma maneira eficaz de ouvir as alterações de tamanho do elemento DOM.

Consulte http://stackoverflow.com/questions/6492683/how-to-detect-divs-dimension-changed para uma discussão, e o mesmo tópico foi discutido anteriormente neste fórum também. Conforme mencionado no tópico, a única maneira possível atualmente parece ser usando setTimeout ou requestAnimationFrame , ambos os quais estão desperdiçando CPU.

Você deu uma olhada no ResizeSensor mencionado na postagem StackOverflow ao qual você criou um link (https://github.com/marcj/css-element-queries)? Ele usa alguns elementos DOM extras e um listener de evento scroll para detectar redimensionamentos. Ele usa apenas requestAnimationFrame para limitar quantos eventos de redimensionamento são enviados (no máximo 1 por quadro).

Há também http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/ que descreve como fazer isso usando uma tag object , que coincidentemente dispara um evento resize quando seu tamanho muda.

No futuro, haverá ResizeObserver implementados nos navegadores, mas isso não é viável agora. Existem vários polyfills para isso listados em https://github.com/WICG/ResizeObserver/issues/3, mas eles parecem depender de MutationObeserver e pesquisas ou outras abordagens inúteis.

Na minha opinião, Highcharts não deveria implementar truques como esse no núcleo. O listener de redimensionamento de janela cobre a maioria dos casos, então temos o gancho chart.reflow para permitir que os implementadores cubram outros casos, quando um elemento é redimensionado sem redimensionar a janela. Portanto, se você testou uma das abordagens acima e ela funciona para o seu caso, sugiro que a use junto com chart.reflow .

1 Acabamos de encontrar esse problema, demorou uma eternidade para perceber que o problema era o flexbox.

uma das razões pelas quais eu parei de usar o flex ... o redimensionamento nunca funciona como o esperado, mesma merda para a grade de exibição

exibir bloco embutido é muito melhor

uma das razões pelas quais eu parei de usar o flex ... o redimensionamento nunca funciona como o esperado, mesma merda para a grade de exibição

esta é apenas outra solução alternativa.

Existe alguma atualização sobre isso? Existe algum plano de consertar este @TorsteinHonsi ?

Estou começando a usar Highcharts em projetos Angular e também encontrei isso.
O que não entendi é que o gráfico é, na verdade, SVG . Portanto, não deve haver nenhum problema em colocá-lo no tamanho 100%. Em vez disso, parece que a largura e a altura estão explicitamente configuradas diretamente para svg.

capture

Olá @GeorgeKnap

Você poderia fornecer uma demonstração ao vivo com Highcharts e um SVG não Highcharts para mostrar o que você tem em mente? Como isso influenciará o dimensionamento e a precisão de pixels da renderização nítida?

@KacperMadej
bem, svg é um gráfico vetorial. Isso significa que colocar a largura e a altura do SVG em 100% não deve ter nenhum efeito negativo na renderização, corrija-me se eu estiver errado.

Consegui substituir o CSS interno de largura do elemento svg :

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

Funciona razoavelmente bem agora.

Obrigado por compartilhar a solução.

O problema com o dimensionamento de SVG é que isso pode causar uma renderização borrada, pois a precisão dos pixels pode ser perdida. Outro problema para uma solução geral de Highcharts é o suporte para navegadores legados (não é um bloqueador, só precisamos ter certeza de que temos um polyfill adequado).

Eu fiz um mergulho profundo neste problema e encontrei três problemas:

  1. Ao determinar o tamanho do contêiner, Highcharts usa scrollWidth , que é arredondado para cima caso a largura real seja subpíxel. No violino original, isso resulta no primeiro gráfico sendo desenhado com uma fração de pixel muito larga, e o layout flexível pulando de 4 para 3 colunas. Isso foi corrigido no commit acima usando Math.floor e element.getBoundingClientRect .

  2. Ao diminuir a largura da página ou do contêiner, os gráficos seriam mais largos do que os 25% atribuídos a eles, quebrando assim o layout. Isso foi corrigido no commit acima, adicionando overflow:hidden ao contêiner de gráfico de nível superior.

  3. Ao aumentar ou diminuir o flex container _sem_ redimensionar a janela, os gráficos não serão redimensionados. Eles ainda não vão, mas pelo menos agora eles não quebram o layout. A solução de @GeorgeKnap pode funcionar para alguns, mas, como Kacper diz, pode resultar em gráficos borrados, além de redimensionar a altura, desrespeitaria o tamanho da fonte etc. Consulte http://jsfiddle.net/highcharts/xLz8tcrc/219 / e a mesma configuração com uma chamada chart.reflow explícita: http://jsfiddle.net/highcharts/xLz8tcrc/221/. A solução desejada seria ouvir um evento quando o div do contêiner for redimensionado, mas por enquanto o único evento nativo desse tipo é o ResizeObserver do Chrome.

Minha solução de substituição de CSS acima causou grandes problemas com a posição do mouse sobre (dica de ferramenta). Para consertar, removi a substituição CSS completamente e implementei ResizeObserver . Ele observa o tamanho do elemento pai e altera as propriedades width e height diretamente nas opções do gráfico:

Esta é a solução Angular, então é usado ElementRef para acessar o elemento DOM nativo e ChangeDetectorRef para acionar a verificação de alteração:

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

Obrigado por compartilhar! Aqui é um plugin geral que responde à chart.reflow opção e libera o built-in window.onresize manipulador se ResizeObserver é suportado:

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

Veja ao vivo no jsFiddle .

Chart1
chart2
Oi,

Eu sou novo no uso de highcharts, naquelas capturas de tela anexadas, você pode ver claramente que o gráfico está excedendo a largura do contêiner. Tenho muitos gráficos na mesma página que abrem quando clico no acordeão. Em cada carregamento inicial, a largura do gráfico está excedendo a largura do contêiner.
existe alguma solução alternativa para este problema?

abaixo está o código referente ao acordeão.

function 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

Adicione uma demonstração ao vivo para o problema, para que possamos recriar o problema, depurá-lo e testar possíveis soluções e soluções alternativas. Além disso, verifique a solução alternativa sugerida neste comentário: https://github.com/highcharts/highcharts/issues/6427#issuecomment -438212322

Nem chamar reflow () no redimensionamento da janela nem usar o plugin resolveram o problema para mim; no entanto, esta resposta do SO sim.

Estou usando highcharts em um container bootstrap 4 flex. O gráfico é redimensionado para cima (mais amplo), mas não redimensionado para baixo, a menos que eu torne o contêiner absoluto:

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

com o CSS:

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

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

Anexei o gráfico ao #chart com renderTo: 'chart' .

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

KacperMadej picture KacperMadej  ·  3Comentários

vivekk123 picture vivekk123  ·  3Comentários

alirezameftahi71 picture alirezameftahi71  ·  3Comentários

balupton picture balupton  ·  3Comentários

bbonczek picture bbonczek  ·  3Comentários