D3.js'de pencere yeniden boyutlandırıldığında svg'yi yeniden boyutlandır


183

Ben d3.js ile bir dağılım grafiği çiziyorum. Bu sorunun yardımıyla:
Ekranın boyutunu, geçerli web sayfasını ve tarayıcı penceresini alın

Bu cevabı kullanıyorum:

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;

Böylece grafiğimi kullanıcının penceresine şu şekilde sığdırabilirim:

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

Şimdi bir şey kullanıcı pencereyi yeniden boyutlandırırken arsa yeniden boyutlandırma ilgilenir istiyorum.

PS: Kodumda jQuery kullanmıyorum.


Yanıtlar:


295

'Duyarlı SVG'yi arayın Bir SVG'yi duyarlı hale getirmek oldukça basittir ve artık boyutlar hakkında endişelenmenize gerek yok.

İşte nasıl yaptım:

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>

Not: SVG görüntüsündeki her şey pencere genişliği ile ölçeklenir. Bu, kontur genişliğini ve yazı tipi boyutlarını (CSS ile ayarlanmış olanlar dahil) içerir. Bu istenmiyorsa, aşağıda daha fazla alternatif çözümler vardır.

Daha fazla bilgi / eğitim:

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

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


8
Bu çok daha zariftir ve ayrıca her ön uç geliştiricinin araç setinde olması gereken bir konteyner ölçekleme yaklaşımı kullanır (belirli bir en boy oranında herhangi bir şeyi ölçeklendirmede kullanılabilir). En iyi cevap olmalı.
kontur

13
Sadece buna eklemek için: viewBoxÖzellik, SVG grafiğinizin ilgili yüksekliğine ve genişliğine ayarlanmalıdır: .attr("viewBox","0 0 " + width + " " + height) nerede widthve heightbaşka bir yerde tanımlanır. Yani, SVG'nizin görünüm kutusuyla kırpılmasını istemiyorsanız. Ayrıca padding-bottomcss'nizdeki parametreyi, svg öğenizin en boy oranıyla eşleşecek şekilde güncellemek isteyebilirsiniz . Mükemmel yanıt olsa - çok yararlı ve bir tedavi çalışır. Teşekkürler!
Andrew Guy

13
Aynı fikre dayanarak, en-boy oranını korumayı içermeyen bir cevap vermek ilginç olacaktır. Soru, yeniden boyutlandırma üzerinde tam pencereyi kaplayan bir svg öğesine sahip olmakla ilgiliydi, bu da çoğu durumda farklı bir en boy oranı anlamına geliyor.
Nicolas Le Thierry d'Ennequin

37
Sadece UX ile ilgili bir not: Bazı kullanım durumlarında, SVG tabanlı d3 grafiğinize sabit en boy oranına sahip bir varlık gibi davranmak ideal değildir. Metin görüntüleyen veya dinamik olan veya genel olarak bir öğeden daha uygun bir Kullanıcı Arayüzü gibi görünen grafiklerin, güncellenmiş boyutlarla yeniden oluşturma yoluyla daha fazla kazancı vardır. Örneğin, viewBox, eksen yazı tiplerini ve keneleri, html metnine kıyasla potansiyel olarak garip görünen, yukarı / aşağı ölçeklendirmeye zorlar. Buna karşılık, yeniden oluşturma, grafiğin tutarlı bir boyutta etiketlemesini sağlar ve ayrıca keneler ekleme veya çıkarma fırsatı sunar.
Karim Hernandez

1
Neden top: 10px;? Benim için yanlış görünüyor ve ofset sağlıyor. 0px değerine koymak işe yarar.
TimZaman

43

Window.onresize komutunu kullanın :

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/


11
Bu, DOM etkinliklerine teklif vermeyi içerdiği için iyi bir cevap değil ... Çok daha iyi bir çözüm sadece d3 ve css kullanıyor
alem0lars

@ alem0lars funnily yeterince, css / d3 sürümü benim için çalışmadı ve bu yaptı ...
Christian

32

GÜNCELLEME sadece @cminatti'den yeni yolu kullan


tarihi amaçlar için eski cevap

IMO select () ve on () kullanmak daha iyidir, çünkü bu şekilde birden fazla yeniden boyutlandırma olay işleyicisi olabilir ...

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/


Daha az katılıyorum. Cminatti yöntemi, uğraştığımız tüm d3js dosyalarında çalışacaktır. Bu grafik başına yeniden boyutlandırma ile dönüştürme yöntemi gereğinden fazla kullanılır. Ayrıca, düşünebileceğimiz her olası grafik için yeniden yazılması gerekir. Yukarıda belirtilen yöntem her şey için işe yarar. Bahsettiğiniz yeniden yükleme türü de dahil olmak üzere 4 tarif denedim ve bunların hiçbiri kübizmde kullanılan tipte birden fazla alan arazisi için çalışmıyor.
Eamonn Kenny

