Textboxen dynamisch darstellen in SVG

Diskutiere Textboxen dynamisch darstellen in SVG im JavaScript Forum im Bereich Programmierung; Hallo allerseits Ich nutze in meinem Geografie-Unterricht gerne Wirkungsgefüge um komplexe Systeme zu visualisieren. Wirkungsgefüge verbinden...
  • Textboxen dynamisch darstellen in SVG Beitrag #1
G
GeoTeach
New member
Beiträge
3
Punkte Reaktionen
1
Hallo allerseits

Ich nutze in meinem Geografie-Unterricht gerne Wirkungsgefüge um komplexe Systeme zu visualisieren.
Wirkungsgefüge verbinden Elemente durch Beziehungen:
R.jpeg


In einem Anflug von Selbstüberschätzung habe ich versucht, das Erstellen von Wirkungsgefügen zu digitalisieren (und eine Art Simulation hinzufügen).
Viele Probleme konnte ich ausmerzen, doch jetzt scheitere ich an den Textboxen:
1704469396440.png
Die Kreise repräsentieren die Elemente. Das Rechteck repräsentiert die Beziehung zwischen den Elementen (in der ersten Abbildung als weisser Pfeil dargestellt). Momentan werden Beziehungen nur als Rechteck zwischen die Elemente gezwängt (in der Abbildung weiss). Ziel ist, dass die Beziehung als Pfeil dargestellt wird, vom Quell-Element-Kreis zum Ziel-Element-Kreis (in der Abbildung schwarz, hab' ich von Hand reingezeichnet).
Aber das kriege ich nicht hin. Meine Idee war, die Mittelpunkte der Element-Kreise als Vektor zu interpretieren. Die Textboxen müssen dann parallel zu diesem Vektor sein. Das hat aber nicht geklappt.

Hat jemand Ideen wie ich die Texte mitsamt der Rechtecke dynamisch so drehen lassen kann, dass sie passend zwischen 2 Elementen stehen?
Ich habe von html sehr wenig Ahnung und von Javascript ein bisschen. Daher wär's für mich besser, wenn es eher mittels Javascript umsetzbar wäre.

Ich bin für jeden Vorschlag dankbar!
Vielen Dank im Voraus!

Liebe Grüsse aus der Schweiz
Adrian

PS: Ich habe absichtlich ALLES in eine einzige html-Datei geschrieben. Dann müssen die Schüler nur runterladen und draufklicken. Alles andere führt oft zu Komplikationen...
Hier der vermutlich relevante Javascript-Code, der die Beziehungen zeichnen soll. Der restliche Code steht in der Zipdatei.

Javascript:
            function drawRelations() {
                const svg = document.getElementById('visualisierung').querySelector('svg');

                relations.forEach((relation, index) => {
                    const sourceElement = elements.find(el => el.beschreibung === relation.quelle.beschreibung);
                    const targetElement = elements.find(el => el.beschreibung === relation.ziel.beschreibung);

                    if (!sourceElement || !targetElement) return;

                    const sourceAngle = elements.indexOf(sourceElement) * angleIncrement - Math.PI / 2;
                    const targetAngle = elements.indexOf(targetElement) * angleIncrement - Math.PI / 2;

                    const sourceX = centerX + polygonRadius * Math.cos(sourceAngle);
                    const sourceY = centerY + polygonRadius * Math.sin(sourceAngle);
                    const targetX = centerX + polygonRadius * Math.cos(targetAngle);
                    const targetY = centerY + polygonRadius * Math.sin(targetAngle);

                    const distance = Math.sqrt((targetX - sourceX) ** 2 + (targetY - sourceY) ** 2);

                    // Berechnung der Mitte zwischen den beiden Elementen
                    const midX = (sourceX + targetX) / 2;
                    const midY = (sourceY + targetY) / 2;

                    // Berechnung des Winkels zwischen den beiden Elementen
                    const angle = Math.atan2(targetY - sourceY, targetX - sourceX);

                    // Berechnung der Breite und Höhe des Rechtecks
                    const rectWidth = distance - elementRadius * 2;
                    const rectHeight = elementRadius * 1.6; // 80% des Element-Kreis-Durchmessers

                    // Erstellen eines Rechtecks
                    const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
                    rect.setAttribute('x', midX - rectWidth / 2);
                    rect.setAttribute('y', midY - rectHeight / 2);
                    rect.setAttribute('width', rectWidth);
                    rect.setAttribute('height', rectHeight);
                    rect.setAttribute('fill', 'white');
                    rect.setAttribute('stroke', 'black');

                    // Text für die Beziehung im Rechteck
                    const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                    text.textContent = relation.quelle.beschreibung + ' wirkt ' + relation.operator + ' ' + relation.operand + ' auf ' + relation.ziel.beschreibung + ' ein';
                    
                    let fontSize = getMaxFontSize4Relations(text.textContent, rectWidth, rectHeight);
                
                    text.setAttribute('font-size', fontSize);
                    text.setAttribute('x', midX);
                    text.setAttribute('y', midY);
                    text.setAttribute('text-anchor', 'middle');
                    text.setAttribute('dominant-baseline', 'middle');

                    svg.appendChild(rect);
                    svg.appendChild(text);
                });
            }
 
Anhänge
  • Wirkungsgefüge.zip
    6,2 KB · Aufrufe: 1
  • Textboxen dynamisch darstellen in SVG Beitrag #2
S
Sempervivum
Well-known member
Beiträge
760
Punkte Reaktionen
125
Hallo Adrian,
so etwas von Grund auf selbst aufbauen ist allerdings eine Herausforderung. Ich empfehle, dass Du dich eher nach einer Bibliothek umsiehst, die das unterstützt.
Es ist nicht genau das gleiche aber ein Flussdiagramm oder Flowchart geht in etwa in die Richtung. Sieh dich doch zunächst mal nach diesem Begriff um ob Du es damit umsetzen kannst.
Beste Grüße aus Norddeutschland, Ulrich
 
  • Textboxen dynamisch darstellen in SVG Beitrag #3
G
GeoTeach
New member
Beiträge
3
Punkte Reaktionen
1
@Sempervivum : Ich danke Dir für den Hinweis. Ich habe mir das am Anfang überlegt und zuerst ein bisschen mit dem Dash-Framework herumprobiert, da ich mich mit Python eigentlich wohler fühle. Weil das Einarbeiten etwas zäh war und weil ich dem Irrtum erlag, dass das doch gar nicht so kompliziert sei ;), habe ich mich dazu entschieden, alles selber zu schreiben. Auch um Lernfortschritte mit Javascript und html zu machen.

