2013-01-11 25 views
13

Tôi đang tìm cách tạo biểu đồ tròn có nhãn nổi bằng D3. Tôi mới đến D3 và tôi thậm chí không chắc chắn điều này là có thể? Bạn có thể sử dụng các nhãn của một đồ thị bằng cách khác không? Nếu bạn có thể, bạn có thể chỉ cho tôi một ví dụ không?D3 - Biểu đồ hình tròn & Nhãn hướng dẫn có hiệu lực

Giải thích ngắn: Tôi muốn nhãn từ: http://bl.ocks.org/1691430

enter image description here ... phải được trên một biểu đồ pie.

Dưới đây là đoạn code tôi đang chạy bên dưới: Hoặc trong một JSBIN: http://jsbin.com/awilak/1/edit

Nếu tôi hiểu code của mình một cách chính xác, đây là phần có thêm các nhãn. Tôi không hiểu những gì các labelForce.update nào. Từ đó, tôi không quan tâm đến quá trình chuyển đổi, vì vậy dòng đó không cần thiết. Sau đó, phần còn lại chỉ vẽ vòng tròn và thêm liên kết/dòng? Nếu ai đó có thể tích hợp điều đó sẽ thật tuyệt vời nhưng nếu bạn có thể giúp tôi hiểu những gì đang xảy ra và những gì tôi thiếu tôi sẽ cảm thấy biết ơn hơn.

// Now for the labels 
// This is the only function call needed, the rest is just drawing the labels 
anchors.call(labelForce.update) 

labels = svg.selectAll(".labels") 
    .data(data, function(d,i) {return i;}) 
labels.exit() 
    .attr("class","exit") 
    .transition() 
    .delay(0) 
    .duration(500) 
    .style("opacity",0) 
    .remove(); 

// Draw the labelbox, caption and the link 
newLabels = labels.enter().append("g").attr("class","labels") 

newLabelBox = newLabels.append("g").attr("class","labelbox") 
newLabelBox.append("circle").attr("r",11) 
newLabelBox.append("text").attr("class","labeltext").attr("y",6) 
newLabels.append("line").attr("class","link") 

labelBox = svg.selectAll(".labels").selectAll(".labelbox") 
links = svg.selectAll(".link") 
labelBox.selectAll("text").text(function(d) { return d.num}) 
} 

<!DOCTYPE html> 
<html> 
<head>  
    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> 
    <title>Testing Pie Chart</title> 
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.1.3"></script> 
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js?2.1.3"></script> 
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?2.1.3"></script> 

    <style type="text/css"> 
    .slice text { 
     font-size: 16pt; 
     font-family: Arial; 
    } 
    </style> 
