web-dev-qa-db-fra.com

amCharts n'affiche pas de graphique pour les divs initialement cachés

J'utilise amCharts (qui utilise Raphaël en coulisse) pour rendre certains graphiques au format SVG; et avons remarqué que si le fichier SVG est rendu dans un div initialement invisible, le navigateur ne restitue pas immédiatement l'image lorsque le div devient visible. Si je modifie cependant l'affichage, par exemple En redimensionnant le navigateur ou en effectuant un zoom Ctrl-molette-souris, l'image SVG est alors restituée comme prévu lorsque la page est redessinée.

La méthode exacte de commutation de la visibilité div est via La barre de navigation à onglets de Bootstrap .

J'avoue ne pas être très expérimenté en SVG - s'agit-il d'un problème de rendu des navigateurs, ou de balisage SVG d'amCharts, ou dois-je explicitement appeler une méthode de repeinte lorsque je peux dire que la visibilité d'un SVG a changé?

Voici un jsFiddle qui illustre le problème ; Si vous passez à la section 2 (dans Chrome, Firefox), le graphique n'est pas visible initialement. En redimensionnant l'affichage, il apparaît.

20
Andrzej Doyle

J'ai trouvé la raison du comportement initial et de la solution de contournement - et comme tout est spécifique à amCharts (rien à voir avec SVG en tant que tel), je reformule le titre en conséquence.

Qu'est-ce qui se passe, c'est que lorsque amCharts crée le SVG, il doit (ou du moins décide de le faire) de définir la largeur et la hauteur en termes absolus. Celles-ci sont basées sur la taille de la div cible, obtenue via les propriétés offsetWidth et offsetHeight.

L'onglet inactif a la propriété display: none définie et, par conséquent, cette partie du DOM n'est même pas rendue, donc renvoie zéro pour les deux propriétés de taille. Cela conduit finalement à amCharts créant un graphique 0x0 SVG lorsque chart.write est appelé pour le div masqué.

Le redimensionnement résout les problèmes, car chaque graphique enregistre un écouteur dans l'événement de fenêtre onresize, qui appelle la méthode handleResize du graphique. Cela oblige à recalculer la largeur et la hauteur en fonction des nouvelles dimensions (actuelles) de la div.


Donc, en conclusion, je pense qu'il y a deux manières alternatives de gérer cela:

  • Appelez chart.write pour un graphique quand et seulement quand son onglet devient visible.
  • Appelez la méthode handleResize de chaque graphique lorsque les onglets changent.

