15

是 a 的 jsfiddle Sankey diagram

在此处输入图像描述

我正在尝试修改链接的颜色,以便每个链接的颜色实际上是从其源节点颜色渐变到其目标节点颜色。(假设不透明度将保持 0.2 或 0.5,具体取决于鼠标是否悬停在链接上;因此链接将比节点保持“淡”一点)

我看了一下这个很好且有启发性的示例,它绘制了这个渐变填充循环:

在此处输入图像描述

但是,我根本无法将该解决方案集成到我的解决方案中,对于给定的任务来说它看起来太复杂了。

另外,请注意原始桑基图中的链接在节点被拖动时移动,并且即使在那些临时状态下也必须显示渐变。一个小问题也是链接和节点的透明度,以及绘图的顺序。我会很感激想法,提示。

4

1 回答 1

37

@VividD:刚刚看到你的评论,但无论如何我都快完成了。在您自己弄清楚之前,请随意忽略这一点,但我想确保我也知道该怎么做。另外,这是一个非常常见的问题,非常值得参考。

如何获得沿线定位的渐变

稍后阅读本文的任何人都需要注意,它只会起作用,因为路径几乎是直线,所以线性渐变看起来不太像 - 将路径笔划设置为渐变不会使渐变曲线与路径!

  1. 在初始化时,在 SVG 中创建一个<defs>(定义)元素并将选择保存到一个变量中:

    var defs = svg.append("defs");
    
  2. 定义一个函数,该函数将从链接数据对象为您的渐变创建唯一id。为确定节点颜色的函数命名也是一个好主意:

    function getGradID(d){return "linkGrad-" + d.source.name + d.target.name;}
    function nodeColor(d) { return d.color = color(d.name.replace(/ .*/, ""));}
    
  3. 在其中创建<linearGradient>对象选择<defs>并将其连接到您的链接数据,然后根据源和目标数据对象设置停止偏移和线坐标。

    对于您的示例,如果您只是使所有渐变水平,它可能看起来不错。由于这是方便的默认设置,我认为我们所要做的就是告诉渐变以适应它正在绘制的路径的大小:

    var grads = defs.selectAll("linearGradient")
                     .data(graph.links, getLinkID);
    
    grads.enter().append("linearGradient")
                 .attr("id", getGradID)
                 .attr("gradientUnits", "objectBoundingBox"); //stretch to fit
    
    grads.html("") //erase any existing <stop> elements on update
         .append("stop")
         .attr("offset", "0%")
         .attr("stop-color", function(d){
               return nodeColor( (d.source.x <= d.target.x)? d.source: d.target) 
              });
    
    grads.append("stop")
         .attr("offset", "100%")
         .attr("stop-color", function(d){
               return nodeColor( (d.source.x > d.target.x)? d.source: d.target) 
              });
    

    不幸的是,当路径是一条完全直线时,它的边界框不存在(无论笔划宽度有多宽),最终结果是没有绘制渐变

    所以我不得不切换到更一般的模式,其中渐变沿着源和目标之间的线定位和倾斜:

    grads.enter().append("linearGradient")
        .attr("id", getGradID)
        .attr("gradientUnits", "userSpaceOnUse");
    
    grads.attr("x1", function(d){return d.source.x;})
        .attr("y1", function(d){return d.source.y;})
        .attr("x2", function(d){return d.target.x;})
        .attr("y2", function(d){return d.target.y;});
    
    /* and the stops set as before */
    
  4. 当然,既然梯度是基于坐标系而不是基于路径的长度定义的,你必须在节点移动时更新这些坐标,所以我必须将这些定位语句包装在一个我可以调用的函数中在dragmove()函数中。

  5. 最后,在创建链接路径时,将它们的填充设置为url()引用从数据派生的相应唯一渐变 id 的 CSS 函数(使用预定义的实用函数):

    link.style("stroke", function(d){
        return "url(#" + getGradID(d) + ")";
    })
    


带有来自数据的自定义梯度的桑基图

于 2014-01-18T18:47:17.223 回答