</head> 
<body> 
    <button id="button"> Test </button> 
    <br> 
    <form id="controls"> 
     <div> 
      <h2>Y axis</h2> 
      <ul id="y-axis"> 
       <li><label><input checked="checked" type="radio" name="y-axis" value="Component">Component</label></li> 
       <li><label><input type="radio" name="y-axis" value="Browser">Browser</label></li> 
       <li><label><input type="radio" name="y-axis" value="Version">Version</label></li> 
      </ul> 
     </div> 
    </form> 
    <script type="text/javascript"> 
    // return a list of types which are currently selected 
    function plottableTypes() { 
     var types = [].map.call (document.querySelectorAll ("#coaster-types input:checked"), function (checkbox) { return checkbox.value;}); 
     return types; 
    } 


    var w = 600,      //width 
    h = 600,       //height 
    r = 100, 
    r2 = 200,       //radius 
    axis = getAxis(),     //axes 
    color = d3.scale.category20c();  //builtin range of colors 

    data = [ 
     {"Browser":"Internet Explorer ","Version":"8.0","Toatl":2000,"Component":"6077447412293130422"}, 
     {"Browser":"Internet Explorer ","Version":"9.0 ","Toatl":1852,"Component":"6077447412293130422"}, 
     {"Browser":"Internet Explorer ","Version":"6.0 ","Toatl":1754,"Component":"6077447412293130422"}, 
     {"Browser":"Firefox ","Version":"16.0 ","Toatl":1020,"Component":"6077447412293130422"}, 
     {"Browser":"Chrome ","Version":"23.0 ","Toatl":972,"Component":"6077447412293130422"}, 
     {"Browser":"Internet Explorer ","Version":"7.0 ","Toatl":700,"Component":"6077447412293130422"}, 
     {"Browser":"Mobile Safari ","Version":"6.0 ","Toatl":632,"Component":"6077447412293130422"}, 
     {"Browser":"BOT ","Version":"BOT ","Toatl":356,"Component":"6077447412293130422"}, 
     {"Browser":"Firefox ","Version":"8.0 ","Toatl":196,"Component":"6077447412293130422"}, 
     {"Browser":"Mobile Safari ","Version":"5.1 ","Toatl":184,"Component":"6077447412293130422"} 
    ]; 

    var vis = d3.select("body") 
     .append("svg:svg")    //create the SVG element inside the <body> 
     .data([data])     //associate our data with the document 
     .attr("width", w)   //set the width and height of our visualization (these will be attributes of the <svg> tag 
     .attr("height", h) 
     .append("svg:g")    //make a group to hold our pie chart 
     .attr("transform", "translate(" + r2 + "," + r2 + ")") //move the center of the pie chart from 0, 0 to radius, radius 

    var arc = d3.svg.arc()    //this will create <path> elements for us using arc data 
     .outerRadius(r); 


    var pie = d3.layout.pie()   //this will create arc data for us given a list of values 
     .value(function(d) { return d.Toatl; }); //we must tell it out to access the value of each element in our data array 

    var arcs = vis.selectAll("g.slice")  //this selects all <g> elements with class slice (there aren't any yet) 
     .data(pie)       //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties) 
     .enter()       //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array 
     .append("svg:g")    //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice) 
     .attr("class", "slice"); //allow us to style things in the slices (like text) 


    arcs.append("svg:path") 
     .attr("fill", function(d, i) { return color(i); }) //set the color for each slice to be chosen from the color function defined above 
     .attr("d", arc);         //this creates the actual SVG path using the associated data (pie) with the arc drawing function 


    arcs.append("svg:text")          //add a label to each slice 
     .attr("transform", function(d) {     //set the label's origin to the center of the arc 
      //we have to make sure to set these before calling arc.centroid 
      d.innerRadius = r2; 
      d.outerRadius = r; 
      return "translate(" + arc.centroid(d) + ")";  //this gives us a pair of coordinates like [50, 50] 
     }) 
     .attr("text-anchor", "middle")       //center the text on it's origin 
     .text(function(d, i) { 
      if(axis.yAxis == "Component"){ 
       return data[i].Component; 
      } 
      return data[i].Browser;  //get the label from our original data array 
     });  

     d3.select('#button').on('click', reColor); 

     var arcOver = d3.svg.arc() 
      .outerRadius(r + 30) 
      .innerRadius(0); 
     var arc = d3.svg.arc() 
      .outerRadius(r) 
      .innerRadius(0); 

     var arcs = vis.selectAll("g.slice") 
      .attr("class", "slice") 
      .on("mouseover", function(d) { 
       getAxis(); 
       d3.select(this) 
        .select("path") 
        .transition() 
        .duration(500) 
       .attr("d", arcOver); 
       d3.select(this).select("text") 
        .text(function(d, i) { 
         if(axis.yAxis == "Component"){ 
          return data[i].Component; 
         } 
        return data[i].Browser;  //get the label from our original data array 
       });  
      }) 
      .on("mouseout", function(d) { 
       getAxis(); 
       d3.select(this) 
        .select("path") 
        .transition() 
        .duration(500) 
        .attr("d", arc); 
       d3.select(this) 
        .select("text") 
        .text(function(d, i) { 
         if(axis.yAxis == "Component"){ 
          return data[i].Component; 
         } 
         return data[i].Browser;  //get the label from our original data array 
        }); 
       }); 


     function reColor(){ 
      var slices = d3.select('body').selectAll('path'); 
      slices.transition() 
       .duration(2000) 
       .attr("fill", function(d, i) { return color(i+2); }); 
      slices.transition() 
       .delay(2000) 
       .duration(2000) 
       .attr("fill", function(d, i) { return color(i+10); }) 
     } 
     function makeData(){ 

     } 
     // return an object containing the currently selected axis choices 
     function getAxis() { 
      var y = document.querySelector("#y-axis input:checked").value; 
      return { 
       yAxis: y, 
      }; 
     } 
     function update() { 
      axis = getAxis() 
      arcs.selectAll("text")   //add a label to each slice    
       .text(function(d, i) { 
        if(axis.yAxis == "Component"){ 
         return data[i].Component; 
        } 
        return data[i].Browser;  //get the label from our original data array 
       }); 
      } 

     document.getElementById("controls").addEventListener ("click", update, false); 
     document.getElementById("controls").addEventListener ("keyup", update, false); 
    </script> 
