2016-04-28 5 views
0

Ich versuche, ein Netzwerk-Diagramm mit NetworkD3 in R zu plotten. Ich wollte einige Änderungen an der Anzeige vornehmen, so dass die Textbeschriftungen (die beim MouseOver angezeigt werden) leicht gelesen werden können.Wie ändert sich die Stapelreihenfolge von Textbeschriftungen in JavaScript?

Ein Beispiel finden Sie unter dem Link here. Hinweis: Wechseln Sie zum d3ForceNetwork-Plot.

Wie im Beispiel zu sehen, sind die Beschriftungen aufgrund ihrer Farbe schwer zu lesen und werden oft von den umgebenden Knoten blockiert. Ich habe mit der JS-Datei herumgesprochen und es geschafft, die Farbe des Textlabels in Schwarz zu ändern. Da ich jedoch keine Kenntnisse über JS oder CSS habe (ich kann nicht einmal den Unterschied zwischen den beiden unterscheiden), habe ich keine Ahnung, wie ich die Stapelreihenfolge so ändern kann, dass die Textbeschriftungen immer über anderen Objekten angezeigt werden.

Kann mir jemand sagen, wie ich das gewünschte Ergebnis erreichen kann?

Unten ist die vollständige JS-Datei:

HTMLWidgets.widget({ 

    name: "forceNetwork", 

    type: "output", 

    initialize: function(el, width, height) { 

    d3.select(el).append("svg") 
     .attr("width", width) 
     .attr("height", height); 

    return d3.layout.force(); 
    }, 

    resize: function(el, width, height, force) { 

    d3.select(el).select("svg") 
     .attr("width", width) 
     .attr("height", height); 

    force.size([width, height]).resume(); 
    }, 

    renderValue: function(el, x, force) { 

    // Compute the node radius using the javascript math expression specified 
    function nodeSize(d) { 
      if(options.nodesize){ 
        return eval(options.radiusCalculation); 

      }else{ 
        return 6} 

    } 


    // alias options 
    var options = x.options; 

    // convert links and nodes data frames to d3 friendly format 
    var links = HTMLWidgets.dataframeToD3(x.links); 
    var nodes = HTMLWidgets.dataframeToD3(x.nodes); 

    // get the width and height 
    var width = el.offsetWidth; 
    var height = el.offsetHeight; 

    var color = eval(options.colourScale); 

    // set this up even if zoom = F 
    var zoom = d3.behavior.zoom(); 

    // create d3 force layout 
    force 
     .nodes(d3.values(nodes)) 
     .links(links) 
     .size([width, height]) 
     .linkDistance(options.linkDistance) 
     .charge(options.charge) 
     .on("tick", tick) 
     .start(); 

    // thanks http://plnkr.co/edit/cxLlvIlmo1Y6vJyPs6N9?p=preview 
    // http://stackoverflow.com/questions/22924253/adding-pan-zoom-to-d3js-force-directed 
     var drag = force.drag() 
     .on("dragstart", dragstart) 
     // allow force drag to work with pan/zoom drag 
     function dragstart(d) { 
     d3.event.sourceEvent.preventDefault(); 
     d3.event.sourceEvent.stopPropagation(); 
     } 

    // select the svg element and remove existing children 
    var svg = d3.select(el).select("svg"); 
    svg.selectAll("*").remove(); 
    // add two g layers; the first will be zoom target if zoom = T 
    // fine to have two g layers even if zoom = F 
    svg = svg 
     .append("g").attr("class","zoom-layer") 
     .append("g") 

    // add zooming if requested 
    if (options.zoom) { 
     function redraw() { 
     d3.select(el).select(".zoom-layer").attr("transform", 
      "translate(" + d3.event.translate + ")"+ 
      " scale(" + d3.event.scale + ")"); 
     } 
     zoom.on("zoom", redraw) 

     d3.select(el).select("svg") 
     .attr("pointer-events", "all") 
     .call(zoom); 

    } else { 
     zoom.on("zoom", null); 
    } 

    // draw links 
    var link = svg.selectAll(".link") 
     .data(force.links()) 
     .enter().append("line") 
     .attr("class", "link") 
     .style("stroke", function(d) { return d.colour ; }) 
     //.style("stroke", options.linkColour) 
     .style("opacity", options.opacity) 
     .style("stroke-width", eval("(" + options.linkWidth + ")")) 
     .on("mouseover", function(d) { 
      d3.select(this) 
      .style("opacity", 1); 
     }) 
     .on("mouseout", function(d) { 
      d3.select(this) 
      .style("opacity", options.opacity); 
     }); 

    // draw nodes 
    var node = svg.selectAll(".node") 
     .data(force.nodes()) 
     .enter().append("g") 
     .attr("class", "node") 
     .style("fill", function(d) { return color(d.group); }) 
     .style("opacity", options.opacity) 
     .on("mouseover", mouseover) 
     .on("mouseout", mouseout) 
     .on("click", click) 
     .call(force.drag); 

    node.append("circle") 
     .attr("r", function(d){return nodeSize(d);}) 
     .style("stroke", "#fff") 
     .style("opacity", options.opacity) 
     .style("stroke-width", "1.5px"); 

    node.append("svg:text") 
     .attr("class", "nodetext") 
     .attr("dx", 12) 
     .attr("dy", ".35em") 
     .text(function(d) { return d.name }) 
     .style("font", options.fontSize + "px " + options.fontFamily) 
     .style("opacity", options.opacityNoHover) 
     .style("pointer-events", "none"); 

    function tick() { 
     node.attr("transform", function(d) { 
     if(options.bounded){ // adds bounding box 
      d.x = Math.max(nodeSize(d), Math.min(width - nodeSize(d), d.x)); 
      d.y = Math.max(nodeSize(d), Math.min(height - nodeSize(d), d.y)); 
     } 

     return "translate(" + d.x + "," + d.y + ")"}); 

     link 
     .attr("x1", function(d) { return d.source.x; }) 
     .attr("y1", function(d) { return d.source.y; }) 
     .attr("x2", function(d) { return d.target.x; }) 
     .attr("y2", function(d) { return d.target.y; }); 
    } 

    function mouseover() { 
     d3.select(this).select("circle").transition() 
     .duration(750) 
     .attr("r", function(d){return nodeSize(d)+5;}); 
     d3.select(this).select("text").transition() 
     .duration(750) 
     .attr("x", 13) 
     .style("stroke-width", ".5px") 
     .style("font", options.clickTextSize + "px ") 
     .style('fill', 'black') 
     .style('position','relative') 
     .style("opacity", 1); 
    } 

    function mouseout() { 
     d3.select(this).select("circle").transition() 
     .duration(750) 
     .attr("r", function(d){return nodeSize(d);}); 
     d3.select(this).select("text").transition() 
     .duration(1250) 
     .attr("x", 0) 
     .style("font", options.fontSize + "px ") 
     .style("opacity", options.opacityNoHover); 
    } 

    function click(d) { 
     return eval(options.clickAction) 
    } 

    // add legend option 
    if(options.legend){ 
     var legendRectSize = 18; 
     var legendSpacing = 4; 
     var legend = svg.selectAll('.legend') 
      .data(color.domain()) 
      .enter() 
      .append('g') 
      .attr('class', 'legend') 
      .attr('transform', function(d, i) { 
      var height = legendRectSize + legendSpacing; 
      var offset = height * color.domain().length/2; 
      var horz = legendRectSize; 
      var vert = i * height+4; 
      return 'translate(' + horz + ',' + vert + ')'; 
      }); 

     legend.append('rect') 
      .attr('width', legendRectSize) 
      .attr('height', legendRectSize) 
      .style('fill', color) 
      .style('stroke', color); 

     legend.append('text') 
      .attr('x', legendRectSize + legendSpacing) 
      .attr('y', legendRectSize - legendSpacing) 
      .style('fill', 'darkOrange') 
      .text(function(d) { return d; }); 
    } 

    // make font-family consistent across all elements 
    d3.select(el).selectAll('text').style('font-family', options.fontFamily); 
    }, 
}); 

