2012-01-05 20 views
66

Estoy trabajando en un gráfico dirigido por fuerza en D3. Quiero resaltar el nodo mouseover'd, sus enlaces y sus nodos secundarios al establecer todos los otros nodos y enlaces a una opacidad menor.Resalte el nodo seleccionado, sus enlaces y sus elementos secundarios en un gráfico dirigido por fuerza D3

En este ejemplo, http://jsfiddle.net/xReHA/, soy capaz de desaparecer todos los enlaces y nodos luego se desvanecen en los enlaces conectados, pero, hasta ahora, no he sido capaz de desvanecimiento elegantemente en los nodos conectados que son los niños del nodo actualmente mouseover'd.

Ésta es la función clave del código:

function fade(opacity) { 
    return function(d, i) { 
     //fade all elements 
     svg.selectAll("circle, line").style("opacity", opacity); 

     var associated_links = svg.selectAll("line").filter(function(d) { 
      return d.source.index == i || d.target.index == i; 
     }).each(function(dLink, iLink) { 
      //unfade links and nodes connected to the current node 
      d3.select(this).style("opacity", 1); 
      //THE FOLLOWING CAUSES: Uncaught TypeError: Cannot call method 'setProperty' of undefined 
      d3.select(dLink.source).style("opacity", 1); 
      d3.select(dLink.target).style("opacity", 1); 
     }); 
    }; 
} 

estoy recibiendo un error de Uncaught TypeError: Cannot call method 'setProperty' of undefined cuando trato de establecer la opacidad en un elemento que he cargado desde el source.target. Sospecho que esta no es la forma correcta de cargar ese nodo como un objeto d3, pero no puedo encontrar otra manera de cargarlo sin iterar sobre todos los nodos nuevamente para encontrar los que coinciden con el destino o fuente del enlace. Para mantener el rendimiento razonable, no quiero iterar sobre todos los nodos más de lo necesario.

Me tomó el ejemplo de la decoloración de los enlaces http://mbostock.github.com/d3/ex/chord.html:

enter image description here

Sin embargo, que no muestra cómo alterar los nodos secundarios conectados.

Cualquier buenas sugerencias sobre cómo resolver o mejorar esto será furiosamente upvoted :)

Respuesta

86

El error se debe a que está seleccionando los objetos de datos (d.source y d.target) en lugar de los elementos DOM asociados con esos objetos de datos.

Hay que destacar la línea de trabajo, pero probablemente combinaría su código en una sola iteración, así:

link.style("opacity", function(o) { 
    return o.source === d || o.target === d ? 1 : opacity; 
}); 

Destacando los nodos vecinos es más difícil porque lo que necesita saber para los vecinos cada nodo Esta información no es tan fácil de determinar con sus estructuras de datos actuales, ya que todo lo que tiene es una matriz de nodos y una matriz de enlaces. Olvide el DOM por un segundo, y pregúntese cómo determinaría si dos nodos a y b son vecinos.

function neighboring(a, b) { 
    // ??? 
} 

una manera costosa de hacer esto es para iterar sobre todos los enlaces y ver si hay un enlace que conecta a y b:.

function neighboring(a, b) { 
    return links.some(function(d) { 
    return (d.source === a && d.target === b) 
     || (d.source === b && d.target === a); 
    }); 
} 

(Esto supone que los enlaces no son dirigidas Si solo desea resaltar los vecinos conectados hacia adelante, luego eliminar la segunda mitad del O).

Una forma más eficiente de calcular esto, si tiene que hacerlo con frecuencia, es tener un mapa o una matriz que permita búsqueda de tiempo constante para probar si a y b son vecinos.Por ejemplo:

var linkedByIndex = {}; 
links.forEach(function(d) { 
    linkedByIndex[d.source.index + "," + d.target.index] = 1; 
}); 

Ahora usted puede decir:

function neighboring(a, b) { 
    return linkedByIndex[a.index + "," + b.index]; 
} 

Y por lo tanto, ahora se puede iterar sobre los nodos y actualizar su opacidad correctamente:

node.style("opacity", function(o) { 
    return neighboring(d, o) ? 1 : opacity; 
}); 

(Usted también puede querer en casos especiales, el enlace mouseovered en sí mismo, estableciendo un enlace automático para cada nodo en linkedByIndex, o probando para d directamente al calcular el estilo, o mediante el uso de a! importante css :hover estilo.)

Lo último que cambiaría en su código es usar opacidad de relleno y opacidad de trazo en lugar de opacidad, porque ofrecen un rendimiento mucho mejor.

+1

Eso funciona genial @mbostock, muchas gracias: D He actualizado [el jsfiddle] (http://jsfiddle.net/xReHA/1/) con su solución. –

+1

Se eliminó una llamada innecesaria al estilo en el enlace: http://jsfiddle.net/xReHA/2/ –

+0

Mike, esa solución era simplemente hermosa. Sólo digo'. – Vivek

Cuestiones relacionadas