2012-09-14 11 views
21

Tengo un gráfico, y quiero que la línea que representa el gráfico muestre un círculo en la coordenada x cuando se sitúa sobre el área svg. Este círculo debe seguir el camino de la línea que representa la curva. El problema es que no sé cómo hacer esto.¿Cómo devuelvo la coordenada y de una ruta en d3.js?

El siguiente código muestra qué tan lejos he tenido éxito, y de hecho agrega un círculo al documento en la coordenada x a la derecha. Ahora, ¿con qué reemplazo el signo de interrogación?

svg.on("mousemove", function() { 
     d3.select("path.line") 
      .style("stroke-width", "2.5px"); 

     svg.append("svg:circle") 
      .attr("cx", Math.floor(event.offsetX-m[1])) 
      .attr("cy", ?) 
      .attr("r", "10") 
      .attr("fill", "red"); 
    }); 

Respuesta

25

SVG proporciona una función nativa llamada .getPointAtLength() el que devuelve un valores X e Y de un camino en cualquier longitud que pasa en ella.

Debería iterar a lo largo de la línea hasta encontrar la posición y correspondiente. Esta es la manera que lo haría en D3:

var svg = d3.select("#line").append("svg") 
var path = 
    svg.append("path") 
     .attr("d", "M0,168L28,95.99999999999997L56,192L84,71.99999999999997L112,120L140,192L168,240L196,168L224,48L252,24L280,192L308,120L336,24L364,168L392,95.99999999999997L420,168L448,95.99999999999997L476,192L504,71.99999999999997L532,120L560,192L588,216L616,168L644,48L672,24L700,192L728,120L756,24L784,192L812,71.99999999999997") 
     .attr("fill", "none") 
     .attr("stroke", "black"); 

var circle = 
    svg.append("circle") 
     .attr("cx", 100) 
     .attr("cy", 350) 
     .attr("r", 3) 
     .attr("fill", "red"); 

var pathEl = path.node(); 
var pathLength = pathEl.getTotalLength(); 
var BBox = pathEl.getBBox(); 
var scale = pathLength/BBox.width; 
var offsetLeft = document.getElementById("line").offsetLeft; 
var randomizeButton = d3.select("button"); 

svg.on("mousemove", function() { 
    var x = d3.event.pageX - offsetLeft; 
    var beginning = x, end = pathLength, target; 
    while (true) { 
    target = Math.floor((beginning + end)/2); 
    pos = pathEl.getPointAtLength(target); 
    if ((target === end || target === beginning) && pos.x !== x) { 
     break; 
    } 
    if (pos.x > x)  end = target; 
    else if (pos.x < x) beginning = target; 
    else    break; //position found 
    } 
    circle 
    .attr("opacity", 1) 
    .attr("cx", x) 
    .attr("cy", pos.y); 
}); 

Se puede ver una demo aquí: http://bl.ocks.org/3824661

+0

estoy bastante seguro de que esto no funcionará para las formas de trazado arbitrario de estilo línea de tiempo, por ejemplo, una línea con una punta muy filosa en un punto no permitirá una interpolación lineal entre la longitud del camino y la posición x. – nrabinowitz

+0

@nrabinowitz es lo suficientemente simple para probar http://jsfiddle.net/FPfFJ/ y sí, no está del todo allí. – Duopixel

+0

Sí, creo que la única forma de usar '.getPointAtLength()' para obtener un punto preciso en este caso es buscar a lo largo de la línea hasta que encuentre un punto con un valor 'x' próximo a' d3.event.pageX' . – nrabinowitz