2012-09-07 48 views
6

Tôi đã tạo một cây trong D3.js dựa trên cây nút liên kết của Mike Bostock. Vấn đề tôi có và tôi cũng thấy trong cây của Mike là nhãn văn bản chồng chéo/gạch dưới các nút vòng tròn khi không có đủ không gian thay vì mở rộng các liên kết để rời khỏi một số không gian.Làm thế nào để tránh sự chồng chéo của các yếu tố văn bản trên TreeMap khi các phần tử con được mở trong D3.js?

Là người dùng mới, tôi không được phép tải lên hình ảnh, vì vậy đây là link đến Cây của Mike nơi bạn có thể thấy nhãn của các nút trước chồng chéo các nút sau.

tôi đã cố gắng mọi thứ khác nhau để khắc phục vấn đề bằng cách phát hiện dài điểm ảnh của văn bản với:

d3.select('.nodeText').node().getComputedTextLength(); 

Tuy nhiên đây chỉ hoạt động sau khi tôi trả lại trang khi tôi cần chiều dài của mặt hàng văn bản dài nhất trước khi tôi render.

Bắt mục văn bản dài nhất trước khi tôi làm với:

nodes = tree.nodes(root).reverse(); 

var longest = nodes.reduce(function (a, b) { 
    return a.label.length > b.label.length ? a : b; 
}); 

node = vis.selectAll('g.node').data(nodes, function(d, i){ 
    return d.id || (d.id = ++i); 
}); 

nodes.forEach(function(d) { 
    d.y = (longest.label.length + 200); 
}); 

chỉ trả về độ dài chuỗi, trong khi sử dụng

d.y = (d.depth * 200); 

làm cho mỗi liên kết một chiều dài tĩnh và không thay đổi kích thước đẹp khi các nút mới được mở hoặc đóng.

Có cách nào để tránh trùng lặp này không? Nếu vậy, cách tốt nhất để làm điều này và giữ cấu trúc động của cây là gì?

Có 3 giải pháp khả thi mà tôi có thể đưa ra nhưng không phải là đơn giản:

  1. Phát hiện chiều dài nhãn và sử dụng một dấu chấm lửng nơi nó vượt nút con. (làm cho nhãn ít đọc được)
  2. chia tỷ lệ bố cục động bằng cách phát hiện độ dài nhãn và yêu cầu các liên kết điều chỉnh cho phù hợp. (Điều tốt nhất nhưng có vẻ khó thực hiện)
  3. chia tỷ lệ phần tử svg và sử dụng thanh cuộn khi nhãn bắt đầu chạy. (không chắc chắn điều này là có thể vì tôi đã làm việc với giả định rằng SVG cần phải có thiết lập chiều cao và chiều rộng)

Trả lời

7

Vì vậy, cách tiếp cận sau đây có thể cung cấp các cấp độ khác nhau về bố cục "chiều cao" khác nhau.

Điều quan trọng là nhận ra rằng bố cục cây chỉ đơn giản là ánh xạ mọi thứ vào một không gian tùy ý có chiều rộng và chiều cao và rằng đường chéo al chiếu bản đồ chiều rộng (x) đến góc và chiều cao (y) đến bán kính. Hơn nữa bán kính là một hàm đơn giản của độ sâu của cây.

Vì vậy here là một cách để phân công lại chiều sâu dựa trên độ dài văn bản:

Trước hết, tôi sử dụng sau đây (jQuery) để tính toán kích thước văn bản tối đa cho:

var computeMaxTextSize = function(data, fontSize, fontName){ 
    var maxH = 0, maxW = 0; 

    var div = document.createElement('div'); 
    document.body.appendChild(div); 
    $(div).css({ 
     position: 'absolute', 
     left: -1000, 
     top: -1000, 
     display: 'none', 
     margin:0, 
     padding:0 
    }); 

    $(div).css("font", fontSize + 'px '+fontName); 

    data.forEach(function(d) { 
     $(div).html(d); 
     maxH = Math.max(maxH, $(div).outerHeight()); 
     maxW = Math.max(maxW, $(div).outerWidth()); 
    }); 

    $(div).remove(); 
    return {maxH: maxH, maxW: maxW}; 
} 

Bây giờ tôi sẽ đệ quy xây dựng một mảng với một chuỗi các chuỗi cho mỗi cấp độ:

var allStrings = [[]]; 
var childStrings = function(level, n) { 
    var a = allStrings[level]; 
    a.push(n.name); 

    if(n.children && n.children.length > 0) { 
     if(!allStrings[level+1]) { 
      allStrings[level+1] = []; 
     } 
     n.children.forEach(function(d) { 
      childStrings(level + 1, d); 
     }); 
    } 
}; 
childStrings(0, root); 

Và sau đó tính toán độ dài văn bản tối đa cho mỗi cấp.

var maxLevelSizes = []; 
allStrings.forEach(function(d, i) { 
    maxLevelSizes.push(computeMaxTextSize(allStrings[i], '10', 'sans-serif')); 
}); 

Sau đó, tôi tính tổng chiều rộng của văn bản cho tất cả các cấp (thêm dấu cách cho biểu tượng vòng tròn nhỏ và một số đệm để trông đẹp). Đây sẽ là bán kính của bố cục cuối cùng. Lưu ý rằng tôi sẽ sử dụng lại cùng một số tiền đệm sau này.

var padding = 25; // Width of the blue circle plus some spacing 
var totalRadius = d3.sum(maxLevelSizes, function(d) { return d.maxW + padding}); 

var diameter = totalRadius * 2; // was 960; 

var tree = d3.layout.tree() 
    .size([360, totalRadius]) 
    .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2)/a.depth; }); 

Bây giờ chúng tôi có thể gọi bố cục như bình thường. Có một mảnh cuối cùng: để tìm ra bán kính cho các mức khác nhau, chúng ta sẽ cần một tổng tích lũy của bán kính của các mức trước đó. Một khi chúng ta có chúng ta chỉ cần gán bán kính mới cho các nút được tính toán.

// Compute cummulative sums - these will be the ring radii 
var newDepths = maxLevelSizes.reduce(function(prev, curr, index) { 
    prev.push(prev[index] + curr.maxW + padding);     
    return prev; 
},[0]);              

var nodes = tree.nodes(root); 

// Assign new radius based on depth 
nodes.forEach(function(d) { 
    d.y = newDepths[d.depth]; 
}); 

Eh thì đấy! Đây có lẽ không phải là giải pháp sạch nhất, và có lẽ không giải quyết mọi mối quan tâm, nhưng nó sẽ giúp bạn bắt đầu. Chúc vui vẻ!

+0

Rất tiếc, đã quá lâu để phản hồi nhưng tôi không thấy điều này cho đến bây giờ. Cuối cùng tôi chưa bao giờ kết thúc việc khắc phục vấn đề này nhưng cảm ơn câu trả lời của bạn. Tôi sẽ xem liệu tôi có thể tìm mã cũ và thử khắc phục sự cố không. – alengel