d3中力向图的语义缩放

d3中力向图的语义缩放

问题描述:

许多案例已通过 SVG几何缩放显示为力导向图几何缩放.

Many cases have been shown for force directed graph geometric zooming by SVG Geometric Zooming.

在几何缩放中,我只需要在缩放功能中添加一个transform属性.但是,在语义缩放中,如果仅在node中添加一个transform属性,则链接将不会连接到node.因此,我想知道d3中是否有针对力导向图的几何缩放的解决方案.

In geometric zooming, I only need to add a transform attribute in zoom function. However, in semantic zooming, if I only add a transform attribute in node, links won't connect to node. So, I am wondering whether there exist a solution for geometric zooming for force directed graph in d3.

这是我的示例,其中几何缩放与前一种情况相同.我有两个问题:

Here is my example with geometric zooming following previous case. I have two problems:

  1. 当我缩小然后拖动整个图时,该图会奇怪地消失.
  2. 使用相同的重绘功能

function zoom() {
  vis.attr("transform", transform);
}
function transform(d){
  return "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")";
}

这只会更新一个svg元素的"transform"属性.但是如何使函数改变节点位置呢?

This only update one svg element's "transform" attribute. But how to make the function to change the node position?

但是我想做的是语义缩放.我试图修改缩放和变换功能,但不确定正确的方法. 这里是我尝试的.我已更改的功能:

But what I want to do is semantic zooming. I have tried to modify zoom and transform function, but not sure the right way to do. Here is what I try. Functions I have changed:

function zoom() {
  node.call(transform);
  // update link position
  update();
}
function transform(d){
  // change node x, y position, not sure what function to put here.
}


我试图找到一个可以链接的好教程,但是找不到真正涵盖所有问题的内容,因此我将其写出来一步一步地自己.

I tried to find a good tutorial to link to, but couldn't find anything that really covered all the issues, so I'm going to write it out step-by-step myself.

首先,您需要清楚地了解您要完成的任务.这对于两种缩放类型是不同的.我真的不喜欢Mike Bostock引入的术语(这与术语的非d3使用并不完全一致),但我们也可能坚持与其他d3示例保持一致.

First, you need to clearly understand what you're trying to accomplish. This is different for the two types of zooming. I don't really like the terminology Mike Bostock has introduced, (it's not entirely consistent with non-d3 uses of the terms) but we might as well stick with it to be consistent with other d3 examples.

几何缩放" 中,您将缩放整个图像.圆和线越来越大,也越来越远. SVG具有通过"transform"属性完成此操作的简便方法.在SVG元素上设置transform="scale(2)"时,将其绘制为好像所有内容都大一倍.对于一个圆,它的半径被绘制成两倍大,并且它的cxcy位置被绘制成距(0,0)点的距离的两倍.整个坐标系发生变化,因此现在一个单位等于屏幕上的两个像素,而不是一个.

In "geometric zooming" you are zooming the entire image. Circles and lines get bigger as well as farther apart. SVG has an easy way to accomplish this through the "transform" attribute. When you set transform="scale(2)" on an SVG element, it is drawn as if everything was twice as big. For a circle, it's radius gets drawn twice a big, and it's cx and cy positions get plotted twice the distance from the (0,0) point. The entire coordinate system changes, so one unit is now equal to two pixels on screen, not one.

同样,transform="translate(-50,100)"更改整个坐标系,以使坐标系的(0,0)点向左移动50个单位,并从左上角向下移动100个单位(这是默认原点)点).

Likewise, transform="translate(-50,100)" changes the entire coordinate system, so that the (0,0) point of the coordinate system gets moved 50 units to the left and 100 units down from the top-left corner (which is the default origin point).

如果同时翻译和缩放SVG元素,则顺序很重要.如果翻译是之前标度,则翻译将以原始单位表示.如果翻译是标度,则翻译单位是缩放后的单位.

If you both translate and scale an SVG element, the order is important. If translate is before scale, than the translation is in the original units. If translate is after scale, than the translation is in the scaled units.

