如果用d3画人物图的话,这个问题会经常遇到。
例如:人物A和人物B之间的关系有:同学、亲戚、同事;人物A和人物C之间的关系有:同学、同事;
需求:不想看到亲戚关系。(为什么会有这样的需求:节点多了后,各种复杂关系都显示出来了,看着就密密麻麻,隐藏部分关系后,看起来就清爽多了)
原始图: 隐藏“亲戚”关系后的图:
要实现这样的隐藏部分关系效果的解决方案:
(1)修改json文件:方法:把“亲戚”所在的关系进行处理,relation.replace("亲戚",“”),重新加载一次json文件。
缺点:界面相当于重新绘制了一遍,用户体验不太好,尤其是你的人物关系图是动态的时候。
难易程度:简单
(2)界面刷新:方法:遍历每条线上的关系,找到“亲戚”所在的关系,标记“亲戚”(添加一个类),然后设置该类不可见。
优点:界面没有进行刷新,用户体验比较好
优点:要给一个文字关系添加一个类
难易程度:困难
以上是我的两个解决问题的思路。
方案一代码:
<html>
<head>
<meta charset="utf-8">
<title>隐藏部分关系通过切换json文件</title>
<style>
.nodetext {
font-size: 12px ;
font-family: SimSun;
fill:#000000;
}
.linetext {
font-size: 12px ;
font-family: SimSun;
fill:#1f77b4;
}
.circleImg {
stroke: #ff7f0e;
stroke-width: 1.5px;
}
</style></head>
<body>
<div class="lay" style="position: fixed;top: 20px;left: 20px;">
<button id="button">隐藏 亲戚 关系</button>
</div>
<script src="d3.js" type="text/javascript" charset="utf-8"></script>
<script>
var data1 = "data1.json";
var data2 = "data2.json";
var width = 900;
var height = 800;
var img_w = 77;
var img_h = 80;
var radius = 30;
var svg = d3.select("body").append("svg")
.attr("width",width)
.attr("height",height);
function updatesvg(soursefile){
d3.select("svg").selectAll("*").remove();
d3.json(soursefile,function(error,root){
if( error ){
return console.log(error);
}
console.log(root);
//D3力导向布局
var force = d3.layout.force()
.nodes(root.nodes)
.links(root.edges)
.size([width,height])
.linkDistance(200)
.charge(-1500)
.start();
//边
var edges_line = svg.selectAll("line")
.data(root.edges)
.enter()
.append("line")
.style("stroke","#ccc")
.style("stroke-width",1);
//边上的文字(人物之间的关系)
var edges_text = svg.selectAll(".linetext")
.data(root.edges)
.enter()
.append("text")
.attr("class","linetext")
.style("display","block")
.style("color","red")
.text(function(d){
return d.relation;
});
// 圆形图片节点(人物头像)
var nodes_img = svg.selectAll("image")
.data(root.nodes)
.enter()
.append("circle")
.attr("class", "circleImg")
.attr("r", radius)
.attr("fill", function(d, i){
//创建圆形图片
var defs = svg.append("defs").attr("id", "imgdefs")
var catpattern = defs.append("pattern")
.attr("id", "catpattern" + i)
.attr("height", 1)
.attr("width", 1)
catpattern.append("image")
.attr("x", - (img_w / 2 - radius))
.attr("y", - (img_h / 2 - radius))
.attr("width", img_w)
.attr("height", img_h)
.attr("xlink:href", d.image)
return "url(#catpattern" + i + ")";
})
.call(force.drag);
var text_dx = -20;
var text_dy = 20;
var nodes_text = svg.selectAll(".nodetext")
.data(root.nodes)
.enter()
.append("text")
.attr("class","nodetext")
.attr("dx",text_dx)
.attr("dy",text_dy)
.text(function(d){
return d.name;
});
force.on("tick", function(){
//更新连接线的位置
edges_line.attr("x1",function(d){ return d.source.x; });
edges_line.attr("y1",function(d){ return d.source.y; });
edges_line.attr("x2",function(d){ return d.target.x; });
edges_line.attr("y2",function(d){ return d.target.y; });
//更新连接线上文字的位置
edges_text.attr("x",function(d){ return (d.source.x + d.target.x) / 2 ; });
edges_text.attr("y",function(d){ return (d.source.y + d.target.y) / 2 ; });
//更新结点图片和文字
nodes_img.attr("cx",function(d){ return d.x });
nodes_img.attr("cy",function(d){ return d.y });
//联系
nodes_text.attr("x",function(d){ return d.x });
nodes_text.attr("y",function(d){ return d.y + img_w/2; });
});
});
}
updatesvg(data1);
var btn = document.getElementById("button");
btn.onclick = function(){
//这里应该要遍历json文件,找到"亲戚"再删除后生成新的json,再作为参数传递
var data3 = "data3.json";
updatesvg(data3);
}
</script>
</body>
</html>
通过改动json文件来实现部分隐藏,此时要复原就基本不可能了。
于是测试第二种方案。目前我把包含“亲戚”的关系都隐藏起来了,并未实现部分隐藏,希望大家改进改进,共同学习。如果我解决了,也会及时更新。
以下是代码:
<html>
<head>
<meta charset="utf-8">
<title>隐藏部分关系</title>
<style>
.nodetext {
font-size: 12px ;
font-family: SimSun;
fill:#000000;
}
.linetext {
font-size: 12px ;
font-family: SimSun;
fill:#1f77b4;
}
.baohan{
display: none;
opacity: 0;
}
.circleImg {
stroke: #ff7f0e;
stroke-width: 1.5px;
}
</style></head>
<body>
<div class="lay" style="position: fixed;top: 20px;left: 20px;">
<button id="button3">隐藏 亲戚 关系</button>
<button id="button4">显示 亲戚 关系</button>
</div>
<script src="d3.js" type="text/javascript" charset="utf-8"></script>
<script>
var data1 = "data1.json";
var data2 = "data2.json";
var width = 900;
var height = 800;
var img_w = 77;
var img_h = 80;
var radius = 30;
var svg = d3.select("body").append("svg")
.attr("width",width)
.attr("height",height);
function updatesvg(soursefile){
d3.select("svg").selectAll("*").remove();
d3.json(soursefile,function(error,root){
if( error ){
return console.log(error);
}
console.log(root);
//D3力导向布局
var force = d3.layout.force()
.nodes(root.nodes)
.links(root.edges)
.size([width,height])
.linkDistance(200)
.charge(-1500)
.start();
//边
var edges_line = svg.selectAll("line")
.data(root.edges)
.enter()
.append("line")
.style("stroke","#ccc")
.style("stroke-width",1);
//边上的文字(人物之间的关系)
var edges_text = svg.selectAll(".linetext")
.data(root.edges)
.enter()
.append("text")
.attr("class","linetext")
.style("display","block")
.style("color","red")
.text(function(d){
return d.relation;
});
// 圆形图片节点(人物头像)
var nodes_img = svg.selectAll("image")
.data(root.nodes)
.enter()
.append("circle")
.attr("class", "circleImg")
.attr("r", radius)
.attr("fill", function(d, i){
//创建圆形图片
var defs = svg.append("defs").attr("id", "imgdefs")
var catpattern = defs.append("pattern")
.attr("id", "catpattern" + i)
.attr("height", 1)
.attr("width", 1)
catpattern.append("image")
.attr("x", - (img_w / 2 - radius))
.attr("y", - (img_h / 2 - radius))
.attr("width", img_w)
.attr("height", img_h)
.attr("xlink:href", d.image)
return "url(#catpattern" + i + ")";
})
.call(force.drag);
var text_dx = -20;
var text_dy = 20;
var nodes_text = svg.selectAll(".nodetext")
.data(root.nodes)
.enter()
.append("text")
.attr("class","nodetext")
.attr("dx",text_dx)
.attr("dy",text_dy)
.text(function(d){
return d.name;
});
force.on("tick", function(){
//更新连接线的位置
edges_line.attr("x1",function(d){ return d.source.x; });
edges_line.attr("y1",function(d){ return d.source.y; });
edges_line.attr("x2",function(d){ return d.target.x; });
edges_line.attr("y2",function(d){ return d.target.y; });
//更新连接线上文字的位置
edges_text.attr("x",function(d){ return (d.source.x + d.target.x) / 2 ; });
edges_text.attr("y",function(d){ return (d.source.y + d.target.y) / 2 ; });
//更新结点图片和文字
nodes_img.attr("cx",function(d){ return d.x });
nodes_img.attr("cy",function(d){ return d.y });
//联系
nodes_text.attr("x",function(d){ return d.x });
nodes_text.attr("y",function(d){ return d.y + img_w/2; });
});
document.getElementById("button3").onclick = function(){
d3.selectAll(".linetext").each(function(d,i){
console.log("关系:"+d.relation);
if(d.relation.indexOf("亲戚")>-1){
console.log("包含:亲戚");
var result = d.relation.replace("亲戚", "");
console.log("替换后的结果:"+result);
d3.select(this).attr("class","linetext baohan")
}
})
}
document.getElementById("button4").onclick = function(){
d3.selectAll(".linetext").each(function(d,i){
console.log("关系:"+d.relation);
if(d.relation.indexOf("亲戚")>-1){
console.log("包含:亲戚");
var result = d.relation.replace("亲戚", "");
console.log("替换后的结果:"+result);
d3.select(this).attr("class","linetext")
}
})
}
});
}
updatesvg(data1);
</script>
</body>
</html>
附:json文件
data1.json:
{
"nodes":[
{ "name": "测试1" , "image" : "renwu.jpg" },
{ "name": "测试2" , "image" : "renwu.jpg" },
{ "name": "测试3" , "image" : "renwu.jpg" }
],
"edges":[
{ "source": 0 , "target": 1 , "relation":"同学 同事 亲戚" },
{ "source": 0 , "target": 2 , "relation":"同学 同事 " }
]
}
data3.json:
{
"nodes":[
{ "name": "测试1" , "image" : "renwu.jpg" },
{ "name": "测试2" , "image" : "renwu.jpg" },
{ "name": "测试3" , "image" : "renwu.jpg" }
],
"edges":[
{ "source": 0 , "target": 1 , "relation":"同学 同事" },
{ "source": 0 , "target": 2 , "relation":"同学 同事" }
]
}
以上是我在7月25做的,但是并没有实现我想要的结果,今天实在是气不过,花了时间,效果还没有出来。于是我又做了调整,终于实现了预想的设计图:
代码附上:
<html>
<head>
<meta charset="utf-8">
<title>隐藏部分关系</title>
<style>
.nodetext {
font-size: 12px ;
font-family: SimSun;
fill:#000000;
}
.linetext {
font-size: 12px ;
font-family: SimSun;
fill:#1f77b4;
}
.circleImg {
stroke: #ff7f0e;
stroke-width: 1.5px;
}
.btn{
position: fixed;
top: 50px;
left: 20px;
}
.btn2{
position: fixed;
top: 100px;
left: 20px;
}
.btn3{
position: fixed;
top: 150px;
left: 20px;
}
.btn4{
position: fixed;
top: 200px;
left: 20px;
}
.baohan{
display: none;
opacity: 0;
}
.unbaohan{
display: block;
opacity: 1;
}
</style></head>
<body>
<p>我想点击按钮,把“挚友”关系删掉</p>
<hr />
<button id="btn" class="btn">隐藏 挚友 关系</button>
<button id="btn2" class="btn2">显示 挚友 关系</button>
<button id="btn3" class="btn3">隐藏 同学 关系</button>
<button id="btn4" class="btn4">显示 同学 关系</button>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var width = 900;
var height = 800;
var img_w = 77;
var img_h = 80;
var radius = 30;
var svg = d3.select("body").append("svg")
.attr("width",width)
.attr("height",height);
d3.json("json/relation.json",function(error,root){
if( error ){
return console.log(error);
}
console.log(root);
createD3(root);
});
function createD3(root){
//D3力导向布局
var force = d3.layout.force()
.nodes(root.nodes)
.links(root.edges)
.size([width,height])
.linkDistance(200)
.charge(-1500)
.start();
//边
var edges_line = svg.selectAll("line")
.data(root.edges)
.enter()
.append("line")
.style("stroke","#ccc")
.style("stroke-width",1);
//边上的文字(人物之间的关系)
var edges_text = svg.selectAll(".linetext")
.data(root.edges)
.enter()
.append("text")
.attr("class","linetext")
.style("display","block")
.style("color","red")
.text(function(d){
return d.relation;
});
// 圆形图片节点(人物头像)
var nodes_img = svg.selectAll("image")
.data(root.nodes)
.enter()
.append("circle")
.attr("class", "circleImg")
.attr("r", radius)
.attr("fill", function(d, i){
//创建圆形图片
var defs = svg.append("defs").attr("id", "imgdefs")
var catpattern = defs.append("pattern")
.attr("id", "catpattern" + i)
.attr("height", 1)
.attr("width", 1)
catpattern.append("image")
.attr("x", - (img_w / 2 - radius))
.attr("y", - (img_h / 2 - radius))
.attr("width", img_w)
.attr("height", img_h)
.attr("xlink:href", d.image)
return "url(#catpattern" + i + ")";
})
.call(force.drag);
var text_dx = -20;
var text_dy = 20;
var nodes_text = svg.selectAll(".nodetext")
.data(root.nodes)
.enter()
.append("text")
.attr("class","nodetext")
.attr("dx",text_dx)
.attr("dy",text_dy)
.text(function(d){
return d.name;
});
force.on("tick", function(){
//更新连接线的位置
edges_line.attr("x1",function(d){ return d.source.x; });
edges_line.attr("y1",function(d){ return d.source.y; });
edges_line.attr("x2",function(d){ return d.target.x; });
edges_line.attr("y2",function(d){ return d.target.y; });
//更新连接线上文字的位置
edges_text.attr("x",function(d){ return (d.source.x + d.target.x) / 2 ; });
edges_text.attr("y",function(d){ return (d.source.y + d.target.y) / 2 ; });
//更新结点图片和文字
nodes_img.attr("cx",function(d){ return d.x });
nodes_img.attr("cy",function(d){ return d.y });
//联系
nodes_text.attr("x",function(d){ return d.x });
nodes_text.attr("y",function(d){ return d.y + img_w/2; });
});
}
document.getElementById("btn").onclick = function(){
xx("挚友",true)
}
document.getElementById("btn2").onclick = function(){
xx("挚友",false)
}
document.getElementById("btn3").onclick = function(){
xx("同学",true)
}
document.getElementById("btn4").onclick = function(){
xx("同学",false)
}
function xx(info,flag){
d3.selectAll(".linetext").each(function(d,i){
var old = d3.select(this).text();
if(flag){
if(d.relation.indexOf(info)>-1){
var result = old.replace(info,"");
d3.select(this).text(result);
}
}else{
if(d.relation.indexOf(info)>-1){
if(old.indexOf(info)>-1){
d3.select(this).text(old);
}else{
d3.select(this).text(old+" "+info);
}
}
}
})
}
</script>
</body>
</html>
其中xx函数是实现的核心函数。效果非常好,高兴之余,也分享给大家!