(La première option évite la frappe initiale consistant à rendre un graphique invisible, mais effectue ensuite un rafraîchissement complet à chaque changement d'onglet. Cette dernière prend une frappe au début mais est probablement plus rapide par la suite. Pour les marques de bonus, la meilleure solution serait: rendre chaque graphique exactement une fois entre chaque redimensionnement, la première fois qu'il devient visible, mais c'est beaucoup plus complexe car cela impliquerait d'interférer avec les écouteurs d'événements par défaut, entre autres choses.)

Mise à jour : Il y a d'autres complications avec le rendu d'un graphique invisible; en particulier, j’ai rencontré des problèmes avec les calculs de hauteur ne prenant pas en compte l’espace requis par l’axe de domaine et l’extension du graphique de son div. Cela n'a pas été corrigé en appelant handleResize - appeler measureMargins au préalable semblait indiquer que cela devrait fonctionner, mais cela n'a pas fonctionné. (Il y a probablement une autre méthode que l'on pourrait appeler après ceci pour le faire fonctionner comme resetMargins mais à ce stade, il a commencé à se sentir très feuilleté ...)

En tant que tel, je ne pense pas qu'il soit pratique de rendre un graphique pour la première fois sur un div non visible. Je suis donc parti avec une combinaison des puces ci-dessus. J'écoute le moment où l'onglet d'un graphique devient visible pour la première fois, puis appelle chart.write pour l'objet graphique approprié - et chaque fois que les onglets changent, all les graphiques précédemment rendus sont appelés à gérer le redimensionnement.

23
Andrzej Doyle

* édité *  

Voici une mise à jour fiddle . Le canevas ne sera rendu qu'une fois l'onglet affiché. Je stocke les identifiants chartdiv dans un tableau et vérifie s’il en contient ou non.

* édité *  

La seule solution que j'ai trouvée consistait à afficher le graphique après l'affichage de l'onglet spécifique.

Comme vous le voyez dans ce jsFiddle

var tabs = $('.tabbable').tab()

tabs.on('shown', function(e){
   id = $(e.target).attr('href');        
   chartdiv_id = $(id).find('.chartdiv').attr('id');                        
   doChart(chartdiv_id, true);
});

Je suppose que ce n'est pas exactement ce que vous recherchez, mais j'espère que cela vous aidera pour le moment. 

4
Robert Merten

J'ai eu le même problème, mais ma solution c'est une alternative à afficher: aucune, vous pouvez utiliser cette classe dans le css

.hidden {
   position: absolute !important;
   top: -9999px !important;
   left: -9999px !important;
}

cette disparition de l'écran mais visible pour l'amchart, donc la résolution de la carte ne perd jamais la taille !!!!

3
DonVidela
<a href="#" class="show_charts">Show me charts</a>
<div class="charts_div" style="display:hidden;">
some charts here
</div>
<script>
    $(document).on('click', '.show_charts', function(){
        $('.charts_div').toggle();
        //just redraw all charts available on the page
        for(var i = 0; i < AmCharts.charts.length; i++) {
            AmCharts.charts[i].validateData();
        }
    });
</script>
1
RSV_70
var chart = null;
AmCharts.ready(function () {
    chart = AmCharts.makeChart('chart', .....);
});
$(document).ready(function(){
    $("#view_chart").click(function(){
        chart.validateSize();
    });
});


<button type="button" id="view_chart">View Chart</button>
<div style="display:none;" id="chart"></div>
1
yassine

Cela pourrait vous aider à résoudre le problème. J'ai mon amchart montrant dans différents pan. Le composant SVG ne leur permet pas de montrer cette div en raison d'un problème de redimensionnement. 

$(window).resize(function() {
    setTimeout(function() {
        chart.write("chartdiv1");
    }, 300);
});

redimensionner votre fenêtre pendant que vous créez vos graphiques ..

1
chetnashahu

Je suis complètement d'accord avec Andrzej Doyle.

L'émission de handleresize sur le graphique lorsque vous cliquez sur le div sélectionné (onglet) fonctionne pour moi sur cs-cart avec des onglets personnalisés (et non jquery).

Ce qui suit fonctionne pendant que le panier est défini globalement.

    function refreshchart(){
        chart.handleResize();
    };

J'ai aussi rencontré le problème et je l'ai corrigé en faisant de l'initialiseur une fonction. Violon de travail .

$("#button").click(function(){
    $("#chartdiv").toggle();
    makeChart();
});

function makeChart() {
     var chart = AmCharts.makeChart("chartdiv", {
         //all the stuff
     });
}
1
Perry

J'ai deux graphiques sur différents onglets. Un graphique boursier à placer sur #chartdiv et un graphique à secteurs à placer sur #chartdivpie. Voici comment j'ai résolu mon problème.

Mon css personnalisé - pour écraser le bootstrap -

#chartdivpie { width: 1138px; height: 500px; }
.tab-content .tab-pane {
   position: absolute;
   top: -9999px;
   left: -9999px;
   display: inline;
}
.tab-content .tab-pane.active {
    position: inherit !important;
}

Appel JQuery

$('#myTab a').click(function (e) {
      e.preventDefault()
        $(this).tab('show');
        chart.invalidateSize();
        chart.write('chartdiv');
    })
0
PJunior

Un autre travail autour serait 

drawTransactionTypeChart();
setTimeout(function(){hidePieCharts();},1);

Initialement, définissez display:inline sur div, qui est un conteneur de graphique, il sera rendu.

Puis définissez display:none en utilisant setTimeout() après 1ms.

J'espère que cela t'aides... 

0
Chakradhar Vyza