d3.zoom.behavior() 方法创建了一个侦听鼠标滚轮和拖动事件以及与缩放相关的触摸屏事件.它将这些用户事件转换为自定义的缩放"事件.

The d3.zoom.behavior() method creates a function that listens for mouse wheel and drag events, as well as for touch screen events associated with zooming. It converts these user events into a custom "zoom" event.

缩放事件具有比例因子(一个数字)和一个转换因子(两个数字的数组),行为对象根据用户的移动来计算它们.您如何处理这些数字取决于您自己; 他们不会直接更改任何东西. (除了将缩放比例附加到缩放行为功能时,如后面所述).

The zoom event is given a scale factor (a single number) and a translate factor (an array of two numbers), which the behaviour object calculates from the user's movements. What you do with these numbers is up to you; they don't change anything directly. (With the exception of when you attach a scale to the zoom behaviour function, as described later.)

对于几何缩放,通常要做的是在包含要缩放内容的<g>元素上设置比例并转换transform属性.此示例在由均匀放置的网格线组成的简单SVG上实现了几何缩放方法:
http://jsfiddle.net/LYuta/2/

For geometric zooming, what you usually do is set a scale and translate transform attribute on a <g> element that contains the content you want to zoom. This example implements that geometric zooming method on a simple SVG consisting of evenly placed gridlines:
http://jsfiddle.net/LYuta/2/

缩放代码很简单:

function zoom() {
    console.log("zoom", d3.event.translate, d3.event.scale);
    vis.attr("transform", 
             "translate(" + d3.event.translate + ")" 
                + " scale(" + d3.event.scale + ")"
             );
}

缩放是通过在"vis"上设置transform属性来完成的,"vis"是d3选择,其中包含一个<g>元素,该元素本身包含我们要缩放的所有内容.平移和缩放因子直接来自d3行为创建的缩放事件.

The zoom is accomplished by setting the transform attribute on "vis", which is a d3 selection containing a <g> element which itself contains all the content we want to zoom. The translate and scale factors come directly from the zoom event that the d3 behaviour created.

结果是一切变大或变小-网格线的宽度以及它们之间的间距.这些行仍然具有stroke-width:1.5;,但是屏幕上的1.5等于的定义已经更改,并且转换后的<g>元素中的其他任何内容都没有变化.

The result is that everything gets bigger or smaller -- the width of the gridlines as well as the spacing between them. The lines still have stroke-width:1.5; but the definition of what 1.5 equals on the screen has changed for them and anything else within the transformed <g> element.

对于每个缩放事件,平移和缩放因子也会记录到控制台.看到这一点,您会注意到,如果缩小,则缩放比例将在0到1之间;否则,缩放比例将在0到1之间.如果放大,则它将大于1.如果平移(拖动以移动)图形,则比例将完全不变.但是,转换编号在平移缩放上都改变.这是因为平移表示相对于SVG左上角的位置的图形中(0,0)点的位置.缩放时,(0,0)与图形上任何其他点之间的距离都会改变.因此,为了使鼠标或手指触摸下的内容保持在屏幕上的相同位置,必须移动(0,0)点的位置.

For every zoom event, the translate and scale factors are also logged to the console. Looking at that, you'll notice that if you're zoomed out the scale will be between 0 and 1; if you're zoomed in it will be greater than 1. If you pan (drag to move) the graph, the scale won't change at all. The translate numbers, however, change on both pan and zoom. That's because the translate represents the position of the (0,0) point in the graph relative to the position of the top-left-corner of the SVG. When you zoom, the distance between (0,0) and any other point on the graph changes. So in order to keep the content under the mouse or finger-touch in the same position on the screen, the position of the (0,0) point has to move.

在该示例中,您还应注意许多其他事项:

