जब विंडो d3.js में आकार बदल दी जाए तो svg का आकार बदलें


183

मैं 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");

अब मैं चाहूंगा कि उपयोगकर्ता द्वारा विंडो का आकार बदलने पर प्लॉट के आकार का कुछ ध्यान रखा जाए।

पुनश्च: मैं अपने कोड में jQuery का उपयोग नहीं कर रहा हूँ।


जवाबों:


294

'उत्तरदायी एसवीजी' देखें एसवीजी को उत्तरदायी बनाने के लिए यह बहुत सरल है और आपको किसी भी आकार के बारे में चिंता करने की आवश्यकता नहीं है।

यहाँ देखें कि मैंने यह कैसे किया:

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>

नोट: एसवीजी छवि में सब कुछ खिड़की की चौड़ाई के साथ होगा। इसमें स्ट्रोक की चौड़ाई और फ़ॉन्ट आकार (यहां तक ​​कि सीएसएस के साथ सेट) भी शामिल हैं। यदि यह वांछित नहीं है, तो नीचे दिए गए वैकल्पिक समाधान शामिल हैं।

अधिक जानकारी / ट्यूटोरियल:

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

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


8
यह बहुत अधिक सुरुचिपूर्ण है और एक कंटेनर स्केलिंग दृष्टिकोण का उपयोग करता है जो हर फ्रंट-एंड डेवलपर के टूलकिट में होना चाहिए (किसी विशेष पहलू अनुपात में कुछ भी स्केलिंग पर इस्तेमाल किया जा सकता है)। शीर्ष उत्तर होना चाहिए।
कांट

13
बस इसे जोड़ने के लिए: viewBoxविशेषता को आपके एसवीजी प्लॉट की प्रासंगिक ऊंचाई और चौड़ाई पर सेट किया जाना चाहिए: .attr("viewBox","0 0 " + width + " " + height) जहां widthऔर heightकहीं और परिभाषित किए गए हैं। यही है, जब तक आप नहीं चाहते कि आपका एसवीजी व्यू बॉक्स से टकरा जाए। आप padding-bottomअपने एसवीजी तत्व के पहलू अनुपात से मिलान करने के लिए अपने सीएसएस में पैरामीटर को अपडेट करना चाह सकते हैं । उत्कृष्ट प्रतिक्रिया हालांकि - बहुत उपयोगी है, और एक इलाज काम करता है। धन्यवाद!
एंड्रयू गाय

13
एक ही विचार के आधार पर, एक जवाब देना दिलचस्प होगा जिसमें पहलू अनुपात को संरक्षित करना शामिल नहीं है। प्रश्न एक svg तत्व के होने के बारे में था जो आकार बदलने पर पूर्ण विंडो को कवर करता रहता है, जिसका ज्यादातर मामलों में एक अलग पहलू अनुपात होता है।
निकोलस ले थिएरी डी'नेनेक्विन

37
यूएक्स के बारे में बस एक नोट: कुछ उपयोग के मामलों के लिए, आपके एसवीजी-आधारित डी 3 चार्ट का एक निश्चित पहलू अनुपात वाली संपत्ति की तरह व्यवहार करना आदर्श नहीं है। चार्ट जो पाठ प्रदर्शित करते हैं, या गतिशील होते हैं, या सामान्य रूप से एक परिसंपत्ति की तुलना में एक उचित उपयोगकर्ता इंटरफ़ेस की तरह महसूस करते हैं, अद्यतन आयामों के साथ फिर से प्रतिपादन करके अधिक हासिल करते हैं। उदाहरण के लिए, व्यूबॉक्स बलों को फॉन्ट करता है और टिक / स्केल अप करने के लिए टिक करता है, संभवतः HTML टेक्स्ट की तुलना में अजीब लग रहा है। इसके विपरीत, एक पुन: रेंडर चार्ट को एक सुसंगत आकार का लेबल रखने के लिए सक्षम बनाता है, साथ ही यह टिक जोड़ने या हटाने का अवसर प्रदान करता है।
करीम हर्नांडेज़

1
क्यों top: 10px;? मेरे लिए गलत लगता है और ऑफसेट प्रदान करता है। 0px को लगा देना ठीक रहता है।
टिमजमैन

43

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/


11
यह एक अच्छा जवाब नहीं है क्योंकि इसमें डोम इवेंट्स के लिए
बाइंडिंग

@ anlem0lars फन्नी काफी, सीएसएस / d3 संस्करण मेरे लिए काम नहीं किया और यह एक ...
ईसाई

32

अद्यतन केवल @ तरीके से नए तरीके का उपयोग करें


ऐतिहासिक उद्देश्यों के लिए पुराना उत्तर

IMO बेहतर है कि आप उस तरह से सिलेक्टेड इवेंट हैंडलर्स का चयन कर सकते हैं (जैसे) और () का उपयोग करें ... बस बहुत पागल मत बनो

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/


