//最新小提琴 http://jsfiddle.net/rbLk2fbe/2/
我正在尝试构建此特定图表,我可以控制弯曲路径以显示网络。两个正确间隔的树干 - 然后是弯曲的树枝,分散给不同的人。
var w = 600;
var h = 600;
var data = [{
"userName": "You",
"userImage": "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcSTzjaQlkAJswpiRZByvgsb3CVrfNNLLwjFHMrkZ_bzdPOWdxDE2Q"
}, {
"userName": "Johnny",
"userImage": "https://crossovercomicblog.files.wordpress.com/2012/08/johnny-depp-sexy.jpg"
}, {
"userName": "Jeri",
"userImage": "https://68.media.tumblr.com/avatar_3b6d6241698f_128.png"
}, {
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Angelina",
"userImage": "https://pbs.twimg.com/profile_images/713650489032908800/nO1dMt6M_400x400.jpg"
}, {
"userName": "Them",
"userImage": "https://68.media.tumblr.com/avatar_8f199caf2d82_128.png"
}];
var viz = d3.select("#viz")
.append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(40,100)");
var patternsSvg = viz.append('g')
.attr('class', 'patterns');
var labelholder = viz.append("g")
.attr("class", "labelholder");
var treeholder = viz.append("g")
.attr("class", "treeholder");
var userholder = viz.append("g")
.attr("class", "userholder");
var smallRadius = 20;
var bigRadius = 30;
var smallX = smallRadius + (bigRadius/2);
var bigX = (bigRadius*2) + (smallRadius/2);
var verticalGap = (bigRadius * 2) - 5;
var count = data.length;
var extendedY = (count-2 * (smallRadius*2)) + ((bigRadius*2) * 2);
var arcRadiusLeft = (bigRadius / 2);
var arcRadiusRight = -(bigRadius / 2);
$.each(data, function(index, value) {
var defs = patternsSvg.append('svg:defs');
//big design
defs.append('svg:pattern')
.attr('id', index + "--" + value.userName.toLowerCase())
.attr('width', 1)
.attr('height', 1)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', bigRadius * 2)
.attr('height', bigRadius * 2);
//small design
defs.append('svg:pattern')
.attr('id', index + "-" + value.userName.toLowerCase())
.attr('width', 1)
.attr('height', 1)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', smallRadius * 2)
.attr('height', smallRadius * 2);
});
//plot people circles
var circle = userholder.append("g").selectAll("circle")
.data(data);
circle
.enter()
.append("svg:circle")
.attr("id", function(d) {
return d.userName.toLowerCase();
})
.attr("r", function(d, i) {
var rad = smallRadius;
//first and last items -- so you and them
if (i == 0 || i == count - 1) {
rad = bigRadius;
}
return rad;
})
.attr("cx", function(d, i) {
var cx;
if (i == 0) {
cx = 0; //first one
} else if (i == count - 1) {
cx = bigX; //last one
} else {
cx = smallX; //small ones
}
return cx;
})
.attr("cy", function(d, i) {
var cy;
if (i == 0) {
cy = 0;
} else if (i == count - 1) {
cy = verticalGap * (i-1) + extendedY + bigRadius;
} else {
cy = verticalGap * i
}
return cy;
})
.style("fill", function(d, i) {
var id = i + "-" + d.userName.toLowerCase(); //small circles
//large circles
if (i == 0 || i == count - 1) {
id = i + "--" + d.userName.toLowerCase();
}
return "url(#" + id + ")";
});
//plot people circles
//__labels
var labelholder = d3.select(".labelholder");
//__ enter
var labels = labelholder.selectAll("text")
.data(data);
labels.enter()
.append("text")
.attr("text-anchor", "left")
//__ update
labels
.attr("x", function(d, i) {
var displacement = (bigRadius/2) + smallRadius;
var cx = (smallRadius * 2);
if (i == 0) {
cx = bigRadius;
displacement = bigRadius/2;
}
if (i == count - 1) {
cx = (bigRadius * 2) + bigRadius;
displacement = bigRadius;
}
cx += displacement;
return cx;
})
.attr("y", function(d, i) {
var cy = verticalGap * i;
if (i == count - 1) {
cy += extendedY - (bigRadius/2);
}
return cy;
})
.text(function(d) {
return d.userName;
});
//__labels
var backbone = treeholder.append("g")
.append("svg:path");
backbone.attr("d", function(d, i) {
var sx = (bigRadius / 2) - (bigRadius / 2);
var tx = (bigRadius / 2) - (bigRadius / 2);
var r = smallRadius;
if (i == 0 || i == count - 1) {
r = bigRadius;
}
var sy = ((r / 2) * i) + (r);
var ty = verticalGap * (count - 2) - arcRadiusLeft;
var dr = 0;
return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty;
});
var displaceYBackboneRight = (bigRadius / 2) + 5;
var backbone = treeholder.append("g")
.append("svg:path");
backbone.attr("d", function(d, i) {
var sx = (bigRadius * 2) + smallRadius/2;
var tx = (bigRadius * 2) + smallRadius/2;
var r = smallRadius;
if (i == 0 || i == count - 1) {
r = bigRadius;
}
var sy = ((r / 2) * i) + (r) + smallRadius + displaceYBackboneRight;
var ty = verticalGap * (count - 2) + extendedY;
var dr = 0;
return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty;
});
//branches on the left
var leftpath = treeholder.append("g").selectAll("path.leftpath")
.data(data)
leftpath
.enter().append("svg:path")
.attr("class", function(d) {
return "leftpath";
});
leftpath.attr("d", function(d, i) {
var sx = 0;
var tx = arcRadiusLeft;
var sy = verticalGap * i - arcRadiusLeft;
var ty = verticalGap * i;
if (i != 0 && i != count - 1) {
return "M" + sx + "," + sy + "A" + arcRadiusLeft + "," + arcRadiusLeft + " 0 0,0 " + tx + "," + ty;
}
});
//branches on the left
var rightpath = treeholder.append("g").selectAll("path.rightpath")
.data(data)
rightpath
.enter().append("svg:path")
.attr("class", function(d) {
return "rightpath";
});
rightpath.attr("d", function(d, i) {
var sx = (bigRadius*2) + (smallRadius/2);
var tx = arcRadiusRight + (bigRadius*2) + (smallRadius/2);
var sy = verticalGap * i + (bigRadius / 2);
var ty = verticalGap * i + arcRadiusRight + (bigRadius / 2);
if (i != 0 && i != count - 1) {
return "M" + sx + "," + sy + "A" + arcRadiusLeft + "," + arcRadiusLeft + " 0 0,0 " + tx + "," + ty;
}
});
//老 http://jsfiddle.net/NYEaX/1811/
所以这里的关键是使用数学正确创建小弧。
像这样的东西
path.attr("d", function (d, i) {
const sx = 0;
const sy = height/2;
//width - total width of chart from subject a to subject b
const a = width/2;
//a - distance between subject a and center
const b = (1.5-i)*distanceBetween;
//b - distance between trait 1 and trait 2 (between the dots)
const c = Math.sqrt(a * a + b * b);
//c - is the diagonal distance between subject a and trait 1
const angle=Math.atan(a/b);
//angle - between group to subject a
const r=1/2*c/Math.cos(angle);
//r - 1/2 the distance of c -- divided by cos of angle -- will create a radius to draw the arc
//also equals c/b*(c/2)
// const r=c/b*(c/2);
return `M${sx},${sy} A${r},${r} 0 0,${b>0?1:0} ${width},${height/2}`;
});
答案 0 :(得分:1)
现场演示: http://jsfiddle.net/blackmiaool/857edt69/3/
问题中的第一个链接提供了完整的解决方案。
这个问题没有什么困难。你需要的只是一个不错的坐标系。
设置头像d
的代码:
.attr("cx", function (d, i) {
var cx;
if (i == 0) {
cx = 0; //first one
} else if (i == count - 1) {
cx = bigX; //last one
} else {
cx = smallX; //small ones
}
return cx;
})
.attr("cy", function (d, i) {
var cy;
if (i == 0) {
cy = 0;
} else if (i == count - 1) {
cy = verticalGap * (i - 1) + extendedY;
} else {
cy = verticalGap * i
}
return cy;
})
形成左弧的代码:
leftpath.attr("d", function (d, i) {
var dist = bigRadius * 2;
var sx = 0;
var tx = arcRadiusLeft;
var sy = verticalGap * i - arcRadiusLeft;
var ty = verticalGap * i;
if (i != 0 && i != count - 1) {
return "M" + sx + "," + sy + "A" + arcRadiusLeft + "," + arcRadiusLeft + " 0 0,0 " + tx + "," + ty;
}
});
处理左弧。我想你可以自己处理好的。
答案 1 :(得分:-1)
//最新的js小提琴 - 稳定控制所有路径 - 基于blackmiaool的答案。
http://jsfiddle.net/rbLk2fbe/2/
var w = 600;
var h = 600;
var data = [{
"userName": "You",
"userImage": "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcSTzjaQlkAJswpiRZByvgsb3CVrfNNLLwjFHMrkZ_bzdPOWdxDE2Q"
}, {
"userName": "Johnny",
"userImage": "https://crossovercomicblog.files.wordpress.com/2012/08/johnny-depp-sexy.jpg"
}, {
"userName": "Jeri",
"userImage": "https://68.media.tumblr.com/avatar_3b6d6241698f_128.png"
}, {
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Angelina",
"userImage": "https://pbs.twimg.com/profile_images/713650489032908800/nO1dMt6M_400x400.jpg"
}, {
"userName": "Them",
"userImage": "https://68.media.tumblr.com/avatar_8f199caf2d82_128.png"
}];
var viz = d3.select("#viz")
.append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(40,100)");
var patternsSvg = viz.append('g')
.attr('class', 'patterns');
var labelholder = viz.append("g")
.attr("class", "labelholder");
var treeholder = viz.append("g")
.attr("class", "treeholder");
var userholder = viz.append("g")
.attr("class", "userholder");
var smallRadius = 20;
var bigRadius = 30;
var smallX = smallRadius + (bigRadius/2);
var bigX = (bigRadius*2) + (smallRadius/2);
var verticalGap = (bigRadius * 2) - 5;
var count = data.length;
var extendedY = (count-2 * (smallRadius*2)) + ((bigRadius*2) * 2);
var arcRadiusLeft = (bigRadius / 2);
var arcRadiusRight = -(bigRadius / 2);
$.each(data, function(index, value) {
var defs = patternsSvg.append('svg:defs');
//big design
defs.append('svg:pattern')
.attr('id', index + "--" + value.userName.toLowerCase())
.attr('width', 1)
.attr('height', 1)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', bigRadius * 2)
.attr('height', bigRadius * 2);
//small design
defs.append('svg:pattern')
.attr('id', index + "-" + value.userName.toLowerCase())
.attr('width', 1)
.attr('height', 1)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', smallRadius * 2)
.attr('height', smallRadius * 2);
});
//plot people circles
var circle = userholder.append("g").selectAll("circle")
.data(data);
circle
.enter()
.append("svg:circle")
.attr("id", function(d) {
return d.userName.toLowerCase();
})
.attr("r", function(d, i) {
var rad = smallRadius;
//first and last items -- so you and them
if (i == 0 || i == count - 1) {
rad = bigRadius;
}
return rad;
})
.attr("cx", function(d, i) {
var cx;
if (i == 0) {
cx = 0; //first one
} else if (i == count - 1) {
cx = bigX; //last one
} else {
cx = smallX; //small ones
}
return cx;
})
.attr("cy", function(d, i) {
var cy;
if (i == 0) {
cy = 0;
} else if (i == count - 1) {
cy = verticalGap * (i-1) + extendedY + bigRadius;
} else {
cy = verticalGap * i
}
return cy;
})
.style("fill", function(d, i) {
var id = i + "-" + d.userName.toLowerCase(); //small circles
//large circles
if (i == 0 || i == count - 1) {
id = i + "--" + d.userName.toLowerCase();
}
return "url(#" + id + ")";
});
//plot people circles
//__labels
var labelholder = d3.select(".labelholder");
//__ enter
var labels = labelholder.selectAll("text")
.data(data);
labels.enter()
.append("text")
.attr("text-anchor", "left")
//__ update
labels
.attr("x", function(d, i) {
var displacement = (bigRadius/2) + smallRadius;
var cx = (smallRadius * 2);
if (i == 0) {
cx = bigRadius;
displacement = bigRadius/2;
}
if (i == count - 1) {
cx = (bigRadius * 2) + bigRadius;
displacement = bigRadius;
}
cx += displacement;
return cx;
})
.attr("y", function(d, i) {
var cy = verticalGap * i;
if (i == count - 1) {
cy += extendedY - (bigRadius/2);
}
return cy;
})
.text(function(d) {
return d.userName;
});
//__labels
var backbone = treeholder.append("g")
.append("svg:path");
backbone.attr("d", function(d, i) {
var sx = (bigRadius / 2) - (bigRadius / 2);
var tx = (bigRadius / 2) - (bigRadius / 2);
var r = smallRadius;
if (i == 0 || i == count - 1) {
r = bigRadius;
}
var sy = ((r / 2) * i) + (r);
var ty = verticalGap * (count - 2) - arcRadiusLeft;
var dr = 0;
return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty;
});
var displaceYBackboneRight = (bigRadius / 2) + 5;
var backbone = treeholder.append("g")
.append("svg:path");
backbone.attr("d", function(d, i) {
var sx = (bigRadius * 2) + smallRadius/2;
var tx = (bigRadius * 2) + smallRadius/2;
var r = smallRadius;
if (i == 0 || i == count - 1) {
r = bigRadius;
}
var sy = ((r / 2) * i) + (r) + smallRadius + displaceYBackboneRight;
var ty = verticalGap * (count - 2) + extendedY;
var dr = 0;
return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty;
});
//branches on the left
var leftpath = treeholder.append("g").selectAll("path.leftpath")
.data(data)
leftpath
.enter().append("svg:path")
.attr("class", function(d) {
return "leftpath";
});
leftpath.attr("d", function(d, i) {
var sx = 0;
var tx = arcRadiusLeft;
var sy = verticalGap * i - arcRadiusLeft;
var ty = verticalGap * i;
if (i != 0 && i != count - 1) {
return "M" + sx + "," + sy + "A" + arcRadiusLeft + "," + arcRadiusLeft + " 0 0,0 " + tx + "," + ty;
}
});
//branches on the left
var rightpath = treeholder.append("g").selectAll("path.rightpath")
.data(data)
rightpath
.enter().append("svg:path")
.attr("class", function(d) {
return "rightpath";
});
rightpath.attr("d", function(d, i) {
var sx = (bigRadius*2) + (smallRadius/2);
var tx = arcRadiusRight + (bigRadius*2) + (smallRadius/2);
var sy = verticalGap * i + (bigRadius / 2);
var ty = verticalGap * i + arcRadiusRight + (bigRadius / 2);
if (i != 0 && i != count - 1) {
return "M" + sx + "," + sy + "A" + arcRadiusLeft + "," + arcRadiusLeft + " 0 0,0 " + tx + "," + ty;
}
});
答案 2 :(得分:-3)
http://jsfiddle.net/NYEaX/652/
我已经添加了创建萌芽点的路径。如何让它们向下弯曲?另外,我如何确保图像在圆圈内对齐?有些图像会出现裁剪?
path
.enter().append("svg:path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
path.attr("d", function(d, i) {
var sx = bigRad/2 + 10;
var sy = 45*i;
var tx = 120;
var ty = 45*i;
var dx = 45,
dy = 45*i,
dr = Math.sqrt(dx * dx + dy * dy);
if(i!=0){
return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty;
}
});