2016-03-14 9 views
11

Jestem nowym użytkownikiem D3 i próbuję dynamicznie aktualizować wykres, jeśli json źródła jest zmodyfikowany. Ale nie jestem w stanie tego osiągnąć.Dynamicznie aktualizuj D3 Sunburst, jeśli źródło json jest zaktualizowane (element dodany lub usunięty)

Proszę sprawdzić this plunkr

JS:

var width = 500, 
    height = 500, 
    radius = Math.min(width, height)/2; 

var x = d3.scale.linear() 
    .range([0, 2 * Math.PI]); 

var y = d3.scale.sqrt() 
    .range([0, radius]); 

var color = d3.scale.category10(); 

var svg = d3.select("body").append("svg") 
    .attr("width", width) 
    .attr("height", height) 
    .append("g") 
    .attr("transform", "translate(" + width/2 + "," + (height/2 + 10) + ") rotate(-90 0 0)"); 

var partition = d3.layout.partition() 
    .value(function(d) { 
     return d.size; 
    }); 

var arc = d3.svg.arc() 
    .startAngle(function(d) { 
     return Math.max(0, Math.min(2 * Math.PI, x(d.x))); 
    }) 
    .endAngle(function(d) { 
     return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); 
    }) 
    .innerRadius(function(d) { 
     return Math.max(0, y(d.y)); 
    }) 
    .outerRadius(function(d) { 
     return Math.max(0, y(d.y + d.dy)); 
    }); 

//d3.json("/d/4063550/flare.json", function(error, root) { 
var root = initItems; 

var g = svg.selectAll("g") 
    .data(partition.nodes(root)) 
    .enter().append("g"); 

var path = g.append("path") 
    .attr("d", arc) 
    .style("fill", function(d) { 
     return color((d.children ? d : d.parent).name); 
    }) 
    .on("click", click) 
    .each(function(d) { 
     this.x0 = d.x; 
     this.dx0 = d.dx; 
    }); 


//.append("text") 
var text = g.append("text") 
    .attr("x", function(d) { 
     return y(d.y); 
    }) 
    .attr("dx", "6") // margin 
    .attr("dy", ".35em") // vertical-align 
    .attr("transform", function(d) { 
     return "rotate(" + computeTextRotation(d) + ")"; 
    }) 
    .text(function(d) { 
     return d.name; 
    }) 
    .style("fill", "white"); 

function computeTextRotation(d) { 
    var angle = x(d.x + d.dx/2) - Math.PI/2; 
    return angle/Math.PI * 180; 
} 



function click(d) { 
    console.log(d) 
    // fade out all text elements 
    if (d.size !== undefined) { 
     d.size += 100; 
    }; 
    text.transition().attr("opacity", 0); 

    path.transition() 
     .duration(750) 
     .attrTween("d", arcTween(d)) 
     .each("end", function(e, i) { 
      // check if the animated element's data e lies within the visible angle span given in d 
      if (e.x >= d.x && e.x < (d.x + d.dx)) { 
       // get a selection of the associated text element 
       var arcText = d3.select(this.parentNode).select("text"); 
       // fade in the text element and recalculate positions 
       arcText.transition().duration(750) 
        .attr("opacity", 1) 
        .attr("transform", function() { 
         return "rotate(" + computeTextRotation(e) + ")" 
        }) 
        .attr("x", function(d) { 
         return y(d.y); 
        }); 
      } 
     }); 
} //}); 

// Word wrap! 
var insertLinebreaks = function(t, d, width) { 
    alert(0) 
    var el = d3.select(t); 
    var p = d3.select(t.parentNode); 
    p.append("g") 
     .attr("x", function(d) { 
      return y(d.y); 
     }) 
     // .attr("dx", "6") // margin 
     //.attr("dy", ".35em") // vertical-align 
     .attr("transform", function(d) { 
      return "rotate(" + computeTextRotation(d) + ")"; 
     }) 
     //p 
     .append("foreignObject") 
     .attr('x', -width/2) 
     .attr("width", width) 
     .attr("height", 200) 
     .append("xhtml:p") 
     .attr('style', 'word-wrap: break-word; text-align:center;') 
     .html(d.name); 
    alert(1) 
    el.remove(); 
    alert(2) 
}; 

//g.selectAll("text") 
// .each(function(d,i){ insertLinebreaks(this, d, 50); }); 


d3.select(self.frameElement).style("height", height + "px"); 

// Interpolate the scales! 
function arcTween(d) { 
    var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]), 
     yd = d3.interpolate(y.domain(), [d.y, 1]), 
     yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]); 
    return function(d, i) { 
     return i ? function(t) { 
      return arc(d); 
     } : function(t) { 
      x.domain(xd(t)); 
      y.domain(yd(t)).range(yr(t)); 
      return arc(d); 
     }; 
    }; 
} 

function arcTweenUpdate(a) { 
    console.log(path); 
    var _self = this; 
    var i = d3.interpolate({ x: this.x0, dx: this.dx0 }, a); 
    return function(t) { 
     var b = i(t); 
     console.log(window); 
     _self.x0 = b.x; 
     _self.dx0 = b.dx; 
     return arc(b); 
    }; 
} 

