Ich versuche, ein benutzerdefiniertes Stammbaum-Layout mit D3.js (ohne d3.tree) und manueller Positionierung zu erstelleJavaScript

Javascript-Forum
Anonymous
 Ich versuche, ein benutzerdefiniertes Stammbaum-Layout mit D3.js (ohne d3.tree) und manueller Positionierung zu erstelle

Post by Anonymous »

Anforderungen
1.Alle Söhne müssen linksbündig sein
2.Alle Töchter müssen rechtsbündig sein
3.Die gesamte Familie jedes Kindes (Kind → Ehepartner → Nachkommen) sollte als ein vertikaler Block behandelt werden
4.Wenn die Familie des ersten Sohnes endet, beginnt die Familie des zweiten Sohnes direkt darunter
5.Gleiche vertikale Stapellogik gilt für Tochterknoten
6.Eltern stellen über einen einzelnen Mittelpunkt eine Verbindung zu Kindern her und verzweigen dann zu untergeordneten Knoten
Hinweis: Ich habe ein Referenzbild angehängt, das die erwartete Ausgabe zeigt.
Was ich versucht habe
Ich berechne Positionen manuell mithilfe von Rekursion und verfolge Y-Offsets.
Der Code unten ist ein minimal reproduzierbares Beispiel, das die Layoutlogik demonstriert.
Minimal reproduzierbares Beispiel

Code: Select all






.link {
fill: none;
stroke: #000;
stroke-width: 2px;
}

.node rect {
fill: #fff;
stroke-width: 1.5px;
}

.male { stroke: #007bff; }
.female { stroke: #dc3545; }

text {
font-family: Arial, sans-serif;
font-size: 12px;
text-anchor: middle;
dominant-baseline: middle;
}

.dot {
fill: #333;
}







const data = {
name: "Shobhan",
spouse: "Jatabai",
children: [
{
name: "Mahesh",
side: "left",
spouse: "Sunita",
children: [
{ name: "Sogan", spouse: "Samarta", children: [
{ name: "Samaraj" },
{ name: "Satyam" }
]}
]
},
{
name: "Renjana",
side: "right",
spouse: "Ramchandra",
children: [
{ name: "Aakash", spouse: "Neka" },
{ name: "Vishal", spouse: "Priti" }
]
}
]
};

const svg = d3.select("#svg");
const centerX = 600;
const topY = 80;
const leftX = 300;
const rightX = 900;

const nodeW = 100;
const nodeH = 36;
const vGap = 30;

let nodes = [];
let links = [];

// calculate height of an entire family block
function familyHeight(person) {
let h = nodeH;
if (person.children) {
person.children.forEach(c => {
h += familyHeight(c) + vGap;
});
}
return h;
}

// recursive layout
function layoutFamily(person, x, y, parentMid) {
const node = { ...person, x, y };
nodes.push(node);

if (parentMid) {
links.push({
x1: parentMid.x,
y1: parentMid.y,
x2: x,
y2: y
});
}

if (!person.children) return y + nodeH + vGap;

let cy = y + nodeH + vGap;
person.children.forEach(child => {
cy = layoutFamily(child, x, cy, { x, y });
});

return cy;
}

// root connector
svg.append("line")
.attr("x1", centerX)
.attr("y1", 20)
.attr("x2", centerX)
.attr("y2", topY)
.attr("stroke", "#000")
.attr("stroke-width", 2);

let leftY = topY + 60;
let rightY = topY + 60;

// layout children
data.children.forEach(child => {
const h = familyHeight(child);
const x = child.side === "left" ? leftX : rightX;
const y = child.side === "left" ? leftY : rightY;

links.push({
x1: centerX,
y1: topY,
x2: x,
y2: y
});

layoutFamily(child, x, y, null);

if (child.side === "left") leftY += h + vGap;
else rightY += h + vGap;
});

// draw links
svg.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d", d =>  `
M ${d.x1} ${d.y1}
V ${(d.y1 + d.y2) / 2}
H ${d.x2}
V ${d.y2}
`);

// draw nodes
const g = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", d => `translate(${d.x},${d.y})`);

g.append("rect")
.attr("class", "male")
.attr("x", -nodeW - 10)
.attr("y", -nodeH / 2)
.attr("width", nodeW)
.attr("height", nodeH);

g.append("text")
.attr("x", -nodeW / 2 - 10)
.text(d => d.name);

const couples = g.filter(d => d.spouse);

couples.append("rect")
.attr("class", "female")
.attr("x", 10)
.attr("y", -nodeH / 2)
.attr("width", nodeW)
.attr("height", nodeH);

couples.append("text")
.attr("x", nodeW / 2 + 10)
.text(d => d.spouse);

couples.append("circle")
.attr("r", 4);





Referenzbild

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post