Merhaba,
Forum üyelerimizin kullanımına sunmak üzere, YıldızTopoloji.js isimli, D3.js tabanlı etkileşimli ağ topolojisi görselleştirme sınıfını hazırladık. Bu sınıf sayesinde web sayfalarınızda kolayca, sürükle-bırak, zoom ve tooltip destekli ağ topolojileri oluşturabilirsiniz.
Öne Çıkan Özellikler
Modern ES6 sınıf yapısı ile modüler kullanım
İstenilen herhangi bir HTML konteynırında topoloji oluşturabilme
Düğümler arasında dinamik kuvvet tabanlı yerleşim (force simulation)
Düğümlere ait detaylı bilgi için tooltip gösterimi
Zoom ve sürükle bırak ile kullanıcı dostu etkileşim
Düğüm vurgulama (highlight) özelliği
Özelleştirilebilir renk paleti, boyut ve kuvvet ayarları

Gerekli Kütüphaneler
D3.js v7 — Yerel kullanım için lütfen kütüphaneyi indirip proje dizininize ekleyiniz.
local ortamda kullanıcaksınzı bu kütüphaneyi indir dosya yolunuza bırakabilirsiniz.
YıldızTopolojisi.js
class YildizTopoloji {
constructor(selector, options = {}) {
this.container = d3.select(selector);
if (this.container.empty()) {
throw new Error(`Container element "${selector}" bulunamadı.`);
}
// Varsayılan seçenekler
this.options = Object.assign({
width: this.container.node().clientWidth || 960,
height: this.container.node().clientHeight || 600,
nodeRadius: 10,
linkDistance: 200,
chargeStrength: -500,
colorScheme: d3.schemeCategory10,
zoomScaleExtent: [0.1, 4]
}, options);
this.color = d3.scaleOrdinal(this.options.colorScheme);
this.tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("display", "none")
.style("pointer-events", "none")
.style("background", "rgba(0,0,0,0.7)")
.style("color", "white")
.style("padding", "8px")
.style("border-radius", "4px")
.style("font-size", "10px")
.style("max-width", "150px");
this.svg = this.container.append("svg")
.attr("width", this.options.width)
.attr("height", this.options.height)
.attr("viewBox", `0 0 ${this.options.width} ${this.options.height}`)
.attr("preserveAspectRatio", "xMidYMid meet");
this.g = this.svg.append("g");
this.simulation = null;
}
render(data) {
if (!data || !data.nodes || !data.links) {
throw new Error("Geçerli bir data nesnesi verilmelidir (nodes ve links içermeli).");
}
this.simulation = d3.forceSimulation(data.nodes)
.force("link", d3.forceLink(data.links).id(d => d.id).distance(this.options.linkDistance))
.force("charge", d3.forceManyBody().strength(this.options.chargeStrength))
.force("center", d3.forceCenter(this.options.width / 2, this.options.height / 2));
// Linkler
const link = this.g.append("g")
.attr("class", "links")
.selectAll("line")
.data(data.links)
.enter().append("line")
.attr("stroke-width", 2)
.attr("stroke", "#999");
// Düğümler
const node = this.g.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(data.nodes)
.enter().append("circle")
.attr("r", this.options.nodeRadius)
.attr("fill", d => this.color(d.type))
.attr("data-id", d => d.id)
.call(d3.drag()
.on("start", (event, d) => this.dragstarted(event, d))
.on("drag", (event, d) => this.dragged(event, d))
.on("end", (event, d) => this.dragended(event, d))
)
.on("mouseover", (event, d) => {
this.tooltip.style("display", "inline")
.html(`<strong>ID:</strong> ${d.id}<br><strong>Type:</strong> ${d.type}<br>${d.not || ""}`)
.style("left", (event.pageX + 5) + "px")
.style("top", (event.pageY - 28) + "px");
})
.on("mouseout", () => {
this.tooltip.style("display", "none");
});
node.append("title")
.text(d => d.name);
// Etiketler
const labels = this.g.append("g")
.attr("class", "labels")
.selectAll("text")
.data(data.nodes)
.enter().append("text")
.attr("dy", -15)
.text(d => d.name);
this.simulation
.nodes(data.nodes)
.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
labels
.attr("x", d => d.x)
.attr("y", d => d.y);
});
this.simulation.force("link").links(data.links);
// Zoom
const zoom = d3.zoom()
.scaleExtent(this.options.zoomScaleExtent)
.on("zoom", (event) => {
this.g.attr("transform", event.transform);
});
this.svg.call(zoom);
// Kaydet
this.nodeElements = node;
}
highlightNode(nodeId) {
if (!this.nodeElements) return;
this.nodeElements.classed("highlight", false);
this.nodeElements.filter(d => d.id === nodeId).classed("highlight", true);
}
// Drag eventleri
dragstarted(event, d) {
if (!event.active) this.simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
dragended(event, d) {
if (!event.active) this.simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
}
index.html
<html>
<head> <title> Yıldız Topolojisi </title>
<style>
.highlight {
animation: blink 1s infinite;
stroke: #ffea00;
stroke-width: 4px;
fill: #ffea00 !important;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
</style>
</head>
<body>
<div id="topoloji-container" style="width: 100%; height: 600px;"></div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="YildizTopoloji.js"></script>
<script>
// Örnek veri
const data = {
nodes: [
{ id: "2", name: "Router", type: "Router", not: "IP: 192.168.1.2" },
{ id: "1", name: "Güvenlik Duvarı", type: "firewall", not: "IP: 192.168.1.1" },
{ id: "3", name: "Switch_Kat_1", type: "Switch", not: "IP: 192.168.1.5" },
{ id: "4", name: "Switch_Kat_2", type: "Switch", not: "IP: 192.168.1.6" },
{ id: "5", name: "Switch_Kat_3", type: "Switch", not: "IP: 192.168.1.7" },
{ id: "6", name: "Switch_Kat_4", type: "Switch", not: "IP: 192.168.1.8" }
],
links: [
{ source: "1", target: "2" },
{ source: "2", target: "1" },
{ source: "3", target: "2" },
{ source: "4", target: "2" },
{ source: "5", target: "2" },
{ source: "6", target: "2" }
]
};
// Nesne oluştur
const topoloji = new YildizTopoloji("#topoloji-container");
// Render et
topoloji.render(data);
// Belirli düğümü vurgula
topoloji.highlightNode("2");
</script>
</body>
</html>
Saygılarımızla,
Bilgi ve Teknoloji