</body> 
</html> 
+0

Có thể thực hiện được. Ví dụ mà bạn đã đăng đã khá gần với những gì tôi nghĩ bạn muốn làm. Bạn có thể đăng một số mã của những gì bạn đã thử xin vui lòng? –

+0

Tôi đang bối rối khi tôi cần phải gọi các nhãn, tôi sẽ chỉnh sửa trong biểu đồ hình tròn của tôi ở trên. Gọi (labelForce.update) làm gì chính xác? Cảm ơn sự giúp đỡ của bạn! – gbam

+1

'anchors' trong ví dụ là những thứ được dán nhãn. Đó sẽ là phân khúc bánh của bạn. Nó có thể dễ dàng hơn trong đầu để vẽ biểu đồ hình tròn một cách độc lập và sử dụng mã trong ví dụ như là với dữ liệu cho các điểm ở đâu đó trong các phân khúc bánh. –

Trả lời

-1

Bạn cần tạo hai vòng cung. Một cho bản vẽ biểu đồ hình tròn và hình vẽ lớn để các nhãn nằm trên đó.

// first arc used for drawing the pie chart 
var arc = d3.svg.arc() 
    .outerRadius(radius - 10) 
    .innerRadius(0); 

// label attached to first arc 
g.append("text") 
    .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; }) 
    .attr("dy", ".35em") 
    .style("text-anchor", "middle") 
    .text(function(d) { return d.data.age; }); 

// second arc for labels 
var arc2 = d3.svg.arc() 
    .outerRadius(radius + 20) 
    .innerRadius(radius + 20); 

// label attached to second arc 
g.append("text") 
    .attr("transform", function(d) { return "translate(" + arc2.centroid(d) + ")"; }) 
    .attr("dy", ".35em") 
    .style("text-anchor", "middle") 
    .text(function(d) { return d.data.age; }); 
0

Có, bạn chắc chắn có thể kết hợp nhãn hiệu với biểu đồ tròn! Không có gì đặc biệt đặc biệt về các nhãn biểu đồ hình tròn mà bạn đã bắt đầu, chúng chỉ là các phần tử văn bản có thể được định vị như bất kỳ thứ gì khác bằng cách sử dụng biến đổi hoặc x/y. Dường như ban đầu bạn định vị các nhãn này theo trọng tâm của các vòng cung mà họ đang ghi nhãn, nhưng bạn có thể dễ dàng sử dụng các tiêu chí khác (như đầu ra của bố cục lực).

Bố cục lực của D3 tính toán các vị trí cho mọi thứ dựa trên một tập hợp các ràng buộc về những gì được cố định, những gì có thể di chuyển và được kết nối với nó. Phương pháp labelForce.update từ ví dụ bl.ocks của Mike đang được sử dụng để thông báo bố cục lực lượng về số lượng đối tượng cần được định vị và vị trí cố định "neo". Sau đó nó lưu các vị trí được tính toán cho các nhãn vào mô hình dữ liệu của sơ đồ và chúng được sử dụng sau này trong hàm redrawLabels.

2

Như những người khác được đề cập trong nhận xét cho bài giới thiệu của bạn, bạn có thể đạt được giải pháp như bạn đã mô tả và có thể sử dụng mã của bạn cùng với các phần của "di chuyển nhãn". Nếu tôi hiểu bạn một cách chính xác, bạn muốn đạt được các nhãn không chồng chéo bằng cách sử dụng bố cục lực, đó là một ý tưởng khá hay mà tôi chưa vấp ngã.

