2015-12-16 63 views
0

小提琴:https://jsfiddle.net/vpkarep8/D3:在更新重新定位饼图标签

我有三个饼图与新的数据更新时的动画,我似乎无法得到标签正确地更新。上面附上小提琴。

为了让文字发生变化,我不得不对文本进行另一个数据连接(第486-489行),但后来我无法使用arc.centroid()。将其缩小到我如何处理更新,但不知道处理所有这些问题的最佳方法。似乎质心需要d,但更新文本需要d.values。

有什么想法?

试过Label outside arc (Pie chart) d3.jsHow to update both the content and location of text labels on a D3 pie chart的答案。

function drawESGraph() { 
    d3.selectAll('.ES__graph__container svg') 
    .remove(); 

    d3.selectAll('.ES__buttons button') 
    .remove(); 

    var $container = $('.ES__graph__container'); 

    var width = $container.width()/3; 

    var m = 40, 
     r = width/3, 
     labelr = r + 20; 

    var arc = d3.svg.arc() 
       .outerRadius(r) 
       .innerRadius(r/2); 

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


    var allBrands = d3.set(data.map(function(d) { 
         return d.brand; 
        })).values(); 

    var buttons = d3.select('.ES__buttons') 
        .selectAll('button') 
        .data(allBrands) 
        .enter() 
        .append('button') 
        .attr('class', function(d) { 
        return d + ' button'; 
        }) 
       .text(function(d) { 
        return d; 
        }) 
       .on('click', function(d) { 
        updateChart(d); 
       }) 
       .style('opacity', 0); 

    buttons.transition().duration(1000) 
     .style('opacity', 1); 

    d3.select('.brand1.button') 
    .attr('class', 'brand1 button active'); 

    function updateChart(brand) { 
var brandData = data.filter(function(d) { 
    return d.brand === brand; 
}); 

var brandDataByYear = d3.nest() 
    .key(function(d) { 
    return d.year; 
    }) 
    .entries(brandData); 

var svg = d3.select('.ES__graph__container') 
    .selectAll('svg') 
    .data(brandDataByYear) 
    .enter() 
    .append('svg') 
    .style('margin-top', '25px') 
    .attr('width', (r + m) * 2) 
    .attr('height', (r + m) * 2) 
    .attr('id', function(d, i) { 
    return 'pie' + i; 
    }) 
    .append('svg:g') 
    .attr('transform', 'translate(' + (r + m) + ',' + (r + m) + ')'); 

var pieLabel = svg.append('svg:text') 
    .attr('dy', '.35em') 
    .attr('text-anchor', 'middle') 
    .text(function(d) { 
    return d.key; 
    }) 
    .style('fill', 'black') 
    .style('opacity', 0); 

pieLabel.transition().duration(1000) 
    .style('opacity', 1); 

var slice = svg.selectAll('.arc') 
    .data(function(d) { 
    return pie(d.values); 
    }) 
    .enter() 
    .append('g') 
    .attr('class', 'arc'); 

var path = slice.append('svg:path') 
    .attr('d', arc) 
    .attr('class', function(d) { 
    return 'arc ' + d.data.platform; 
    }) 
    .each(function(d) { 
    this._current = d; 
    }); 

var text = slice.append('text') 
    .text(function(d) { 
    if (d.data.val > 0) { 
     return d.data.val + '%'; 
    } 
    }) 
    .attr('transform', function(d) { 
    if (d.data.val > 3) { 
     return 'translate(' + arc.centroid(d) + ')'; 
    } else { 
     var c = arc.centroid(d), 
     x = c[0], 
     y = c[1], 
     h = Math.sqrt(x * x + y * y); 

     return 'translate(' + (x/h * labelr) + ',' + (y/h * labelr) + ')'; 
    } 
    }) 
    .attr('text-anchor', function(d) { 
    if (d.data.val < 3) { 
     return (d.endAngle + d.startAngle)/2 > Math.PI ? 'end' : 'start'; 
    } 
    }) 
    .attr('dx', function(d) { 
    return d.data.val > 3 ? -15 : 18; 
    }) 
    .attr('dy', function(d) { 
    return d.data.val > 3 ? 5 : 3; 
    }) 
    .style('fill', function(d) { 
    return d.data.val > 3 ? 'white' : 'black'; 
    }) 
    .attr('class', 'label'); 

change(); 

function change() { 
    var newdata = brandDataByYear; 

    for (x in newdata) { 
    var nslice = d3.select('#pie' + x) 
     .data(newdata); 

    var npath = nslice.selectAll('path') 
     .data(function(d) { 
     return pie(d.values); 
     }) 
     .attr('class', function(d) { 
     return 'arc ' + d.data.platform; 
     }); 

    npath.transition().duration(1000) 
     .attrTween('d', arcTween); 

    npath.exit() 
     .remove(); 

    var ntext = nslice.selectAll('.label') 
     .data(function(d) { 
     return d.values; 
     }) 
     .style('opacity', 0); 

    ntext.transition().duration(1000) 
     .style('opacity', 1) 
     .text(function(d) { 
     if (d.val > 0) { 
      return d.val + '%'; 
     } 
     }) 
     // .attr("transform", function(d) { 
     // return "translate(" + 
     //  ((radius - 12) * Math.sin(((d.endAngle - d.startAngle)/2) + d.startAngle)) + 
     //  ", " + 
     //  (-1 * (radius - 12) * Math.cos(((d.endAngle - d.startAngle)/2) + d.startAngle)) + 
     // ")"; 
     // }) 
     // .style("text-anchor", function(d) { 
     // var rads = ((d.endAngle - d.startAngle)/2) + d.startAngle; 
     // if ((rads > 7 * Math.PI/4 && rads < Math.PI/4) || (rads > 3 * Math.PI/4 && rads < 5 * Math.PI/4)) { 
     //  return "middle"; 
     // } else if (rads >= Math.PI/4 && rads <= 3 * Math.PI/4) { 
     //  return "start"; 
     // } else if (rads >= 5 * Math.PI/4 && rads <= 7 * Math.PI/4) { 
     //  return "end"; 
     // } else { 
     //  return "middle"; 
     // } 
     // }) 

    // ntext.exit() 
    //  .remove(); 
    } 
} 

function arcTween(a) { 
    var i = d3.interpolate(this._current, a); 

    this._current = i(0); 

    return function(t) { 
    return arc(i(t)); 
    } 
} 
    } 

    updateChart('brand1'); 
} 

