2016-08-18 3 views
10

D3에서 만든 햇살 차트가 있습니다. 각 '꽃잎'은 데이터의 하위 집합을 나타냅니다. 사용자가 '꽃잎'을 중 하나를 클릭하면, 나는 (이미지 참조) 그 일부를 보여에만 밖으로 패닝, 그것은 전환하려는 :D3 - 햇살 차트의 전환 호

enter image description here

나는 문제가 제대로에 코드를 얻는 데 전이.

클릭하면 모든 '꽃잎'이 사라지고 나머지 패스는 원을 따라 움직여야합니다 (attrTween, arcTween 및 보간? 사용). 변경되는 기본 값은 angleSize (var angleSize = (2 * Math.PI)/theData.length;)입니다.

나는 많은 성공없이 기준으로 this, this, thisthis를 사용하여 시도했습니다. 애니메이션을 처리하는 가장 좋은 방법은 무엇입니까?

시간 내 주셔서 감사합니다.

->Plunker Here을 참조하십시오. < -

코드는 다음과 같습니다 :

var colors = { 
    'Rank1' : '#3FA548', 
    'Rank2' : '#00B09E', 
    'Rank3' : '#8971B3', 
    'Rank4' : '#DFC423', 
    'Rank5' : '#E74341' 
    }; 

    var $container = $('.chart'), 
     m = 40, 
     width = $container.width() - m, 
     height = $container.height() - m, 
     r = Math.min(width, height)/2; 

    var study = null; 
    var arc = d3.svg.arc(); 

    d3.csv('text.csv', ready); 

    function ready(err, data) { 
    if (err) console.warn('Error', err); 

    var svg = d3.select('.chart') 
       .append('svg') 
       .attr({ 
        'width' : (r + m) * 2, 
        'height' : (r + m) * 2, 
        'class' : 'container' 
       }) 
       .append('g') 
       .attr('transform', 'translate(' + (width/4) + ', ' + (height/2) + ')'); 

    var slice = svg.selectAll('.slice'); 

    function updateChart(study) { 
     if (study) { 
     var theData = data.filter(function(d) { 
       return d.study_name === study; 
      }); 
     } else { 
     var theData = data; 
     } 

     slice = slice.data(theData); 

     slice.enter() 
      .append('g') 
      .attr('class', 'slice'); 

     var angleSize = (2 * Math.PI)/theData.length; 

     var startRadArr = [], 
      endRadArr = []; 

     for (var i = 0; i < data.length; i++) { 
     var startRadius = (width/20), 
      endRadius = startRadius; 

     for (var x = 0; x < 4; x++) { 
      startRadArr.push(startRadius); 

      if (x == 0) { 
      endRadius += Number(data[i].group1_score) * (width/500); 
      } else if (x == 1) { 
      endRadius += Number(data[i].group2_score) * (width/500); 
      } else if (x == 2) { 
      endRadius += Number(data[i].group3_score) * (width/500); 
      } else { 
      endRadius += Number(data[i].group4_score) * (width/500); 
      } 

      endRadArr.push(endRadius); 
      startRadius = endRadius + 0.3; 
     } 
     } 

     var startRadGroup = [], 
      endRadGroup = []; 

     for (i = 0; i < startRadArr.length; i += 4) { 
     startRadGroup.push(startRadArr.slice(i, i + 4)); 
     } 

     for (i = 0; i < endRadArr.length; i += 4) { 
     endRadGroup.push(endRadArr.slice(i, i + 4)); 
     } 

     slice.selectAll('path') 
      .remove(); 

     for (var x = 0; x < 4; x++) { 
     slice.append('path') 
      .attr({ 
       'class' : function(d, i) { 
       if (x == 0) { 
        return d.group1_class; 
       } else if (x == 1) { 
        return d.group2_class; 
       } else if (x == 2) { 
        return d.group3_class; 
       } else { 
        return d.group4_class; 
       } 
       }, 
       'company' : function(d, i) { 
       return d.brand_name; 
       }, 
       'cat' : function(d, i) { 
        if (x == 0) { 
        return 'Group1'; 
       } else if (x == 1) { 
        return 'Group2'; 
       } else if (x == 2) { 
        return 'Group3'; 
       } else { 
        return 'Group4'; 
       } 
       }, 
       'study' : function(d, i) { 
       return d.study_name; 
       }, 
       'companyid' : function(d, i) { 
       return d.brand_id; 
       }, 
       'startradius' : function(d, i) { 
       return startRadGroup[i][x]; 
       }, 
       'endradius' : function(d, i) { 
       return endRadGroup[i][x]; 
       }, 
       'startangle' : function(d, i) { 
       return angleSize * i; 
       }, 
       'endangle' : function(d, i) { 
       return angleSize * (i + 1); 
       } 
      }) 
      .on('click', selectStudy); 
     } 

     slice.exit() 
      .remove(); 

     slice.selectAll('path') 
      .attr({ 
      'd' : function(d) { 
       return arc({ 
        innerRadius : +d3.select(this)[0][0].attributes.startradius.nodeValue, 
        outerRadius : +d3.select(this)[0][0].attributes.endradius.nodeValue, 
        startAngle : +d3.select(this)[0][0].attributes.startangle.nodeValue, 
        endAngle : +d3.select(this)[0][0].attributes.endangle.nodeValue 
       }) 
       } 
      }); 
    } 

    function selectStudy(d) { 
     study = $(this).attr('study'); 
     updateChart(study); 
    } 

    updateChart(); 
    } 