1
@EamonnKenny Size daha fazla katılıyorum. Gelecekte 7 ay diğer cevap üstün :)
slf

Bu yöntem için: "d3.select (window) .on" Bulamadım "d3.select (window) .off" veya "d3.select (window) .unbind"
deniz-kg


Bu cevap hiçbir şekilde aşağı değildir. Yanıtları, grafiğin bazı ekran boyutlarında, diğerlerinde kötü ve aşırı boyutlarda okunamayan garip görünebilecek her yönünü (keneler, eksen, çizgiler ve metin ek açıklamaları dahil) ölçeklendirir. Bu (veya daha modern bir çözüm için @Angu Agarwal) yöntemini kullanarak yeniden boyutlandırmayı daha iyi buluyorum.
Rúnar Berg

12

Yeniden boyutlandırma kodu neredeyse grafiği ilk etapta oluşturmak için kod kadar uzunsa çirkin. Öyleyse mevcut grafiğin her öğesini yeniden boyutlandırmak yerine, neden sadece yeniden yüklemiyorsunuz? Benim için böyle çalıştı:

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

Önemli olan d3.select('svg').remove();. Aksi takdirde, her yeniden boyutlandırma bir öncekinin altına başka bir SVG öğesi ekleyecektir.


Her pencere yeniden boyutlandırma etkinliğinde tüm öğeyi yeniden
çiziyorsanız

2
Ancak bu doğrudur: Oluştururken, içsel eksene sahip olup olmadığınızı, bir göstergeyi gizleyip gizlemeyeceğinizi, mevcut içeriğe göre başka şeyler yapacağınızı belirleyebilirsiniz. Tamamen bir CSS / görünüm kutusu çözümü kullanarak, tek yapmanız gereken görselleştirmenin ezilmiş görünmesini sağlamaktır. Görüntüler için iyi, veriler için kötü.
Chris Knoll

8

Kuvvet mizanpajlarında sadece 'height' ve 'width' niteliklerinin ayarlanması, grafiği yeniden ortalamak / svg konteynerine taşımak için çalışmaz. Bununla birlikte, burada bulunan Force Layouts için çalışan çok basit bir cevap var . Özetle:

Beğendiğiniz aynı (herhangi bir) etkinliği kullanın.

window.on('resize', resize);

Sonra svg & force değişkenlerine sahip olduğunuzu varsayarsak:

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

Bu şekilde, grafiği tamamen yeniden oluşturmazsınız, öznitelikleri ayarlarız ve d3 işleri gerektiği gibi yeniden hesaplar. Bu en azından bir yerçekimi noktası kullandığınızda işe yarar. Bunun bu çözüm için bir ön koşul olup olmadığından emin değilim. Herhangi biri doğrulayabilir veya reddedebilir mi ?

Şerefe, g


Bu, yaklaşık 100 bağlantıya sahip kuvvet düzenimde mükemmel çalıştı. Teşekkürler.
tribe84

1

D3 v4 / v5'te zorla yönlendirilen grafikler kullananlar için sizeyöntem artık mevcut değil. Aşağıdaki gibi bir şey benim için çalıştı ( bu github sorununa dayanarak ):

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

1

Özel mantığı yeniden boyutlandırma olayına bağlamak istiyorsanız, günümüzde bir SVGElement öğesinin sınırlayıcı kutusu için ResizeObserver tarayıcı API'sını kullanmaya başlayabilirsiniz .
Bu, yakındaki öğelerin boyut değişikliği nedeniyle konteyner yeniden boyutlandırıldığında da durumu ele alır. Daha geniş tarayıcı desteği için
bir çoklu dolgu var .

UI bileşeninde şu şekilde çalışabilir:

function redrawGraph(container, { width, height }) {
  d3
    .select(container)
    .select('svg')
    .attr('height', height)
    .attr('width', width)
    .select('rect')
    .attr('height', height)
    .attr('width', width);
}

// Setup observer in constructor
const resizeObserver = new ResizeObserver((entries, observer) => {
  for (const entry of entries) {
    // on resize logic specific to this component
    redrawGraph(entry.target, entry.contentRect);
  }
})

// Observe the container
const container = document.querySelector('.graph-container');
resizeObserver.observe(container)
.graph-container {
  height: 75vh;
  width: 75vw;
}

.graph-container svg rect {
  fill: gold;
  stroke: steelblue;
  stroke-width: 3px;
}
<script src="https://unpkg.com/resize-observer-polyfill@1.5.1/dist/ResizeObserver.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<figure class="graph-container">
  <svg width="100" height="100">
    <rect x="0" y="0" width="100" height="100" />
  </svg>
</figure>

// unobserve in component destroy method
this.resizeObserver.disconnect()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.