206 votes

Redimensionner le svg lorsque la fenêtre est redimensionnée dans d3.js

Je dessine un nuage de points avec d3.js. Avec l'aide de cette question :
Obtenir la taille de l'écran, de la page web actuelle et de la fenêtre du navigateur

J'utilise cette réponse :

var w = window,
    d = document,
    e = d.documentElement,
    g = d.getElementsByTagName('body')[0],
    x = w.innerWidth || e.clientWidth || g.clientWidth,
    y = w.innerHeight|| e.clientHeight|| g.clientHeight;

Je suis donc en mesure d'adapter mon graphique à la fenêtre de l'utilisateur comme ceci :

var svg = d3.select("body").append("svg")
        .attr("width", x)
        .attr("height", y)
        .append("g");

J'aimerais maintenant que quelque chose se charge de redimensionner le tracé lorsque l'utilisateur redimensionne la fenêtre.

PS : Je n'utilise pas jQuery dans mon code.

332voto

cminatti Points 378

Cherchez "responsive SVG", il est assez simple de rendre un SVG responsive et vous n'avez plus à vous soucier des tailles.

Voici comment j'ai procédé :

d3.select("div#chartId")
   .append("div")
   // Container class to make it responsive.
   .classed("svg-container", true) 
   .append("svg")
   // Responsive SVG needs these 2 attributes and no width and height attr.
   .attr("preserveAspectRatio", "xMinYMin meet")
   .attr("viewBox", "0 0 600 400")
   // Class to make it responsive.
   .classed("svg-content-responsive", true)
   // Fill with a rectangle for visualization.
   .append("rect")
   .classed("rect", true)
   .attr("width", 600)
   .attr("height", 400);

.svg-container {
  display: inline-block;
  position: relative;
  width: 100%;
  padding-bottom: 100%; /* aspect ratio */
  vertical-align: top;
  overflow: hidden;
}
.svg-content-responsive {
  display: inline-block;
  position: absolute;
  top: 10px;
  left: 0;
}

svg .rect {
  fill: gold;
  stroke: steelblue;
  stroke-width: 5px;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<div id="chartId"></div>

Nota: Tout ce qui se trouve dans l'image SVG sera mis à l'échelle avec la largeur de la fenêtre. Cela inclut la largeur du trait et la taille des polices (même celles définies par CSS). Si cela n'est pas souhaité, il existe d'autres solutions plus complexes ci-dessous.

Plus d'infos / tutoriels :

http://thenewcode.com/744/Make-SVG-Responsive

http://soqr.fr/testsvg/embed-svg-liquid-layout-responsive-web-design.php

48voto

Adam Pearce Points 3594

Utilisez fenêtre.onresize :

function updateWindow(){
    x = w.innerWidth || e.clientWidth || g.clientWidth;
    y = w.innerHeight|| e.clientHeight|| g.clientHeight;

    svg.attr("width", x).attr("height", y);
}
d3.select(window).on('resize.updatesvg', updateWindow);

http://jsfiddle.net/Zb85u/1/

33voto

slf Points 15327

UPDATE utilisez simplement la nouvelle méthode de @cminatti


ancienne réponse à des fins historiques

Je pense qu'il est préférable d'utiliser select() et on() car de cette façon, vous pouvez avoir plusieurs gestionnaires d'événements de redimensionnement... mais ne vous emballez pas trop.

d3.select(window).on('resize', resize); 

function resize() {
    // update width
    width = parseInt(d3.select('#chart').style('width'), 10);
    width = width - margin.left - margin.right;

    // resize the chart
    x.range([0, width]);
    d3.select(chart.node().parentNode)
        .style('height', (y.rangeExtent()[1] + margin.top + margin.bottom) + 'px')
        .style('width', (width + margin.left + margin.right) + 'px');

    chart.selectAll('rect.background')
        .attr('width', width);

    chart.selectAll('rect.percent')
        .attr('width', function(d) { return x(d.percent); });

    // update median ticks
    var median = d3.median(chart.selectAll('.bar').data(), 
        function(d) { return d.percent; });

    chart.selectAll('line.median')
        .attr('x1', x(median))
        .attr('x2', x(median));

    // update axes
    chart.select('.x.axis.top').call(xAxis.orient('top'));
    chart.select('.x.axis.bottom').call(xAxis.orient('bottom'));

}

http://eyeseast.github.io/visible-data/2013/08/28/responsive-charts-with-d3/

12voto

Raik Points 76

C'est un peu laid si le code de redimensionnement est presque aussi long que le code de construction du graphique en premier lieu. Donc, au lieu de redimensionner chaque élément du graphique existant, pourquoi ne pas simplement le recharger ? Voici comment cela a fonctionné pour moi :

function data_display(data){
   e = document.getElementById('data-div');
   var w = e.clientWidth;
   // remove old svg if any -- otherwise resizing adds a second one
   d3.select('svg').remove();
   // create canvas
   var svg = d3.select('#data-div').append('svg')
                                   .attr('height', 100)
                                   .attr('width', w);
   // now add lots of beautiful elements to your graph
   // ...
}

data_display(my_data); // call on page load

window.addEventListener('resize', function(event){
    data_display(my_data); // just call it again...
}

La ligne cruciale est d3.select('svg').remove(); . Sinon, chaque redimensionnement ajoutera un autre élément SVG sous le précédent.

8voto

gavs Points 11

Dans les mises en page forcées, il ne suffit pas de définir les attributs "height" et "width" pour recentrer ou déplacer le graphique dans le conteneur svg. Cependant, il existe une réponse très simple qui fonctionne pour les mises en page de type "Force Layouts". aquí . En résumé :

Utilisez le même concours (ou n'importe quel concours) que vous aimez.

window.on('resize', resize);

Puis en supposant que vous avez des variables svg & force :

var svg = /* D3 Code */;
var force = /* D3 Code */;    

function resize(e){
    // get width/height with container selector (body also works)
    // or use other method of calculating desired values
    var width = $('#myselector').width(); 
    var height = $('#myselector').height(); 

    // set attrs and 'resume' force 
    svg.attr('width', width);
    svg.attr('height', height);
    force.size([width, height]).resume();
}

De cette façon, on ne refait pas entièrement le graphique, on définit les attributs et d3 recalcule les choses si nécessaire. Cela fonctionne au moins lorsque vous utilisez un point de gravité. Je ne suis pas sûr qu'il s'agisse d'une condition préalable à cette solution. Quelqu'un peut-il confirmer ou infirmer ?

Santé, g

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X