Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 selective Zoom

I am working on a force directed graph layout with some added features: selectable links/nodes, tooltips, fisheye effect, and -- important for my question -- zoom and pan.

Now, the zooming works very well like this:

d3 ... .append('svg:g').call(d3.behavior.zoom().on("zoom", redraw))... 

Where the redraw function looks like this...

function redraw() {
  trans = d3.event.translate;
  scale = d3.event.scale;
  vis.attr("transform", "translate(" + trans + ")" + " scale(" + scale + ")");
}

However, this method zooms the entire SVG graphic, including font sizes, graph edges, the line stroke-widths surrounding the nodes, etc.

Is it somehow possible not to zoom certain elements? The only solution I have seen so far is to put a line like this (took it from here http://jsfiddle.net/56RDx/2/)

node.attr("font-size", (nodeFontSize / d3.event.scale) + "px");

in the redraw method, to basically invert the zooming on certain elements on the fly. My problem is however (apart from this being an ugly hack), that my edge-widths are dynamically generated on graph-drawing (according to some graph properties...), so this 'invertion' method does not work...

like image 910
fgysin Avatar asked Oct 29 '25 11:10

fgysin


2 Answers

  1. you can add a class to the element you want to trigger the zoom on:

    d3 ... .append('svg:g').classed("some_classname", true).call(d3.behavior.zoom().on("zoom", redraw))...
    

    then do:

    function redraw() {
      trans = d3.event.translate;
      scale = d3.event.scale;
      vis.selectAll("some_classname").attr("transform", "translate(" + trans + ")" + " scale(" + scale + ")");
    }
    

  2. or you can add a class to all elements you don't want to trigger the zoom on then use the CSS3 :not pseudo-class:

    function redraw() {
      trans = d3.event.translate;
      scale = d3.event.scale;
      vis.selectAll("*:not(.some_classname)").attr("transform", "translate(" + trans + ")" + " scale(" + scale + ")");
    }
    
like image 150
hungastryke Avatar answered Nov 01 '25 03:11

hungastryke


The only solution I could find is an "ugly hack", if (I assume you are) you're trying to not zoom lines for example, the you should try the below, it works for both zooming in and out:

Demo: http://jsfiddle.net/SO_AMK/gJMTb/

JavaScript:

function redraw() {
  vis.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
  vis.attr("font-size", (nodeFontSize / d3.event.scale) + "px");
  vis.selectAll("line.link").style("stroke-width", getStrokeWidth); // Function so it runs for each element individually
}

function getStrokeWidth(){
    if (!this.__data__.stroke) { // Doesn't exist, so set it to the original stroke-width
        this.__data__.stroke = parseFloat(d3.select(this).style("stroke-width"));
        // I found __data__ to be easier than d3's .data()
    }
    return this.__data__.stroke / d3.event.scale + "px";
}

Please see the documentation for details on using a function with style()

like image 20
A.M.K Avatar answered Nov 01 '25 01:11

A.M.K



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!