沿着D3 Sankey图中的链接的渐变

时间:2014-01-18 15:28:16

标签: javascript d3.js gradient sankey-diagram

HereSankey diagram

的jsfiddle

enter image description here

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

我看了一下这个漂亮而富有启发性的example,它绘制了这个渐变填充循环:

enter image description here

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

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

1 个答案:

答案 0 :(得分:34)

@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. <defs>中创建一组<linearGradient>个对象,并将其连接到您的链接数据,然后根据源数据对象和目标数据对象设置停止偏移和线坐标。

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

    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) 
              });
    

    不幸的是,当路径是完全直线时,其边界框不存在(无论笔划宽度有多宽),还有net result is the gradient doesn't get painted

    所以我不得不切换到更一般的模式,其中渐变位置和沿源和目标之间的线成角度:

    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. 最后,在创建链接路径时,将其填充设置为CSS url()函数,引用从数据派生的相应唯一渐变ID(使用预定义的实用程序函数):

    link.style("stroke", function(d){
        return "url(#" + getGradID(d) + ")";
    })
    
  6. 而且Voila!
    Sankey diagram with custom gradients from the data