मैं कम सहमत नहीं हो सकता। Cminatti की विधि उन सभी d3js फाइलों पर काम करेगी, जिनसे हम निपटते हैं। प्रति चार्ट के आकार के साथ बदलने की यह विधि ओवरकिल है। इसके अलावा, इसे हर संभव चार्ट के लिए फिर से लिखना होगा जो हम सोच सकते थे। ऊपर बताई गई विधि हर चीज के लिए काम करती है। मैंने 4 व्यंजनों की कोशिश की है जिसमें आपके द्वारा उल्लिखित प्रकार के पुनः लोड शामिल हैं और उनमें से कोई भी कई क्षेत्र भूखंडों के लिए काम नहीं करता है जैसे कि क्यूबिज़्म में इस्तेमाल किया गया प्रकार।
Eamonn केनी

1
@EamonnKenny मैं आपसे अधिक सहमत नहीं हो सकता। अन्य जवाब में 7 महीने के भविष्य में बेहतर है :)
SLF

इस विधि के लिए: "d3.select (विंडो) .on" मैं "d3.select (विंडो) .off" या "d3.select (विंडो) .unbind" नहीं ढूंढ सका
समुद्र

1
@ समुद्री किलो stackoverflow.com/questions/20269384/...
SLF

यह उत्तर किसी भी तरह से हीन नहीं है। उनका उत्तर ग्राफ के हर पहलू (टिक, अक्ष, रेखाएं, और पाठ एनोटेशन सहित) को स्केल करेगा जो कुछ स्क्रीन आकार में, दूसरों में खराब और चरम आकारों में अपठनीय हो सकता है। मुझे इसका उपयोग करना बेहतर लगता है (या, अधिक आधुनिक समाधानों के लिए, @ अँगु अग्रवाल) विधि।
रूनर बर्ग

12

यह बदसूरत की तरह है अगर आकार बदलने के कोड के रूप में लगभग पहली जगह में ग्राफ के निर्माण के लिए कोड है। इसलिए मौजूदा चार्ट के प्रत्येक तत्व का आकार बदलने के बजाय, बस इसे फिर से लोड क्यों नहीं किया जाए? यहाँ है कि यह मेरे लिए कैसे काम किया:

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();। अन्यथा प्रत्येक आकार बदलना पिछले एक से नीचे एक और एसवीजी तत्व जोड़ देगा।


यह पूरी तरह से प्रदर्शन को मार देगा, यदि आप प्रत्येक विंडो को आकार देने वाले ईवेंट के संपूर्ण तत्व को पुनः साझा कर रहे हैं
sdemurjian

2
लेकिन यह सही है: जैसा कि आप प्रस्तुत करते हैं कि आप यह निर्धारित कर सकते हैं कि आपके पास इनसेट अक्ष होना चाहिए, एक किंवदंती छिपाएं, उपलब्ध सामग्री के आधार पर अन्य चीजें करें। विशुद्ध रूप से CSS / viewbox समाधान का उपयोग करते हुए, यह सभी विज़ुअलाइज़ेशन लुक को पूरा करता है। छवियों के लिए अच्छा है, डेटा के लिए बुरा है।
क्रिस नॉल

8

केवल 'ऊंचाई' और 'चौड़ाई' विशेषताओं को सेट करने वाले बल लेआउट में, svg कंटेनर में प्लॉट को फिर से केंद्र में ले जाने / स्थानांतरित करने के लिए काम नहीं करेगा। हालांकि, यहां पाया जाने वाला फोर्स लेआउट के लिए बहुत ही सरल उत्तर है । संक्षेप में:

आप की तरह ही (किसी भी) घटना का उपयोग करें।

window.on('resize', resize);

फिर आपके पास svg और बल चर है:

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

इस तरह, आप ग्राफ़ को पूरी तरह से फिर से प्रस्तुत नहीं करते हैं, हम विशेषताओं को सेट करते हैं और डी 3 आवश्यक चीजों की फिर से गणना करते हैं। यह कम से कम काम करता है जब आप गुरुत्वाकर्षण के बिंदु का उपयोग करते हैं। मुझे यकीन नहीं है कि अगर इस समाधान के लिए एक शर्त है। क्या कोई पुष्टि या इनकार कर सकता है?

चीयर्स, जी


यह मेरे बल लेआउट पर पूरी तरह से काम करता है जिसमें लगभग 100 कनेक्शन हैं। धन्यवाद।
tribe84

1

D3 v4 / v5 में बल निर्देशित ग्राफ़ का उपयोग करने वालों के लिए, sizeविधि किसी भी अधिक मौजूद नहीं है। मेरे लिए काम करने वाले कुछ इस तरह से ( इस गीथूब मुद्दे पर आधारित ):

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

यदि आप इवेंट को आकार देने के लिए कस्टम लॉजिक को बाँधना चाहते हैं, तो आजकल आप एक SVGElement के बाउंडिंग बॉक्स के लिए ResizeObserver ब्राउज़र API का उपयोग करना शुरू कर सकते हैं ।
यह उस मामले को भी संभालेगा जब पास के तत्वों के आकार में परिवर्तन के कारण कंटेनर को आकार दिया जाता है। व्यापक ब्राउज़र समर्थन के लिए
एक पॉलीफ़िल है।

यह यूआई घटक में काम कर सकता है:

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()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.