Pesquisa de site

javascript - Qual é a melhor maneira de tornar um layout de visualização d3.js responsivo?


Suponha que eu tenha um script de histograma que cria um gráfico SVG de 960 500. Como faço para torná-lo responsivo para que, ao redimensionar, as larguras e alturas do gráfico sejam dinâmicas?

<script> 

var n = 10000, // number of trials
    m = 10,    // number of random variables
    data = [];

// Generate an Irwin-Hall distribution.
for (var i = 0; i < n; i++) {
  for (var s = 0, j = 0; j < m; j++) {
    s += Math.random();
  }
  data.push(s);
}

var histogram = d3.layout.histogram()
    (data);

var width = 960,
    height = 500;

var x = d3.scale.ordinal()
    .domain(histogram.map(function(d) { return d.x; }))
    .rangeRoundBands([0, width]);

var y = d3.scale.linear()
    .domain([0, d3.max(histogram.map(function(d) { return d.y; }))])
    .range([0, height]);

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

svg.selectAll("rect")
    .data(histogram)
  .enter().append("rect")
    .attr("width", x.rangeBand())
    .attr("x", function(d) { return x(d.x); })
    .attr("y", function(d) { return height - y(d.y); })
    .attr("height", function(d) { return y(d.y); });

svg.append("line")
    .attr("x1", 0)
    .attr("x2", width)
    .attr("y1", height)
    .attr("y2", height);

</script> 

O gist de exemplo completo do histograma é: https://gist.github.com/993912

Resposta #1

Há outra maneira de fazer isso que não requer redesenhar o gráfico, e envolve modificar os atributos viewBox e preserveAspectRatio no elemento <svg>:

<svg id="chart" viewBox="0 0 960 500"
  preserveAspectRatio="xMidYMid meet">
</svg>

Atualização 24/11/15: a maioria dos navegadores modernos pode inferir a proporção de aspecto dos elementos SVG a partir do viewBox, então talvez você não precise manter o tamanho do gráfico atualizado. Se você precisar suportar navegadores antigos, pode redimensionar seu elemento quando a janela mudar de tamanho assim:

var aspect = width / height,
    chart = d3.select('#chart');
d3.select(window)
  .on("resize", function() {
    var targetWidth = chart.node().getBoundingClientRect().width;
    chart.attr("width", targetWidth);
    chart.attr("height", targetWidth / aspect);
  });

E o conteúdo SVG será dimensionado automaticamente. Você pode ver um exemplo funcionando disso (com algumas modificações) aqui: basta redimensionar a janela ou o painel inferior direito para ver como ele reage.

Resposta #2

Procure por 'SVG responsivo', é bem simples tornar um SVG responsivo e você não precisa mais se preocupar com tamanhos.

Aqui está como eu fiz isso:

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

O código 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;
}

Mais informações/tutoriais:

http://demosthenes.info/blog/744/Make-SVG-Responsive

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

Resposta #3

Eu escrevi um pequeno gist para resolver isso.

O padrão de solução geral é este:

  1. Divida o script em funções de computação e de desenho.
  2. Certifique-se de que a função de desenho desenhe de forma dinâmica e seja guiada pelas variáveis de largura e altura da visualização (A melhor forma de fazer isso é usar a API d3.scale)
  3. Vincule/cadeie o desenho a um elemento de referência na marcação. (Usei jQuery para isso, então importei.)
  4. Lembre-se de removê-lo se já estiver desenhado. Obtenha as dimensões do elemento referenciado usando jQuery.
  5. Vincule/encadeie a função draw à função de redimensionamento da janela. Introduza um debounce (tempo limite) a essa cadeia para garantir que redesenhamos apenas após um tempo limite.

:

código de referência de retorno do jquery:

$(reference).empty()
var width = $(reference).width();

Código debounce:

var debounce = function(fn, timeout) 
{
  var timeoutID = -1;
  return function() {
     if (timeoutID > -1) {
        window.clearTimeout(timeoutID);
     }
   timeoutID = window.setTimeout(fn, timeout);
  }
};

var debounced_draw = debounce(function() {
    draw_histogram(div_name, pos_data, neg_data);
  }, 125);

 $(window).resize(debounced_draw);

Aproveite!

Artigos relacionados