Javascript 将SVG路径纵向拆分为多种颜色

Javascript 将SVG路径纵向拆分为多种颜色,javascript,html,css,d3.js,svg,Javascript,Html,Css,D3.js,Svg,我有一个树可视化,其中我试图显示节点之间的路径,这些节点表示具有多个类的分布。我想将路径纵向分割成多种颜色,以表示每个分布的频率 例如:假设我们有A类(红色)和B类(黑色),它们的频率都是50。然后我想要一个在节点之间的半红半黑的路径。其思想是表示类的相对频率,因此频率将被标准化 我目前(天真的)尝试是为每个类创建一个单独的路径,然后使用x偏移。看起来像 但是,如图所示,这些线在路径的持续时间内不会保持相等的距离 相关代码段: linkGroup.append("path").attr("cla

我有一个树可视化,其中我试图显示节点之间的路径,这些节点表示具有多个类的分布。我想将路径纵向分割成多种颜色,以表示每个分布的频率


例如:假设我们有A类(红色)和B类(黑色),它们的频率都是50。然后我想要一个在节点之间的半红半黑的路径。其思想是表示类的相对频率,因此频率将被标准化

我目前(天真的)尝试是为每个类创建一个单独的路径,然后使用x偏移。看起来像

但是,如图所示,这些线在路径的持续时间内不会保持相等的距离

相关代码段:

linkGroup.append("path").attr("class", "link")
              .attr("d", diagonal)
              .style("stroke", "red")
              .style("stroke-width", 5)
              .attr("transform", function(d) {
                  return "translate(" + -2.5 + "," + 0.0 + ")"; });

linkGroup.append("path").attr("class", "link")
              .attr("d", diagonal)
              .style("stroke", "black")
              .style("stroke-width", 5)
              .attr("transform", function(d) {
                  return "translate(" + 2.5 + "," + 0.0 + ")"; });
如果有人能提供一些建议,那就太好了


谢谢

一种可能的解决方案是计算各个路径并填充所需的颜色

使用中的库,可以计算路径的特性(x、y、切线),而无需像在中那样先创建路径(这不会计算切线)

代码片段使用了两种颜色,但可以很容易地推广到更多颜色

使用字典指定笔划的颜色、百分比和宽度

var duoProp = { color: ["red", "black"], percent: 0.30, width: 15 };
percent
color[0]
从笔划宽度中获取的量

var duoPath = pathPoints("M30,30C160,30 150,90 250,90S350,210 250,210", 10, duoProp);
duoPath.forEach( (d, i) => {
    svg.append("path")
       .attr("d", d)
       .attr("fill", duoProp.color[i])
       .attr("stroke", "none");
});

路径点
参数

  • 需要笔划的路径可以通过
    d3.line

  • 采样的路径长度间隔(单位像素)。每10个像素提供一个很好的近似值

  • 带有笔划百分比和宽度的字典

  • 它返回一个包含要填充路径的数组,每种颜色1个

    
    文件
    var svg=d3。选择(“图表”);
    函数路径点(路径、步长、duoProp){
    var props=spp.svgPathProperties(路径);
    var length=props.getTotalLength();
    变量tList=d3.范围(0,长度,步长);
    t列表推力(长度);
    var tProps=tList.map(d=>props.getPropertiesAttlength(d));
    var pFactor=百分比=>(百分比-0.5)*duoProp.width;
    tProps.forEach(p=>{
    p、 x0=p.x-p因子(0)*p切向;
    p、 y0=p.y+p因子(0)*p.x;
    p、 xP=p.x-p因子(duoProp.百分比)*p.切向;
    p、 yP=p.y+p因子(duoProp.百分比)*p.x;
    p、 x1=p.x-p因子(1)*p切向;
    p、 y1=p.y+p系数(1)*p.x;
    });
    var format1d=d3.format(“.1f”);
    var createPath=(向前、向后)=>{
    var fp=tProps.map(p=>forward(p));
    var bp=tProps.map(p=>backward(p));
    bp.reverse();
    返回'M'+fp.concat(bp).map(p=>`${format1d(p[0])},${format1d(p[1])}`);
    }
    返回[createPath(p=>[p.x0,p.y0],p=>[p.xP,p.yP]),createPath(p=>[p.xP,p.yP],p=>[p.x1,p.y1])]
    }
    var duoProp={color:[“red”,“black”],百分比:0.30,宽度:15};
    var duoPath=路径点(“M30,30C160,30150,90250,90S350210 250210”,10,duoProp);
    duoPath.forEach((d,i)=>{
    追加(“路径”)
    .attr(“d”,d)
    .attr(“填充”,双色[i])
    .attr(“笔划”、“无”);
    });
    
    作为rioV8出色答案的快速跟进,我能够让他们的代码正常工作,但需要将其推广到使用两种以上的颜色。如果其他人有类似要求,代码如下:

    function pathPoints(path, stepLength, duoProp) {
        // get the properties of the path
        var props = spp.svgPathProperties(path);
        var length = props.getTotalLength();
    
        // build a list of segments to use as approximation points
        var tList = d3.range(0, length, stepLength);
        tList.push(length);
        var tProps = tList.map(function (d) {
            return props.getPropertiesAtLength(d);
        });
    
        // incorporate the percentage
        var pFactor = function pFactor(percent) {
            return (percent - 0.5) * duoProp.width;
        };
    
        // for each path segment, calculate offset points
        tProps.forEach(function (p) {
            // create array to store modified points
            p.x_arr = [];
            p.y_arr = [];
    
            // calculate offset at 0%
            p.x_arr.push(p.x - pFactor(0) * p.tangentY);
            p.y_arr.push(p.y + pFactor(0) * p.tangentX);
    
            // calculate offset at each specified percent
            duoProp.percents.forEach(function(perc) {
                p.x_arr.push(p.x - pFactor(perc) * p.tangentY);
                p.y_arr.push(p.y + pFactor(perc) * p.tangentX);
            });
    
            // calculate offset at 100%
            p.x_arr.push(p.x - pFactor(1) * p.tangentY);
            p.y_arr.push(p.y + pFactor(1) * p.tangentX);
        });
    
        var format1d = d3.format(".1f");
        var createPath = function createPath(forward, backward) {
            var fp = tProps.map(function (p) {
                return forward(p);
            });
            var bp = tProps.map(function (p) {
                return backward(p);
            });
            bp.reverse();
            return 'M' + fp.concat(bp).map(function (p) {
                return format1d(p[0]) + "," + format1d(p[1]);
            }).join(' ') + 'z';
        };
    
        // create a path for each projected point
        var paths = [];
        for(var i=0; i <= duoProp.percents.length; i++) {
            paths.push(createPath(function (p) { return [p.x_arr[i], p.y_arr[i]]; }, function (p) { return [p.x_arr[i+1], p.y_arr[i+1]]; }));
        }
    
        return paths;
    }
    
    // generate the line 
    var duoProp = { color: ["red", "blue", "green"], percents: [0.5, 0.7], width: 15 };
    var duoPath = pathPoints("M30,30C160,30 150,90 250,90S350,210 250,210", 10, duoProp);
    duoPath.forEach( (d, i) => {
        svg.append("path")
           .attr("d", d)
           .attr("fill", duoProp.color[i])
           .attr("stroke", "none");
    });
    
    功能路径点(路径、步长、duoProp){
    //获取路径的属性
    var props=spp.svgPathProperties(路径);
    var length=props.getTotalLength();
    //构建要用作近似点的线段列表
    变量tList=d3.范围(0,长度,步长);
    t列表推力(长度);
    var tProps=tList.map(函数(d){
    返回道具。GetPropertiesAtleLength(d);
    });
    //合并百分比
    var pFactor=函数pFactor(百分比){
    收益率(百分比-0.5)*duoProp.width;
    };
    //对于每个路径段,计算偏移点
    tProps.forEach(函数(p){
    //创建阵列以存储修改的点
    p、 x_arr=[];
    p、 y_arr=[];
    //计算0%的偏移量
    p、 x_arr.push(p.x-p系数(0)*p.切向);
    p、 y方向推力(p.y+p系数(0)*p.X);
    //计算每个指定百分比的偏移量
    每个(功能)的duoProp百分比{
    p、 x_arr.push(p.x-p系数(perc)*p.切向);
    p、 y方向推力(p.y+p系数(perc)*p.X);
    });
    //计算100%的偏移量
    p、 x_arr.push(p.x-p系数(1)*p.切向);
    p、 y方向推力(p.y+p系数(1)*p.X);
    });
    var format1d=d3.format(“.1f”);
    var createPath=函数createPath(向前、向后){
    var fp=tProps.map(函数(p){
    返回向前(p);
    });
    var bp=tProps.map(函数(p){
    向后返回(p);
    });
    bp.reverse();
    返回'M'+fp.concat(bp).map(函数(p){
    返回format1d(p[0])+“,”+format1d(p[1]);
    }).join(“”)加上“z”;
    };
    //为每个投影点创建路径
    var路径=[];
    对于(var i=0;i{
    追加(“路径”)
    .attr(“d”,d)
    .attr(“填充”,双色[i])
    .attr(“笔划”、“无”);
    });
    

    请注意,
    percents
    数组指定了笔划的累积百分比,而不是宽度的单个百分比。例如,在上面的示例中,红色笔划的宽度范围为0%到50%,蓝色笔划的宽度范围为50%到70%,绿色笔划的宽度范围为70%到100%。

    如果两者的频率都大于50怎么办?比如红色80和黑色75.我们的想法是表示每个类的相对大小,因此它们将被标准化。在您的示例中,这些值将标准化为80/(80+75)和75/(80+75)。非常感谢您提供的详细答案。
    function pathPoints(path, stepLength, duoProp) {
        // get the properties of the path
        var props = spp.svgPathProperties(path);
        var length = props.getTotalLength();
    
        // build a list of segments to use as approximation points
        var tList = d3.range(0, length, stepLength);
        tList.push(length);
        var tProps = tList.map(function (d) {
            return props.getPropertiesAtLength(d);
        });
    
        // incorporate the percentage
        var pFactor = function pFactor(percent) {
            return (percent - 0.5) * duoProp.width;
        };
    
        // for each path segment, calculate offset points
        tProps.forEach(function (p) {
            // create array to store modified points
            p.x_arr = [];
            p.y_arr = [];
    
            // calculate offset at 0%
            p.x_arr.push(p.x - pFactor(0) * p.tangentY);
            p.y_arr.push(p.y + pFactor(0) * p.tangentX);
    
            // calculate offset at each specified percent
            duoProp.percents.forEach(function(perc) {
                p.x_arr.push(p.x - pFactor(perc) * p.tangentY);
                p.y_arr.push(p.y + pFactor(perc) * p.tangentX);
            });
    
            // calculate offset at 100%
            p.x_arr.push(p.x - pFactor(1) * p.tangentY);
            p.y_arr.push(p.y + pFactor(1) * p.tangentX);
        });
    
        var format1d = d3.format(".1f");
        var createPath = function createPath(forward, backward) {
            var fp = tProps.map(function (p) {
                return forward(p);
            });
            var bp = tProps.map(function (p) {
                return backward(p);
            });
            bp.reverse();
            return 'M' + fp.concat(bp).map(function (p) {
                return format1d(p[0]) + "," + format1d(p[1]);
            }).join(' ') + 'z';
        };
    
        // create a path for each projected point
        var paths = [];
        for(var i=0; i <= duoProp.percents.length; i++) {
            paths.push(createPath(function (p) { return [p.x_arr[i], p.y_arr[i]]; }, function (p) { return [p.x_arr[i+1], p.y_arr[i+1]]; }));
        }
    
        return paths;
    }
    
    // generate the line 
    var duoProp = { color: ["red", "blue", "green"], percents: [0.5, 0.7], width: 15 };
    var duoPath = pathPoints("M30,30C160,30 150,90 250,90S350,210 250,210", 10, duoProp);
    duoPath.forEach( (d, i) => {
        svg.append("path")
           .attr("d", d)
           .attr("fill", duoProp.color[i])
           .attr("stroke", "none");
    });