EDIT 제대로 입력 작업, 업데이트 및 종료 패턴을 포함하는 (this 기준) 코드를 업데이트
. 그러나 전환에 대해 아직 확실하지 않습니다. 대부분의 예제는 d3.interpolate(this._current, a);과 비슷한 것을 사용하도록 링크했습니다. 서로 다른 데이터간에 트위닝됩니다.

이 차트에서 this._current와 a는 동일하지만 angleSize (var angleSize = (2 * Math.PI)/theData.length;), startAngle 및 endAngle만이 변경됩니다.

+1

먼저 슬라이스를 전환하기 전에 모든 슬라이스를 제거하고 있지만 전환을 시작하려면 슬라이스를 시작 지점과 함께 다시 만들어야합니다. 또한 데이터를 더 깊은 구조로 다시 포맷하여 각 지점에 대해 4 번 반복 할 필요가 없는지 고려하십시오. – Owen

+0

@ 오웬 예. remove()는 이상한 동작을위한 임시 픽스였습니다 (새 데이터는 이전 데이터에 추가됩니다). 제거 -> [링크] (https://plnkr.co/edit/JwCy84YBVSxDERZOp1z0?p=preview)를 주석 처리했습니다. 데이터에 관해서는 불행히도 형식에 대한 어떠한 통제도하지 않습니다. JS 솔루션을 찾을 것인가? Excel에서 수동으로 변경하지 않는 것이 좋습니다. – BastionGamma

+0

또한 v4 대신 d3 v3을 사용하는 이유는 무엇입니까? – Owen

답변

3

문제는 실제로 요소에 데이터를 바인딩하지 않으므로 전환이 불가능하다는 것입니다. 나는 코드를 약간 변형시켜 데이터에 시작과 끝 각에 대한 모든 중첩 된 정보가 포함되어 있으므로 각 슬라이스 내부의 경로에 바인딩 될 수 있습니다.

이 Plunker 살펴 보자 여기 https://plnkr.co/edit/a7cxRplzy66Pc1arM2a9?p=preview

는 수정 된 버전의 목록 : 당신은 키가 제대로 데이터를 준비하고 볼 수 있듯이

var colors = { 
    Rank1: '#3FA548', 
    Rank2: '#00B09E', 
    Rank3: '#8971B3', 
    Rank4: '#DFC423', 
    Rank5: '#E74341' 
}; 

// Configuration 
var $container = $('.chart'), 
    m = 40, 
    width = $container.width() - m, 
    height = $container.height() - m, 
    r = Math.min(width, height)/2; 

var study = null; 

var arc = d3.svg.arc(); 

// Load data 
d3.csv('text.csv', ready); 

// Data loaded callback 
function ready(err, data) { 
    if (err) console.warn('Error', err); 

    var svg = d3.select('.chart') 
     .append('svg') 
     .attr({ 
      'width': (r + m) * 2, 
      'height': (r + m) * 2, 
      'class': 'container' 
     }) 
     .append('g') 
     .attr('transform', 'translate(' + (width/4) + ', ' + (height/2) + ')'); 

    var slices = svg.selectAll('.slice'); 

    function updateChart(study) { 
     var theData = data; 

     if (study) { 
      theData = data.filter(function (d) { 
       return d.study_name === study; 
      }); 
     } 

     var angleSize = (2 * Math.PI)/theData.length; 

     theData.forEach(function (item, i) { 
      var startRadius = (width/20), 
       endRadius = startRadius, 
       groupName; 

      item.paths = []; 

      for (var g = 0; g < 4; g++) { 
       item.paths[g] = {}; 

       item.paths[g].startRadius = startRadius; 

       groupName = 'group' + (g + 1) + '_score'; 

       endRadius += Number(item[groupName]) * (width/500); 

       item.paths[g].endRadius = endRadius; 

       startRadius = endRadius + 0.3; 
      } 
     }); 

     // Set the data 
     slices = slices.data(theData); 

     // Enter 
     slices.enter() 
      .append('g') 
      .attr('class', 'slice'); 

     // Exit 
     slices.exit() 
      .remove(); 

     // Update 
     slices 
      .transition() 
      .duration(750) 
      .each(function (dSlice, iSlice) { 
       var slice = d3.select(this); 

       var paths = slice.selectAll('path'); 

       // Set data 
       paths = paths.data(dSlice.paths); 

       // Exit 
       paths.exit() 
        .remove(); 

       // Enter 
       paths.enter() 
        .append('path') 
        .attr('class', 'path'); 

       // Update 
       paths 
        .transition() 
        .attr({ 
         'class': function (d, i) { 
          return dSlice['group' + (i + 1) + '_class']; 
         }, 
         'company': dSlice.brand_name, 
         'cat': function (d, i) { 
          return 'Group' + (i + 1); 
         }, 
         'study': function (d, i) { 
          return dSlice.study_name; 
         }, 
         'companyid': function (d, i) { 
          return dSlice.brand_id; 
         }, 
         'startradius': function (d, i) { 
          return d.startRadius; 
         }, 
         'endradius': function (d, i) { 
          return d.endRadius; 
         }, 
         'startangle': function (d, i) { 
          return angleSize * iSlice; 
         }, 
         'endangle': function (d, i) { 
          return angleSize * (iSlice + 1); 
         }, 
         'd': function (d) { 
          return arc({ 
           innerRadius: +d.startRadius, 
           outerRadius: +d.endRadius, 
           startAngle: +angleSize * iSlice, 
           endAngle: +angleSize * (iSlice + 1) 
          }) 
         } 
        }) 
        .duration(750); 

       paths.on('click', selectStudy); 
      }); 

     function selectStudy(d, i) { 
      study = $(this).attr('study'); 

      updateChart(study); 
     } 
    } 

    updateChart(); 
} 

은 (의이 형식을 가정 해 봅시다 당신의 예를 들어 .tsv 파일이 최선의 선택은 아니지만 때로는 데이터 소스를 선택할 수 없습니다.)

그런 다음 경로 생성 코드를 .each 번에 호출하여 슬라이스가 있으면 데이터는 function (d, i) { ... } 콜백에서 액세스 할 수 있으며 모든 요소는 해당 데이터를 수신합니다.

또 다른 속임수는 경로의 콜백에서 슬라이스 데이터 (dSliceiSlice 바를 통해 .each 함수 내에서 액세스 됨)를 사용하고 있습니다. 이렇게하면 경로가이 데이터를 자체 용도로 사용할 수 있습니다. 이 경우 companystudy_name 속성

이제 전환을 미세 조정하여보다 정확하게 만들기 위해 시작 속성이 변경 될 수 있습니다. .enter() 단계에서 경로에 대한 일부 속성을 설정하여 시도 할 수 있습니다.

+2

전환을 안정화하려면 초기로드시 데이터를 마사지하고 키 기능을 제공해야 입력/업데이트/종료 기능이 어느 슬라이스가 어디에 속하는지 알 수 있습니다. [this plunkr] (https://plnkr.co/edit/b91oOy2PYRdGclZfwzOp?p=preview)을 참조하십시오 (그러나 전환은 단순히 각도를 전환하는 것이 아니라 경로를 전환하는 것입니다). – Owen

+0

감사합니다! 당신들은 생명을 구하는 사람입니다. – BastionGamma