fgysin
fgysin

Reputation: 11913

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...

Upvotes: 4

Views: 4166

Answers (3)

Eystein Thanisch
Eystein Thanisch

Reputation: 57

In D3 v7, I was able to achieve what you are looking for with the following:

function redraw() {
  vis.attr("transform", "translate(1, 1) scale(1)");
}

Here, the vis element will remain the same size (scale is 1) and stay in a fixed position (x and y translation is 1) while the rest of the graphics zoom (handled elsewhere).

Upvotes: 0

hungastryke
hungastryke

Reputation: 106

  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 + ")");
    }
    

Upvotes: 9

A.M.K
A.M.K

Reputation: 16795

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()

Upvotes: 2

Related Questions