setTimeout(function() { 
    path.data(partition.nodes(newItems)) 
     .transition() 
     .duration(750) 
     .attrTween("d", arcTweenUpdate) 

}, 2000); 

Odpowiedz

4

oprócz tego, co @Cyril zasugerował o usunięcie następujący wiersz:

d3.select(self.frameElement).style("height", height + "px"); 

Zrobiłem dalsze modyfikacje ations twojej skrzypce: working fiddle

Chodzi tu użyty jest dodanie funkcji updateChart która przyjmuje items a następnie wygenerować wykres:

var updateChart = function (items) { 
    // code to update the chart with new items 
} 

updateChart(initItems); 

setTimeout(function() { updateChart(newItems); }, 2000); 

ta nie korzysta z utworzonego funkcję arcTweenUpdate ale postaram się wyjaśnić podstawowe pojęcia:

Po pierwsze, trzeba będzie JOIN nowe dane z istniejących danych:

// DATA JOIN - Join new data with old elements, if any. 
var gs = svg.selectAll("g").data(partition.nodes(root)); 

wtedy, ENTER do tworzenia nowych elementów w razie potrzeby:

// ENTER 
var g = gs.enter().append("g").on("click", click); 

Ale musimy także UPDATE istniejące/nowy path i text węzły nowymi danymi:

// UPDATE 
var path = g.append("path"); 

gs.select('path') 
    .style("fill", function(d) { 
     return color((d.children ? d : d.parent).name); 
    }) 
    //.on("click", click) 
    .each(function(d) { 
     this.x0 = d.x; 
     this.dx0 = d.dx; 
    }) 
    .transition().duration(500) 
    .attr("d", arc); 


    var text = g.append("text"); 

    gs.select('text') 
    .attr("x", function(d) { 
     return y(d.y); 
    }) 
    .attr("dx", "6") // margin 
    .attr("dy", ".35em") // vertical-align 
    .attr("transform", function(d) { 
     return "rotate(" + computeTextRotation(d) + ")"; 
    }) 
    .text(function(d) { 
     return d.name; 
    }) 
    .style("fill", "white"); 

i po wszystko jest tworzone/aktualizowane usuń węzły g, które nie są używane, tzn. EXIT:

// EXIT - Remove old elements as needed. 
gs.exit().transition().duration(500).style("fill-opacity", 1e-6).remove(); 

Cały ten wzór JOIN + ENTER + UPDATE + EXIT wykazano w następujących artykułach, których autorem jest Mike Bostock:

  1. General Update Pattern - I
  2. General Update Pattern - II
  3. General Update Pattern - III
+0

Świetna odpowiedź. Jak możemy podać końcówkę narzędzia (nazwa i wartość) dla każdej sekcji wykresu? – Kiran

1

Enter() i przejść do pracy trzeba dać d3 sposób zidentyfikować każdą pozycję w swoich danych. funkcja .data() ma drugi parametr, który pozwala zwrócić coś, co zostanie użyte jako id. enter() użyje identyfikatora, aby zdecydować, czy obiekt jest nowy.

Spróbuj zmienić

path.data(partition.nodes(newItems)) 
.data(partition.nodes(root)); 

do

path.data(partition.nodes(newItems), function(d){return d.name}); 
.data(partition.nodes(root), function(d){return d.name}); 
2

Na stronie skrzypcach setTimeout nie jest uruchomiony, ponieważ:

d3.select(self.frameElement).style("height", height + "px"); 

dostaniesz Uncaught SecurityError: Nie można odczytać Właściwość 'frame' z 'Window': Zablokowana ramka z origi n "https://fiddle.jshell.net" z dostępu do ramki o początku i setTimeout nigdy nie zostanie wywołany.

Możesz więc usunąć tę linię d3.select(self.frameElement).style("height", height + "px"); tylko dla skrzypiec.

Poza tym:

Twoja funkcja limitu czasu powinna wyglądać następująco:

setTimeout(function() { 
    //remove the old graph 
    svg.selectAll("*").remove(); 
    root = newItems; 
    g = svg.selectAll("g") 
    .data(partition.nodes(newItems)) 
    .enter().append("g"); 
    /make path 
    path = g.append("path") 
    .attr("d", arc) 
    .style("fill", function(d) { 
     return color((d.children ? d : d.parent).name); 
    }) 
    .on("click", click) 
    .each(function(d) { 
     this.x0 = d.x; 
     this.dx0 = d.dx; 
    }); 
    //make text 
    text = g.append("text") 
    .attr("x", function(d) { 
     return y(d.y); 
    }) 
    .attr("dx", "6") // margin 
    .attr("dy", ".35em") // vertical-align 
    .attr("transform", function(d) { 
     return "rotate(" + computeTextRotation(d) + ")"; 
    }) 
    .text(function(d) { 
     return d.name; 
    }) 
    .style("fill", "white"); 

} 

pracy skrzypce here

+0

muszę rozwiązanie, w którym ja nie usuwając cały wykres całkowicie i ponowne rysowanie całego wykresu, zamiast tego powinno być coś podobnego do tego, co dzieje się po kliknięciu, w którym aktualizujemy wykres z animacją Twean. –

Powiązane problemy