D3选择性缩放

4

我正在开发一个力导向图布局,并添加了一些功能:可选择的链接/节点、工具提示、鱼眼效果以及 - 对于我的问题非常重要的 - 缩放和平移。

现在,缩放功能的效果非常好,如下所示:

d3 ... .append('svg:g').call(d3.behavior.zoom().on("zoom", redraw))... 

重新绘制函数如下所示...
function redraw() {
  trans = d3.event.translate;
  scale = d3.event.scale;
  vis.attr("transform", "translate(" + trans + ")" + " scale(" + scale + ")");
}

然而,这种方法会缩放整个SVG图形,包括字体大小、图形边缘、围绕节点的线条宽度等。
是否有一种方法可以不缩放某些元素?到目前为止,我看到的唯一解决方案是像这样加一行代码(从这里获取:http://jsfiddle.net/56RDx/2/
node.attr("font-size", (nodeFontSize / d3.event.scale) + "px");

在重绘方法中,基本上是动态反转某些元素的缩放。然而我的问题是(除了这个丑陋的解决方法),我的边宽是根据图形属性在图形绘制时动态生成的,因此这种“反转”方法不起作用...

为什么不在单个SVG形状上将scale()设置为较小的值呢? - A.M.K
你有演示吗?你试图不缩放什么? - A.M.K
@A.M.K 这段代码相当庞大,很难将其放入演示中...但这里是许多使用功能的源代码:http://bl.ocks.org/2514276#index.html - fgysin
@A.M.K 我该如何将“scale()”设置为仅适用于某些SVG形状的较小值? - fgysin
没关系,那个方法行不通,请看下面的答案。 - A.M.K
3个回答

9
  1. you can add a class to the element you want to trigger the zoom on:

    d3 ... .append('svg:g').classed("some_classname", true).call(d3.behavior.zoom().on("zoom", redraw))...
    

    then do:

    function redraw() {
      trans = d3.event.translate;
      scale = d3.event.scale;
      vis.selectAll("some_classname").attr("transform", "translate(" + trans + ")" + " scale(" + scale + ")");
    }
    

  2. or you can add a class to all elements you don't want to trigger the zoom on then use the CSS3 :not pseudo-class:

    function redraw() {
      trans = d3.event.translate;
      scale = d3.event.scale;
      vis.selectAll("*:not(.some_classname)").attr("transform", "translate(" + trans + ")" + " scale(" + scale + ")");
    }
    

2

我能找到的唯一解决方案是一个"丑陋的黑科技",如果(我猜想你是)试图不要缩放行的话,你应该尝试下面的方法,它适用于放大和缩小:

示例:http://jsfiddle.net/SO_AMK/gJMTb/

JavaScript:

function redraw() {
  vis.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
  vis.attr("font-size", (nodeFontSize / d3.event.scale) + "px");
  vis.selectAll("line.link").style("stroke-width", getStrokeWidth); // Function so it runs for each element individually
}

function getStrokeWidth(){
    if (!this.__data__.stroke) { // Doesn't exist, so set it to the original stroke-width
        this.__data__.stroke = parseFloat(d3.select(this).style("stroke-width"));
        // I found __data__ to be easier than d3's .data()
    }
    return this.__data__.stroke / d3.event.scale + "px";
}

请查看文档,了解如何使用style()函数的详细信息。

然而,我不确定我真正理解这里发生了什么......在函数getStrokeWidth内,引用this指向SVG线元素,对吗?所以\_\_data\_\_属性指向使用D3添加的实际数据元素(图形边缘)。但是这与描边有什么关系呢?难道描边不是在SVG元素上定义的,而不是在数据元素上定义的吗? - fgysin
一旦更改了stroke-width值,就不能将其乘以d3.event.scale,因为(d3.event.scale)它不是相对值。所以,我在元素的__data__对象中缓存原始的stroke-width值以便稍后进行计算。您可以在这些值上使用console.log()来更好地查看正在发生的情况。 - A.M.K
忘了提一下,__data__ 是 D3 的 .data() 用来存储东西的,所以你也可以通过 .data() 来检索它。但是,这会创建一些额外的对象,并不会使它变得更容易。 - A.M.K

0
在 D3 v7 中,我能够使用以下代码实现你想要的功能:
function redraw() {
  vis.attr("transform", "translate(1, 1) scale(1)");
}

在这里,vis元素将保持相同的大小(比例为1),并保持固定位置(x和y平移为1),而其余图形将缩放(在其他地方处理)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接