在d3 js中调整组的比例以确保内部形状尽可能大

时间:2014-03-13 21:16:05

标签: javascript svg d3.js logic

我使用类似于此示例的d3树布局:http://bl.ocks.org/mbostock/4339083

我实施了一个搜索框,在输入时,将您的屏幕置于虚拟"平均值"所有适当节点的位置。

Searching for a term bolds and centers item

我想调整比例,以便所选节点

  1. 所有可见
  2. 尽可能放大。
  3. 如果搜索匹配正好为1,则模拟点击节点,否则居中到此虚拟位置。

        if (matches[0].length === 1) {
            click(matches.datum(), 0, 0, false);
        }
        else {
            var position = GetAveragePosition(matches);
    
            centerToPosition(position.x, position.y, 1);
        }
    

    这就是centerToPosition函数的样子:

    function centerToPosition(x0, y0, newScale) {
    
        if (typeof newScale == "undefined") {
            scale = zoomListener.scale();
        }
        else {
            scale = newScale;
        }
        var x = y0 * -1; //not sure why this is.. but it is
        var y = x0 * -1;
        x = x * scale + viewerWidth / 2;
        y = y * scale + viewerHeight / 2;
        d3.select('g').transition()
            .duration(duration)
            .attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")");
        zoomListener.scale(scale);
        zoomListener.translate([x, y]);
    }
    

    那么如何计算新的比例?我通过获取数据点的范围来尝试不同的变体

            var xExtent = d3.extent(matches.data(), function (d) {
                return d.x0;
            });
            var yExtent = d3.extent(matches.data(), function (d) {
                return d.y0;
            });
    

    还尝试在居中屏幕之前查看组的变换属性。

    var components = d3.transform(svgGroup.attr("transform"));
    

    我会尽快添加js小提琴! 编辑:这是:http://jsfiddle.net/7SJqC/

1 个答案:

答案 0 :(得分:3)

有趣的项目。

确定合适的比例以适合积分的方法是相当简单的,虽然我花了很长时间才弄清楚它为什么不适合我 - 我还没有找到因为(因为你是水平地画树)" x"从树形布局表示垂直位置," y"代表水平位置,所以我得到了明显的任意结果。

清理完毕后,为了找出缩放,您只需找到要显示的区域的高度和宽度(以数据坐标为单位),然后将其与视口的高度和宽度进行比较(或者其他)您的原始最大和最小尺寸是。)

ScaleFactor = oldDomain / newDomain

通常,您不希望使用不同的水平和垂直比例扭曲图像,因此您分别为宽度和高度计算比例因子并采用最小值(因此整个区域将适合视口)

你可以使用d3数组函数来计算每个方向的位置范围,然后找到增加最大值和最小值并除以2的范围的中间值。

        var matches = d3.selectAll(".selected");

        /*...*/

        if ( matches.empty() ) { 
            centerToPosition(0, 0, 1); //reset
        }
        else if (matches.size() === 1) {
            click(matches.datum(), 0, 0, false);
        }
        else {
            var xExtent = d3.extent(matches.data(), function (d) {
                return d.x0;
            });
            var yExtent = d3.extent(matches.data(), function (d) {
                return d.y0;
            });

            //note: the "x" values are used to set VERTICAL position,
            //while the "y" values are setting the HORIZONTAL position

            var potentialXZoom = viewerHeight/(xExtent[1] - xExtent[0] + 20);
            var potentialYZoom = viewerWidth/(yExtent[1] - yExtent[0] + 150);
            //The "20" and "150" are for height and width of the labels
            //You could (should) replace with calculated values 
            //or values stored in variables

            centerToPosition( (xExtent[0] + xExtent[1])/2,
                              (yExtent[0] + yExtent[1])/2,
                             Math.min(potentialXZoom, potentialYZoom)
            );
        }

http://jsfiddle.net/7SJqC/2/