Gozintograph: Unterschied zwischen den Versionen
Markierung: Manuelle Zurücksetzung |
Markierung: Zurückgesetzt |
||
| Zeile 26: | Zeile 26: | ||
<html> | <html> | ||
<style> | <style> | ||
#gozinto_svg { | |||
width: 100%; | |||
height: 520px; | |||
border: 1px solid #ccc; | |||
margin-top: 0; | |||
padding-top: 0; | |||
display: block; | |||
} | |||
.draggable { | |||
cursor: move; | |||
} | |||
text { | |||
user-select: none; | |||
font-family: Arial, sans-serif; | |||
} | |||
</style> | </style> | ||
<svg id="gozinto_svg"></svg> | |||
<svg id="gozinto_svg" | |||
</svg | |||
<script> | <script> | ||
// ==================================================================== | |||
// Hilfsfunktionen | |||
// ==================================================================== | |||
function createRect(svg, x, y, w, h, text) { | |||
const g = document.createElementNS("http://www.w3.org/2000/svg", "g"); | |||
g.classList.add("draggable"); | |||
g.dataset.type = "rect"; | |||
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); | |||
rect.setAttribute("x", x); | |||
const | rect.setAttribute("y", y); | ||
rect.setAttribute("width", w); | |||
rect.setAttribute("height", h); | |||
rect.setAttribute("rx", 6); | |||
rect.setAttribute("fill", "#3498db"); | |||
rect.setAttribute("stroke", "#1f4e78"); | |||
rect.setAttribute("stroke-width", 2); | |||
rect.dataset.role = "body"; | |||
const label = document.createElementNS("http://www.w3.org/2000/svg", "text"); | |||
label.setAttribute("x", x + w / 2); | |||
const | label.setAttribute("y", y + h / 2 + 4); | ||
label.setAttribute("text-anchor", "middle"); | |||
label.setAttribute("fill", "black"); | |||
label.setAttribute("font-size", "14"); | |||
label.textContent = text; | |||
g.appendChild(rect); | g.appendChild(rect); | ||
g.appendChild( | g.appendChild(label); | ||
svg.appendChild(g); | svg.appendChild(g); | ||
return g; | |||
} | |||
// | function createCircle(svg, x, y, labelText) { | ||
const g = document.createElementNS("http://www.w3.org/2000/svg", "g"); | |||
g.classList.add("draggable"); | |||
g.dataset.type = "circle"; | |||
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle"); | |||
circle.setAttribute("cx", x); | |||
circle.setAttribute("cy", y); | |||
circle.setAttribute("r", 12); // KLEINERER KREIS | |||
circle.setAttribute("fill", "white"); | |||
circle.setAttribute("stroke", "black"); | |||
circle.setAttribute("stroke-width", 2); | |||
circle.dataset.role = "body"; | |||
const label = document.createElementNS("http://www.w3.org/2000/svg", "text"); | |||
label.setAttribute("x", x); | |||
label.setAttribute("y", y + 4); | |||
label.setAttribute("text-anchor", "middle"); | |||
label.setAttribute("fill", "black"); | |||
label.setAttribute("font-size", "10"); | |||
label.textContent = labelText; | |||
g.appendChild(circle); | |||
g.appendChild(label); | |||
svg.appendChild(g); | |||
return g; | |||
} | |||
function createLine(svg, from, to, arrowEnd) { | |||
const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); | |||
line.setAttribute("stroke", "black"); | |||
line.setAttribute("stroke-width", 2); | |||
line.dataset.from = from; | |||
line.dataset.to = to; | |||
line.dataset.arrow = arrowEnd ? "1" : "0"; | |||
svg.appendChild(line); | |||
updateLine(line); | |||
return line; | |||
} | |||
function centerOf(element) { | |||
const body = element.querySelector("[data-role='body']"); | |||
const | if (body.tagName === "rect") { | ||
return { | |||
x: parseFloat(body.getAttribute("x")) + parseFloat(body.getAttribute("width")) / 2, | |||
y: parseFloat(body.getAttribute("y")) + parseFloat(body.getAttribute("height")) / 2 | |||
}; | |||
} | } | ||
if( | if (body.tagName === "circle") { | ||
return { | |||
x: parseFloat(body.getAttribute("cx")), | |||
y: parseFloat(body.getAttribute("cy")) | |||
}; | |||
} | } | ||
} | |||
function updateLine(line) { | |||
const fromEl = document.getElementById(line.dataset.from); | |||
const | const toEl = document.getElementById(line.dataset.to); | ||
const | |||
const A = centerOf(fromEl); | |||
const B = centerOf(toEl); | |||
const | |||
const | |||
const dx = B.x - A.x; | |||
const dy = B.y - A.y; | |||
const len = Math.sqrt(dx * dx + dy * dy); | |||
const ux = dx / len; | |||
const | const uy = dy / len; | ||
const | |||
const | const offsetA = 25; // Abstand vom Rechteck/Kreis | ||
const offsetB = line.dataset.arrow === "1" ? 25 : 25; | |||
line.setAttribute("x1", A.x + ux * offsetA); | |||
line.setAttribute("y1", A.y + uy * offsetA); | |||
line.setAttribute("x2", B.x - ux * offsetB); | |||
line.setAttribute("y2", B.y - uy * offsetB); | |||
} | |||
function enableDragging(svg) { | |||
let selected = null; | |||
const | let offsetX = 0, offsetY = 0; | ||
svg.addEventListener("mousedown", e => { | |||
if (!e.target.closest(".draggable")) return; | |||
selected = e.target.closest(".draggable"); | |||
const body = selected.querySelector("[data-role='body']"); | |||
const box = body.getBoundingClientRect(); | |||
offsetX = e.clientX - box.x; | |||
offsetY = e.clientY - box.y; | |||
e.preventDefault(); | |||
}); | |||
window.addEventListener("mousemove", e => { | |||
if (!selected) return; | |||
const body = selected.querySelector("[data-role='body']"); | |||
if (body.tagName === "rect") { | |||
const w = parseFloat(body.getAttribute("width")); | |||
const h = parseFloat(body.getAttribute("height")); | |||
const newX = e.clientX - offsetX; | |||
const newY = e.clientY - offsetY; | |||
body.setAttribute("x", newX); | |||
body.setAttribute("y", newY); | |||
const text = selected.querySelector("text"); | |||
text.setAttribute("x", newX + w / 2); | |||
text.setAttribute("y", newY + h / 2 + 4); | |||
} | |||
if (body.tagName === "circle") { | |||
const newX = e.clientX - offsetX + 12; | |||
const newY = e.clientY - offsetY + 12; | |||
body.setAttribute("cx", newX); | |||
body.setAttribute("cy", newY); | |||
const text = selected.querySelector("text"); | |||
text.setAttribute("x", newX); | |||
text.setAttribute("y", newY + 4); | |||
} | |||
document.querySelectorAll("line").forEach(updateLine); | |||
}); | |||
window.addEventListener("mouseup", () => selected = null); | |||
} | |||
// ==================================================================== | |||
// Gozintograph aufbauen | |||
// ==================================================================== | |||
const svg = document.getElementById("gozinto_svg"); | |||
// Einzelteile (oben) | |||
const E1 = createRect(svg, 30, 10, 80, 35, "E1"); | |||
E1.id = "E1"; | |||
const E2 = createRect(svg, 140, 10, 80, 35, "E2"); | |||
E2.id = "E2"; | |||
const E3 = createRect(svg, 250, 10, 80, 35, "E3"); | |||
E3.id = "E3"; | |||
const E4 = createRect(svg, 360, 10, 80, 35, "E4"); | |||
E4.id = "E4"; | |||
// Bauteile (unten) | |||
const B1 = createRect(svg, 30, 260, 80, 35, "B1"); B1.id = "B1"; | |||
const B2 = createRect(svg, 140, 260, 80, 35, "B2"); B2.id = "B2"; | |||
const B3 = createRect(svg, 250, 260, 80, 35, "B3"); B3.id = "B3"; | |||
const B4 = createRect(svg, 360, 260, 80, 35, "B4"); B4.id = "B4"; | |||
const B5 = createRect(svg, 470, 260, 80, 35, "B5"); B5.id = "B5"; | |||
// Verbindungen (mit Kreisen) | |||
function connect(E, B, amount) { | |||
const eC = centerOf(E); | |||
const bC = centerOf(B); | |||
const | |||
const midY = (eC.y + bC.y) / 2; | |||
const midX = (eC.x + bC.x) / 2; | |||
const circle = createCircle(svg, midX, midY, amount); | |||
const idC = "C" + Math.random().toString(36).substring(2); | |||
circle.id = idC; | |||
createLine(svg, E.id, idC, false); | |||
createLine(svg, idC, B.id, true); | |||
} | |||
// B1: E1 + E2 | |||
connect(E1, B1, "2"); | |||
connect(E2, B1, "1"); | |||
// B2: E1 + E2 | |||
connect(E1, B2, "2"); | |||
connect(E2, B2, "1"); | |||
// B3: E1 + E2 + E3 | |||
connect(E1, B3, "1"); | |||
connect(E2, B3, "1"); | |||
connect(E3, B3, "1"); | |||
// B4: E1 + E3 + E4 | |||
connect(E1, B4, "2"); | |||
connect(E3, B4, "1"); | |||
connect(E4, B4, "1"); | |||
// B5: E1 + E4 | |||
connect(E1, B5, "1"); | |||
connect(E4, B5, "2"); | |||
// Dragging aktivieren | |||
enableDragging(svg); | |||
</script> | </script> | ||
</html> | </html> | ||