<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<style>
html,body,#kgDiv{
height: 100%;
width: 100%;
margin: 0;
}
svg circle{
fill: white;
transition: r 0.2s;
}
svg circle.node.active{
r:30;
}
svg circle.node.centerNode{
fill: #195ff3;
filter: url("#nodeShadow");
}
svg circle.node.centerNode.active{
r:55;
}
svg .nodeImage{
height: 20px;
width: 20px;
}
svg .nodeText{
fill:#000;
font-size: 14px;
}
svg .nodeText.active{
font-size: 20px;
}
svg .linkLine{
stroke: #ccc;/*线的颜色*/
stroke-width:1;/*线的宽度*/
}
svg .linkLine.active{
stroke:#1768fd;
}
svg .linkTextRect{
height: 25;
fill: #fff;
filter: url("#linkTextRectShadow");
ry:5;
rx:5;
}
svg .linkText{
fill: #000;
font-size:12px;
fill-opacity: 0;
}
svg .linkText.active{
fill-opacity: 1;
}
svg .linkMarker{
fill: #ccc;
stroke-width:2;
}
svg .linkMarker.active{
fill: #1768fd;
}
/*滤镜svg不显示*/
svg.filterSvg{
height: 0;
width: 0;
}
/*右键菜单的样式*/
#contextmenu{
display: none;
background-color: white;
padding: 10px 0 0 10px;
width: 300px;
height: 283px;
box-shadow: 0px 0px 23px 1px rgba(0, 0, 0, 0.2);
position: absolute; /*自定义菜单相对与body元素进行定位*/
border-radius: 5px;
}
#contextmenu .contextmenuHead{
height:21px;
margin-right: 15px;
}
#contextmenu .contextmenuHead label{
font-size:14px;
float: left;
color: #000;
display: block;
}
#contextmenu .contextmenuHead label span{
display: block;
float: left;
height: 14px;
width: 5px;
background-color: #195FF3;
margin: 3px 5px 0 0;
border-radius: 3px;
}
#contextmenu .contextmenuHead .operation{
float: right;
}
#contextmenu .contextmenuHead .operation span{
display: inline-block;
margin-right: 10px;
cursor: pointer;
}
#contextmenu .contextmenuHead .operation span.toview{
height: 12px;
width: 12px;
background: url('./toView.png');
}
#contextmenu .contextmenuHead .operation span.toview:hover{
background: url('./toView_select.png');
}
#contextmenu .contextmenuHead .operation span.collection{
height: 14px;
width: 14px;
background: url('./collection.png');
}
#contextmenu .contextmenuHead .operation span.collection:hover{
background: url('./collection_select.png');
}
#contextmenu .contentmenucontent{
clear: both;
padding:15px 10px 0 10px;
height: 247px;
overflow: auto;
}
#contextmenu .contentmenucontent div{
line-height: 25px;
font-size: 14px;
color: #666;
}
#contextmenu .contentmenucontent div.name{
color: #000;
font-size: 16px;
margin-bottom: 10px;
font-weight: bold;
}
/*节点菜单的样式*/
#nodeOperationMenu{
position:absolute;
z-index:10;
}
#nodeOperationMenu .ring{
display:none;
position:fixed;
width:114px;
height:114px;
border-radius:70px;
border: 6px solid #a4acba
}
#nodeOperationMenu .ring .btnList{
list-style:none;
}
#nodeOperationMenu .ring .btnList li{
position: absolute;
box-shadow: 0 4px 5px 0 rgba(24,49,88,.2);
width: 40px;
height: 40px;
color: #456;
font-size: 12px;
line-height: 100%;
border: 1px solid #dfe7f5;
border-radius: 30px;
text-align: center;
line-height: 38px;
background: #fff;
}
#nodeOperationMenu .ring .btnList li:nth-child(1){
left: 37px;
top: -26px;
}
#nodeOperationMenu .ring .btnList li:nth-child(2){
left: 81.5477px;
top: -7.54773px;
}
#nodeOperationMenu .ring .btnList li:nth-child(3){
left: 100px;
top: 37px;
}
#nodeOperationMenu .ring .btnList li:nth-child(4){
left: 81.5477px;
top: 81.5477px;
}
#nodeOperationMenu .ring .btnList li:nth-child(5){
left: 37px;
top: 100px;
}
#nodeOperationMenu .ring .btnList li:nth-child(6){
left: -7.54773px;
top: 81.5477px;
}
#nodeOperationMenu .ring .btnList li:nth-child(7){
left: -26px;
top: 37px;
}
#nodeOperationMenu .ring .btnList li:nth-child(8){
left: -7.54773px;
top: -7.54773px;
}
</style>
</head>
<body>
<div id="kgDiv">
</div>
<svg class="filterSvg">
<defs>
<filter id="linkTextRectShadow" y="-10" height="40" x="-10" width="150">
<feOffset in="SourceGraphic" dx="1" dy="1" result="offset2" />
<feColorMatrix type="matrix" in="offset2" result="matrixOut"
values="0.81 0 0 0 0
0 0.87 0 0 0
0 0 0.98 0 0
0 0 0 1 0">
</feColorMatrix>
<feGaussianBlur in="matrixOut" stdDeviation="3" result="blur2"/>
<feMerge>
<feMergeNode in="blur2" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<filter id="nodeShadow" y="-50" height="150" x="-50" width="150">
<feOffset in="SourceGraphic" dx="0" dy="0" result="offset2" />
<feColorMatrix type="matrix" in="offset2" result="matrixOut"
values="0.54 0 0 0 0
0 0.68 0 0 0
0 0 0.97 0 0
0 0 0 1 0">
</feColorMatrix>
<feGaussianBlur in="matrixOut" stdDeviation="6" result="blur2"/>
<feMerge>
<feMergeNode in="blur2" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
</svg>
<div id="contextmenu">
<!-- <div class="contextmenuHead">
<label>
<span></span>
基本信息
</label>
<div class="operation">
<span title="查看简历" class="toview" id="toViewResume"></span>
<span title="收藏简历" class="collection" id="collectResume"></span>
</div>
</div>
<div class='contentmenucontent'>
<div class="name">邵先生</div>
<div><span>出生年月:</span></div>
<div><span>性别:</span></div>
<div><span>年龄:</span></div>
<div><span>现居:</span></div>
<div><span>教育经历:</span></div>
<div><span>工作经历:</span></div>
</div> -->
</div>
<!--节点菜单-->
<div id="nodeOperationMenu">
<div class="ring">
<ul class="btnList">
<li>切换</li>
<li>备注</li>
<li>关注</li>
<li>扩展</li>
<li>探寻</li>
<li>路径</li>
<li>隐藏</li>
<li>详情</li>
<div>
</div>
</div>
</body>
<script>
//数据
var nodes = [{id:1,name:'小黄',img:'./icon1.png'},
{id:2,name:'小红',img:'./icon_lock.png'},
{id:3,name:'小蓝',img:'./icon_2.png'},
{id:4,name:'小绿',img:'./icon_3.png'}];
var links = [{source:1,target:2,relation:'朋友'},{source:1,target:3,relation:'老师历史老师老师'},{source:1,target:4,relation:'学生'}];
var force;
var linkLine;
var linkText;
var linkTextRect;
var linkMarker;
//tick表示运动进行中每更新一帧时
function ticked(){
//更新连接线的位置
linkLine.attr('d',function(d){
var path = 'M ' + d.source.x + ' ' + d.source.y
+ ' L ' + d.target.x + ' ' + d.target.y;
return path;
});
//更新连接线上的矩形的位置
linkTextRect.attr('x',function(d){
return (d.source.x + d.target.x) / 2 - 10;
}).attr('y',function(d){
return (d.source.y + d.target.y) / 2 - 16.5;
}).style('width',function(d){
return d.relation.length * 12 + 20;
});
//更新连接线上的文字的位置
linkText.attr('x',function(d){
return (d.source.x + d.target.x) / 2;
}).attr('y',function(d){
return (d.source.y + d.target.y) / 2;
});
//更新节点和文字
d3.selectAll('circle')
.attr('cx',function(d){
return d.x
})
.attr('cy',function(d){return d.y});
d3.selectAll('.nodeText').attr('x',function(d){return d.x}).attr('y',function(d){if(d.id === 1){return d.y + 75} return d.y + 50});
d3.selectAll('.nodeImage').attr('x',function(d){return d.x - 10}).attr('y',function(d){return d.y - 10});
}
//鼠标悬浮到节点、节点名称、节点图片上时 节点、节点名称、连线的样式随之变换
function mouseover(self,d){
d3.select(self.parentNode).select('.node').classed('active',true);
d3.select(self.parentNode).select('text').classed('active',true);
linkLine.classed("active",function(link){
if(link.source === d || link.target === d){
return true;
}else{
return false;
}
});
linkMarker.classed('active',function(link){
if(link.source === d || link.target === d){
return true;
}else{
return false;
}
});
}
//鼠标从节点、节点名称、节点图片上移开时 节点、节点名称、连线的样式还原
function mouseout(self){
d3.select(self.parentNode).select('.node').classed('active',false);
d3.selectAll('.nodeText').classed('active',false);
linkLine.classed('active',false);
linkMarker.classed('active',false);
}
//右键节点、节点名称、节点图片显示菜单
function contextmenu(self,d){
mouseover(self,d);
//停止布局
force.stop();
/*var menu = document.querySelector("#contextmenu");
menu.style.left = d3.event.clientX + 'px';
menu.style.top = d3.event.clientY +'px';
//显示右键菜单
document.querySelector('#contextmenu').style.display='block';
document.querySelector('#contextmenu').innerHTML = '<div class="contextmenuHead"><label><span></span>基本信息</label><div class="operation"><span title="查看简历" class="toview" id="toViewResume"></span><span title="收藏简历" class="collection" id="collectResume"></span></div></div><div class="contentmenucontent"><div class="name">'+ d.name +'</div><div><span>出生年月:</span></div><div><span>性别:</span></div><div><span>年龄:</span></div><div><span>现居:</span></div><div><span>教育经历:</span></div><div><span>工作经历:</span></div></div>';
document.getElementById("toViewResume").onclick = function (ev) {console.log("查看简历");};
document.getElementById("collectResume").onclick = function (ev) {console.log("收藏简历")}; */
var e = d3.event;
var x = $(d3.event.target).offset();
console.log(d3.event,x,"d3.event");
//显示右键菜单
//document.querySelector('#nodeOperationMenu > .ring').style.display = 'block';
//document.querySelector('#nodeOperationMenu > .ring').style.top = x.top + 25 + 25 - 63;
//document.querySelector('#nodeOperationMenu > .ring').style.left = x.left + 25 + 25 - 63;
}
//双击节点 以该节点为中心重新生成图谱
function dblclickNode(d){
//双击中心节点无操作。
if(d.id == 1){
return;
}
//停止布局
force.stop();
var nodes = [{id:1,name:d.name,img:d.img},
{id:5,name:'小花',img:'./icon_lock.png'},
{id:6,name:'小草',img:'./icon_2.png'},
{id:7,name:'小衰',img:'./icon_3.png'}];
var links = [{source:1,target:5,relation:'朋友'},{source:1,target:7,relation:'历史老师'},{source:1,target:6,relation:'学生'}];
kgBuider(nodes,links);
}
//生成图谱
function kgBuider(nodes,links){
//生成图谱之前先清空之前的图谱
document.getElementById('kgDiv').textContent = '';
var scope = document.getElementById('kgDiv');
scope.oncontextmenu = function (e) {
e.preventDefault();
};
scope.onclick = function (e) {
//关闭右键菜单
document.querySelector('#contextmenu').style.display='none';
};
//图谱的宽高
var height = document.body.clientHeight;
var width = document.body.clientWidth;
//设置画布
var svg = d3.select("#kgDiv").append("svg")
.style("height","100%").style("width","100%");
//设置力导向图
force = d3.forceSimulation()
//设置画板中心点
.force('center',d3.forceCenter(width/2,height/2))
//设置节点间的作用力
.force('charge',d3.forceManyBody().strength(-200))
.on('tick',ticked)
//设置node和link
.nodes(nodes)
.force('link',d3.forceLink(links).id(function(d){return d.id}).distance(211).strength(0.2))
.restart();;
//设置缩放,svg嵌套g标签,缩放在g标签上进行
var g = svg.append('g');
var zoomObj = d3.zoom()
//监听zoom事件,缩放时调用
.on('zoom',function(){
var transform = d3.event.transform;
g.attr('transform',transform);
})
//监听end事件,缩放结束时调用
.on('end',function(){
var transform = d3.event.transform;
g.attr('transform',transform);
});
svg.call(zoomObj).on("dblclick.zoom", null);//取消双击缩放
var relationship = g.append('g').attr('class','relationships').selectAll('.relationship').data(links).enter().append('g').attr('class','relationship');
//设置连线上的箭头
linkMarker = relationship.append('marker')
.attr('class','linkMarker')
.attr('id',function(d,i){
return 'marker_' + i;
})
.attr('markerUnits','userSpaceOnUse')//设置为strokeWidth箭头会随着线的粗细发生变化,userSpaceOnUse用于确定marker是否进行缩放。取值strokeWidth和userSpaceOnUse,
.attr('viewBox','0 -5 10 10')
.attr('refX',30)//箭头坐标
.attr('refY',0)
.attr('markerWidth',12)//标识的大小
.attr('markerHeight',12)
.attr('orient','auto')//绘制方向 可设定为:auto(自动确认方向)或者 角度值
//设置箭头的路径
linkMarker.append('path')
.attr('d','M0,-5L10,0L0,5');//箭头的路径
//设置连线
linkLine = relationship
.append('path')
.attr('class','linkLine')
//设置线的末尾为箭头
.attr('marker-end',function(d,i){
return 'url(#marker_' + i + ')';
})
//设置一个矩形,将文本放到其中
linkTextRect = relationship
.append('rect')
.attr('class','linkTextRect')
//设置连接线上的文本
linkText = relationship
.append('text')
.attr('class','linkText active')
.text(function(d){
//连线文字为节点之间的关系
return d.relation;
})
//设置拖拽
var drag = d3.drag()
.on('start',function(d,i){
if(!d3.event.active){
//拖拽开始回调
force.alphaTarget(0.3).restart();//可以使用在交互时重新启动仿真,重新进行布局。必须设置,不然拖动不了。
}
d.fixed = false;//偏移后是否固定不动
d.fx = d.x;//记录当前默认位置(x - 节点当前的 x-位置,如果要为某个节点设置默认的位置,则需要为该节点设置如下两个属性:fx =x位置)
d.fy = d.y;
})
.on('drag',function(d,i){
d.fx = d3.event.x;
d.fy = d3.event.y;
})
.on('end',function(d,i){
//拖动结束后
if(!d3.event.active){
force.alphaTarget(0);
}
d.fx = null;
d.fy = null;
})
var node = g.append('g').attr('class','nodeGroup').selectAll('.node').data(nodes).enter().append('g');
//设置节点
node.append('circle')
.attr('class',function(d,i){
if(d.id === 1){
return 'node centerNode';
}else{
return 'node';
}
})
.attr('r',function(d,i){
if(d.id === 1){
return 50;
}else{
return 25;
}
})
.style('fill',function(d){
if(d.name == '小黄'){
return 'yellow';
}else if(d.name == '小红'){
return 'red';
}else if(d.name == '小蓝'){
return 'blue';
}else if(d.name == '小绿'){
return 'green';
}
})
//.on('mouseover',function(d){mouseover(this,d); /*d3.select(this).raise()*/})
//.on("mouseout",function(d){mouseout(this)})
.on('contextmenu',function(d){contextmenu(this,d);})
//.on('dblclick',function(d){dblclickNode(d);})
.call(drag);
//设置节点文字
node.append('text').attr('text-anchor','middle')
.attr('class', 'nodeText')
.text(function (d,i) { return d.name })
//.on('mouseover', function(d){mouseover(this,d); /*d3.select(this).raise()*/})
//.on('mouseout', function(d){mouseout(this)})
.on('contextmenu',function(d){contextmenu(this,d);})
//.on('dblclick',function(d){dblclickNode(d);})
.call(drag);
//设置节点图片
node.append('image').attr('class','nodeImage')
.attr('href',function(d,i){
return d.img;
})
//.on('mouseover', function(d){mouseover(this,d); /*d3.select(this).raise()*/})
//.on('mouseout', function(d){mouseout(this)})
.on('contextmenu',function(d){contextmenu(this,d);})
//.on('dblclick',function(d){dblclickNode(d);})
.call(drag);
}
kgBuider(nodes,links);
$(document).ready(function(){
$('svg *').mousedown(function(e){
if(e.button != 2){ document.querySelector('#nodeOperationMenu > .ring').style.display = 'none';}
})
})
</script>
</html>
使用D3.js实现简单的图谱展示
最新推荐文章于 2024-03-20 23:58:16 发布