There are a number of other things you should pay attention to in that example:

  • 我已经使用 .scaleExtent([min,max]) 方法.这样就限制了缩放事件中行为将使用的缩放比例值的大小,无论用户旋转了多少滚轮.

  • I've modified the zoom behaviour object with the .scaleExtent([min,max]) method. This sets a limit on the scale values that the behaviour will use in the zoom event, no matter how much the user spins the wheel.

转换是在<g>元素上,而不是在<svg>本身上.这是因为SVG元素作为一个整体被视为HTML元素,并且具有不同的转换语法和属性.

The transform is on a <g> element, not the <svg> itself. That's because the SVG element as a whole is treated as an HTML element, and has a different transform syntax and properties.

缩放行为附加到不同 <g>元素,该元素包含主<g>和背景矩形.此处有背景矩形,因此即使鼠标或触摸不在一行上,也可以观察到鼠标和触摸事件. <g>元素本身没有任何高度或宽度,因此无法直接响应用户事件,它仅接收来自其子级的事件.我将矩形保留为黑色,以便您可以知道它的位置,但是只要将其样式设置为pointer-events:all;,就可以将其样式设置为fill:none;.矩形不能在要转换的<g> ,因为当您缩小时,响应缩放事件的区域也会缩小,并且可能会看不到矩形的边缘SVG.

The zoom behaviour is attached to a different <g> element, that contains the main <g> and a background rectangle. The background rectangle is there so that mouse and touch events can be observed even if the mouse or touch isn't right on a line. The <g> element itself doesn't have any height or width and so can't respond to user events directly, it only receives events from its children. I've left the rectangle black so you can tell where it is, but you can set it's style to fill:none; so long as you also set it to pointer-events:all;. The rectangle can't be inside the <g> that gets transformed, because then the area that responds to zoom events would also shrink when you zoom out, and possibly go out of sight off the edge of the SVG.

可以通过将缩放行为直接附加到SVG对象来跳过矩形和第二个<g>元素,如

You could skip the rectangle and second <g> element by attaching the zoom behaviour directly to the SVG object, as in this version of the fiddle. However, you often don't want events on the entire SVG area to trigger the zoom, so it is good to know how and why to use the background rectangle option.

以下是相同的几何缩放方法,适用于您的部队布局的简化版本:
http://jsfiddle.net/cSn6w/5/

Here's the same geometric zooming method, applied to a simplified version of your force layout:
http://jsfiddle.net/cSn6w/5/

我减少了节点和链接的数量,并取消了节点拖动行为和节点展开/折叠行为,因此您可以专注于缩放.我还更改了摩擦"参数,以使图形停止移动需要更长的时间;缩放它,同时它仍然在移动,您会看到一切都将像以前一样保持移动.

I've reduced the number of nodes and links, and taken away the node-drag behaviour and the node-expand/collapse behaviour, so you can focus on the zoom. I've also changed the "friction" parameter so that it takes longer for the graph to stop moving; zoom it while it's still moving, and you'll see that everything will keep moving as before .

图像的几何缩放"非常简单,只需很少的代码即可实现,并且浏览器可以快速,平滑地进行更改.但是,通常要放大图的原因是因为数据点太靠近且重叠.在这种情况下,仅使所有内容变大都无济于事.您想将元素延伸到更大的空间,同时保持各个点的大小相同.那就是语义缩放"的地方.

"Geometric zooming" of the image is fairly straightforward, it can be implemented with very little code, and it results in fast, smooth changes by the browser. However, often the reason you want to zoom in on a graph is because the datapoints are too close together and overlapping. In that case, just making everything bigger doesn't help. You want to stretch the elements out over a larger space while keeping the individual points the same size. That's where "semantic zooming" comes into place.

图形的语义缩放" ,即 Mike Bostock使用术语,是在不缩放单个元素的情况下缩放图形的 layout . (注意,对于其他上下文,还有语义缩放"的其他解释.)

"Semantic zooming" of a graph, in the sense that Mike Bostock uses the term, is to zoom the layout of the graph without zooming on individual elements. (Note, there are other interpretations of "semantic zooming" for other contexts.)

