2013-04-28 50 views
139

Tôi đang vẽ một phân tán với d3.js. Với sự giúp đỡ của câu hỏi này:
Get the size of the screen, current web page and browser windowThay đổi kích thước svg khi cửa sổ được thay đổi kích thước trong d3.js

Tôi đang sử dụng câu trả lời này:

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; 

Vì vậy, tôi có thể để phù hợp với cốt truyện của tôi để cửa sổ của người sử dụng như thế này:

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

Bây giờ tôi muốn một cái gì đó sẽ chăm sóc thay đổi kích thước cốt truyện khi người dùng thay đổi kích thước cửa sổ.

PS: Tôi không sử dụng jQuery trong mã của mình.

+1

Có thể trùng lặp [Whats cách tốt nhất để tạo bố cục trực quan hóa d3.js là gì?] (Http://stackoverflow.com/questions/9400615/whats-the-best-way-to-make-a-d3-js -visualisation-layout-responsive) – ColinE

Trả lời

218

Look cho 'SVG đáp ứng' nó là khá đơn giản để thực hiện một SVG đáp ứng và bạn không phải lo lắng về kích thước nữa.

Sau đây là cách tôi đã làm nó:

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

Các mã 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; 
} 

Thông tin thêm/hướng dẫn:

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

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

+8

Điều này là thanh lịch hơn nhiều và cũng sử dụng một cách tiếp cận mở rộng quy mô container phải ở trong mỗi bộ công cụ của nhà phát triển front-end (có thể được sử dụng để mở rộng bất cứ điều gì trong một tỷ lệ khía cạnh cụ thể). Nên là câu trả lời hàng đầu. – kontur

+10

Chỉ cần thêm vào điều này: Thuộc tính 'viewBox' phải được đặt thành chiều cao và chiều rộng có liên quan của cốt truyện SVG của bạn: ' .attr ("viewBox", "0 0" + width + "" + height) ' trong đó 'width' và' height' được định nghĩa ở nơi khác. Đó là, trừ khi bạn muốn SVG của bạn bị cắt bớt bởi hộp xem. Bạn cũng có thể muốn cập nhật tham số 'padding-bottom' trong css của mình để khớp với tỷ lệ khung hình của phần tử svg của bạn. Phản hồi tuyệt vời mặc dù - rất hữu ích và hoạt động tốt. Cảm ơn! –

+0

điều này thật tuyệt vời! –

36

Sử dụng 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

Đây không phải là câu trả lời hay vì nó liên quan đến việc đặt giá thầu cho các sự kiện DOM ... Một giải pháp tốt hơn là chỉ sử dụng d3 và css – alem0lars

32

CẬP NHẬT chỉ cần sử dụng các phương pháp mới từ @cminatti


câu trả lời cũ cho các mục đích lịch sử

IMO nó tốt hơn để sử dụng lựa chọn() và trên() từ cách mà bạn có thể có nhiều xử lý sự kiện thay đổi kích thước ... chỉ không nhận được quá điên

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/

+0

Tôi không thể đồng ý ít hơn. Phương thức cminatti sẽ hoạt động trên tất cả các tệp d3js mà chúng ta xử lý. Phương pháp chuyển đổi này với biểu đồ thay đổi kích thước cho mỗi biểu đồ là quá mức cần thiết. Ngoài ra, nó cần phải được viết lại cho mọi biểu đồ có thể mà chúng ta có thể nghĩ ra. Phương pháp được đề cập ở trên hoạt động cho mọi thứ. Tôi đã thử 4 công thức nấu ăn bao gồm các loại tải lại bạn đã đề cập và không ai trong số họ làm việc cho nhiều khu vực lô như loại được sử dụng trong hình khối. –

+1

@EamonnKenny Tôi không thể đồng ý với bạn nhiều hơn. Câu trả lời khác 7 tháng trong tương lai là cấp trên :) – slf

+0

Đối với phương pháp này: "d3.select (cửa sổ) .on" Tôi không thể tìm thấy "d3.select (cửa sổ) .off" hoặc "d3.select (cửa sổ). unbind " –

11

Đó là loại xấu xí nếu mã thay đổi kích thước gần như miễn là mã để tạo biểu đồ ở vị trí đầu tiên. Vì vậy, thay vì thay đổi kích thước mọi phần tử của biểu đồ hiện tại, tại sao không tải lại đơn giản? Đây là cách nó hoạt động cho tôi:

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

Dòng quan trọng là d3.select('svg').remove();. Nếu không, mỗi thay đổi kích thước sẽ thêm một phần tử SVG khác bên dưới phần trước đó.

+0

Điều này sẽ loại bỏ hoàn toàn hiệu suất nếu bạn vẽ lại toàn bộ phần tử trên mỗi sự kiện thay đổi kích thước cửa sổ – sdemurjian

7

Trong bố cục lực, chỉ cần đặt thuộc tính 'chiều cao' và 'chiều rộng' sẽ không hoạt động để căn giữa lại/di chuyển cốt truyện vào vùng chứa svg. Tuy nhiên, có một câu trả lời rất đơn giản hoạt động cho các Bố cục lực lượng được tìm thấy here. Tóm lại:

Sử dụng cùng (bất kỳ) sự kiện nào bạn thích.

window.on('resize', resize); 

Sau đó, giả sử bạn có svg & biến lực:

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

Bằng cách này, bạn không tái làm cho đồ thị hoàn toàn, chúng tôi thiết lập các thuộc tính và d3 lại tính toán mọi thứ khi cần thiết . Điều này ít nhất là hoạt động khi bạn sử dụng một điểm hấp dẫn. Tôi không chắc đó có phải là điều kiện tiên quyết cho giải pháp này không. Bất cứ ai có thể xác nhận hay từ chối ?

Chúc mừng, g

+0

Điều này làm việc hoàn hảo trên bố cục lực lượng của tôi có khoảng 100 kết nối. Cảm ơn. – tribe84

Các vấn đề liên quan