2013-01-11 58 views
13

我正在創建一個使用D3浮動標籤的餅圖。我是D3新手,我甚至不確定這是可能的嗎?你可以以某種方式使用另一張圖的標籤嗎?如果可以的話,你能指點我一個例子嗎?D3 - 餅圖和力導向標籤

較短的說明: 我想從標籤: http://bl.ocks.org/1691430

enter image description here ...是一個餅圖上。

這是我在下面運行的代碼: 或者在JSBIN:http://jsbin.com/awilak/1/edit

如果我正確理解他的代碼,這是增加了標籤的部分。我不明白labelForce.update的作用。從那裏開始,我不在乎過渡,所以不需要線路。然後剩下的只是繪製圓圈並添加鏈接/線條?如果有人能夠整合這將是驚人的,但如果你能幫助我瞭解發生了什麼事以及我錯過了什麼,我會感激不盡。

// 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

是的,這是可能的。你發佈的例子已經非常接近我認爲你想要做的事情。你可以發佈一些你已經嘗試過的代碼嗎? –

+0

我很困惑,因爲當我需要調用標籤時,我會在上面的餅圖中進行編輯。什麼是call(labelForce.update)正在做什麼? 感謝您的幫助! – gbam

+1

這個例子中的'錨'是被標記的東西。那些將是你的餡餅細分市場。在開始繪製餅圖時可能會比較容易,並且可以使用示例中的代碼作爲餅圖片段內某些點的數據。 –

回答

-1

您需要創建兩個弧。一個用於餅圖繪圖,另一個用於座標的標籤。

// 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

是的,你絕對可以將力標與餅圖結合起來!開始的餅圖標籤沒有特別的特殊之處,它們只是文本元素,可以像使用transform或x/y的其他任何東西一樣定位。看起來你最初是根據它們標註的圓弧的質心定位這些標籤,但是你可以很容易地使用另一個標準(比如力佈局的輸出)。

D3的力佈局基於一組關於什麼是固定的,什麼是可移動的以及哪些與哪些連接的約束來計算物體的位置。 Mike的bl.ocks示例中的labelForce.update方法正在用於通知強制佈局需要定位多少個對象以及固定「錨點」的位置。然後它將標籤的計算位置保存到圖表的數據模型中,稍後將在redrawlabel函數中使用它們。

2

正如其他人在評論中提到的對您的介紹文章所述,您可以實現像您描述的解決方案,並且可以使用您的代碼加上「移動標籤」部分 - 例如。如果我理解正確,你想用force-layout來實現不重疊的標籤,這是一個非常好的主意,我還沒有發現。

您從示例中粘貼的代碼部分只是繪製了標籤和行,正如您已經正確解釋的那樣。下一步是重新排列標籤在餅圖周圍的力量佈局。

,在示例重新排列標籤(和鏈接)的部分是以下各項:

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) 

的功能是改變標籤和線的位置的一個。力由D3計算並且以d3.force_labels()...開始。如您所見,該函數被分配爲tick-event的事件處理程序。換句話說:在計算力的每一步之後,D3爲每個標籤調用「抽屜」並更新位置。

不幸的是,我不是很熟悉D3的force_labels()方法,但我認爲它的工作原理與常規force()非常相似。 對於每個標籤,錨定器都放置在每個餡餅的某處。每個餡餅中心越集中(不是餡餅本身)越好。不幸的是,你必須以某種方式計算這個錨定位置(sin和cos的東西)並將行結束符設置爲redrawLabels()內的這個固定位置。

完成此操作後,您將看到第一個結果。你可能不得不在重力,linkDistance等力的作用下取得好的效果。 (這就是本例中的silders做。)

見D3文檔的更多信息:https://github.com/mbostock/d3/wiki/Force-Layout

然後,你將可能偶然發現,該標籤周圍的餡餅下令不重疊,但在一些奇怪的順序問題。您可以通過最初將標籤以正確的順序放置在餡餅周圍的較大圓上,而不是將它們隨機放置在面板周圍來解決此問題,這是導致問題的原因。這樣你將會體驗到更少的抖動和錯位。

的想法也以另一種塊示例描述:http://bl.ocks.org/mbostock/7881887

在這個例子中,節點最初放置在假想圓。該定位是通過以下功能計算:

X:Math.cos(I/M * 2 * Math.PI)* 200 +寬度/ 2 +的Math.random(),

Y:數學。 sin(i/m * 2 * Math.PI)* 200 + height/2 + Math.random()

它們表示半徑爲200的圓,位於繪圖面板的中心。這個圓被分成m個同樣大的部分。我/米只是計算'piece-positions',其中我的範圍從0到m-1。

希望我能幫上忙!