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
文件
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");
});