d3.js缩放并将力布局转换为视口的中心

时间:2016-04-06 15:10:51

标签: javascript d3.js

我有一个d3.js力布局,对力计算进行了一些调整。由于我处理大型数据集,有时图表部分或全部位于视口之外。我想添加一个命令来重新缩放并将图形居中放在视口内,但是遇到了一些麻烦。

什么有效:

我有一个画布和一个视口:

this.svg_canvas = d3.select("#" + this.container_id)
    .append("svg")
    .attr("width", this.width)
    .attr("height", this.height)
    .call(this.zoom_behavior.bind(this))
;

this.viewport = this.svg_canvas.append("g")
    .attr("id", "viewport")
;

我有缩放和平移视口的缩放行为:

this.zoom_behavior = d3.behavior.zoom()
    .scaleExtent([GraphView.MIN_ZOOM, GraphView.MAX_ZOOM])
    .on('zoom', this._handleZoom.bind(this))
;

GraphView.prototype._handleZoom = function() {
    var translate = d3.event.translate;
    var scale = d3.event.scale;
    this.viewport.attr("transform",
                       "translate(" + translate + ") " +
                       "scale(" + scale + ")");
};

所有这一切都可行。

什么不起作用

我添加了一个"重新定位和缩放"应该执行缩放和转换以将图形带到视口的方法。它应该工作的方式是首先找到图形的范围(通过我的boundingBox()方法),然后使用适当的缩放和转换参数调用zoom.behavior

GraphView.prototype.recenterAndScale = function(nodes) {
    var bounding_box = this._boundingBox(nodes || this.force.nodes());

    var viewport = this.zoom_behavior.size();      // viewport [width, height]
    var tx = viewport[0]/2 - bounding_box.x0;
    var ty = viewport[1]/2 - bounding_box.y0;
    var scale = Math.min(viewport[0]/bounding_box.dx, viewport[1]/bounding_box.dy);
    this.zoom_behavior.translate([tx, ty])
        .scale(scale)
        .event(this.svg_canvas)
    ;
};

这不起作用。它(通常)将图形定位在视口的边缘。也许我正在使用错误的引用。是否存在如何执行此操作的在线示例"正确" (使用d3成语)?

为了完整性,这里是我对boundingBox()的定义 - 它返回图中节点的几何中心和范围。据我所知,这是正常的:

// Return {dx:, dy:, x0:, y0:} for the given nodes.  If no nodes
// are given, return {dx: 0, dy: 0, x0: 0, y0: 0}
GraphView.prototype._boundingBox = function(nodes) {
    if (nodes.length === 0) {
        return {dx: 0, dy: 0, x0: 0, y0: 0};
    } else {
        var min_x = Number.MAX_VALUE;
        var min_y = Number.MAX_VALUE;
        var max_x = -Number.MAX_VALUE;
        var max_y = -Number.MAX_VALUE;
        nodes.forEach(function(node) {
            if (node.x < min_x) min_x = node.x;
            if (node.x > max_x) max_x = node.x;
            if (node.y < min_y) min_y = node.y;
            if (node.y > max_y) max_y = node.y;
        });
        return {dx: max_x - min_x,
                dy: max_y - min_y,
                x0: (max_x + min_x) / 2.0,
                y0: (max_y + min_y) / 2.0
               };
    }                                  
}

1 个答案:

答案 0 :(得分:0)

这实际上非常简单:我需要在计算翻译时应用缩放效果。更正后的recenterAndScale()方法是:

GraphView.prototype.recenterAndScale = function(nodes) {
    var bbox = this._boundingBox(nodes || this.force.nodes());
    var viewport = this.zoom_behavior.size();  // => [width, height]
    var scale = Math.min(viewport[0]/bbox.dx, viewport[1]/bbox.dy);
    var tx = viewport[0]/2 - bbox.x0 * scale;  // <<< account for scale
    var ty = viewport[1]/2 - bbox.y0 * scale;  // <<< account for scale
    this.zoom_behavior.translate([tx, ty])
        .scale(scale)
        .event(this.svg_canvas)
    ;
};