D3 tiene una fuerza dirigida diseño aquí . ¿Hay alguna forma de agregar zoom a este gráfico? Actualmente, pude capturar el evento de la rueda del mouse, pero no estoy realmente seguro de cómo escribir la función de redibujo en sí. ¿Alguna sugerencia?
var vis = d3.select("#graph")
.append("svg:svg")
.call(d3.behavior.zoom().on("zoom", redraw)) // <-- redraw function
.attr("width", w)
.attr("height", h);
javascript
jquery
d3.js
zoom
force-layout
Leyenda
fuente
fuente
Respuestas:
Actualización 4/6/14
Consulte también la respuesta de Mike Bostock aquí para conocer los cambios en D3 v.3 y el ejemplo relacionado . Creo que esto probablemente reemplaza la respuesta a continuación.
Actualización 18/02/2014
Creo que la respuesta de @ahaarnos es preferible si desea que todo el SVG se mueva y se acerque. Los
g
elementos anidados en mi respuesta a continuación son realmente necesarios solo si tiene elementos sin zoom en el mismo SVG (no es el caso en la pregunta original). Si haces aplicar el comportamiento de ung
elemento, a continuación, un fondorect
se requiere elemento o similar para asegurar que lag
recibe eventos de puntero.Respuesta original
Conseguí que esto funcionara basado en el ejemplo de zoom-pan-transform - puede ver mi jsFiddle aquí: http://jsfiddle.net/nrabinowitz/QMKm3/
Fue un poco más complejo de lo que esperaba: tienes que anidar varios
g
elementos para que funcione, establecer elpointer-events
atributo SVG enall
y luego agregar un rectángulo de fondo para recibir los eventos del puntero (de lo contrario, solo funciona cuando el puntero se termina un nodo o enlace). Laredraw
función es comparativamente simple, simplemente estableciendo una transformación en el más internog
:var vis = d3.select("#chart") .append("svg:svg") .attr("width", w) .attr("height", h) .attr("pointer-events", "all") .append('svg:g') .call(d3.behavior.zoom().on("zoom", redraw)) .append('svg:g'); vis.append('svg:rect') .attr('width', w) .attr('height', h) .attr('fill', 'white'); function redraw() { console.log("here", d3.event.translate, d3.event.scale); vis.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")"); }
Esto escala efectivamente todo el SVG, por lo que también escala el ancho del trazo, como al hacer zoom en una imagen.
Hay otro ejemplo que ilustra una técnica similar.
fuente
body
etiqueta, por lo que recomiendo mirar la fuente del marco y ver lo que se está perdiendo.zoom.scaleExtent()
¿Por qué los anidados
<g>
?Este código a continuación funcionó bien para mí (solo uno
<g>
, sin blanco grande al azar<rect>
:var svg = d3.select("body") .append("svg") .attr({ "width": "100%", "height": "100%" }) .attr("viewBox", "0 0 " + width + " " + height ) .attr("preserveAspectRatio", "xMidYMid meet") .attr("pointer-events", "all") .call(d3.behavior.zoom().on("zoom", redraw)); var vis = svg .append('svg:g'); function redraw() { vis.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")"); }
Donde todos los elementos en su svg se agregan al
vis
elemento.fuente
transformation
atributo en elg
elemento, no en elsvg
elemento.Las respuestas proporcionadas funcionan en D3 v2 pero no en v3. Sinteticé las respuestas en una solución limpia y resolví el problema de v3 usando la respuesta proporcionada aquí: ¿Por qué d3.js v3 rompe mi gráfico de fuerza al implementar el zoom cuando v2 no lo hace?
Primero el código principal. Esta es una versión limpia de la respuesta de @ahaarnos:
var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height) .call(d3.behavior.zoom().on("zoom", redraw)) .append('g'); function redraw() { svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")"); }
Ahora tiene panorámica y zoom, pero no podrá arrastrar nodos porque la función de panorámica anulará la función de arrastre. Entonces tenemos que hacer esto:
var drag = force.stop().drag() .on("dragstart", function(d) { d3.event.sourceEvent.stopPropagation(); // to prevent pan functionality from //overriding node drag functionality. // put any other 'dragstart' actions here });
Aquí está el violín de @nrabinowitz modificado para usar esta implementación de zoom más limpia, pero ilustrando cómo D3v3 rompe el arrastre del nodo: http://jsfiddle.net/QMKm3/718/
Y aquí está el mismo violín modificado para funcionar con D3v3: http://jsfiddle.net/QMKm3/719/
fuente
Conseguí que mi gráfico funcionara sin el segundo añadido "svg: g".
[...].attr("pointer-events", "all") .attr("width", width2) .attr("height", height2) .append('svg:g') .call(d3.behavior.zoom().on("zoom", redraw));
El resto es igual.
fuente
Obtuve una solución para el gráfico dirigido por fuerza D3 con opción de zoom.
var m = [40, 240, 40, 240], width = 960, height = 700, root; var svg = d3.select("body").append("svg") .attr("class", "svg_container") .attr("width", width) .attr("height", height) .style("overflow", "scroll") .style("background-color", "#EEEEEE") .append("svg:g") .attr("class", "drawarea") .append("svg:g") .attr("transform", "translate(" + m[3] + "," + m[0] + ")"); //applying zoom in&out for svg d3.select("svg") .call(d3.behavior.zoom() .scaleExtent([0.5, 5]) .on("zoom", zoom)); //zooming function zoom() { //zoom in&out function var scale = d3.event.scale, translation = d3.event.translate, tbound = -height * scale, bbound = height * scale, lbound = (-width + m[1]) * scale, rbound = (width - m[3]) * scale; // limit translation to thresholds translation = [ Math.max(Math.min(translation[0], rbound), lbound), Math.max(Math.min(translation[1], bbound), tbound) ]; d3.select(".drawarea") .attr("transform", "translate(" + translation + ")" + " scale(" + scale + ")"); }
fuente
Si desea hacer zoom y mover el diseño de fuerza sin cambiar el tamaño del nodo, intente a continuación. También puede arrastrar nodos sin temblar. Este código se basa en el ejemplo de diseño de fuerza original. En cuanto a los datos de nodos y enlaces, consulte los datos de muestra originales. http://bl.ocks.org/mbostock/4062045
Tenga en cuenta las variables xScale y yScale, las funciones dragstarted (), dragged () y dragended (). La función tick () también se cambió.
Puede ver el resultado en http://steelblue.tistory.com/9 El idioma del sitio es coreano. Sin embargo, puede encontrar fácilmente el resultado en el tercer ejemplo de la página.
var graph = { "nodes": [ { "name": "Myriel", "group": 1 }, { "name": "Napoleon", "group": 1 }, // ...... { "name": "Mme.Hucheloup", "group": 8 } ], "links": [ { "source": 1, "target": 0, "value": 1 }, { "source": 2, "target": 0, "value": 8 }, // ....... { "source": 76, "target": 58, "value": 1 } ] }; var width = 640, height = 400; var color = d3.scale.category20(); var xScale = d3.scale.linear() .domain([0, width]) .range([0, width]); var yScale = d3.scale.linear() .domain([0, height]) .range([0, height]); var zoomer = d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([0.1, 8]).on("zoom", zoom); function zoom() { tick(); }; var drag = d3.behavior.drag() .origin(function (d) { return d; }) .on("dragstart", dragstarted) .on("drag", dragged) .on("dragend", dragended); function dragstarted(d) { d3.event.sourceEvent.stopPropagation(); d.fixed |= 2; } function dragged(d) { var mouse = d3.mouse(svg.node()); d.x = xScale.invert(mouse[0]); d.y = yScale.invert(mouse[1]); d.px = d.x; d.py = d.y; force.resume(); } function dragended(d) { d.fixed &= ~6; } var force = d3.layout.force() .charge(-120) .linkDistance(30) .size([width, height]); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); svg.call(zoomer); force .nodes(graph.nodes) .links(graph.links) .start(); var link = svg.selectAll(".link") .data(graph.links) .enter().append("line") .attr("class", "link") .style("stroke-width", function (d) { return Math.sqrt(d.value); }); var node = svg.selectAll(".node") .data(graph.nodes) .enter().append("circle") .attr("class", "node") .attr("r", 5) .style("fill", function (d) { return color(d.group); }) .call(drag); node.append("title") .text(function (d) { return d.name; }); force.on("tick",tick); function tick(){ link.attr("x1", function (d) { return xScale(d.source.x); }) .attr("y1", function (d) { return yScale(d.source.y); }) .attr("x2", function (d) { return xScale(d.target.x); }) .attr("y2", function (d) { return yScale(d.target.y); }); node.attr("transform", function (d) { return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"; }); };
fuente