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.
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
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);
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/
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.
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
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();
J'utilise window.innerWidth pour la largeur du client et window.innerHeight pour la hauteur du client.
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()