Изменение размера svg при изменении размера окна в d3.js


Я рисую диаграмму рассеяния с d3.js. С помощью этого вопроса :
получить размер экрана, текущей веб-страницы и окна браузера

Я использую этот ответ:

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;

поэтому я могу подогнать свой сюжет к окну пользователя следующим образом:

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

теперь я хотел бы, чтобы что-то позаботилось об изменении размера сюжета, когда пользователь изменяет размер окна.

PS: я не использую jQuery в моем коде.

6 156

6 ответов:

ищите "отзывчивый SVG" довольно просто сделать SVG отзывчивым, и вам больше не нужно беспокоиться о размерах.

вот как я сделал это:

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

код 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;
}

Подробнее / туториалы:

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

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

использовать

обновление просто используйте новый способ от @cminatti


старый ответ для исторических целей

IMO лучше использовать select() и on (), так как таким образом вы можете иметь несколько обработчиков событий изменения размера... только не сходи с ума

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/

это довольно уродливо, если код изменения размера почти такой же длинный, как код для построения графика в первую очередь. Поэтому вместо изменения размера каждого элемента существующей диаграммы, почему бы просто не перезагрузить его? Вот как это работало для меня:

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

решающая строка d3.select('svg').remove();. В противном случае каждое изменение размера добавит еще один элемент SVG ниже предыдущего.

в force layouts просто установка атрибутов "высота" и "ширина" не будет работать, чтобы повторно центрировать/перемещать участок в контейнер svg. Тем не менее, есть очень простой ответ, который работает для Force Layouts found здесь. Вкратце:

используйте то же самое (любое) событие, которое вам нравится.

window.on('resize', resize);

тогда предполагая, что у вас есть 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();
}

таким образом, вы не перерисовываете график полностью, мы устанавливаем атрибуты и D3 повторно вычисляет вещи по мере необходимости. Это по крайней мере работает, когда вы используете центра тяжести. Я не уверен, что это необходимое условие для этого решения. Кто-нибудь может подтвердить или опровергнуть ?

Ура, g

Я использую окно.innerWidth для ширины и окна клиента.внутренняя высота для высоты клиента.