d3 Preserve scale/translate after resetting range

前端 未结 3 1971
不知归路
不知归路 2020-12-31 13:09

I have an whose width is 100% of its container. When the container is resized, I update the linear xScale.range() to represent the new

相关标签:
3条回答
  • 2020-12-31 13:45

    I'm still in D3v3 with one of my libraries, and have struggled off and on with this problem for years.

    I think the principle of @Philip's solution is really great: since D3 wants to take the scales only when they represent translate [0,0] and scale 0, save the zoom parameters and then reset the zoom to that state. And when replacing the scales, make sure they are also in the reset state.

    I couldn't get Philip's code to work for me, perhaps because I need to set both dimensions, or perhaps because I need to resize the domain based on the new div size. But I think I came up with a nice generalization of his technique.

    Taking it one step further, resetting the scale and translate on the zoom behavior will cause it to change the domains of the scales to where they should be for the reset state. Very helpful!

    So now we can just

    • multiply the scale domains by the ratio of new to old size
    • set the ranges to the new div size
    • plunk the scales (which are still untranslated and unscaled but a different size) back into the zoom
    • restore the translate and scaling

    At least, it worked for me!

    Here's the source, with inputs oldWidth, oldHeight, newWidth, newHeight all in screen coordinates:

    var scale = _zoom.scale(), translate = _zoom.translate();
    _zoom.scale(1).translate([0,0]);
    var xDomain = _diagram.x().domain(), yDomain = _diagram.y().domain();
    _diagram.x()
        .domain([xDomain[0], xDomain[0] + (xDomain[1] - xDomain[0])*newWidth/oldWidth])
        .range([0, newWidth]);
    _diagram.y()
        .domain([yDomain[0], yDomain[0] + (yDomain[1] - yDomain[0])*newHeight/oldHeight])
        .range([0, newHeight]);
    _zoom
        .x(_diagram.x()).y(_diagram.y())
        .translate(translate).scale(scale);
    

    I will be sure to check back when I port my library to D3v4.

    0 讨论(0)
  • 2020-12-31 13:54

    For those that stumbled upon this looking for a v4 solution, using the awesome setup from Philip above that is for v3, I adapted a loosely-based v4 solution. I spread out the variables to explain it the way v3 used to do it (since it makes more sense in v3). v4 does not have the ability to force the X value like v3 did, so you have to calculate out the existing X and then divide by the scale (K). (There may be a better way to do the final calculation + setting it on the zoom, but the d3-zoom documentation is a little confusing on this)

    let transform = d3.zoomTransform(node);
    let oldFullWidth = (oldWidth * transform.k);
    let newFullWidth = (newWidth * transform.k);
    
    // this is the result you want X to be
    let newX = -(newFullWidth * ((transform.x * -1) / oldFullWidth));
    // this is just deducting from the existing so you can call .translate
    let translateBy = (newX - transform.x) / transform.k;
    d3.select(node).call(myZoom.transform, transform.translate(translateBy, 0));
    
    0 讨论(0)
  • 2020-12-31 14:00

    It looks like the best strategy is to cache the scale and translate values, reset, then reapply. For the record, this code (within my resize handler) roughly shows my solution:

    // Cache scale
    var cacheScale = zoom.scale();
    
    // Cache translate
    var cacheTranslate = zoom.translate();
    
    // Cache translate values as percentages/ratio of the full width
    var cacheTranslatePerc = zoom.translate().map( function( v, i, a )
    {
      return (v * -1) / getFullWidth();
    } );
    
    // Manually reset the zoom
    zoom.scale( 1 ).translate( [0, 0] );
    
    // Update range values based on resized container dimensions
    xScale.range( [0, myResizedContainerWidth] );
    
    // Apply the updated xScale to the zoom
    zoom.x( xScale );
    
    // Revert the scale back to our cached value
    zoom.scale( cacheScale );
    
    // Overwrite the x value of cacheTranslate based on our cached percentage
    cacheTranslate[0] = -(getFullWidth() * cacheTranslatePerc[0]);
    
    // Finally apply the updated translate
    zoom.translate( cacheTranslate );
    
    
    function getFullWidth()
    {
      return xScale.range()[1] * zoom.scale();
    }
    
    0 讨论(0)
提交回复
热议问题