drawESGraph(); 
+0

这是完全一样的问题,因为在第二个问题,你'我l签署。你不更新绑定到'text'元素的数据。 –

回答

1

首先,不要在两个不同的对象重用的类名arc;你在父母g上为每个切片和孩子path

其次,将数据重新绑定到父节点g,以便pathtext都可以使用它。

var slice = svg.selectAll('.slice') //<-- for the g 
    .data(function(d) { 
    return pie(d.values); 
    }) 
    .enter() 
    .append('g') 
    .attr('class', 'slice'); 
在更新

后来:

var npath = nslice.selectAll('.slice') //<-- rebind to `g` 
    .data(function(d) { 
    return pie(d.values); 
    }); 

npath 
    .select("path") 
    .attr('class', function(d) { 
    return 'arc ' + d.data.platform; 
    }) 
    .transition().duration(1000) 
    .attrTween('d', arcTween); //<-- update the paths 

npath.exit() 
    .remove(); //<-- remove the whole g 

npath.select("text") //<-- update the text 
    .transition() 
    .duration(1000) 
    .style('opacity', 1) 
    .text(function(d) { 
    if (d.data.val > 0) { 
     return d.data.val + '%'; 
    } 
    }) 
    .attr('transform', function(d) { 
    console.log(arc.centroid(d)); //<-- you can now use centroid 
    }); 

编辑

我不好,我应该抓住这一点。问题在于你正在将.selectAll.data与显式循环结合在一起。数据绑定就是基于数据进行循环。那么,我们如何解决它?

我们从正确的数据绑定开始。

var svg = d3.select('.ES__graph__container') 
    .selectAll('svg') 
    .data(brandDataByYear) 
    .enter() 
    .append('svg') 
    .style('margin-top', '25px') 
    .attr('width', (r + m) * 2) 
    .attr('height', (r + m) * 2) 
    .attr('class', 'pie') //<-- give each svg a class, not id 
    ... 

