先展示一波最终效果
这段时间再学习D3,力导向图
力导向图(Force-Directed Graph),是绘图的一种算法。在二维或三维空间里配置节点,节点之间用线连接,称为连线。各连线的长度几乎相等,且尽可能不相交。节点和连线都被施加了力的作用,力是根据节点和连线的相对位置计算的。根据力的作用,来计算节点和连线的运动轨迹,并不断降低它们的能量,最终达到一种能量很低的安定状态。
力导向图能表示节点之间的多对多的关系。
定义一个力导向图的布局关键方法 force()
d3在v3和v4版本上对于 force() 这个方法变动很大
话不多说,代码如下:(注意:这次使用的D3.js版本是V3的)
代码不是最终代码,但是功能基本已经实现,后续会更新这一版的最终代码
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>The ENDtitle>
<script src="http://d3js.org/d3.v3.min.js">script>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js">script>
<script src="./js/smartMenu.js">script>
<script src="./fun.js">script>
<link rel="stylesheet" href="./css/smartMenu.css">
head>
<body>
<div id="tpContainer">
div>
body>
html>
//获取数据
d3.json('./preson.json', function(error,data){
if (error) {
return console.log(error);
}
// console.log(data);
drawToPofun(data.nodes,data.lines);
});
function drawToPofun(nodes,edges){
//定义初始变量
var width = 1503;
var height = 654;
var img_w = 40;
var img_h = 40;
var text_dx = -20;
var text_dy = 20;
var i = 0;
//初始化拖拽方法
var drag = d3.behavior.drag()
.origin(function(d) {
return d;
})
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
//初始化缩放方法
var zoom=d3.behavior.zoom()
.scaleExtent([-10,10]).on("zoom",zoomed);
//定义画布
var svg = d3.select("#tpContainer")
.append("svg")
.attr("width",width)
.attr("height",height)
.call(zoom)
.on("dblclick.zoom", () => {});//禁止双击放大
//拆分子节点
var view = svg.append("g")
.attr("class","graphCon");
var node = view.selectAll("g.node").data(nodes,function (d) {
return d.id || (d.id = ++i);
});
var nodeEnter = node
.enter()
.append("g")
.attr("class","node")
.attr("id",d=>d.id);
//添加节点,以自定义图标代替
var nodes_img = nodeEnter.append("image")
.data(nodes)
.attr("width",function(d){
return img_w;
})
.attr("height",function(d){
return img_h;
})
.attr("xlink:href",function(d){
return d.image;
})
.call(drag)//开始拖拽方法
//添加连线
var edges_line = nodeEnter.append("line")
.data(edges)
.attr("class","line")
.style("stroke","#ccc")
.style("stroke-width",1);
//添加节点说明(文本)
var nodes_text = nodeEnter.append("text")
.data(nodes)
.attr("class","nodetext")
.attr("dx",text_dx)
.attr("dy",text_dy)
.text(function(d){
return d.name;
});
//定义力学图布局
var force = d3.layout.force().nodes(nodes)
.links(edges)
.size([width,height])
.linkDistance(200)
.charge(-800)
.start()
.on("tick", function(){
//限制结点的边界
nodes.forEach(function(d,i){
d.x = d.x - img_w/2 < 0? img_w/2 : d.x ;
d.x = d.x + img_w/2 > width ? width - img_w/2 :
d.x ;
d.y = d.y - img_h/2 < 0? img_h/2 : d.y ;
d.y = d.y + img_h/2 + text_dy > height ?
height - img_h/2 - text_dy : d.y ;
});
//更新连接线的位置
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; });
//更新结点图片和文字
nodes_img.attr("x",function(d){
return d.x - img_w/2;
});
nodes_img.attr("y",function(d){
return d.y - img_h/2;
});
nodes_text.attr("x",function(d){ return d.x; });
nodes_text.attr("y",function(d){
return d.y + img_h/2;
});
});
// 定义菜单选项
var userMenuData = [
[{
text: "菜单1",
func: function () {
var id = Number($(this).attr("id"))
alert("菜单1"+",No."+ id)
}
},
{
text: "菜单2",
func: function () {
var id = Number($(this).attr("id"))
alert("菜单2"+",No."+ id)
}
},
{
text: "菜单3",
func: function () {
var id = Number($(this).attr("id"))
alert("菜单3"+",No."+ id)
}
}
]
];
// 事件监听方式添加事件绑定
$("body").smartMenu(userMenuData, {
name: "chatRightControl",
container: "g.node"
});
function zoomed(){
view.attr("transform",
"translate("+d3.event.translate+")scale(" +
d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
force.start();
}
function dragged(d) {
d.x = d3.event.x;
d.y = d3.event.y;
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
}
{
"nodes": [
{"name": "preson1", "image": "./person.png"},
{"name": "preson2", "image": "./person.png"},
{"name": "preson3", "image": "./person.png"},
{"name": "preson4", "image": "./person.png"},
{"name": "preson5", "image": "./person.png"},
{"name": "preson6", "image": "./person.png"},
{"name": "preson7", "image": "./person.png"},
{"name": "preson8", "image": "./person.png"},
{"name": "preson9", "image": "./person.png"},
{"name": "preson10","image": "./person.png"}
],
"lines": [
{ "source": 0 , "target": 1 },
{ "source": 0 , "target": 2 },
{ "source": 0 , "target": 3 },
{ "source": 1 , "target": 4 },
{ "source": 1 , "target": 5 },
{ "source": 1 , "target": 6 },
{ "source": 0 , "target": 7 },
{ "source": 7 , "target": 8 },
{ "source": 7 , "target": 9 }
]
}
菜单我用的是jQuery插件,下载下来的,js/css代码就不展示了。