这是通过更改元素的 position 的计算方式以及连接对象的任何线或路径的 length 来实现的,不包含更改用于定义像素大小的基础坐标系,以设置线宽或形状或文本的大小.

This is done by changing the way the position of elements is calculated, as well as the length of any lines or paths that connect objects, without changing the underlying coordinate system that defines how big a pixel is for the purpose of setting line width or the size of shapes or text.

可以自己进行这些计算,使用平移和缩放值根据以下公式定位对象:

You can do these calculations yourself, using the translate and scale values to position the objects based on these formulas:

zoomedPositionX = d3.event.translate[0] + d3.event.scale * dataPositionX 

zoomedPositionY = d3.event.translate[1] + d3.event.scale * dataPositionY

在此版本的网格线示例中,我使用了这种方法来实现语义缩放:
http://jsfiddle.net/LYuta/4/

I've used that approach to implement semantic zooming in this version of the gridlines example:
http://jsfiddle.net/LYuta/4/

对于垂直线,它们最初的定位是这样的

For the vertical lines, they were originally positioned like this

vLines.attr("x1", function(d){return d;})
    .attr("y1", 0)
    .attr("x2", function(d){return d;})
    .attr("y2", h);

在缩放功能中,将其更改为

In the zoom function, that gets changed to

vLines.attr("x1", function(d){
        return d3.event.translate[0] + d*d3.event.scale;
    })
    .attr("y1", d3.event.translate[1])
    .attr("x2", function(d){
        return d3.event.translate[0] + d*d3.event.scale;
    })
    .attr("y2", d3.event.translate[1] + h*d3.event.scale);

水平线的变化类似.结果?线条的位置和长度在缩放时会发生变化,而线条不会变粗或变细.

The horizontal lines are changed similarly. The result? The position and length of the lines changes on the zoom, without the lines getting thicker or thinner.

当我们尝试对力布局进行相同操作时,它会变得有些复杂.这是因为在每次滴答"事件之后,力布局图中的对象也会重新定位.为了将它们定位在正确的缩放位置,刻度线定位方法将必须使用缩放位置公式.这意味着:

It gets a little complicated when we try to do the same for the force layout. That's because the objects in the force layout graph are also being re-positioned after every "tick" event. In order to keep them positioned in the correct places for the zoom, the tick-positioning method is going to have to use the zoomed-position formulas. Which means that:

  1. 刻度和平移必须保存在一个刻度函数可以访问的变量中;并且,
  2. 如果用户尚未缩放任何内容,则必须具有默认的刻度和平移值才能使用刻度功能.

默认比例为1,默认转换为[0,0],表示正常比例,不转换.

The default scale will be 1, and the default translation will be [0,0], representing normal scale and no translation.

这是简化的部队布局上的语义缩放的外观:
http://jsfiddle.net/cSn6w/6/

Here's what it looks like with semantic zooming on the simplified force layout:
http://jsfiddle.net/cSn6w/6/

缩放功能现在可用

function zoom() {
    console.log("zoom", d3.event.translate, d3.event.scale);
    scaleFactor = d3.event.scale;
    translation = d3.event.translate;
    tick(); //update positions
}

它设置scaleFactor和平移变量,然后调用tick函数.滴答功能会进行所有定位:在初始化时,强制布局滴答事件之后以及缩放事件之后.看起来像

It sets the scaleFactor and translation variables, then calls the tick function. The tick function does all the positioning: at initialization, after force-layout tick events, and after zoom events. It looks like

function tick() {
    linkLines.attr("x1", function (d) {
            return translation[0] + scaleFactor*d.source.x;
        })
        .attr("y1", function (d) {
            return translation[1] + scaleFactor*d.source.y;
        })
        .attr("x2", function (d) {
            return translation[0] + scaleFactor*d.target.x;
        })
        .attr("y2", function (d) {
            return translation[1] + scaleFactor*d.target.y;
        });

    nodeCircles.attr("cx", function (d) {
            return translation[0] + scaleFactor*d.x;
        })
        .attr("cy", function (d) {
            return translation[1] + scaleFactor*d.y;
        });
}