Jetzt trennt mich gefühlt nur noch diese Textbox vor dem erfolgreichen Abschluss - da mag ich nicht nochmal von vorne anfangen.

Es gibt übrigens auch einige pfannenfertige Tools, die ähnliche Funktionalität anbieten,im Bereich mindmap/Flussdiagramm. Aber keines davon, zumindest keines das ich entdeckt habe, hat die Möglichkeit, die Beziehungen zu parametrisieren und so kleine Simulationen zu ermöglichen. Das sehe ich als einen grossen Vorteil, da ich hoffe, so den Schülern die manchmal explosive Dynamik bestimmter Systeme zu veranschaulichen.

Liebe Grüsse aus der Schweiz,
Adrian
 
  • Textboxen dynamisch darstellen in SVG Beitrag #4
S
Sempervivum
Well-known member
Beiträge
760
Punkte Reaktionen
125
  • Textboxen dynamisch darstellen in SVG Beitrag #5
G
GeoTeach
New member
Beiträge
3
Punkte Reaktionen
1
@Sempervivum: Yeah, genau diesen Hinweis habe ich gebraucht! Jetzt klappts! Es hat zwar noch Schönheitsfehler, aber ich find's ziemlich brauchbar. Vielen Dank dafür!
Es ist tatsächlich möglich ausschliesslich im Javasript. Hier noch die Funktion drawRelations():
Javascript:
            function drawRelations() {
                const svg = document.getElementById('visualisierung').querySelector('svg');

                relations.forEach((relation, index) => {
                    const sourceElement = elements.find(el => el.beschreibung === relation.quelle.beschreibung);
                    const targetElement = elements.find(el => el.beschreibung === relation.ziel.beschreibung);

                    if (!sourceElement || !targetElement) return;

                    const sourceIndex = elements.indexOf(sourceElement);
                    const targetIndex = elements.indexOf(targetElement);
                    const sourceAngle = sourceIndex * angleIncrement - Math.PI / 2;
                    const targetAngle = targetIndex * angleIncrement - Math.PI / 2;

                    const sourceX = centerX + polygonRadius * Math.cos(sourceAngle);
                    const sourceY = centerY + polygonRadius * Math.sin(sourceAngle);
                    const targetX = centerX + polygonRadius * Math.cos(targetAngle);
                    const targetY = centerY + polygonRadius * Math.sin(targetAngle);

                    const midX = (sourceX + targetX) / 2;
                    const midY = (sourceY + targetY) / 2;

                    const angleRadians = Math.atan2(targetY - sourceY, targetX - sourceX);
                    const angleDegrees = angleRadians * (180 / Math.PI);

                    const distance = Math.sqrt((targetX - sourceX) ** 2 + (targetY - sourceY) ** 2);
                    const rectWidth = distance - elementRadius * 2; 
                    const rectHeight = elementRadius * 0.6;
                    const rectX = midX - rectWidth / 2;
                    const rectY = midY - rectHeight / 2;

                    const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
                    group.setAttribute('transform', `rotate(${angleDegrees}, ${midX}, ${midY})`);

                    const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
                    rect.setAttribute('x', rectX);
                    rect.setAttribute('y', rectY);
                    rect.setAttribute('width', rectWidth);
                    rect.setAttribute('height', rectHeight);
                    rect.setAttribute('fill', 'white');
                    rect.setAttribute('stroke', 'black');

                    const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                    const textContent = `${relation.quelle.beschreibung} wirkt ${relation.operator} ${relation.operand} auf ${relation.ziel.beschreibung}`;
                    text.textContent = textContent;
                    const fontSize = getMaxFontSize4Relations(textContent, rectWidth, rectHeight); 
                    text.setAttribute('font-size', fontSize);
                    text.setAttribute('x', midX);
                    text.setAttribute('y', midY + fontSize / 3); // um den Text vertikal zu zentrieren
                    text.setAttribute('text-anchor', 'middle');
                    text.setAttribute('dominant-baseline', 'central');

                    group.appendChild(rect);
                    group.appendChild(text);

                    svg.appendChild(group);
                });
            }
 
  • Textboxen dynamisch darstellen in SVG Beitrag #6
S
Sempervivum
Well-known member
Beiträge
760
Punkte Reaktionen
125
Freut mich, dass es funktioniert. Pfeil sollte dann auch machbar sein, wenn Du statt des Rechtecks einen Pfad nimmst.
 
Thema:

Textboxen dynamisch darstellen in SVG

Oben Unten