一.效果展示
二.代碼示例
<!DOCTYPE html>
<html lang="zh-cn">
<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>D3 test</title>
</head>
<body>
<div class="container">
<div id="graph"></div>
</div>
</body>
</html>
<script src="/static/js/d3-v4.js"></script>
<script>
var nodes = [ { name: "桂林" }, { name: "廣州" },
{ name: "廈門" }, { name: "杭州" },
{ name: "上海" }, { name: "青島" },
{ name: "天津" } ];
var links = [ { source : 0 , target: 1 } , { source : 0 , target: 2 } ,
{ source : 0 , target: 3 } , { source : 1 , target: 4 } ,
{ source : 1 , target: 5 } , { source : 1 , target: 6 } ];
var width = 800;
var height = 600;
var svg = d3.select("#graph")
.append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.zoom() //創建縮放行爲
.scaleExtent([-5, 2])
.on('zoom',zoom_actions)); //設置縮放範圍
//初始化力學仿真器,通過佈局函數格式化數據
var simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).distance(100)) //distance設置連線距離
.force("charge", d3.forceManyBody().strength(-100)) //注1>
.force("center", d3.forceCenter(width / 2, height / 2)) //設置力學仿真器的中心
.on("tick", ticked);
//佈局函數格式化後的數據,注2>
console.log('nodes', nodes);
console.log('links', links);
var color = d3.scaleOrdinal(d3.schemeCategory20); //生成顏色選擇器函數,此函數返回顏色數組 (string [])
//添加group包裹svg元素以進行縮放,目的是在縮放時不會影響整個容器的位置
var g = svg.append("g")
.attr("class", "everything");
// 繪製連線
var svg_links = g.append('g')
.selectAll("line")
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 3)
// 繪製節點
var svg_nodes = g.append('g')
.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r", '20')
.style("fill", function (d, i) {
return color(i);
}).call(d3.drag().on("start", dragstarted) //d3.drag() 創建一個拖曳行爲
.on("drag", dragged)
.on("end", dragended));
//繪製描述節點的文字
var svg_texts = g.append('g')
.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function (d) {
return d.name;
});
//監聽拖拽開始
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart(); //alpha是動畫的冷卻係數,運動過程中會不斷減小,直到小於0.005爲止,此時動畫會停止。
d.fx = d.x; //fx爲固定座標,x爲初始座標 注3>
d.fy = d.y;
}
//監聽拖拽中
function dragged(d) {
d.fx = d3.event.x; //fevent.x爲拖拽移動時的座標
d.fy = d3.event.y;
}
//監聽拖拽結束
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null; //固定座標清空
d.fy = null;
}
function zoom_actions() {
g.attr("transform", d3.event.transform)
}
//拖拽時的事件監聽器 以實時更新座標
function ticked() {
svg_links.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
svg_nodes.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
});
svg_texts.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
});
}
</script>
三.註釋詳解
相關函數的說明在代碼的註釋中已經寫的很清楚了,下面我針對以上註釋中的注1>,2>,3>做一些詳細的解釋。
1> forceManyBody()函數爲創建多體力函數,strength爲相互作用力,正的爲吸力,負的爲斥力,何爲多體力?你可以把它理解成是一個創建作用力,設置作用力閾值和計算作用力大小的高階函數
這是github上D3.js對於forceManyBody()
的解釋。
簡單來說,就是此函數可以創建一個帶參的多體力對象,此對象中有一些函數,咱們可以調用,具體有如下幾個函數:
strength
,distanceMin
和distanceMax
都是字面意思,無需多說,我稍微解釋下theta
函數。
我先放上github官網上對於此manyBody.theta()
函數的解釋。
什麼意思呢?簡單來說,就是這個函數用來計算你每個節點所受到的其他所有節點的合力的近似值,而theta
函數就是用來設置此近似值的精度,官方給了一個默認值是0.9。
我們可以看到,這個函數依賴的是Barnes-Hut
算法,那麼何爲Barnes-Hut
?
Barnes-Hut算法是一種用於實現N體問題(n-body)模擬的近似算法。
以下是維基百科的解釋:
上面那一堆東西就是說,這個算法是從節點的根數據開始遞歸,並遵循一定的原則,最終計算出合力。具體規則如下:
1.如果當前節點是一個外部節點(而且它不是物體bb),計算當前節點施加在物體bb上的力,並將其加到bb的合力上。
2.計算商s/ds/d的值。如果s/d<θs/d<θ,將這個內部節點看成一個單獨的物體
3.計算其施加在物體bb上的力,並將其加到bb的合力上。否則,在當前節點的每個孩子節點上遞歸地執行上述步驟。
2>,3>原始數據被佈局函數轉化後使什麼格式呢?我們可以看下打印後的結果。
以上參數什麼含義呢?
index - 節點的索引號
vx, vy - 節點上一個時刻的座標
x, y - 節點的當前座標
拖拽函數中的fx
,就對應着節點中的x
,是節點的初始橫座標;fy
就對應着節點中的y
,是節點的初始縱座標。
四.參考文章
【1】D3.js GitHub
【2】D3.js 4.x API中文手冊
【3】Barnes-Hut算法(quad-tree的一個應用)