ich, dass ich auf den Code einige Änderungen vornehmen müssen vermuten hier über:

function mouseover() { 
    d3.select(this).select("circle").transition() 
    .duration(750) 
    .attr("r", function(d){return nodeSize(d)+5;}); 
    d3.select(this).select("text").transition() 
    .duration(750) 
    .attr("x", 13) 
    .style("stroke-width", ".5px") 
    .style("font", options.clickTextSize + "px ") 
    .style('fill', 'black') 
    .style("opacity", 1); 
} 

Antwort

1

Sie müssen die Knotengruppen greifen die Kreise halten und Text, so dass der aktuell mouseovered der letzte in dieser Gruppe ist und somit der letzte gezeichnet wird, so dass er über den anderen erscheint. Siehe die erste Antwort hier ->

Updating SVG Element Z-Index With D3

In Ihrem Fall, wenn Ihre Daten nicht ein ID-Feld hat müssen Sie unter Umständen verwenden, ‚name‘ statt, wie unten (angepasst ist, die Mouseover-Funktion nutzen zu können habe vor):

function mouseover(d) { 
    d3.selectAll("g.node").sort(function (a, b) { 
      if (a.name != d.name) return -1;    // a is not the hovered element, send "a" to the back 
      else return 1;        // a is the hovered element, bring "a" to the front (by making it last) 
    }); 
    // your code continues 

Der Schmerz könnte sein, dass Sie diese Bearbeitung zu tun haben, für jeden graph dieser R-Skript erzeugt d3, wenn Sie das R-Code/Paket selbst bearbeiten können. (oder Sie könnten es dem Paketautor als Erweiterung vorschlagen.)

+0

Hallo, danke für die Antwort. Da ich jedoch völlig neu in JS und CSS bin (dies ist das erste Mal, dass ich mir eine JS-Datei anschaue), bin ich mir nicht sicher, wo ich den obigen Code anhängen soll. Können Sie sich genauer darüber informieren, wie die obige Lösung in die JS-Datei integriert werden kann? Ich habe versucht, den Code aufgrund von Versuch und Irrtum mehrfach anzuhängen, aber ohne Erfolg. –

+0

Es sollte am Anfang Ihres mouseover() - Funktionscodes gehen, den Sie in Ihr q eingefügt haben, ich habe die Antwort etwas geändert, um Ihr Beispiel zu spiegeln (jetzt 'mouseover (d)' statt .on ("Mouseover ", Funktion (d))", das sollte funktionieren, wenn Sie es einfügen. Beachten Sie, dass die MouseOver-Funktion jetzt ein (d) Argument hat, das Ihre nicht tut - das sind die Daten für den aktuell Mouseover-Knoten. – mgraham

+0

Ja funktioniert jetzt, vielen Dank. –