Search code examples
javascriptd3.jszooming

D3: Constrained Semantic Zooming on a Tree Layout


I'm trying to apply constrained and semantic zoom behavior based on these two examples: Constrained Zoom and SVG Semantic Zooming, on my tree visualization. I might have been applying the constrained zoom part correctly, but it's getting complicated and fishy when I tried to integrate the semantic zooming part: The cursor doesn't stay on the particular node which I zoom to. I'm starting to think that I should only choose either constrained or semantic zooming, but not both. Is it actually possible to combine the two (constrained + semantic zooming) on a tree layout?

Here is my effort so far: http://jsfiddle.net/glenn/GpjFN/.

The relevant code for the zooming:

...

var zoom = d3.behavior.zoom()
    .scaleExtent([1, 5])
    .on('zoom', move);

function move() {
    var t = d3.event.translate,
        s = d3.event.scale,
        width = viewportSize[0],
        height = viewportSize[1];

    t[0] = Math.min(width * (s - 1), Math.max(width * (1 - s), t[0]));
    t[1] = Math.min(height * (s - 1), Math.max(height * (1 - s), t[1]));

    zoom.translate(t);

    viewport.attr('transform',
        'translate(' + t + ')' + ' scale(' + s + ')');

    // TODO: ???
};

svg.call(zoom);

Solution

  • The problem is that you have several nested g elements with translations applied. That is, any translation you apply to the viewport element is relative to the translation you have applied to the parent g element. The event coordinates (d3.event) are determined by the absolute mouse position however. What you're seeing is the offset between the two g elements when zooming in -- that's by how much the position is shifted.

    There's a simple fix though, simply add this offset to the even translation coordinates in your move function:

    t[0] += translationVector[0];
    t[1] += translationVector[1];
    

    This will transform the absolute event coordinates into relative container coordinates. Complete example here.

    Oh and in principle there's no restriction on how you can combine different things, so yes, constrained and semantic zoom together are certainly possible.

    Edit:

    There's quite a bit of translation and offsetting going on in your fiddle. Much of it is nested, which makes it difficult to use the absolute coordinates from the event. I've rewritten parts of your code to remove the nested offsets, which makes it much easier to handle and also removes the need to add the offsets as above. I've also fixed the functions that constrain the translation.

    Complete code here.