2013-10-27 84 views
0

我在d3.js中有一個簡單的圓環圖,它只會用於比較2或3個項目。d3.js:圓環圖中文本的垂直對齊

http://jsfiddle.net/Ltqu2/

我想傳說和價值觀結合在圖表的中心文本。

在當前的實現中,文本對齊對於3個項目是可以的,但對於2個項目它不會進行調整。對齊有點硬編碼:

var l = svg.selectAll('.legend') 
     .data(data) 
     .enter().append('g') 
     .attr('class', 'legend'); 

    l.append('text') 
     .attr('x', 0) 
     .attr('y', function(d, i) { return i * 40 - radius/2 + 10; }) 
     .attr('class', function(d, i){return 'legend-label data-label value-' + (i+1)}) 
     .text(function(d, i) { return d + '%'; }); 

    l.append('text') 
     .attr('x', 0) 
     .attr('y', function(d, i) { return i * 40 - radius/2 + 22; }) 
     .attr('class', function(d, i){return 'legend-label units-label value-' + (i+1)}) 
     .text(function(d, i) { return legend[i]; }); 

如何使文本對齊更加靈活,因此它對於2和3項均勻分佈?有什麼我可以使用.rangeround for?

以下是完整的腳本

/** 
* Donut chart for d3.js 
*/ 

function donutChart() { 
    var width = 420, 
     height = 420, 
     radius = 0, 
     factor = 0.7; 

    var legend = ['Low', 'Medium', 'High']; 

    function chart(selection) { 
     selection.each(function(data) { 
      if (radius == 0) { 
       radius = Math.min(width, height)/2 - 10; 
      } 

      var arc = d3.svg.arc() 
       .innerRadius(radius * factor) 
       .outerRadius(radius); 

      var pie = d3.layout.pie() 
       .sort(null) 
       .value(function(d) { return d; }); 

      var svg = d3.select(this).append('svg') 
       .attr('width', width) 
       .attr('height', height) 
       .attr('class', 'donut') 
       .append('g') 
       .attr('transform', 'translate(' + width/2 + ',' + height/2 + ')'); 
      var g = svg.selectAll('.arc') 
       .data(pie(data)) 
       .enter().append('g') 
       .attr('class', 'arc'); 

      g.append('path') 
       .attr('d', arc) 
       .attr('class', function(d, i){return 'value-' + (i+1)}) 
       .style('stroke', '#fff'); 

      var l = svg.selectAll('.legend') 
       .data(data) 
       .enter().append('g') 
       .attr('class', 'legend'); 

      l.append('text') 
       .attr('x', 0) 
       .attr('y', function(d, i) { return i * 40 - radius/2 + 10; }) 
       .attr('class', function(d, i){return 'legend-label data-label value-' + (i+1)}) 
       .text(function(d, i) { return d + '%'; }); 

      l.append('text') 
       .attr('x', 0) 
       .attr('y', function(d, i) { return i * 40 - radius/2 + 22; }) 
       .attr('class', function(d, i){return 'legend-label units-label value-' + (i+1)}) 
       .text(function(d, i) { return legend[i]; }); 
     }); 
    } 

    chart.width = function(_) { 
     if (!arguments.length) return width; 
     width = _; 
     return chart; 
    }; 

    chart.height = function(_) { 
     if (!arguments.length) return height; 
     height = _; 
     return chart; 
    }; 

    chart.radius = function(_) { 
     if (!arguments.length) return radius; 
     radius = _; 
     return chart; 
    }; 

    chart.factor = function(_) { 
     if (!arguments.length) return factor; 
     factor = _; 
     return chart; 
    }; 

    chart.legend = function(_) { 
     if (!arguments.length) return legend; 
     legend = _; 
     return chart; 
    }; 

    return chart; 
} 

d3.select('#chart') 
    .datum([78, 20]) 
    .call(donutChart() 
    .width(220) 
    .height(220) 
    .legend(['This Thing', 'That Thing']) 
); 

d3.select('#chart2') 
    .datum([63, 20, 15]) 
    .call(donutChart() 
    .width(220) 
    .height(220) 
    .legend(['This Thing', 'That Thing', 'Another Thing']) 
); 

回答

1

我會把所有在一個共同的<g>元素,然後可以轉換成垂直居中傳說的文本元素。確定SVG中的實際文本大小總是很痛苦,你可以查看getBBox()瞭解更多信息。但是使用預定的文本高度可以正常工作。這裏的一個比特計算的(分離成爲了清楚許多步驟):

// hardcoding 36 here, you could use getBBox to get the actual SVG text height using sample text if you really wanted. 
var legendItemHeight = 36; 

// calculated height of all legend items together 
var actualLegendHeight = data.length * legendItemHeight; 

// inner diameter 
var availableLegendHeight = radius * factor * 2; 

// y-coordinate of first legend item (relative to the center b/c the main svg <g> element is translated 
var legendOffset = (availableLegendHeight - actualLegendHeight)/2 - (radius*factor); 

,然後創建一個<g>元件保持<text>元素:

// append all the legend items to a common group which is translated 
var l = svg.selectAll('.legend') 
    .data(data) 
    .enter().append('g') 
    .attr('class', 'legend') 
    .attr('transform', function(d, i) { 
     return 'translate(0, '+ (legendOffset + i * legendHeight)+')'; 
    }); 

改性的jsfiddle:http://jsfiddle.net/rshKs/2/

+0

感謝@redmallard !我很欣賞這種方法的簡單性。我實際上剛剛用一種更復雜的方法解決了這個問題,但我並不完全瞭解它(修飾的一個晚上):http://jsfiddle.net/rolfsf/vsQcH/2/ – rolfsf