web-dev-qa-db-fra.com

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

Je dessine un diagramme de dispersion avec d3.js. Avec l'aide de cette question:
Obtenir la taille de l’écran, la page Web actuelle et 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 peux donc adapter mon intrigue à la fenêtre de l'utilisateur comme ceci:

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

Maintenant, j'aimerais que quelque chose prenne soin de redimensionner le tracé lorsque l'utilisateur redimensionne la fenêtre.

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

164
mthpvg

Recherchez «SVG réactif», il est assez simple de rendre un SVG réactif et vous n'avez plus à vous soucier de la taille. 

Voici comment je l'ai fait: 

d3.select("div#chartId")
   .append("div")
   .classed("svg-container", true) //container class to make it responsive
   .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); 

Le code CSS:

.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;
}

Plus d'infos/tutoriels: 

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

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

262
cminatti

Utilisez window.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/

41
Adam Pearce

UPDATE utilise simplement la nouvelle méthode de @cminatti


ancienne réponse à des fins historiques

IMO, il est préférable d'utiliser select () et on (), car cela vous permet d'avoir plusieurs gestionnaires d'événements redimensionnés ... mais ne soyez pas trop fous

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/

32
slf

C'est un peu moche si le code de redimensionnement est presque aussi long que le code pour construire le graphique en premier lieu. Donc, au lieu de redimensionner tous les éléments 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 en dessous du précédent. 

12
Raik

Dans les mises en forme forcées, le simple fait de définir les attributs 'hauteur' et 'largeur' ne fonctionnera pas pour recentrer/déplacer le tracé dans le conteneur svg. Cependant, il existe une réponse très simple qui fonctionne pour les dispositions de force trouvées ici . En résumé: 

Utilisez le même (tout) concours que vous aimez.

window.on('resize', resize);

En supposant que vous ayez les 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, vous ne refaitrez pas entièrement le graphique, nous définissons les attributs et d3 recalcule les choses si nécessaire. Cela fonctionne au moins lorsque vous utilisez un point de gravité. Je ne sais pas si c'est une condition préalable à cette solution. Quelqu'un peut-il confirmer ou nier? 

À la vôtre

8
gavs

Pour ceux qui utilisent des graphes orientés vers la force dans D3 v4/v5, la méthode size n'existe plus. Quelque chose comme ce qui suit a fonctionné pour moi (basé sur ce problème de github ):

simulation
    .force("center", d3.forceCenter(width / 2, height / 2))
    .force("x", d3.forceX(width / 2))
    .force("y", d3.forceY(height / 2))
    .alpha(0.1).restart();
0
Justin Lewis

J'utilise window.innerWidth pour la largeur du client et window.innerHeight pour la hauteur du client.

0
Akshay

Si vous souhaitez lier une logique personnalisée pour redimensionner un événement, de nos jours vous pouvez commencer à utiliser l'API de navigateur ResizeObserver pour le cadre de sélection d'un SVGElement.
Cela traitera également le cas où le conteneur est redimensionné en raison du changement de taille des éléments voisins.
Il existe un polyfill pour un support plus large du navigateur.

Voici comment cela peut fonctionner dans le composant d'interface utilisateur:

<div class="container">
  <svg></svg>
</div>

Composant:

// Setup observer in constructor
const container = document.querySelector('.container')

const resizeObserver = new ResizeObserver((entries, observer) => {
  // on resize logic specific to this component
})

resizeObserver.observe(container)

// unobserve in component destroy method
this.resizeObserver.disconnect()
0
Anbu Agarwal