圆和链接的

每个位置值通过平移和比例因子进行调整.如果这对您有意义,那么这对于您的项目应该足够了,您不需要使用比例尺.只要确保始终使用此公式在 data 坐标(dx和dy)和 display 坐标(cx,cy,x1,x2等)之间转换即可.用于放置物体.

Every position value for the circles and the links is adjusted by the translation and the scale factor. If this makes sense to you, this should be sufficient for your project and you don't need to use scales. Just make sure that you always use this formula to convert between the data coordinates (d.x and d.y) and the display coordinates (cx, cy, x1, x2, etc.) used to position the objects.

如果需要从显示坐标到数据坐标的反向转换,这会变得很复杂.如果希望用户能够拖动单个节点,则需要执行此操作-您需要根据所拖动节点的屏幕位置设置数据坐标. (请注意,这在您的两个示例中均无法正常工作.)

Where this gets complicated is if you need to do the reverse conversion from display coordinates to data coordinates. You need to do this if you want the user to be able to drag individual nodes -- you need to set the data coordinate based on the screen position of the dragged node. (Note that this wasn't working properly in either of your examples).

对于几何缩放,可以使用 d3.mouse() .使用d3.mouse(SVGElement)计算鼠标在该SVGElement 使用的坐标系中的位置.因此,如果我们传入表示转换后的可视化效果的元素,它将返回可直接用于设置对象位置的坐标.

For geometric zoom, converting between screen position and data position can be down with d3.mouse(). Using d3.mouse(SVGElement) calculates the position of the mouse in the coordinate system used by that SVGElement. So if we pass in the element representing the transformed visualization, it returns coordinates that can be used directly to set the position of the objects.

可拖动的几何缩放力布局如下所示:
http://jsfiddle.net/cSn6w/7/

The draggable geometric-zoom force-layout looks like this:
http://jsfiddle.net/cSn6w/7/

拖动功能是:

function dragged(d){
    if (d.fixed) return; //root is fixed

    //get mouse coordinates relative to the visualization
    //coordinate system:    
    var mouse = d3.mouse(vis.node());
    d.x = mouse[0]; 
    d.y = mouse[1];
    tick();//re-position this node and any links
}

对于语义缩放d3.mouse()返回的SVG坐标不再直接与数据坐标相对应.您必须考虑比例和翻译.您可以通过重新排列上面给出的公式来做到这一点:

For semantic zoom, however, the SVG coordinates returned by d3.mouse() no longer directly correspond to the data coordinates. You have to factor in the scale and translation. You do this by re-arranging the formulas given above:

zoomedPositionX = d3.event.translate[0] + d3.event.scale * dataPositionX 

zoomedPositionY = d3.event.translate[1] + d3.event.scale * dataPositionY

成为

dataPositionX = (zoomedPositionX - d3.event.translate[0]) / d3.event.scale

dataPositionY = (zoomedPositionY - d3.event.translate[1]) / d3.event.scale

语义缩放示例的拖动功能为

function dragged(d){
    if (d.fixed) return; //root is fixed

    //get mouse coordinates relative to the visualization
    //coordinate system:
    var mouse = d3.mouse(vis.node());
    d.x = (mouse[0] - translation[0])/scaleFactor; 
    d.y = (mouse[1] - translation[1])/scaleFactor; 
    tick();//re-position this node and any links
}

可拖动的语义缩放强制布局在此处实现:
http://jsfiddle.net/cSn6w/8/

This draggable semantic-zoom force-layout is implemented here:
http://jsfiddle.net/cSn6w/8/

这应该足以让您重回正轨.我待会儿再回来,并解释一下比例尺,以及它们如何使所有这些计算变得更加容易.

...然后我回来了:

看看上面的所有数据到显示转换函数,这是否会让您认为每次都具有一个函数来执行此操作会更容易吗?"这就是 d3刻度的作用:将数据值转换为显示值.

Looking at all the data-to-display conversion functions above, doesn't it make you think "wouldn't it be easier to have a function to do this each time?" That's what the the d3 scales are for: to convert data values to display values.

在力布局示例中通常不会看到比例,因为力布局对象允许您直接设置宽度和高度,然后在该范围内创建d.x和d.y数据值.将布局的宽度和高度设置为可视化的宽度和高度,然后可以直接使用数据值在显示中定位对象.

You don't often see scales in the force-layout examples because the force layout object allows you to set a width and height directly, and then creates d.x and d.y data values within that range. Set the layout width and height to your visualization width and height, and you can use the data values directly for positioning objects in the display.

但是,当您放大图形时,您会从显示整个数据范围切换到仅显示一部分.因此,数据值不再直接与定位值相对应,我们需要在它们之间进行转换.缩放功能将使操作变得简单得多.

However, when you zoom in on the graph, you switch from having the entire extent of the data visible to only having a portion visible. So the data values no longer directly correspond to positioning values, and we need to convert between them. And a scale function would make that a lot easier.

在D3术语中,期望的数据值是 domain ,而期望的输出/显示值是 range .因此,比例尺的初始域将是布局中预期的最大值和最小值,而初始范围将是可视化图像上的最大和最小坐标.

In D3 terminology, the expected data values are the domain and the desired output/display values are the range. The initial domain of the scale will therefore by the expected maximum and minimum values from the layout, while the initial range will be the maximum and minimum coordinates on the visualization.

缩放时,域和范围之间的关系会更改,因此这些值之一必须在比例上更改.幸运的是,我们不必自己找出公式,因为D3缩放行为会为我们计算公式- if 我们使用其.x().y()方法.

When you zoom, the relationship between domain and range changes, so one of those values will have to change on the scale. Luckily, we don't have to figure out the formulas ourselves, because the D3 zoom behaviour calculates it for us -- if we attach the scale objects to the zoom behaviour object using its .x() and .y() methods.

因此,如果我们更改绘制方法以使用比例尺,则在zoom方法中要做的就是调用绘制函数.

As a result, if we change the drawing methods to use the scales, then all we have to do in the zoom method is call the drawing function.

这是使用比例尺实现的网格示例的语义缩放:
http://jsfiddle.net/LYuta/5/

Here's the semantic zoom of the grid example implemented using scales:
http://jsfiddle.net/LYuta/5/

关键代码:

/*** Configure zoom behaviour ***/
var zoomer = d3.behavior.zoom()
                .scaleExtent([0.1,10])
        //allow 10 times zoom in or out
                .on("zoom", zoom)
        //define the event handler function
                .x(xScale)
                .y(yScale);
        //attach the scales so their domains
        //will be updated automatically

function zoom() {
    console.log("zoom", d3.event.translate, d3.event.scale);

    //the zoom behaviour has already changed
    //the domain of the x and y scales
    //so we just have to redraw using them
    drawLines();
}
function drawLines() {
    //put positioning in a separate function
    //that can be called at initialization as well
    vLines.attr("x1", function(d){
            return xScale(d);
        })
        .attr("y1", yScale(0) )
        .attr("x2", function(d){
            return xScale(d);
        })
        /* etc. */

d3缩放行为对象通过更改标度域来修改标度.通过更改刻度范围,您可以得到类似的效果,因为重要的部分是更改域和范围之间的关系.但是,该范围还有另一个重要含义:代表显示中使用的最大值和最小值.通过仅通过缩放行为更改刻度的范围侧,该范围仍表示有效的显示值.当用户调整显示尺寸时,这允许我们实现不同类型的缩放.通过让SVG根据窗口大小更改大小,然后根据SVG大小设置缩放范围,图形可以响应不同的窗口/设备大小.

The d3 zoom behaviour object modifies the scales by changing their domain. You could get a similar effect by changing the scale range, since the important part is changing the relationship between domain and range. However, the range has another important meaning: representing the maximum and minimum values used in the display. By only changing the domain side of the scale with the zoom behaviour, the range still represents the valid display values. Which allows us to implement a different type of zoom, for when the user re-sizes the display. By letting the SVG change size according to the window size, and then setting the range of the scale based on the SVG size, the graph can be responsive to different window/device sizes.

这是语义缩放网格示例,该示例以比例缩放方式进行了响应:
http://jsfiddle.net/LYuta/9/

我在CSS中提供了基于SVG百分比的高度和宽度属性,它将覆盖属性的高度和宽度值.在脚本中,我已将与显示高度和宽度有关的所有行移动到一个函数中,该函数检查实际svg元素的当前高度和宽度.最后,我添加了一个窗口大小调整侦听器来调用此方法(它也会触发重新绘制).

I've given the SVG percentage-based height and width properties in CSS, which will over-ride the attribute height and width values. In the script, I've moved all the lines which relate to the display height and width into a function that checks the actual svg element for it's current height and width. Finally, I've added a window resize listener to call this method (which also triggers a re-draw).

关键代码:

/* Set the display size based on the SVG size and re-draw */
function setSize() {
    var svgStyles = window.getComputedStyle(svg.node());
    var svgW = parseInt(svgStyles["width"]);
    var svgH = parseInt(svgStyles["height"]);

    //Set the output range of the scales
    xScale.range([0, svgW]);
    yScale.range([0, svgH]);

    //re-attach the scales to the zoom behaviour
    zoomer.x(xScale)
          .y(yScale);

    //resize the background
    rect.attr("width", svgW)
            .attr("height", svgH);

    //console.log(xScale.range(), yScale.range());
    drawLines();
}

//adapt size to window changes:
window.addEventListener("resize", setSize, false)

setSize(); //initialize width and height

相同的想法-使用比例尺来布置图形,缩放范围发生变化,窗口调整大小事件发生范围变化-当然可以应用于强制布局.但是,我们仍然必须处理上面讨论的复杂问题:在处理节点拖动事件时,如何反转从数据值到显示值的转换. d3线性标度对此也有一个方便的方法:scale.invert().如果为w = scale(x),则为x = scale.invert(w).

The same ideas -- using scales to layout the graph, with a changing domain from the zoom and a changing range from window resize events -- can of course be applied to the force-layout. However, we still have to deal with the complication discussed above: how to reverse the conversion from data values to display values when dealing with node-drag events. The d3 linear scale has a convenient method for that, too: scale.invert(). If w = scale(x) then x = scale.invert(w).

因此,在node-drag事件中,使用scale的代码为:

In the node-drag event, the code using scales is therefore:

function dragged(d){
    if (d.fixed) return; //root is fixed

    //get mouse coordinates relative to the visualization
    //coordinate system:
    var mouse = d3.mouse(vis.node());
    d.x = xScale.invert(mouse[0]); 
    d.y = yScale.invert(mouse[1]); 
    tick();//re-position this node and any links
}

语义缩放力布局示例的其余部分,使缩放响应在此处:
http://jsfiddle.net/cSn6w/10/

The rest of the semantic zoom force-layout example, made responsive with scales is here:
http://jsfiddle.net/cSn6w/10/

我确定讨论比您期望的要长得多,但是我希望它不仅可以帮助您了解需要做什么,而且可以帮助您了解为什么您需要这样做.当我看到代码实际上是被实际上并不了解代码功能的人从多个示例中剪切并粘贴在一起时,我感到非常沮丧.如果您了解该代码,则可以轻松地将其适应您的需求.希望这将为其他试图弄清楚如何完成类似任务的人提供很好的参考.

I'm sure that was a lot longer a discussion than you were expecting, but I hope it helps you understand not only what you need to do, but also why you need to do it. I get really frustrated when I see code that has obviously been cut-and-pasted together from multiple examples by someone who doesn't actually understand what the code does. If you understand the code, it's a lot easier to adapt it to your needs. And hopefully, this will serve as a good reference for other people trying to figure out how to do similar tasks.