然后在您的更新:

// for (x in newdata) { //<-- NO EXPLICIT LOOPING! 
var nslice = d3.selectAll('.pie') 
    .data(newdata); 

更新全码:

<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
    <script data-require="[email protected]" data-semver="2.1.4" src="https://code.jquery.com/jquery-2.1.4.js"></script> 
 
    <script src="//d3js.org/d3.v3.js" charset="utf-8"></script> 
 
    <style> 
 
    .arc.platform1 { 
 
     fill: #e74341; 
 
    } 
 
    
 
    .arc.platform2 { 
 
     fill: #3c5a96; 
 
    } 
 
    
 
    .arc.platform3 { 
 
     fill: #3c94d1; 
 
    } 
 
    
 
    .arc.platform4 { 
 
     fill: #837369; 
 
    } 
 
    </style> 
 
</head> 
 

 
<body> 
 
    <div class="ES__buttons"></div> 
 
    <div class="ES__graph__container"></div> 
 
    <script> 
 
    function drawESGraph() { 
 
     d3.selectAll('.ES__graph__container svg') 
 
     .remove(); 
 

 
     d3.selectAll('.ES__buttons button') 
 
     .remove(); 
 

 
     var $container = $('.ES__graph__container'); 
 

 
     var width = $container.width()/3; 
 

 
     var m = 40, 
 
     r = width/3, 
 
     labelr = r + 20; 
 

 
     var arc = d3.svg.arc() 
 
     .outerRadius(r) 
 
     .innerRadius(r/2); 
 

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

 
     var data = [{ 
 
     brand: 'brand1', 
 
     platform: 'platform1', 
 
     year: '2012-2013', 
 
     val: 85.8 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform2', 
 
     year: '2012-2013', 
 
     val: 14 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform3', 
 
     year: '2012-2013', 
 
     val: 0.2 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform4', 
 
     year: '2012-2013', 
 
     val: 0 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform1', 
 
     year: '2013-2014', 
 
     val: 91 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform2', 
 
     year: '2013-2014', 
 
     val: 8 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform3', 
 
     year: '2013-2014', 
 
     val: 1 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform4', 
 
     year: '2013-2014', 
 
     val: 0 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform1', 
 
     year: '2014-2015', 
 
     val: 77 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform2', 
 
     year: '2014-2015', 
 
     val: 8 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform3', 
 
     year: '2014-2015', 
 
     val: 2 
 
     }, { 
 
     brand: 'brand1', 
 
     platform: 'platform4', 
 
     year: '2014-2015', 
 
     val: 13 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform1', 
 
     year: '2012-2013', 
 
     val: 76.9 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform2', 
 
     year: '2012-2013', 
 
     val: 23 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform3', 
 
     year: '2012-2013', 
 
     val: 0.1 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform4', 
 
     year: '2012-2013', 
 
     val: 0 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform1', 
 
     year: '2013-2014', 
 
     val: 87.6 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform2', 
 
     year: '2013-2014', 
 
     val: 7 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform3', 
 
     year: '2013-2014', 
 
     val: 0.4 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform4', 
 
     year: '2013-2014', 
 
     val: 5 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform1', 
 
     year: '2014-2015', 
 
     val: 55 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform2', 
 
     year: '2014-2015', 
 
     val: 7 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform3', 
 
     year: '2014-2015', 
 
     val: 1 
 
     }, { 
 
     brand: 'brand2', 
 
     platform: 'platform4', 
 
     year: '2014-2015', 
 
     val: 37 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform1', 
 
     year: '2012-2013', 
 
     val: 72.9 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform2', 
 
     year: '2012-2013', 
 
     val: 24 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform3', 
 
     year: '2012-2013', 
 
     val: 0.1 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform4', 
 
     year: '2012-2013', 
 
     val: 3 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform1', 
 
     year: '2013-2014', 
 
     val: 76 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform2', 
 
     year: '2013-2014', 
 
     val: 10 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform3', 
 
     year: '2013-2014', 
 
     val: 1 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform4', 
 
     year: '2013-2014', 
 
     val: 13 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform1', 
 
     year: '2014-2015', 
 
     val: 56 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform2', 
 
     year: '2014-2015', 
 
     val: 12 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform3', 
 
     year: '2014-2015', 
 
     val: 1 
 
     }, { 
 
     brand: 'brand3', 
 
     platform: 'platform4', 
 
     year: '2014-2015', 
 
     val: 31 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform1', 
 
     year: '2012-2013', 
 
     val: 1 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform2', 
 
     year: '2012-2013', 
 
     val: 63 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform3', 
 
     year: '2012-2013', 
 
     val: 1 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform4', 
 
     year: '2012-2013', 
 
     val: 35 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform1', 
 
     year: '2013-2014', 
 
     val: 0 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform2', 
 
     year: '2013-2014', 
 
     val: 22 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform3', 
 
     year: '2013-2014', 
 
     val: 1 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform4', 
 
     year: '2013-2014', 
 
     val: 77 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform1', 
 
     year: '2014-2015', 
 
     val: 0 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform2', 
 
     year: '2014-2015', 
 
     val: 14 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform3', 
 
     year: '2014-2015', 
 
     val: 1 
 
     }, { 
 
     brand: 'brand4', 
 
     platform: 'platform4', 
 
     year: '2014-2015', 
 
     val: 85 
 
     }] 
 

 
     var allBrands = d3.set(data.map(function(d) { 
 
     return d.brand; 
 
     })).values(); 
 

 
     var buttons = d3.select('.ES__buttons') 
 
     .selectAll('button') 
 
     .data(allBrands) 
 
     .enter() 
 
     .append('button') 
 
     .attr('class', function(d) { 
 
      return d + ' button'; 
 
     }) 
 
     .text(function(d) { 
 
      return d; 
 
     }) 
 
     .on('click', function(d) { 
 
      updateChart(d); 
 
     }) 
 
     .style('opacity', 0); 
 

 
     buttons.transition().duration(1000) 
 
     .style('opacity', 1); 
 

 
     d3.select('.brand1.button') 
 
     .attr('class', 'brand1 button active'); 
 

 
     function updateChart(brand) { 
 
     var brandData = data.filter(function(d) { 
 
      return d.brand === brand; 
 
     }); 
 

 
     var brandDataByYear = d3.nest() 
 
      .key(function(d) { 
 
      return d.year; 
 
      }) 
 
      .entries(brandData); 
 

 
     var svg = d3.select('.ES__graph__container') 
 
      .selectAll('svg') 
 
      .data(brandDataByYear) 
 
      .enter() 
 
      .append('svg') 
 
      .style('margin-top', '25px') 
 
      .attr('width', (r + m) * 2) 
 
      .attr('height', (r + m) * 2) 
 
      .attr('class', 'pie') 
 
      .append('svg:g') 
 
      .attr('transform', 'translate(' + (r + m) + ',' + (r + m) + ')'); 
 

 
     var pieLabel = svg.append('svg:text') 
 
      .attr('dy', '.35em') 
 
      .attr('text-anchor', 'middle') 
 
      .text(function(d) { 
 
      return d.key; 
 
      }) 
 
      .style('fill', 'black') 
 
      .style('opacity', 0); 
 

 
     pieLabel.transition().duration(1000) 
 
      .style('opacity', 1); 
 

 
     var slice = svg.selectAll('.slice') 
 
      .data(function(d) { 
 
      return pie(d.values); 
 
      }) 
 
      .enter() 
 
      .append('g') 
 
      .attr('class', 'slice'); 
 

 
     var path = slice.append('svg:path') 
 
      .attr('d', arc) 
 
      .attr('class', function(d) { 
 
      return 'arc ' + d.data.platform; 
 
      }) 
 
      .each(function(d) { 
 
      this._current = d; 
 
      }); 
 

 
     var text = slice.append('text') 
 
      .text(function(d) { 
 
      if (d.data.val > 0) { 
 
       return d.data.val + '%'; 
 
      } 
 
      }) 
 
      .attr('transform', function(d) { 
 
      if (d.data.val > 3) { 
 
       return 'translate(' + arc.centroid(d) + ')'; 
 
      } else { 
 
       var c = arc.centroid(d), 
 
       x = c[0], 
 
       y = c[1], 
 
       h = Math.sqrt(x * x + y * y); 
 

 
       return 'translate(' + (x/h * labelr) + ',' + (y/h * labelr) + ')'; 
 
      } 
 
      }) 
 
      .attr('text-anchor', function(d) { 
 
      if (d.data.val < 3) { 
 
       return (d.endAngle + d.startAngle)/2 > Math.PI ? 'end' : 'start'; 
 
      } 
 
      }) 
 
      .attr('dx', function(d) { 
 
      return d.data.val > 3 ? -15 : 18; 
 
      }) 
 
      .attr('dy', function(d) { 
 
      return d.data.val > 3 ? 5 : 3; 
 
      }) 
 
      .style('fill', function(d) { 
 
      return d.data.val > 3 ? 'white' : 'black'; 
 
      }) 
 
      .attr('class', 'label'); 
 

 
     change(); 
 

 
     function change() { 
 
      
 
      var newdata = brandDataByYear; 
 
      
 
     // for (x in newdata) { 
 
      var nslice = d3.selectAll('.pie') 
 
       .data(newdata); 
 

 
      //return; 
 

 
      var npath = nslice.selectAll('.slice') 
 
       .data(function(d) { 
 
       console.log(d); 
 
       return pie(d.values); 
 
       }); 
 
       
 
      npath 
 
       .select("path") 
 
       .attr('class', function(d) { 
 
       return 'arc ' + d.data.platform; 
 
       }) 
 
       .transition().duration(1000) 
 
       .attrTween('d', arcTween); 
 

 
      npath.exit() 
 
       .remove(); 
 

 
      npath.select("text") 
 
       .transition() 
 
       .duration(1000) 
 
       .style('opacity', 1) 
 
       .text(function(d) { 
 
       if (d.data.val > 0) { 
 
        return d.data.val + '%'; 
 
       } 
 
       }) 
 
       .attr('transform', function(d) { 
 
       if (d.data.val > 3) { 
 
        return 'translate(' + arc.centroid(d) + ')'; 
 
       } else { 
 
        var c = arc.centroid(d), 
 
        x = c[0], 
 
        y = c[1], 
 
        h = Math.sqrt(x * x + y * y); 
 

 
        return 'translate(' + (x/h * labelr) + ',' + (y/h * labelr) + ')'; 
 
       } 
 
       }); 
 
       // .attr("transform", function(d) { 
 
       // return "translate(" + 
 
       //  ((radius - 12) * Math.sin(((d.endAngle - d.startAngle)/2) + d.startAngle)) + 
 
       //  ", " + 
 
       //  (-1 * (radius - 12) * Math.cos(((d.endAngle - d.startAngle)/2) + d.startAngle)) + 
 
       // ")"; 
 
       // }) 
 
       // .style("text-anchor", function(d) { 
 
       // var rads = ((d.endAngle - d.startAngle)/2) + d.startAngle; 
 
       // if ((rads > 7 * Math.PI/4 && rads < Math.PI/4) || (rads > 3 * Math.PI/4 && rads < 5 * Math.PI/4)) { 
 
       //  return "middle"; 
 
       // } else if (rads >= Math.PI/4 && rads <= 3 * Math.PI/4) { 
 
       //  return "start"; 
 
       // } else if (rads >= 5 * Math.PI/4 && rads <= 7 * Math.PI/4) { 
 
       //  return "end"; 
 
       // } else { 
 
       //  return "middle"; 
 
       // } 
 
       // }) 
 

 
      // ntext.exit() 
 
      //  .remove(); 
 
      } 
 
     // } 
 

 
     function arcTween(a) { 
 
      var i = d3.interpolate(this._current, a); 
 

 
      this._current = i(0); 
 

 
      return function(t) { 
 
      return arc(i(t)); 
 
      } 
 
     } 
 
     } 
 

 
     updateChart('brand1'); 
 
    } 
 

 
    drawESGraph(); 
 
    </script> 
 
</body> 
 

 
</html>

+0

谢谢!你是一个拯救生命的人。 我没有意识到我在change()中选择的.arc是另一回事。难怪数据没有被正确地继承。 – BastionGamma

+0

说得太快。所有的饼图都使用相同的数据更新,使它们相同。会进一步挖掘。 – BastionGamma

+0

@BastionGamma,我的坏看到更新回答。 – Mark