Phần mã bạn dán từ ví dụ chỉ cần vẽ nhãn và các dòng như bạn đã giải thích chính xác. Bước tiếp theo là sắp xếp lại các nhãn theo bố cục giống như lực xung quanh biểu đồ hình tròn của bạn.

Phần rằng sắp xếp lại các nhãn (và liên kết) trong ví dụ này là như sau:

function redrawLabels() { 
    labelBox 
     .attr("transform",function(d) { return "translate("+d.labelPos.x+" "+d.labelPos.y+")"}) 

    links 
     .attr("x1",function(d) { return d.anchorPos.x}) 
     .attr("y1",function(d) { return d.anchorPos.y}) 
     .attr("x2",function(d) { return d.labelPos.x}) 
     .attr("y2",function(d) { return d.labelPos.y}) 
}   

// Initialize the label-forces 
labelForce = d3.force_labels() 
    .linkDistance(0.0) 
    .gravity(0) 
    .nodes([]).links([]) 
    .charge(-60) 
    .on("tick",redrawLabels) 

Chức năng là một trong những thay đổi vị trí của các nhãn và dòng. Lực được tính bằng D3 và bắt đầu bằng d3.force_labels().... Như bạn có thể thấy, hàm được gán như một trình xử lý sự kiện cho sự kiện đánh dấu. Nói cách khác: Sau mỗi bước tính toán lực, D3 gọi 'ngăn kéo' cho mỗi nhãn và cập nhật các vị trí.

Thật không may tôi không quen thuộc với phương pháp force_labels() của D3, nhưng tôi cho rằng nó hoạt động khá giống với thường lệ force(). Một neo, trong trường hợp của bạn, được đặt ở đâu đó trong mỗi mảnh bánh cho mỗi nhãn. Càng tập trung nhiều hơn vào từng mảnh bánh (KHÔNG phải bản thân chiếc bánh) thì càng tốt. Thật không may, bạn phải tính toán vị trí neo này bằng cách nào đó (tội lỗi và công cụ cos) và thiết lập các đầu cuối cho vị trí cố định này trong phạm vi redrawLabels().

Sau khi bạn đã thực hiện việc này, bạn sẽ thấy kết quả đầu tiên. Bạn có thể phải chơi xung quanh với trọng lực, linkDistance vv giá trị của lực lượng để đạt được kết quả tốt. (Đó là những gì silders trong ví dụ làm.)

Xem tài liệu d3 để biết thêm: https://github.com/mbostock/d3/wiki/Force-Layout

Sau đó, bạn có thể sẽ vấp ngã khi vấn đề mà các nhãn được sắp xếp xung quanh chiếc bánh mà không chồng chéo nhưng trong một số trật tự lạ . Bạn có thể giải quyết điều này bằng cách ban đầu đặt các nhãn theo thứ tự chính xác trên một vòng tròn lớn hơn quanh chiếc bánh của bạn thay vì định vị chúng ngẫu nhiên quanh bảng điều khiển, đó là nguyên nhân gây ra sự cố. Bằng cách này bạn sẽ trải nghiệm cách ít jitter và misplacements.

Ý tưởng cũng được mô tả trong một ví dụ khác khối: http://bl.ocks.org/mbostock/7881887

Trong ví dụ này, các nút ban đầu được đặt trên một vòng tròn ảo. Việc định vị được tính bằng các chức năng sau:

x: Math.cos (i/m * 2 * Math.PI) * 200 + chiều rộng/2 + Math.random(),

y: Toán. sin (i/m * 2 * Math.PI) * 200 + height/2 + Math.random()

Chúng đại diện cho một vòng tròn có bán kính 200, đặt ở giữa bản vẽ. Vòng tròn được chia thành các phần lớn bằng nhau. i/m chỉ tính toán 'các vị trí mảnh' trong đó tôi nằm trong khoảng từ 0 đến m-1.

Hy vọng tôi có thể trợ giúp!

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