
前言 1.编写目的
课上作业展示的时候看到有人实现了 【鼠标移动到某节点时,与该结点相关的连线加粗,并且只显示这些连线上的关系文字】的交互功能
我觉得这样能使关系更清楚,图不至于很乱,所以我也想搞,但是搜遍了csdn百度之类的,都没找到该怎么实现这个功能
所以自己试着搞了下,分享一下,仅供参考,求多指教
VSCode、Tomcat10.0.16、d3v3
一、如何实现 1. 结点交互连线变粗
想法是:
1.为连线设置id属性:每根线的id都设置为"edgepath"+源结点index+“-”+终结点index,同样,连线上的文字id也设置为 ”pathtext“+源结点index+“-”+终结点index。
.attr("id",function(d,i){return "edgepath"+d.source.index+"-"+d.target.index;})
2.在结点的交互函数中,添加 通过id选中所有与该结点相关的所有连线,改变其粗细(文字改变透明度)
3.交互结束时改回来
所以主要解决的就是,怎么通过在那一堆不一样的id中找出所有含有该结点index的id
一开始想的是用正则表达式,但是怎么用把我给难住了(还是学艺不精),放弃
一番搜索之后,选择用属性选择器用法参考
首先需要script引用jquery,添加代码
然后通过查找sid/fid修改以该结点为起点/终点的线的属性
var sid="edgepath"+d.index;
var fid="-"+d.index;
$( "path[id*="+sid+"]" ).attr("stroke-width","2px");
$( "path[id*="+fid+"]" ).attr("stroke-width","2px");
通过查找stid/ftid修改以该结点为起点/终点的文字的属性
var stid="pathtext"+d.index;
var ftid="-"+d.index;
$( "textPath[id*="+stid+"]" ).attr("opacity","1");
$( "textPath[id*="+ftid+"]" ).attr("opacity","1");
2. 文本自动换行
在这个代码里还实现了文本自动换行,之前也被这个问题困扰过,分享一下
使用foreignObject实现 代码参考
元素的作用是可以在其中使用具有其它XML命名空间的XML元素,换句话说借助标签,我们可以直接在SVG内部嵌入XHTML元素 功能参考
//添加信息框内文字,用foreignObject自动换行
var tooltip=svg.append("foreignObject")
.attr("width",120)
.attr("height",h)
.attr("x",15)
.attr("y",h-160)
.attr("class","tooltip")
.style('overflow', 'visible')
.append("xhtml:div")
.attr("background-color","white")
.style('position', 'absolute')
.text(d.intro)
.attr("overflow","hidden")
.attr("text-overflow","ellipsis");
3. 背景图片设置透明度
这块给自己记录一下,当时实现的时候也搜了不少帖子
.bodystyle{
background: url(pic/wushi32.webp);
z-index: -999;
bottom: 0;
filter: alpha(opacity=50);
-moz-opacity: 0.5;
-ms-opacity: 0.5;
-webkit-opacity: 0.5;
-o-opacity: 0.5;
opacity: 0.5;
position: absolute;
top: 0;
left: 0;
right: 0;
background-size: 100% 100%;
}
在里,最上面加上
<div class="bodystyle">div>
二、实现效果
三、完整代码
1.用到的数据:
{
"nodes":
[
{"name":"杰洛特","image":"杰洛特.png","intro":"游戏主人公,利维亚的杰洛特是一名猎魔人,在上古语中,他被称为“格温布雷德”,意为“白狼”。他身手敏捷,但同时又深不可测。在他冷漠寡言的外表下,隐藏着如海洋一般辽阔的善良和幽默感。像所有猎魔人一样,杰洛特是职业怪物猎手。他拥有超乎常人的力量和速度,杰洛特在猎魔人的青草试炼中展示出不寻常的忍受能力并存活了下来,从而获得了这些能力。"},
{"name":"希里","image":"希里.png","intro":"希里全名希里雅·欧菲娜·伊伦·雷安伦,她是一位技艺精湛的猎魔人,好几个皇位的继承人,上古之血的最后传承者,无与伦比的魔法之源。她是杰洛特通过意外律获得的养女,同时也是他的天命,他的意外之女,他们两人被命运的枷锁紧紧羁绊在一起。"},
{"name":"叶奈法","image":"叶奈法.png","intro":"叶奈法是一位来自亚甸首都温格堡的女术士,她是术士评议会以及其后的女术士集会所成员。她是杰洛特的挚爱,也担当了希瑞养母的角色。叶奈法与杰洛特的感情始于一场关于迪精的冒险,随着这场冒险,他们二人的命运纠缠在了一起。他们的感情经历了许多风暴,他们仍然互相吸引彼此。"},
{"name":"特莉丝","image":"特莉丝.png","intro":"特莉丝·梅莉葛德曾经是弗尔泰斯特国王的前任宫廷顾问,索登山之战的著名英雄、人称山顶第十四人。她拥有聪慧的头脑、温暖的微笑,以及无与伦比的个人魅力。特莉丝是叶奈法和杰洛特的好友,与后者还有一段坎坷的恋情。特莉丝和希瑞的关系也非常亲近。她在女术士中的影响力跟她对魔法的天份一样,都相当令人瞩目。"},
{"name":"艾瑞汀","image":"艾瑞汀.png","intro":"狂猎之王,在游戏中他所率领的狂猎军团带来了死亡与毁灭,并持续寻找希里的下落与踪迹。"},
{"name":"恩希尔·恩瑞斯","image":"恩希尔·恩瑞斯.png","intro":"“敌人坟头上舞动的白色火焰”,尼弗迦德帝国皇帝,希里的生父。是他策划了对北方王国国王的刺杀,并借此机会发起了第三次北境战争。在他意图统一南北领地的同时,也开始寻找女儿希里以期继承他的皇位。"},
{"name":"维瑟米尔","image":"维瑟米尔.png","intro":"维瑟米尔是全大陆资格最老、最有经验的狩魔猎人,最后的狼学派大师。在故事中维瑟米尔加入了杰洛特的行列,与他一同跨越战争肆虐的泰莫利亚,寻找叶妮法的踪迹。"},
{"name":"丹德里恩","image":"丹德里恩.png","intro":"丹德里恩是著名的吟游诗人、作曲家、歌唱家和作家,也是杰洛特最好的朋友之一。在游戏中,丹德里恩在诺维格瑞城定居,还拥有一家旅馆,并帮助希里寻找修复某种物品的办法。"},
{"name":"卓尔坦","image":"卓尔坦.png","intro":"矮人、佣兵、布伦纳之战生还者、无可救药的乐观主义者、大慈善家、烈酒和黄色歌曲的爱好者、纸牌大师,与杰洛特和丹德里恩是形影不移的好友。"}
],
"links":
[
{"source":0,"target":1,"relation":"义父女"},
{"source":0,"target":2,"relation":"恋人"},
{"source":0,"target":3,"relation":"昔日恋人"},
{"source":5,"target":0,"relation":"委托寻找希里"},
{"source":0,"target":6,"relation":"恩师"},
{"source":0,"target":7,"relation":"好友"},
{"source":0,"target":8,"relation":"好友"},
{"source":7,"target":8,"relation":"好友"},
{"source":1,"target":7,"relation":"朋友"},
{"source":1,"target":5,"relation":"父女"},
{"source":2,"target":1,"relation":"如女儿般疼爱"},
{"source":4,"target":1,"relation":"追寻"},
{"source":5,"target":2,"relation":"委托"}
]
}
2.实现代码:
<html>
<header>
<title>D3力导向图title>
<meta http-equiv="Content-Type" content="text/html"; charset="utf-8">
<style>
p.title {
font-weight: bold;
color:#73345C;
text-align: center;
font-size: 26;
}
.bodystyle{
background: url(pic/wushi32.webp);
z-index: -999;
bottom: 0;
filter: alpha(opacity=50);
-moz-opacity: 0.5;
-ms-opacity: 0.5;
-webkit-opacity: 0.5;
-o-opacity: 0.5;
opacity: 0.5;
position: absolute;
top: 0;
left: 0;
right: 0;
background-size: 100% 100%;
}
.tooltip{
position: absolute;
width: 240px;
height: auto;
font-family: Impact;
font-size: 10px;
text-align: left;
color: #C03747;
border-width: 1px solid black;
background-color: #7FFF00;
border-radius: 3px;
}
style>
<script src="https://code.jquery.com/jquery-3.5.0.js">script>
header>
<body>
<div class="bodystyle">div>
<p class="title">《巫师3:狂猎》主要人物关系梳理p>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8">script>
<script>
// 获取屏幕分辨率
var w=window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
var h=window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
w=w*0.98;
h=h*0.89;
//获取svg,并设置svg为全屏
var svg=d3.select("body")
.append("svg")
.attr("width",w)
.attr("height",h);
//初始化力导向
var force=d3.layout.force()
.charge(-2200) //设置电荷
.linkDistance(200) //连接距离
.size([w,h]);
//设置颜色数据
linkcolor="#73345C";
//nodecolor="#730746";
nodecolor="#C03747"
//设置画图参数
var radius=27;
var img_w=50;
var img_h=50;
//获取数据并画力导向图
var readData=d3.json("wushi3.json",function(error,graph){
console.log(graph);
//将数据信息绑定到力导向中
force.nodes(graph.nodes)
.links(graph.links)
.start();
//绘制箭头
var defs=svg.append("defs");
var arrowMarker=defs.append("marker")
.attr("id","arrow")
.attr("markerUnits","strokeWidth")
.attr("markerWidth",8)
.attr("markerHeight",8)
.attr("viewBox","0 0 8 8")
.attr("refX",17+radius/8-2) //实际上是radius/strokeWidth
.attr("refY",4)
.attr("orient","auto")
.attr("fill",linkcolor);
var arrow_path="M0,2 L8,4 L0,6 L0,0";
arrowMarker.append("path")
.attr("d",arrow_path);
//画线
var link=svg.selectAll(".link")
.data(graph.links)
.enter()
.append("path")
.attr("class","link")
.attr("id",function(d,i){
//console.log(d);
return "edgepath"+d.source.index+"-"+d.target.index;})
.attr("stroke-width","1px")
.attr("stroke",linkcolor)
.attr("marker-end","url(#arrow)");//路径终点添加箭头
//添加线上文字
var pathtext=svg.selectAll(".pathText")
.data(graph.links)
.enter()
.append("text")
.attr("class","pathText")
.append("textPath")
.attr("id",function(d,i){
return "pathtext"+d.source.index+"-"+d.target.index;
})
.attr("text-anchor","middle")
.attr("startOffset","50%")
.attr("xlink:href",function(d,i){return "#edgepath"+d.source.index+"-"+d.target.index;})
.attr("fill",linkcolor)
.attr("opacity",0)
.attr("font-size",12)
.attr("font-weight","bold")
.text(function(d){return d.relation;});
//画点
var node=svg.selectAll(".node")
.data(graph.nodes)
.enter()
.append("circle")
.attr("class","node")
.attr("r",radius)
.attr("cx",100)
.attr("cy",100)
.attr("stroke","DarkGray")
.attr("stroke-width",1)
.attr("fill",function(d,i){
//创建圆形图片
var defs1=svg.append("defs")
.attr("id","imgdefs");
var catpattern=defs1.append("pattern")
.attr("id","catpattern"+i)
.attr("height",1)
.attr("width",1);
catpattern.append("image")
.attr("x",-(img_w/2-radius+5.8))
.attr("y",-(img_h/2-radius+3.5))
.attr("width",img_w+11)
.attr("height",img_h+6)
.attr("xlink:href","pic/"+d.image);
return "url(#catpattern"+i+")";
})
.on("mouseover",function(d,i){
//添加信息框
var bottomcolor=svg.append("rect")
.attr("class","bottomcolor")
.attr("fill","yellow")
.attr("fill-opacity",0.5)
.attr("width",120*2+10)
.attr("height",150)
.attr("x",10)
.attr("y",h-160)
//.attr("x",(d3.event.pageX-5)+"px")
//.attr("y",(d3.event.pageY-10)+"px")
//.attr("rx",5)
//.attr("ry",5);
//添加信息框内文字,用foreignObject自动换行
var tooltip=svg.append("foreignObject")
.attr("width",120)
.attr("height",h)
.attr("x",15)
.attr("y",h-160)
//.attr("x",d3.event.pageX+"px")
//.attr("y",(d3.event.pageY-10)+"px")
.attr("class","tooltip")
.style('overflow', 'visible')
.append("xhtml:div")
.attr("background-color","white")
.style('position', 'absolute')
.text(d.intro)
.attr("overflow","hidden")
.attr("text-overflow","ellipsis");
//想整个该结点和其线和线上文字的交互
//为了这个效果把线和线上文字的id改成了edgepath+源index+-+终index / pathtext+源index+-+终index
//以下代码使用了属性选择器,前提是需要script引用jquery
var sid="edgepath"+d.index;
var fid="-"+d.index;
$( "path[id*="+sid+"]" ).attr("stroke-width","2px");
$( "path[id*="+fid+"]" ).attr("stroke-width","2px");
var stid="pathtext"+d.index;
var ftid="-"+d.index;
$( "textPath[id*="+stid+"]" ).attr("opacity","1");
$( "textPath[id*="+ftid+"]" ).attr("opacity","1");
})
.on("mouseout",function(d){
d3.select(".tooltip")
.remove();
d3.select(".bottomcolor")
.remove();
//线改回来
var sid="edgepath"+d.index;
var fid="-"+d.index;
$( "path[id*="+sid+"]" ).attr("stroke-width","1.5px");
$( "path[id*="+fid+"]" ).attr("stroke-width","1.5px");
//文字改回来
var stid="pathtext"+d.index;
var ftid="-"+d.index;
$( "textPath[id*="+stid+"]" ).attr("opacity","0");
$( "textPath[id*="+ftid+"]" ).attr("opacity","0");
})
.call(force.drag); //添加拖拽
//添加节点文字
var nodetext=svg.selectAll(".nodeText")
.data(graph.nodes)
.enter()
.append("text")
.attr("class","nodeText")
.attr("fill",nodecolor)
.attr("font-size",12)
.attr("text-anchor","middle")
.attr("font-weight","bold")
.attr("stroke","red")
.attr("stroke-width",0.05)
//.attr("dx","-1.5em")
.attr("dy",radius+10)
.text(function(d){return d.name;});
//设置刷新方法
force.on("tick",function(){ //三种刷新方法(start\end\tick),tick是每时每刻刷新
node.attr("cx",function(d){return d.x;})
.attr("cy",function(d){return d.y;});
link.attr("d",function(d){
//var dx=d.target.x-d.source.x;
//var dy=d.target.y-d.source.y;
return "M"+d.source.x+","+d.source.y+"L"+d.target.x+","+d.target.y;
});
nodetext.attr("x",function(d){return d.x;})
.attr("y",function(d){return d.y;});
})
})
//数据读取完毕后,再进行绘制
Promise.all([readData]).then(function(results){
addinfo();
});
function addinfo()
{
var myinfo=svg.append("text")
.attr("fill","purple")
.attr("x",w-160)
.attr("y",h-20)
.style("fill-opacity",0.7)
.text("学号")
.append("tspan")
.attr("x",w-160)
.attr("dy","1em")
.text("姓名");
var datafrom=svg.append("text")
.attr("fill","purple")
.attr("font-size",13)
.attr("text-anchor","middle")
.attr("x",w/2)
.attr("y",13)
.style("fill-opacity",0.7)
.text("数据源:百度百科")
}
script>
body>
html>
总结
隔了好久,好多细节记不清了,还是得及时记录啊
录屏gif用的是screentogif这个软件,刚下好还没摸索透怎么用,但是好喜欢哈哈哈,成为了我写这的动力了可以说是 下载教程可以看它
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)