Cytoscape.js 是一個處理資料視覺化的 javascript library,當我們要對資料關係進行可視化顯示時,例如社交網路關係或網路拓樸圖時,Cytoscape.js 是個不錯的選擇。
Cytoscape 和 Cytoscape.js 是兩個完全獨立不同的軟體
Cytoscape
使用 Java 語言編寫的用於網絡可視化的桌面應用程序
需要安裝 Java SDK 才能使用
用於大型網絡分析和可視化的高性能應用程序
Cytoscape.js
用於網絡可視化的 javascript library,本身不是一個完整的 Web Application
可以在大多數瀏覽器上使用
不需要 plugin 即可運行
需要編寫程式來建構 Web Application
支援 Extensions
基於 CSS 將資料映射到元件屬性
sample1
建立一個 圓形排列 的 4 個節點 (A, B, C, D),節點之間有箭頭連線,點擊節點會有事件
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>test1</title>
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<style>
#cy {
width: 800px;
height: 600px;
border: 1px solid #ccc;
display: block;
}
</style>
</head>
<body>
<h2>test1</h2>
<div id="cy"></div>
<script>
const cy = cytoscape({
container: document.getElementById('cy'),
elements: [
{ data: { id: 'a', label: '節點 A' } },
{ data: { id: 'b', label: '節點 B' } },
{ data: { id: 'c', label: '節點 C' } },
{ data: { id: 'd', label: '節點 D' } },
{ data: { id: 'ab', source: 'a', target: 'b' } },
{ data: { id: 'bc', source: 'b', target: 'c' } },
{ data: { id: 'cd', source: 'c', target: 'd' } },
{ data: { id: 'da', source: 'd', target: 'a' } }
],
style: [
{
selector: 'node',
style: {
'background-color': '#0074D9',
'label': 'data(label)',
'color': '#fff',
'text-valign': 'center',
'text-outline-width': 2,
'text-outline-color': '#0074D9'
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#AAAAAA',
'target-arrow-color': '#AAAAAA',
'target-arrow-shape': 'triangle',
'curve-style': 'bezier',
}
}
],
layout: {
name: 'circle'
}
});
// 點擊事件
cy.on('tap', 'node', function(evt) {
let node = evt.target;
console.log('你點了節點: ' + node.id());
});
</script>
</body>
</html>
sample2
流程圖,layout 調整為 dagre extension。
使用時要引用 dagre library 及 Cytoscape.js 的 extension
dagre 正是 Cytoscape.js 常用來畫flowchart或 directed graph 的 layout。適合做 flowchart, network topology, workflow
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>Cytoscape.js Flowchart</title>
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/dagre/dist/dagre.min.js"></script>
<script src="https://unpkg.com/cytoscape-dagre/cytoscape-dagre.js"></script>
<style>
#cy {
width: 800px;
height: 600px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<h2>flowchart</h2>
<div id="cy"></div>
<script>
cytoscape.use(cytoscapeDagre);
const cy = cytoscape({
container: document.getElementById('cy'),
elements: [
{ data: { id: 'start', label: '開始' } },
{ data: { id: 'step1', label: '步驟 1' } },
{ data: { id: 'step2', label: '步驟 2' } },
{ data: { id: 'decision', label: '判斷 ?' } },
{ data: { id: 'end', label: '結束' } },
{ data: { id: 's1', source: 'start', target: 'step1' } },
{ data: { id: 's2', source: 'step1', target: 'step2' } },
{ data: { id: 's3', source: 'step2', target: 'decision' } },
{ data: { id: 's4', source: 'decision', target: 'end' } },
{ data: { id: 's5', source: 'decision', target: 'step1' } }
],
style: [
{
selector: 'node',
style: {
'shape': 'round-rectangle',
'background-color': '#28a745',
'label': 'data(label)',
'color': '#fff',
'text-valign': 'center',
'text-outline-width': 2,
'text-outline-color': '#28a745'
}
},
{
selector: 'node[id="decision"]',
style: {
'shape': 'diamond',
'background-color': '#ffc107',
'text-outline-color': '#ffc107'
}
},
{
selector: 'edge',
style: {
'width': 2,
'line-color': '#555',
'target-arrow-color': '#555',
'target-arrow-shape': 'triangle',
'curve-style': 'bezier',
}
}
],
layout: {
name: 'dagre',
// rankDir: 'TB' // top-to-bottom
rankDir: 'LR' // 由左到右 排列
}
});
</script>
</body>
</html>
sample3
鐵路模擬,增加火車在鐵軌上移動的動畫
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>Cytoscape.js Railway</title>
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<style>
#cy {
width: 800px;
height: 600px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<h2>Railway</h2>
<div id="cy"></div>
<script>
const cy = cytoscape({
container: document.getElementById('cy'),
elements: [
{ data: { id: 'station1', label: '車站 1' } },
{ data: { id: 'station2', label: '車站 2' } },
{ data: { id: 'station3', label: '車站 3' } },
{ data: { id: 'checkpoint4', label: '檢查點 4' } },
{ data: { id: 'checkpoint5', label: '檢查點 5' } },
{ data: { id: 'checkpoint6', label: '檢查點 6' } },
{ data: { id: 's1', source: 'station1', target: 'station2' } },
{ data: { id: 's2', source: 'station2', target: 'station3' } },
{ data: { id: 's3', source: 'station2', target: 'checkpoint4' } },
{ data: { id: 's4', source: 'checkpoint4', target: 'checkpoint5' } },
{ data: { id: 's5', source: 'checkpoint5', target: 'station3' } },
{ data: { id: 's6', source: 'checkpoint5', target: 'checkpoint6' } },
// 列車節點
{ data: { id: 'train1', label: '🚆' }, classes: 'train' }
],
style: [
{
selector: 'node',
style: {
'shape': 'ellipse',
'background-color': '#0074D9',
'label': 'data(label)',
'color': '#fff',
'text-valign': 'center',
'text-outline-width': 2,
'text-outline-color': '#0074D9'
}
},
{
selector: 'node[id^="station"]',
style: {
'shape': 'round-rectangle',
'background-color': '#17a2b8',
'text-outline-color': '#17a2b8'
}
},
{
selector: 'node.train',
style: {
'background-color': 'red',
'shape': 'ellipse',
'label': 'data(label)',
'font-size': 24,
'width': 30,
'height': 30
}
},
{
selector: 'edge',
style: {
'width': 2,
'line-color': '#555',
'target-arrow-color': '#555',
'target-arrow-shape': 'triangle'
}
}
],
layout: {
name: 'breadthfirst',
directed: true,
padding: 20
}
});
function moveAlongEdge(train, fromNode, toNode, duration, callback) {
const start = fromNode.position();
const end = toNode.position();
const startTime = performance.now();
function animate(now) {
const elapsed = now - startTime;
const t = Math.min(elapsed / duration, 1); // 0~1
const x = start.x + (end.x - start.x) * t;
const y = start.y + (end.y - start.y) * t;
train.position({ x, y });
if (t < 1) {
requestAnimationFrame(animate);
} else if (callback) {
callback();
}
}
requestAnimationFrame(animate);
}
function moveTrain(path) {
let i = 0;
const train = cy.getElementById('train1');
function step() {
if (i >= path.length - 1) return;
const fromNode = cy.getElementById(path[i]);
const toNode = cy.getElementById(path[i + 1]);
moveAlongEdge(train, fromNode, toNode, 2000, () => {
i++;
step();
});
}
step();
}
// 定義路徑
const route = ['station1', 'station2', 'checkpoint4', 'checkpoint5', 'station3'];
// 初始化列車位置
cy.getElementById('train1').position(cy.getElementById(route[0]).position());
// 2 秒後啟動列車
setTimeout(() => moveTrain(route), 2000);
</script>
</body>
</html>
沒有留言:
張貼留言