Skripte und Programme

Bearbeiten

Beim Zusammenspiel von SVG mit Skripten oder Programmen sind verschiedene Typen von Skripten oder Programmen zu unterscheiden.

Zunächst gibt es jene Programme, welche ein SVG-Dokument präsentieren sollen, also graphisch darstellen, drucken, vorlesen etc. Dies sind die Darstellungsprogramme, von denen bislang in diesem Buch die Rede war. Diese Programme benötigen korrekte SVG-Dokumente als Eingabe und die korrekte Präsentation ist in den Empfehlungen zu SVG festgelegt.

Dann gibt es Skripte oder Programme, welche von einem Format in ein anderes konvertieren können. Diese benötigen korrekte Dokumente in einem Format als Eingabe und haben die Aufgabe, korrekte Dokumente in einem anderen Format wieder auszugeben. Bei einer Optimierung kann das Ein- und Ausgabeformat auch das gleiche sein. Die Interaktion mit dem Benutzer ist bei solchen Programmen zumeist auf definierte Eingabeparameter begrenzt. Einige dieser Programme sind auch interaktiv und fragen während des Programmablaufes Parameter ab oder werden mit Steuerdateien beeinflusst.
Als Sonderform hierzu können auch Stilvorlagen verstanden werden. So ändert zum Beispiel XSL(T) die Dekoration, die Art der Präsentation eines Dokumentes oder tranformiert es in eine andere Präsentation oder gar in ein anderes Format als Ausgabe. CSS ändert ebenfalls die Dekoration.

Ferner gibt es Dienstprogramme (englisch: server) oder Dienstrechner. Dienstrechner sind jene, auf welchen die Dienstprogramme laufen. Darstellungsprogramme sind demgegenüber sogenannte Kunden (englisch: clients), die Anfragen an Dienstrechner und Dienstprogramme stellen und deren Ausgabe dann präsentieren.

Wird an diese Dienstrechner oder -programme eine solche Anfrage gestellt, kann eine Verarbeitung mit einer Ausgabe die Folge sein. Zum Beispiel liefern Dienstrechner bei Anfragen gemäß dem Protokoll HTTP (Hypertext-Transfer-Protokoll) Dokumente oder allgemeiner Ausgaben aus.

Von so einem Dienstrechner können wiederum Skripte abgefragt werden (PHP - Hypertext-Präprozessor; Perl etc), diese werden dann auf dem Dienstrechner ausgeführt und produzieren eine Ausgabe. Zurückgegeben wird also nicht das Skript selbst, sondern dessen Ausgabe. Im Falle von SVG ist das in der Regel ein komplettes SVG-Dokument, welches ganz oder teilweise gemäß der Anfrage dynamisch erstellt wurde.

Solche Skripte oder Programme werden auch dienstrechnerseitige Skripte (englisch: server-sided scripts) genannt. Diese Skripte funktionieren unabhängig von den Möglichkeiten des Darstellungsprogrammes. Erforderlich ist nur eine Anfrage an das Dienstprogramm.

Das Darstellungsprogramm interpretiert nur die Ausgabe, insofern ist eine klare zeitliche und inhaltliche Aufgabentrennung zwischen dem Skript und dem Darstellungsprogramm gegeben. Entsprechend sind die Interaktionsmöglichkeiten des Nutzers mit dem Skript begrenzt. Eine Interaktion erfordert eine erneute Anfrage an das Dienstprogramm und einen erneuten Durchlauf des Skriptes. Von der Grundidee her erinnert sich ein Dienstprogramm auch nicht an vorherige Anfragen vom selben Darstellungsprogramm, ist es also erforderlich, dessen Antwort abhängig zu machen vom Zustand des Darstellungsprogrammes, des bislang präsentierten Dokumentes oder von Nutzerinteraktionen, so sind diese Informationen bei der Anfrage mitzuschicken. Da das teils recht viele Informationen sein können, wird teilweise auch eine Sitzung für den jeweiligen Nutzer auf dem Dienstrechner eingerichtet und bei Anfragen wird dann neben den aktuellen Änderungen nur eine Identifikation der Sitzung übermittelt. Auch dabei ist die Antwort des Dienstprogrammes ein komplettes Dokument. Die Methode eignet sich also nur schlecht, um zeitnah kleine Änderungen durch den Nutzer zu berücksichtigen, wie dies insbesondere bei interaktiven Anwendungen wie Spielen typisch auftritt.

Für die Darstellung oder Präsentation im Darstellungsprogramm ist es dafür allerdings belanglos, welche Skript- oder Programmiersprache auf dem Dienstrechner verwendet wird. Das Skript oder Programm muss nur in der Lage sein, eine Textausgabe zu erzeugen, die ein korrektes SVG-Dokument ergibt. Die Funktion des Skriptes ist also unabhängig vom Darstellungsprogramm und von dessen Einstellungen und Möglichkeiten, was ein großer Vorteil für den Autor ist, der so die komplette Kontrolle über das Skript und dessen Ausführung behält.

Sicherheits- oder technische Mängel des Skriptes stellen andererseits kein dramatisches Problem für den Nutzer der Ausgabe dar. Schlimmstenfalls kommt die Ausgabe nicht oder nicht korrekt zustande. Dramatisch kann ein Sicherheitsproblem auf einem Dienstrechner für den Nutzer allenfalls sein, falls er selbst dort vertrauliche oder eigentlich vertrauenswürdige Daten abgelegt hat oder solche einen Dienstrechner auch nur temporär überlassen hat.

Bezogen auf dieses Buch sind die zuvor beschriebenen Programme jenseits des Themas dieses Buches und werden nicht weiter diskutiert, eben weil sie unabhängig von den Gegebenheiten von SVG funktionieren und das SVG-Darstellungsprogramm unabhängig von ihnen funktioniert. Sie können unbedenklich von Autoren eingesetzt werden.

Zusätzlich gibt es in der Voll- und in der Basisversion von SVG 1.1 und in SVG tiny 1.2 auch die Möglichkeit, dass das Darstellungsprogramm selbst Skripte ausführt. Dies ist in SVG tiny 1.1 ausgeschlossen. Diese Skripte werden anwenderseitige Skripte genannt (englisch: client-sided scripts) und sind entweder direkt im SVG-Dokument notiert oder vom SVG-Dokument wird das Skript referenziert. Nur um solche Skripte geht es im folgenden Abschnitt dieses Kapitels.

Im Darstellungsprogramm ausführbare Skriptsprachen

Bearbeiten

Neben der eigentlichen Darstellung des SVG-Inhaltes kann ein Darstellungsprogramm auch zusätzlich die Fähigkeit haben, Skriptsprachen zu interpretieren. Ob ein im Dokument befindliches Skript ausgeführt wird, hängt von der verwendeten Skriptsprache, vom Darstellungsprogramm und dessen Fähigkeiten ab und davon, was der Nutzer des Darstellungsprogrammes für Voreinstellungen vorgenommen hat. Weil auch nicht jedes Darstellungsprogramm jede geeignete Skriptsprache interpretiert und der Nutzer die Interpretation per Voreinstellung unterbinden oder erlauben kann, kann sich der Autor nie darauf verlassen, dass ein Skript wirklich wie geplant ausgeführt wird.

SVG selbst sieht keine Skriptsprache vor, welche von einem Darstellungsprogramm interpretiert werden muss. Allerdings verwendet und definiert SVG ein Dokument-Objekt-Modell, in welchem die Skriptsprache agieren kann. Häufig implementiert sind unterschiedliche Umsetzungen des Standards ecmascript (von Ecma International standardisiertes JavaScript).

Allerdings werden anwenderseitige Skripte gegebenenfalls während der Darstellung des Dokumentes ausgeführt, was eine permanente Interaktion des Nutzers mit dem Dokument ermöglicht. SVG bietet dafür Schnittstellen, um Nutzerinteraktionen und Ereignisse auszuwerten und darauf zu reagieren. Komplexere Skripte können sogar mit Dienstprogrammen über das Netz interagieren (Stichwort: AJAX; englisch: Asynchronous JavaScript and XML).

Weil die Skripte im Darstellungsprogramm des Nutzers auf dessen Rechner laufen, trägt dieser auch das Risiko bei technischen Mängeln und bei Sicherheitsmängeln des Skriptes oder des Darstellungsprogrammes. Je nach Skriptsprache oder Umsetzung der Skriptsprache besteht auch die Möglichkeit, Informationen vom Rechner des Nutzers auszuwerten oder auszuspähen.

Durch die AJAX-Interaktionsmöglichkeit ergibt sich weiterhin das Potential, dass die Daten vom Nutzer unbemerkt zu anderen Rechnern weitergeleitet werden. Zwar sind sich die Anbieter der Darstellungsprogramme dessen bewusst und setzen Konzepte um, um ein unbemerktes Versenden brisanter Informationen vom Rechner des Nutzers aus zu verhindern, auch werden die Sprachen so konstruiert, um solche zweifelhaften Anwendungen weitgehend auszuschließen, allerdings hat die Historie der konkreten Umsetzung anwenderseitiger Skriptsprachen bislang gezeigt, dass immer wieder unbeabsichtigte Sicherheitslecks auftreten oder eigentlich gut gemeinte Erweiterungen solcher Programme neue Wege eröffnen, um per Skript unautorisiert und unbemerkt auf Daten des Nutzers zuzugreifen, die dieser gar nicht exponieren wollte. Dieses Sicherheitsrisiko trägt also letztlich der Nutzer, welcher die Interpretation von Skripten erlaubt.

Das technische Risiko von anwenderseitigen Skripten liegt darin, dass schlecht oder fehlerhaft geschriebene Skripte oder eine eventuell beim Darstellungsprogramm ineffektive Umsetzung der Skriptinterpretation zu einer starken Belastung des Prozessors des Rechners des Nutzers führen kann - oder auch zum Einfrieren oder zum Absturz des Darstellungsprogrammes. Hat der Nutzer auf demselben Darstellungsprogramm oder -rechner mehrere Prozesse oder Sitzungen laufen, so kann es dabei zu Datenverlusten auch bei diesen anderen Prozessen kommen.

Weil der Nutzer die Qualität der Skripte beim Aufruf unbekannter Dokumente im vorhinein nicht beurteilen kann, kann dies ein weiterer Grund sein, dass Nutzer die Interpretation von Skripten nicht erlauben, ebenso wie die gegebenenfalls bekannte starke Prozessorbelastung bei ineffektiver Umsetzung der Skriptinterpretation oder die Tendenz zu Abstürzen.

Zugänglichkeitsprobleme und Lösungsstrategie

Bearbeiten

Aus diesen Unwägbarkeiten, ob das Darstellungsprogramm das Skript interpretieren kann oder darf, und wenn ein Skript interpretiert wird, ob dies korrekt passiert, ergibt sich für Dokumente mit Skripten ein vielfältiges Zugänglichkeitsproblem, dem sich der Autor des Dokumentes stellen muss, um Probleme zu vermeiden. Wäre Inhalt nur bei Skriptinterpretation verfügbar, so wäre dies eine Zugänglichkeitsbarriere, die zu vermeiden ist.

Die pauschale Lösung des Problems liegt darin, die komplette Information eines Dokumentes ohne die Notwendigkeit der Skriptinterpretation verfügbar zu machen. Zu diesem Zwecke erstellt der Autor zunächst das komplette Dokument mit der beabsichtigten Information ohne Skript. Dieses Dokument enthält dann per Definition die komplette Information, die mit dem Dokument vermittelt werden soll.

Wie dann anschließend mittels CSS eine alternative Ansicht des Dokumentes formuliert werden kann, so kann zusätzlich über anwenderseitige Skripte ein zusätzlicher alternativer Zugang zur Information des Dokumentes angeboten werden. Wie die Dekoration per CSS den Inhalt, die Information eines Dokumentes nicht ändert, so ändert auch die Ausführung eines Skriptes nicht den Inhalt oder die Information eines Dokumentes, ermöglicht nur einen anderen Zugang.

Mit diesem Ausgangspunkt kann ein Autor an sich durch Skripte keine Barrieren für Nutzer ohne Skriptinterpretation aufbauen. Allenfalls handelt es sich um inhaltsleere Dokumente, bei denen Nutzern mit Skriptinterpretation Inhalt vorgetäuscht wird. Allerdings ergibt sich bei dieser Interpretation das Problem, dass Nutzer den vorgetäuschten Inhalt als abweichend vom wahren Inhalt und eventuell als nützlicher empfinden könnten, woraus sich dann doch wieder ein Zugänglichkeitsproblem zur Täuschung für jene ergibt, bei denen die Skripte nicht ausgeführt werden.

Bei Skriptinterpretation tritt hingegen das Problem auf, dass nicht alle vom Autor verwendeten Strukturen im Darstellungsprogramm korrekt oder überhaupt interpretiert werden, etwa aufgrund von Fehlern und Lücken in solchen Programmen oder auch, weil der Autor nicht standardisierte Funktionen verwendet, die demzufolge eine beliebiges Verhalten zur Folge haben können.

Neben der technischen Realisation der Skripte ist es also zentrale Aufgabe des Autors eines Dokumentes, welches anwenderseitige Skripte enthält, den Zugang zur Information unabhängig von der Skriptinterpretation bereitzustellen, um Missverständnisse und Barrieren zu vermeiden.

Themeneingrenzung

Bearbeiten

Weil SVG selbst keine Skriptsprache vorsieht, welche interpretiert werden muss, ist das Erlernen von Skriptsprachen auch nicht zentrales Thema dieses Buches. SVG stellt jedoch Elemente, Schnittstellen und Methoden bereit, um Skripte einzubetten, zu referenzieren, auf Ereignisse zu reagieren und über das Dokument-Objekt-Modell die Struktur oder die Dekoration eines Dokumentes zu manipulieren. SVG verfügt zudem über eine spezielle Erweiterung des Dokument-Objekt-Modells, welche es ermöglichen oder erleichtern, die für Vektrographiken spezifischen Inhalte zum manipulieren.

In den Beispielen wird ecmascript verwendet. Eine konkrete Umsetzung davon ist java-script. Wikibooks hat auch ein Buch names Javascript, welches das Verständnis der Beispiele erleichtern kann, ebenso wie die Referenzen zum Dokument-Objekt-Modell am Endes dieses Kapitels. Das Wikibook zu Javascript ist im aktuellen Zustand (2014) allerdings sehr auf die Verwendung der Skripte in HTML (Inhaltstype 'text/html') zugeschnitten, nicht auf XML allgemein oder XHTML oder SVG als weitere besondere und häufige Anwendungsfälle. Relevant aus dem Buch sind neben dem eigentlichen ECMAScript-Sprachkern, der dort leider nicht explizit als solcher gekennzeichnet ist, primär die Kapitel Der Kern des Dokument-Objekt-Modells und Ereignisbehandlung im Dokument-Objekt-Modell, welche auch gleich mit SVG-Beispielen ausgestattet sind. Am Ende dieses Kapitels gibt es unter 'weiterführende Literatur' englischsprachige Artikel, welche auf die Verwendung von ECMAScript für SVG speziell eingehen.

Es ist also bei anderen Anleitungen zur Verwendung von Skriptsprachen immer darauf zu achten, daß es sich bei SVG um ein XML-Format handelt, für XHTML oder gar für HTML spezifische Methoden sind also nicht verfügbar. Anders als in HTML, wo der Skriptautor zum Beispiel mit JavaScript noch Inhalte direkt in das Dokument schreiben konnte, erfolgt in SVG die Manipulation immer über das DOM, wobei es einerseits für XML ein generischen DOM-Kern für den allgemeinen Zugriff auf Attribute und Elemente und deren Inhalt gibt als auch für SVG 1.1 und SVG tiny 1.2 die bereits erwähnten speziellen Konstruktionen, die den besonderen Anforderungen des Formates Rechnung tragen. Diese Konstruktionen sind eine formatspezifische DOM-Erweiterung, die in der jeweiligen Empfehlung zur SVG-Version nachgesehen werden kann. Bei Kenntnis des DOM-Kerns und der DOM-Ereignisbehandlung sollten die DOM-Erweiterungen zu SVG keine besonderen inhaltlichen Probleme mehr bereiten, sie sind allerdings in den Empfehlungen recht knapp abgehandelt.

Da zudem bei wikibooks in den Beispieldokumenten keine Skripte erlaubt sind, gibt es in diesem Kapitel die Beispiele auch nur recht rudimentär als Quelltext. Nach dem Verstehen können die Leser bei Bedarf Beispiele kopieren und abspeichern und dann ausführen lassen.

Hinsichtlich Zugänglichkeit und Barrierfreiheit sind die Beispiele in diesem Kapitel eher als inhaltsleer anzusehen. Es wird allenfalls eine Textalternative angegeben. Derartige Beispiele eignen sich also nur zu Übungszwecken oder für die private Verwendung, nicht für Publikationen. Anders als etwa bei den allermeisten (X)HTML-Anwendungen kann nicht ausgeschlossen werden, dass es für SVG-Dokumente mit komplizierteren interaktiven Skriptanwendungen sehr schwierig ist, barrierefreie, zugängliche graphische Alternativen ohne Skriptanwendung verfügbar zu machen. Sofern eine Publikation trotzdem in Erwägung gezogen wird, ist es dann um so wichtiger, eine hinreichende Textalternative bereitzustellen. Auch wenn ein Autor etwa bei einem interaktiven Spiel davon ausgeht, dass ein solches Dokument keinen relevanten Inhalt hat, sollte wenigstens eine Textalternative vorhanden sein, die das Spiel grob beschreibt und auf den Sachverhalt fehlenden Inhaltes hinweist.

Angaben zur verwendeten Skriptsprache

Bearbeiten

Die verwendete Skriptsprache kann explizit angegeben werden. Mit dem Attribut contentScriptType des Elementes svg kann dokumentweit notiert werden, welche Skriptsprache verwendet wird, wenn diese nicht lokal angegeben wird. Das Attribut ist bereits im Kapitel Dokumentstruktur im Abschnitt über das Element svg beschrieben worden. Der Wert von contentScriptType ist ein Medientyp (ehemals MIME-Typ). Typisch für Skripte innerhalb von SVG ist zum Beispiel 'application/ecmascript', was in SVG tiny 1.2 auch die Voreinstellung ist, wenn das Attribut nicht angegeben ist.

Zum Zeitpunkt, als SVG 1.1 spezifiziert wurde, hatte ecmascript noch keinen Medientyp. Statt 'application/ecmascript' wurde damals angenommen, dass der Medientyp 'text/ecmascript' heißen wird, was daher für SVG 1.1 die Voreinstellung ist. Es ist daher zum empfehlen, den Medientyp in SVG 1.1 immer explizit anzugeben, selbst wenn ecmascript verwendet wird.

Mit dem Attribut type kann ferner bei jedem Element, welches Skripte beinhalten kann, der Medientyp gesondert und gegebenenfalls auch abweichend von der globalen Angabe notiert werden. Der Wert ist auch jeweils ein Medientyp.

Beide Attribute sind nicht animierbar.

Durch bedingte Verarbeitung mit dem Element switch und dem Attribut requiredFormats in SVG tiny 1.2 kann ferner die Darstellung davon abhängig gemacht werden, ob eine Interpretation der verwendete Skriptsprache gegeben ist oder nicht.

Ferner kann mit dem Attribut requiredFeatures festgestellt werden, ob überhaupt eine Skriptinterpretation durchgeführt wird.

Durch die bedingte Verarbeitung ist es so möglich, verschiedene Skriptsprachen und skriptfreie Alternativen anzubieten.

Element script

Bearbeiten
SVG                
1.1 1.7 9 3 3.2 4 ? ? ?

Das Element script kann ausführbaren Inhalt enthalten oder ausführbaren Inhalt referenzieren.

Das Skript wird gegebenenfalls ausgeführt, sobald das Element geladen ist. Das bedeutet, im Moment der Ausführung hat das Skript nur Zugriff auf den bis dahin geladenen Teil des DOMs. Daher wird im eigentlichen Skript oft nur gewartet, bis das komplette Dokument geladen ist, um dann jene Funktionen auszuführen, die auf das komplette DOM zugreifen. Das Warten auf Ereignisse mit darauf erfolgender Ereignisbehandlung ist ein typisches Verhalten von solchen Skripten.

Weil Skripte nicht direkt dargestellter Inhalt sind, empfiehlt es sich, script-Elemente im Element defs des Hauptelementes svg zu notieren.

Das Element entspricht dem gleichnamigen Element in XHTML. Man beachte, dass dieses anders als das gleichnamige Element in HTML zu interpretierenden Inhalt enthält. Wird das Skript als Elementinhalt notiert, so sind spezielle Zeichen von XML im Konfliktfalle folglich zu maskieren, insbesondere < als &lt; und > als &gt;. Alternativ kann der Inhalt als CDATA gekennzeichnet werden.

Neben dem bereits beschriebenen Attribut type stehen für script auch Attribute von XLink zur Verfügung. Insbesondere dient href dazu, ein externes Skript zu referenzieren. Sofern href von XLink angegeben ist, wird gegebenenfalls vorhandener Inhalt von script ignoriert und nicht ausgeführt, gegebenenfalls nur der das referenzierte externe Skript. Ist die angegeben IRI ungültig, wird kein Skript ausgeführt.

Kompilierte Skripte/Programme, wie etwa java-Programme (JAR-Archiv) sind immer zu referenzierten und nicht im Element zu notieren.

href ist für das Element script nicht animierbar.

Beispiel, Skript direkt eingebettet:

<script type="application/ecmascript">
 // Skriptquelltext &lt; &gt; etc
<script>

oder:

<script type="application/ecmascript">
<![CDATA[
 // Skriptquelltext < > etc
 ]]>
<script>

Oder es wird auf ein externes Dokument verwiesen:

<script type="application/ecmascript"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xlink:href="scripts/meinScript.js"/>

Oder:

<script type="application/java-archive"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xlink:href="java/Beispiel.jar"/>

Sofern der Namensraum für XLink bereits bei einem Elternelement angegeben ist, ist dies natürlich nicht abermals notwendig.

Ereignisbehandlung in SVG 1.1

Bearbeiten

In SVG 1.1 basiert das ursprüngliche Konzept der Ereignisbehandlung in Verbindung mit Skripten einerseits auf DOM2-Ereignissen und andererseits auf speziellen Attributen. Die Attribute dienen als Empfänger von Ereignissen und damit als Schnittstelle zwischen dem Dokument und der Skriptsprache. Es ist allerdings von der Struktur und Ordnung her besser, die Ereignisbehandlung rein im Skript umzusetzen, also dort auch solch einen Empfänger anzumelden, statt SVG und Skripte über diese speziellen Attribute zu vermischen.

Die Attribute sind onfocusin, onfocusout, onactivate, onclick, onmousedown, onmouseup, onmouseover, onmousemove, onmouseout und onload. Die zugehörigen Ereignisnamen ergeben sich, wenn das vorangestellte 'on' entfernt wird. Zusätzlich sind Dokumentereignisse nutzbar. Dieses sind onunload, onabort, onerror, onresize, onscroll, onzoom. Die Attribute sind anwendbar auf graphische Elemente und Gruppierungselemente.

Eine genauere Beschreibung des jeweiligen Ereignisses ist im Kapitel Interaktivität zu finden.

Ferner gibt es Attribute bezogen auf Animationselemente. Dies sind: onbegin, onend, onrepeat. Die beziehen sich entsprechend auf den Beginn, das Ende, die Wiederholung einer Animation, ebenfalls wie für die entsprechenden Ereignisse im Kapitel Interaktivität beschrieben.

Die Attribute sind nicht animierbar.

Das jeweilige Attribut wird bei dem Element notiert, bei welchem das Ereignis registriert werden soll.

Der Wert eines Attributes ist ein Skript, welches beim Eintreten eines Ereignisses ausgeführt werden soll, oft nur ein Funktionsaufruf samt Parameter für eine Funktion, welche im Element script zu definieren ist, siehe Beispiele unten. Auch hier ist darauf zu achten, dass spezielle XML-Zeichen maskiert werden, insbesondere neben den Zeichen <, > und & auch das jeweilige Anführungszeichen, mit dem der Wert des Attributes eingeschlossen wird.

Beispiel:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
     viewBox="0 0 1000 1000"
     contentScriptType="application/ecmascript"
     xml:lang="de">
  <title>Ereignisbehandlung</title>
  <rect x="20" y="20" width="100" height="100"
        fill="green" stroke="blue" stroke-width="2"
        onmousedown="evt.target.width.baseVal.value += 10"
        onmouseup="evt.target.height.baseVal.value += 10"
        onmousemove="evt.target.height.baseVal.value = 100;
                   evt.target.width.baseVal.value = 100">
     <set attributeName="width" to="50" begin="mouseout" />
     <set attributeName="height" to="50" begin="mouseout" />
  </rect>
</svg>

Bei jedem Runterdrücken der Taste des Zeigergerätes wird bei Skriptinterpretation die Breite des Rechtecks um 10 erhöht, beim Loslassen wird die Höhe um 10 erhöht. Eine Bewegung des Zeigergerätes setzt die Größe zurück auf den Ausgangswert. Allerdings unterbinden die Animationen effektiv eine weitere sichtbare Änderung, sobald das Zeigergerät nicht mehr über dem Rechteck ist, weil die Animation höhere Priorität hat als die Manipulation des DOMs mit dem Skript.

Im Zusammenhang mit Barrierefreiheit und Zugänglichkeit ist zu beachten, dass Inhalt (hier also das SVG) im Rahmen eines Schichtenmodelles strikt von der Dekoration (CSS und hier eben Skripte) getrennt werden sollte. In diesem Sinne sind die beschriebenen Attribute als veraltet anzusehen und sollten vermieden werden. Sofern überhaupt Skripte verwendet werden, ist es stattdessen sinnvoll, im Inhalt nur einen Fragmentidentifizierer anzugeben, um aus dem Skript heraus das betroffene Element adressieren zu können. Der Rest der Ereignisbehandlung ist dann komplett im Skript abzuhandeln, eine Verwendung der Attribute ist dann gar nicht notwendig oder auch nur sinnvoll. Problematisch ist dabei allenfalls, dass bei alten Programmen eventuell nur Ereignisbehandlung mit den genannten speziellen Attributen funktionieren mag. Im Rahmen des Schichtenmodelles ist dies aber kein Problem, denn den Nutzern dieser alten Programme bleibt ja immer noch die Präsentation ohne Skript, die man auch hat, wenn die Skriptinterpretation deaktiviert ist. Da der Inhalt aber per Voraussetzung ohnehin ohne Skriptinterpretation verfügbar sein soll, ergibt sich daraus kein Zugänglichkeitsproblem, wenn das Skript aufgrund mangelnder Implementierung besserer Skripttechniken nicht ausgeführt werden kann.

Das folgende Beispiel entspricht in etwa obigem Beispiel, ist aber etwas erweitert und mit der Ereignisbehandlung komplett im Skript. Ferner enthält das Dokument zumindest einen Alternativtext, um wenigstens Zugänglichkeit auf niedrigem Niveau zu gewährleisten, obgleich bei diesem Beispiel davon auszugehen ist, dass keine weitere inhaltlich relevante Information vermittelt wird, die über die Verwendung von Skripten hinausgeht. Zudem enthält das Skript keine für SVG spezifischen Funktionen, Methoden, Objekte, sondern beschränkt sich auf das allgemeine DOM:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
     viewBox="0 0 1000 1000"
     contentScriptType="application/ecmascript"
     xml:lang="de">
<title>Ereignisbehandlung komplett per DOM</title>
<desc>
Per Skript kann durch Mausknopf runter beziehungsweise rauf 
die Größe des grünen Rechtecks geändert werden. 
Bewegung der Maus führt zur Setzung einer festen Größe.
Verläßt allerdings die Maus das Rechteck, 
setzt eine deklarative Animation die Größe des Rechtecks auf einen kleinen Wert. 
Ist die Maus wieder über dem Rechteck, endet die Animation.
Je nach Zustand ändert sich zudem die Farbe des Rechtecks.
Man beachte, daß das Skript den nicht animierten Wert der Attribute ändert, 
was von der deklarativen, nicht additiven Animation überschrieben wird, 
solange sie aktiv oder eingefroren ist. 
Bei einer to-Animation dient der Skriptwert als drunterliegender Wert.
</desc>

<rect id="r" x="20" y="20" width="100" height="100"
        fill="green" stroke="blue" stroke-width="2">
  <animate attributeName="width" to="50" dur="2" 
    begin="mouseout" end="mouseover" />
  <animate attributeName="height" to="50" dur="2" 
    begin="mouseout" end="mouseover" />
  <animate attributeName="fill" to="green" dur="5" 
    begin="mouseout" fill="freeze" />
</rect>

 <defs>
   <script>
      // Element identifizieren
      var r = document.getElementById("r")
      // relevante Ereignisse mit Funktionen verknüpfen
      r.addEventListener("mousedown", runter, false)
      r.addEventListener("mouseup", rauf, false)
      r.addEventListener("mousemove", beweg, false)
      r.addEventListener("mouseout", raus, false)
      r.addEventListener("mouseover", rein, false)
      r.addEventListener("click", klick, false)
   
   // Funktionen für Ereignisbehandlung definieren
   function runter()
   {
     var r = document.getElementById("r")
     // Basiswert der Breite um 10 erhöhen
     // dazu erstmal Wert auslesen
     // Da der Attributwert immer eine Zeichenkette ist, 
     // in Fließkommazahl umwandeln
     var val = parseFloat(r.getAttributeNS(null,"width"))
     // um 10 erhöhen
     val +=10
     // wenn zu groß, wieder klein anfangen
     if (val &gt; 960) {
         val-=900
     }       
     // Wert wieder setzen
     r.setAttributeNS(null,'width', val)
   }
   function rauf()
   {
     var r = document.getElementById("r")
     // Basiswert der Höhe um 10 erhöhen
     var val = parseFloat(r.getAttributeNS(null,"height"))
     val +=10
     if (val &gt; 960) {
         val -=900 
     }
     r.setAttributeNS(null,'height', val)     
   }
   function beweg()
   {
     var r = document.getElementById("r")
     // Basiswerte von Höhe und Breite auf festen Wert setzen
     r.setAttributeNS(null,'height', '100')
     r.setAttributeNS(null,'width', '100')
   } 
   function raus()
   {
     // Farbe setzen; kurze Schreibweise
     document.getElementById("r").setAttributeNS(null,'fill', 'red')
   }
   
   // sofern angegeben: Funktionsparameter (e) ist dann das Ereignis ...
   function rein(e)
   {
     //hier wird automatisch das Zielelement des Ereignisses (e) verwendet, 
     //vorher wurde ein Element per id identifiziert
     var r = e.target
     // Farbe setzen
     r.setAttributeNS(null,'fill', '#ff0')
   }
   function klick(e)
   {
     // Farbe setzen; kurze Schreibweise
     e.target.setAttributeNS(null,'fill', '#a0a')
   }     
   </script>   
  </defs>  
</svg>

Ereignisbehandlung in SVG tiny 1.2

Bearbeiten

Während in SVG 1.1 die möglichen Ereignisse wie beschrieben explizit in SVG festgelegt sind, wird in SVG tiny 1.2 eine etwas andere Methode verwendet. Diese ermöglicht es, XML-Ereignisse zu verwenden, statt fester Attribute. Dieser Zugang stellt eine Alternative zu der allgemeinen DOM-Verwaltung von Ereignissen dar. Hinsichtlich der Trennung von Inhalt von Skriptbestandteilen ist es hier allerdings so, dass für die Ereignisbehandlung spezielle Elemente im Quelltext stehen. Die Trennung ist dabei also nicht so eindeutig gegeben. Das liegt auch daran, dass SVG tiny 1.2 für mobile Anwendungen bedacht ist, wo das Konzept eher darin besteht, möglichst alles kompakt in einem Dokument vorzuhalten, was zusammengehört.

Entsprechend gibt es das Element handler (zu deutsch etwa: die Anwendung), welches ebenfalls das auszuführende Skript enthält. handler ist dann das Kindelement jenes Elementes, welches der Empfänger eines Ereignisses ist.

Eine Anwendung kann auch mehrfach verwendet werden. Dazu wird ein Empfänger notiert, dies ist das Element aus dem Namensraum von XML Events listener. Dies enthält eine Angabe des zu registrierenden Ereignisses und eine Angabe der auszuführenden Anwendung.

Der Namensraum von XML Events ist:

http://www.w3.org/2001/xml-events

Typisch wird dazu im Hauptelement svg der Namensraum wie folgt notiert:

xmlns:ev="http://www.w3.org/2001/xml-events"

Elemente und Attribute aus diesem Namensraum haben dann das Präfix 'ev'.

Die Liste der verfügbaren Ereignisse ist im Kapitel über Interaktivität angeführt, sie deckt sich inhaltlich in größeren Teilen mit der Liste für SVG 1.1.

Die Ereignisse sind DOMFocusIn, DOMFocusOut, DOMactivate, click, mousedown, mouseup, mouseover, mousemove, mouseout, mousewheel, textInput, keydown, keyup und load.

Zusätzlich sind Dokumentereignisse nutzbar. Dieses sind resize, scroll, SVGZoom, SVGRotate.

Eine genauere Beschreibung des jeweiligen Ereignisses ist im Kapitel Interaktivität zu finden.

Ferner gibt es Attribute bezogen auf Animationselemente. Dies sind: beginEvent, endEvent, repeatEvent.

Dazu gibt es Ereignisse bezüglich des Ladens externer Ressourcen: loadstart, progress, loadend.

Ferner gibt es noch den SVGTimer.

Anwendung, Element handler

Bearbeiten
SVG                
tiny 1.2 - 9.5 - - - ? ? ?

Das Element handler ähnelt dem Element script. Entweder beinhaltet das Element selbst ein Skript oder referenziert es.

Anders als bei script wird das Skript allerdings gegebenenfalls nur ausgeführt, wenn das entsprechende Ereignis eingetreten ist, nicht bereits, wenn das Element geladen ist. Die Funktionalität entspricht also der Ereignisbehandlung in SVG 1.1 mit den entsprechenden Attributen.

Das bereits erwähnte Attribut type dient dazu, den Inhaltstype für die Skriptsprache anzugeben.

Bearbeiten

Mit dem Attribut href von XLink kann ein externes Skript referenziert werden. Ist dies Attribut angegeben, so wird der Inhalt des Elementes nicht interpretiert.

Für dieses Element ist href nicht animierbar.

Mit href sind auch die anderen Attribute von XLink verfügbar.

Attribut event von XML Events
Bearbeiten

Mit dem Attribut event von XML Events wird angegeben, welches Ereignis die Anwendung auslösen soll.

Das Attribut gehört zum Namensraum

http://www.w3.org/2001/xml-events

Dieser Namensraum muss dann auch korrekt angegeben werden.

Die Liste der möglichen Ereignisse wurde bereits angegeben.

Das Attribut ist nicht animierbar.

Beispiel zum Element handler
Bearbeiten

Folgendes Beispiel entspricht dem zur Ereignisbehandlung in SVG 1.1.

<?xml version="1.0" encoding="utf-8" ?>
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:ev="http://www.w3.org/2001/xml-events"
     version="1.2" baseProfile="tiny"
     viewBox="0 0 1000 1000"
     xml:lang="de">

  <desc>Beispiel Element handler</desc>

  <rect id="Rechteck" x="20" y="20" width="100" height="100"
        fill="green" stroke="blue" stroke-width="2">
    <handler type="application/ecmascript" ev:event="mousedown">
      var Rechteck = evt.target;
      var width = Rechteck.getFloatTrait("width");
      Rechteck.setFloatTrait("width", width + 10);
    </handler>
    <handler type="application/ecmascript" ev:event="mouseup">
      var Rechteck = evt.target;
      var height = Rechteck.getFloatTrait("height");
      Rechteck.setFloatTrait("height", height + 10);
    </handler>
    <handler type="application/ecmascript" ev:event="mousemove">
      var Rechteck = evt.target;
      var width = Rechteck.getFloatTrait("width");
      var height = Rechteck.getFloatTrait("height");
      Rechteck.setFloatTrait("width", 100);
      Rechteck.setFloatTrait("height", 100);
    </handler>
     <set attributeName="width" to="50" begin="mouseout" />
     <set attributeName="height" to="50" begin="mouseout" />
  </rect>

</svg>

Empfänger, Element listener von XML Events

Bearbeiten
SVG                
tiny 1.2 - 9.5 - - - ? ? ?

Mit dem Element listener von XML Events kann ein Empfänger von Ereignissen notiert werden, welcher dann die Ausführung von Skripten auslösen kann, wenn das angegeben Ereignis eintritt.

Gegenüber dem Element handler ist listener eine Ergänzung für kompliziertere Konstruktionen.

Das Element gehört zum Namensraum

http://www.w3.org/2001/xml-events

Dieser Namensraum muss dann auch korrekt angegeben werden.

Attribut event
Bearbeiten

Mit dem Attribut wird ein Ereignis angegeben, auf welches der Empfänger warten soll. Der Wert ist ein Ereignis wie bereits beschrieben.

Das Attribut ist nicht animierbar.

Attribut observer
Bearbeiten

Mit dem Attribut kann angegeben werden, welches Element als der Empfänger des zu erwartenden Ereignisses registriert wird. Der Wert ist ein Fragmentidentifizierer im aktuellen Dokument.

Ist das Attribut nicht angegeben, so ist das Elternelement vom listener der Empfänger.

Das Attribut ist nicht animierbar.

Attribut target
Bearbeiten

Das Attribut gibt den Fragmentidentifizierer des Zielelementes des Ereignisses an, wo also das Ereignis wirklich stattfindet. Der Wert ist ein Fragmentidentifizierer im aktuellen Dokument.

Ferner ist das Zielelement immer entweder das Element selbst, welches mit observer identifiziert wird oder ein Kindelement davon. Damit ist es also möglich, ein Kindelement innerhalb eines Gruppierungselementes anzusprechen, wobei das Gruppierungselement der Empfänger ist und das Kindelement das Zielelement des Ereignisses. Exakt das angegebene Element ist dann das Zielelement, nicht etwa ein Kindelement desselben.

Das Attribut ist nicht animierbar.

Beispiel:

<g id="g">
<ev:listener event="click" observer="g"
     target="t" handler="#Anwendung" xmlns:ev="http://www.w3.org/2001/xml-events"/>
<handler id="Anwendung" type="application/ecmascript">
 var t = evt.target;
 t.setFloatTrait("y", 80);
</handler>
<text x="20" y="20" id="t">Hallo <tspan id="s">Welt</tspan>!</text>
</g>

Nur wenn 'Hallo ' oder '!' angeklickert wird, wird das Ereignis ausgelöst, nicht wenn 'Welt' angeklickert wird, weil das Zielelement des Ereignisses dann nicht #t ist, sondern #s.

Attribut handler
Bearbeiten

Das Attribut referenziert eine Anwendung, einen handler, welcher beim Eintritt des Ereignisses gegebenenfalls ausgeführt wird.

Der Wert ist eine IRI einer Anwendung.

Das Attribut ist nicht animierbar.

Attribut phase
Bearbeiten

Das Attribut setzt die Einfangphase für das Ereignis. Der einzige Wert ist 'default', was auch die Voreinstellung ist, daher muss das Attribut nicht explizit notiert werden.

Das Attribut ist nicht animierbar.

Attribut propagate
Bearbeiten

Das Attribut gibt an, ob Ereignisse nach dem Eintritt weitergeleitet werden sollen oder nicht, also ob Einfang und Blubbern eingeleitet werden sollen.

Der Wert des Attributes ist entweder 'stop' oder 'continue'.

Mit 'stop' wird das Ereignis nicht weitergeleitet, das Ereignis blubbert also nicht weiter zu den Elternelementen. Mit 'continue' wird das Ereignis gegebenenfalls weitergeleitet, sofern es blubbert und wird auch von den Elternelementen registiert.

Die Voreinstellung ist 'continue'.

Das Attribut ist nicht animierbar.

Attribut defaultAction
Bearbeiten

Das Attribut gibt an, ob nach der Skriptausführung die voreingestellte Aktion ausgeführt wird oder nicht. Bei einem Element a ist beispielsweise beim Anklickern die voreingestellte Aktion der Aufruf des Verweiszieles.

Der Wert des Attributes ist entweder 'cancel' oder 'perform'.

Mit 'cancel' wird die voreingestellte Aktion unterbunden. Mit 'perform' findet die voreingestellte Aktion nach der Skriptausführung statt.

Voreinstellung ist 'perform'.

Nicht alle Aktionen können unterbunden werden. Können sie nicht unterbunden werden, wird das Attribut ignoriert. Im Kapitel Interaktivität ist etwa angegeben, welche Ereignisse unterbrochen/unterbunden werden können.

Das Attribut ist nicht animierbar.

Beispiel für Element listener
Bearbeiten

Ist Skriptinterpretation für ecmascript verfügbar, so springt das jeweilige Quadrat ein Stück weiter nach links unten, wenn sich das Zeigergerät ein bestimmtes Ereignis auslöst. Beim grünen Quadrat ist es 'mouseover', beim gelben 'mousewheel', beim magenta 'mousemove'. Wird der untere oder der rechte Rand des Dokumentes nahezu erreicht, springt das Quadrat nach oben, beziehungsweise links.

<?xml version="1.0" encoding="utf-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny"
     xmlns:ev="http://www.w3.org/2001/xml-events"
     viewBox="0 0 1200 1000"
     xml:lang="de">

<title>Beispiel Elemente handler und listener</title>
<defs>
  <ev:listener event="mouseover" observer="rect1" handler="#Anwendung"/>
  <ev:listener event="mousewheel" observer="rect2" handler="#Anwendung"/>
  <ev:listener event="mousemove" observer="rect3" handler="#Anwendung"/>

  <handler id="Anwendung" type="application/ecmascript">
    var rect = evt.target;
    var x = rect.getFloatTrait("x");
    if (x &gt;= 1090) {
      x=x-1090;
    }
    var y = rect.getFloatTrait("y");
    if (y &gt;= 890) {
      y=y-890;
    }
    rect.setFloatTrait("x", (x+10));
    rect.setFloatTrait("y", (y+10));
  </handler>
</defs>

  <rect width="1200" height="1000" fill="#ddd"/>
  <rect id="rect1" x="10" y="10" width="100" height="100"
        fill="#080" stroke="blue" stroke-width="2"/>
  <rect id="rect2" x="215" y="10" width="100" height="100"
        fill="#ff0" stroke="blue" stroke-width="2"/>
  <rect id="rect3" x="420" y="10" width="100" height="100"
        fill="#a0f" stroke="blue" stroke-width="2"/>

</svg>

Weitere Beispiele

Bearbeiten

Aufgrund der bereits diskutierten Sicherheitsprobleme ist es bei Wikibooks unterbunden, Dokumente hochzuladen, die Skripte enthalten, daher sind die Beispiele dieses Kapitels nicht direkt ausführbar, sondern nur als Quelltext verfügbar. Nach einer entsprechenden Prüfung, kann der Nutzer diese in ein SVG-Dokument kopieren und ausprobieren, sofern bei der Prüfung keine Bedenken aufgekommen sind und ein Darstellungsprogramm zur Verfügung steht, bei der die verwendete Skriptsprache interpretiert wird.

Ziehen und Fallenlassen

Bearbeiten

Bei aktivierter Skriptinterpretation der Sprache ecmascript können bei folgendem Beispiel Figuren verschoben, gedreht und skaliert werden werden. Das Element desc enthält eine genauere Beschreibung. Neben den üblichen Zugriffen und Manipulationen über das DOM werden hier einige für SVG spezifische Methoden verwendet, etwa um die Mausposition in lokalen Koordinaten zu finden oder aber auch, um auf Listenpunkte im Attribut transform zuzugreifen und diese gezielt zu manipulieren. Um dieses etwas zu vereinfachen, wurden die entsprechenden Transformationen bereits statisch im Attribut vorgegeben. Sonst wäre es notwendig, das Attribut im Skript genauer zu analysieren, bevor man Listenpunkte davon gezielt manipulieren kann. Alternativ könnte man natürlich den aktuellen Status auch immer im Skript selbst vorhalten und zur Änderung immer nur darauf zugreifen und nicht auf den aktuellen Attributwert und diesen dann immer komplett neu setzen. Mit der Strategie wäre es dann auch nicht notwendig, die für SVG spezifischen Methoden zu verwenden, um das Attribut transform zu ändern, die Analyse ohne spezifische Methoden ist bei so komplizierten Attributwerten allerdings ohne die für SVG spezifischen Methoden recht mühsam.

<?xml version="1.0" encoding="utf-8" ?>
 <svg version="1.1"
      viewBox="-60 0 660 400" 
      xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xml:lang="de">
<title>Ziehen und Fallenlassen</title>
<desc>Per Skript können mit der Maus Objekte 
verschoben, gedreht und skaliert werden.
Zur Auswahl der Funktion dient das Menü links, 
V für verschieben, D für drehen, S für skalieren.  
Um die Funktion bei einer Figur auszuführen, 
wird die Figur mit der Maus gedrückt.
Danach folgt die Figur der Mausbewegung 
solange die Maus gedrückt gehalten wird.
Mit dem Loslassen der Maus wird auch die Figur fallengelassen, 
bleibt also im letzten Zustand.
</desc>

<polyline points="0,0 600,0 600,400 0,400 0,0" stroke="#003" fill="#ffc"/>
   
<g id="js"> 
  <ellipse rx="30" ry="60" 
   fill="#ccf" stroke="#f8f" stroke-width="8" fill-opacity="0.7" 
   transform="translate(100,100) scale(1,1) rotate(20) rotate(0)" />
  <circle r="50" 
   fill="#0f0" stroke="#040" stroke-width="8" fill-opacity="0.7" 
   transform="translate(200,70) scale(1,1) rotate(90) rotate(0)"/>
  <ellipse rx="20" ry="70" 
   fill="#f8f" stroke="#a0a" stroke-width="8" fill-opacity="0.7" 
   transform="translate(400,300) scale(1,1) rotate(-30) rotate(0)"/>
  <rect x="-40" y="-20" width="80" height="40" 
   fill="#0f0" stroke-width="8" stroke="#fa8" fill-opacity="0.7"
   transform="translate(500,100) scale(1,1) rotate(40) rotate(0)"/>
  <polygon points="0,-30 20,10 -10,20" 
   fill="#ff0" stroke="#008" stroke-width="8" fill-opacity="0.7" 
   transform="translate(250,250) scale(1,1) rotate(30) rotate(0)"/>
  <path d="M-50-50C-50 50 50-50 50 50 -50,50 50-50 -50-50z" 
   fill="#f0f" stroke="#606"  stroke-width="8" fill-opacity="0.7"
   transform="translate(450,200) scale(1,1) rotate(70) rotate(0)"/>
  <rect x="-30" y="-20" width="60" height="40" rx="10" 
   fill="#008" fill-opacity="0.7" stroke="#480" stroke-width="8"
   transform="translate(500,350) scale(1,1) rotate(-40) rotate(0)" />
  <line x1="-60" x2="60" y1="0" y2="0" 
   stroke="red" stroke-width="8" fill="none"
   transform="translate(100,300) scale(1,1) rotate(-30) rotate(0)" />
  <polyline points="-50,-50 50,10 50,50 -20,30" 
   stroke="blue" stroke-width="8" fill="none" 
   transform="translate(350,80) scale(1,1) rotate(20) rotate(0)" />
</g>
 
<g id="menue" transform="translate(-30,30)"
   font-family="sans-serif" font-size="20" 
   text-anchor="middle" fill="#004">
   <g id="v">
     <title>Verschieben</title>
     <circle r="25" fill="#ccf" stroke="blue"/>
     <text y="8">V</text>
   </g>
   <g id="d">
     <title>Drehen</title>
     <circle cy="60" r="25" fill="#ccf" stroke="blue"/>
     <text y="68">D</text>
   </g>
   <g id="s">
     <title>Skalieren</title>
     <circle cy="120" r="25" fill="#ccf" stroke="blue"/>
     <text y="128">S</text>
   </g>  
</g>

<defs>
<script type="application/ecmascript">
  // Wurzelelement identifizieren
  var svg = document.rootElement
  
  // Wenn nichts ausgewählt wird, verschieben
  var typ='v'

  // gucken, ob bei einem Element die Maus gedrückt wird
  document.getElementById("js").addEventListener("mousedown", maus, false)
  // Menüpunkt gedrückt?
  document.getElementById("v").addEventListener("click", v, false)
  document.getElementById("d").addEventListener("click", d, false) 
  document.getElementById("s").addEventListener("click", s, false)   
 
// Menü, Typumschaltung  
function v() {
  typ='v'
}
function d() {
  typ='d'
} 
function s() {
  typ='s'
}   
  
function maus(evt) {
  // Koordinatenpaar für weitere Rechnungen definieren
  // (Erzeugt ein Objekt mit Eigenschaften x und y (und 1 für z))
  var pm = svg.createSVGPoint()
     
  // welches Element ist angeklickert?
  ziel = evt.target 
  ziel.addEventListener("mouseup", raus, false)
  document.getElementById("js").removeEventListener("mousedown", maus, false)
  document.getElementById("js").addEventListener("mousemove", bewegen, true)
  // gucken, wo das Objekt gerade ist
  // dazu schauen wir ins transform-Attribut, 
  // dort der Eintrag 0 und dort in die Matrix, 
  // e und f sind die Koordinaten für die Translation
  bx=ziel.transform.baseVal.getItem(0).matrix.e    
  by=ziel.transform.baseVal.getItem(0).matrix.f

  // gucken, wo die Maus in Bildschirmkoordinaten gerade ist
  pm.x = evt.clientX
  pm.y = evt.clientY
  // Mausposition in lokalen Koordinaten berechnen
  var q = pm.matrixTransform(svg.getScreenCTM().inverse())   
  // Abstand
  dx = bx - Math.round(q.x)
  dy = by - Math.round(q.y)
  // Winkel
  dw = Math.atan2(dx,dy)
  // ... damit das Objekt nachher bei der Verschiebung nicht springt ...

  // Element nach oben setzen, als letztes Kind des Elternelementes
  ziel.parentNode.appendChild(ziel)            
}
 
function raus(evt) {    
  // Wenn Objekt angeklickert wird, wieder global gucken
  ziel.removeEventListener("mouseup", bewegen, false)
  document.getElementById("js").addEventListener("mousedown", maus, false); 
  document.getElementById("js").removeEventListener("mousemove", bewegen, true);
}
  
function bewegen(evt) {
  // wenn sich die Maus bewegt, folgen ...
  
  // Bild maximal eine Sekunde nicht neu auffrischen
  var id = ziel.ownerSVGElement.suspendRedraw(1000)
  // Koordinatenpaar für weitere Rechnungen definieren
  var pt = svg.createSVGPoint()
  // gucken, wo die Maus in Bildschirmkoordinaten gerade ist
  pt.x = evt.clientX
  pt.y = evt.clientY
  // Mausposition in lokalen Koordinaten
  var p = pt.matrixTransform(svg.getScreenCTM().inverse()) 
  var x = dx + Math.round(p.x)
  var y = dy + Math.round(p.y) 
  x=Math.min(600,Math.max(0,x))
  y=Math.min(600,Math.max(0,y))
  // Winkelberechnung
  var wx = bx - Math.round(p.x)
  var wy = by - Math.round(p.y)
  var ww = Math.atan2(wx,wy)
  var w = (dw -ww)*180/Math.PI 
  // Skalierungsfaktor berechnen
  var s1 = Math.sqrt(dx*dx+dy*dy)
  var s2 = Math.sqrt(wx*wx+wy*wy)
  if (s1*s2 !=0) {
   var s = s2/s1
  } else {
   var s=1
  }
  // Transformation je nach Typ setzen
  var transe=document.rootElement.createSVGTransform()    
  if (typ=='v') {
    // Translation setzen
    transe.setTranslate(x,y)
    ziel.transform.baseVal.replaceItem(transe,0)
  } else  if (typ=='d') {
    // Drehung setzen
    transe.setRotate(w,0,0)
    ziel.transform.baseVal.replaceItem(transe,3)
  } else  if (typ=='s') {
    // Skalierung setzen
    transe.setScale(s,s)
    ziel.transform.baseVal.replaceItem(transe,1)
  } 
  // Bild auffrischen wieder erlauben
  ziel.ownerSVGElement.unsuspendRedraw(id)
}
</script>
</defs>
</svg>

Markierungsorientierung und einfachen Pfad ziehen

Bearbeiten

Ein Beispiel mit ecmascript, um Markierungen für einfache Pfade anzusehen und einen einfachen Pfad mit Unterpfaden zu ziehen. Eine detailliertere Beschreibung steht im Element desc. Für das Attribut d von Pfaden gibt es in SVG auch spezifische Zugriffs- und Manipulationsmethoden. Statt diese zu verwenden, wird hier allerdings lediglich auf das Attribut zugegriffen und es werden weitere Segmente an den Pfad angehängt.

Hinsichtlich der Markierungen der grünen Linie: Die Spitze des Pfeiles zeigt immer in Richtung der Linie. Das Quadrat ist immer entlang der x- und y-Achse ausgerichtet. Für den selbst ziehbaren blauen Pfad werden Markierungen mit automatischer Ausrichtung am Anfang, Ende und an jeder Ecke automatisch ausgerichtet.

<?xml version="1.0" encoding="UTF-8" ?>
<svg version="1.1" 
     viewBox="0 0 1000 1000"
     xmlns="http://www.w3.org/2000/svg"
     xml:lang="de">
<title>Markierungsorientierung und einfachen Pfad ziehen</title>

<desc>
Um einen Pfad zu ziehen, ist auf die Zeichenfläche zu klicken, 
nicht auf den blauen Kreis.
Damit ergibt sich die Darstellung einer grünen Linie 
mit Anfangs- und Endmarkierung.
Mit weiteren Klicks werden weitere Pfadpunkte gesetzt.
Der blaue Pfad hat eigene Markierungen.
Der Unterpfad wird beendet durch Klicken 
des blauen Unterpfadknopfes.
Dies pausiert die Malaktion.
Durch ein weiteres Klicken des Unterpfadknopfes 
wird die Malaktion wieder aktiviert.
Mit dem nächsten Klick auf die Zeichenfläche 
wird also ein neuer Unterpfad begonnen
und mit weiteren Klicks fortgesetzt.
</desc> 
<defs>
 <marker id="start" markerWidth="20" markerHeight="20" 
  refX="5" refY="5" orient="auto"> 
  <rect fill="red"  width="10" height="10" />
 </marker>
 <marker id="end" markerWidth="10" markerHeight="10" 
  refX="10" refY="5" orient="auto">
  <polygon points="0,0,10,5,0,10,0,0" fill="black"/>
 </marker>
     <marker id="mid"
      viewBox="0 0 10 10" 
      refX="0" refY="5" 
      markerUnits="strokeWidth"
      markerWidth="10" markerHeight="10"
      orient="auto">
      <path fill="blue" d="M 0 2 L 10 5 L 0 8 z" />
 </marker>
 <marker id="mid" markerWidth="10" markerHeight="10" 
  refX="10" refY="5" orient="auto">
  <polygon points="0,2,10,5,0,8,2,0" fill="#00f"/>
 </marker> 
</defs>
 
<path id="p" d="M30,30 " stroke="#00a" stroke-width="2" fill="none" 
      marker-mid="url(#mid)" marker-end="url(#mid)" marker-start="url(#mid)" />
 
<line id="l" x1="30" y1="30" x2="30" y2="30" stroke="green" stroke-width="2"
      marker-end="url(#end)" marker-start="url(#start)" />
 
<circle id="u" r="25" cx="30" cy="30" fill="#ccf" stroke="#00f" />

<defs>
<script type="application/ecmascript">
  // Wurzelelement identifizieren
  var svg = document.rootElement
  
  // Pfadanfang   
  anz=0
  x=500
  y=500
  var linie, p
  
  //Zustand
  zustand=0
  
  // start, wenn alles geladen ist.
  window.addEventListener("load", start, false)
  
function start () {  
  linie = document.getElementById("l") 
  pfad = document.getElementById("p")
  unterpfad = document.getElementById("u")  
  // gucken, ob bei einem Element die Maus gedrückt wird
  document.addEventListener("click", wc, false)
  unterpfad.addEventListener("click", upf, false)
}   

// Unterpfad-Knopf gedrückt:
function upf(evt) {
  // falls bislang ein Pfad gemalt wurde, 
  // Linie unter Knopf setzen, Ereignisse abschalten
  if (zustand==0) {
    linie.setAttributeNS(null,"x2", 30)
    linie.setAttributeNS(null,"y2", 30)
    linie.setAttributeNS(null,"x1", 30)
    linie.setAttributeNS(null,"y1", 30)
    document.removeEventListener("mousemove", wm) 
    document.removeEventListener("click", wc, false)
    // merken Zustand inaktiv
    zustand=1
  } else {
    // wenn Knopf im inaktiven Zustand bedrückt, 
    // Ereignisse wieder anschalten
    document.addEventListener("click", wc, false)
    document.addEventListener("mousemove", wm, false)
    // merken Zustand wieder aktiviert
    zustand=2
    // neuen Unterpfad mit nächstem Klick beginnen
    anz=2
  }
}

// Welches Ende der Linie ändern?
function wm(evt) {
  set(evt,2)
}

function wc(evt) {
  // welcher Mausknopf ist gedrückt?
  var knopf=evt.button

  if (knopf==0) {
    // linke Maustaste weiter, Pfad malen
    document.addEventListener("mousemove", wm, false)
    set(evt,1)
  }
}
   
function set(evt,num) {
  // Koordinatenpaar für weitere Rechnungen definieren
  // (Erzeugt ein Objekt mit Eigenschaften x und y (und 1 für z))
  var pm = svg.createSVGPoint()

  // gucken, wo die Maus in Bildschirmkoordinaten gerade ist
  pm.x = evt.clientX 
  pm.y = evt.clientY 
  // Mausposition in lokalen Koordinaten berechnen
  var q = pm.matrixTransform(svg.getScreenCTM().inverse()) 
 
  // Position
  x = Math.round(q.x)
  y = Math.round(q.y)
  
  if (zustand==0) {
    // wenn aktiv, aber nicht gerade reaktiviert
    linie.setAttributeNS(null,"x"+num, x)
    linie.setAttributeNS(null,"y"+num, y)

    if (num==1) {
      // Pfad erweitern
      var pd = pfad.getAttributeNS(null,"d")

      // neuen Pfadpunkt setzen
      if (anz==0) {
        // Pfadanfang
        pd ='M ' + x + ',' + y + ' '
        anz=1
      } else if (anz==2) {
        // Anfang Unterpfad 
        pd +='M ' + x + ',' + y + ' '
        anz=1
      } else {
        // Pfad fortsetzen
        pd +=' '+ x + ',' + y + ' '
      }
      pfad.setAttributeNS(null,"d", pd)
    }
  } else if (zustand==2) {
    // wenn reaktiviert, nur Zustand auf aktiv setzen,
    // sonst würde der neue Unterpfad immer im Unterpfad-Knopf beginnen
    zustand=0
  }
}
</script>
</defs>
</svg>

Vergleich deklarative Animation und Skript

Bearbeiten

Sofern die Interpretation von Skripten vom Typ emcmascript verfügbar ist, ergibt sich bei folgendem Beispiel ein Vergleich einer kontinuierlichen oder schrittweisen Änderung von Attributen und Eigenschaften mit deklarativer Animation und Skript.

Die gefüllte Objekte werden per deklarativer Animation animierten, die nur mit einem Strich versehenen per Skript. Eine genauere Beschreibung findet sich wieder im Element desc. Für die Skriptanimation werden zwei Methoden verwendet, die eine ist prinzipiell ungenau, die andere in der praktischen Umsetzung in gängigen Darstellungsprogrammen ebenfalls. Insgesamt zeigt sich, dass sich solche Skript-Animationen nicht eignen, wenn es um einen korrekten Zeitablauf geht.

<?xml version="1.0" encoding="UTF-8" ?>
<svg viewBox="-240 -200 440 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>Vergleich deklarative Animation und Skript</title>
<desc>
Die gefüllten Elemente im Hintergrund sind deklarativ animiert, 
grün diskret, violett kontinuierlich.
Die Elemente ohne Füllung werden gegebenenfalls mit zwei Skriptmethoden animiert.
Die Methode setTimeout verzögert jeweils um einen festen Zeitschritt 
und führt dann eine Funktion erneut aus.
Diese Methode wird bei den Elementen mit gelbem Strich verwendet.
Die Methode setInterval wiederholt eine Funktion nach einem festen Zeitintervall,
bis sie mit der Methode clearInterval beendet wird.
Diese Methode wird bei den Elementen mit cyanem Strich verwendet.
Gegenüber den deklarativen Methoden ergibt sich bei setInterval 
folglich eine einmalige Ungenauigkeit oder Verzögerung
bedingt durch die Verzögerung durch vorhergehende Skriptteile.
Ein weiteres Problem ergibt sich, 
wenn die Verarbeitung der Funktion länger dauert als das Intervall lang ist.
Bei der Methode setTimeout ergibt sich hingegen 
bei jedem erneuten Ausführen der Funktion eine Ungenauigkeit, 
die über die gesamte Animation kumuliert. 
Somit ist die Methode setTimeout ungeeignet für präzise Zeitabläufe.
In der Praxis zeigt sich mit den üblichen Darstellungsprogrammen allerdings, 
daß auch setInterval deutlich ungenauer ist als deklarative Animation.

Gestartet werden die beiden Vergleiche jeweils mit den Knöpfen links - 
um zu vermeiden, daß sich die Skriptanimationen hinsichtlich der 
Genauigkeit des Zeitablaufes gegenseitig beeinflussen, kann es sinnvoll sein,
erst den einen Vergleich zu aktivieren und erst nach dessen Ende den zweiten.
Mit dem mittleren Knopf werden beide Gruppen gleichzeitig gestartet, mit den
farblich korrespondierenden Knöpfen jeweils eine Gruppe.
Jede Gruppe kann nur einmal gestartet werden.
Gegebenenfalls ist also das Dokument neu zu laden.
</desc>  
 
  <rect x="-200" y="-200" width="400" height="400" rx="5"
        fill="#eee" stroke="black"/>
 
   <g fill="#060" stroke="#00a" stroke-width="2">
    <desc>Vergleich diskrete deklarative Animation</desc>
    <animate attributeName="fill-opacity" calcMode="discrete" fill="freeze"
      values="0;0.0025" dur="0.025s" repeatDur="10s" accumulate="sum" 
      begin="c2.click; c0.click" restart="never" />
    <animateTransform attributeName="transform" type="scale" fill="freeze" 
      calcMode="discrete" values="0;0.025" dur="0.025s" repeatDur="10s" 
      accumulate="sum" begin="c2.click; c0.click" restart="never" />
   <circle r="9" cx="-10" cy="-10" />
   <rect x="1" y="-19" width="18" height="18" rx="1" />
  </g>  
 
  <g fill="#404" stroke="#f00" stroke-width="2">
    <desc>Vergleich kontinuierliche deklarative Animation</desc>
    <animate attributeName="fill-opacity" fill="freeze" values="0;1" dur="10s" 
             begin="c1.click; c0.click" restart="never" />
    <animateTransform attributeName="transform" type="scale" fill="freeze" 
      values="0;10" dur="10s" begin="c1.click; c0.click" restart="never"/>
   <circle r="9" cx="10" cy="10" />
   <rect x="-19" y="1" width="18" height="18" rx="1" />
  </g> 
 

  <g id="g1" fill="none" stroke="#ff0">
   <desc>Gegebenenfalls per Skript manipulierte Gruppe (setTimeout)</desc>	  
   <circle r="9" cx="10" cy="10" />
   <rect x="-19" y="1" width="18" height="18" rx="1" />
  </g>
  <g id="g2" fill="none" stroke="#0ff">
   <desc>Gegebenenfalls per Skript manipulierte Gruppe (setInterval)</desc>	  
   <circle r="9" cx="-10" cy="-10" />
   <rect x="1" y="-19" width="18" height="18" rx="1" />
  </g>  

<g id="menue">
  <circle id="c0" cx="-220" cy="0" r="18" fill="#fcc" stroke="#006"><title>Start Vergleich alles</title></circle> 
  <circle id="c1" cx="-220" cy="40" r="18" fill="#404" stroke="#ff0"><title>Start Vergleich kontinuierlich - setTimeout</title></circle>
  <circle id="c2" cx="-220" cy="-40" r="18" fill="#060" stroke="#0ff"><title>Start Vergleich diskret - setInterval</title></circle>
</g>


<defs>
<script type="application/ecmascript">
  //Zeiten in Millisekunden
  var Zeit = 0
  var t = 0
  var Zeitschritt = 40
  var Dauer = 10000
  
  // Animation schon gelaufen?
  var b1=false
  var b2=false
  
  var ti
  var Gruppe1 = document.getElementById("g1")
  var Gruppe2 = document.getElementById("g2")
  // Menüpunkt gedrückt?
  document.getElementById("c0").addEventListener("click", c0, false)
  document.getElementById("c1").addEventListener("click", c1, false)
  document.getElementById("c2").addEventListener("click", c2, false) 

function c0() { 
  document.getElementById("c0").removeEventListener("click", c0, false) 
  // nur wenn noch nicht gelaufen ...
  if (!b1) {
    document.getElementById("c1").removeEventListener("click", c1, false)
    b1=true
    Timeout()
  }
  if (!b2) {
    document.getElementById("c2").removeEventListener("click", c2, false)
    b2=true
    ti=window.setInterval(function () {Trans()}, Zeitschritt)
  }
}

function c1() {
  document.getElementById("c1").removeEventListener("click", c1, false)
  b1=true
  Timeout()
}
function c2() {
  document.getElementById("c2").removeEventListener("click", c2, false)
  b2=true     
  ti=window.setInterval(function () {Trans()}, Zeitschritt)
}

 // schrittweise Animation mit Methode setInterval
function Trans() {
  // Gruppe schrittweise vergrößern, bis es zehnfach vergrößert ist
  var Skalierung = (t * 10.) / Dauer
  Gruppe2.setAttribute("transform", "scale(" + Skalierung + ")")
  // Durchsichtigkeit ändern
  var Durchsichtigkeit = 0.5 + 0.5*t / Dauer
  Gruppe2.setAttribute("stroke-opacity", Durchsichtigkeit)
  //Zeit vergrößern
  t=t+Zeitschritt
  if (t &gt; Dauer) {
  window.clearInterval(ti)
  }
}

 // schrittweise Animation mit Methode setTimeout
function Timeout() { 
  // Gruppe schrittweise vergrößern, bis es zehnfach vergrößert ist
  var Skalierung = (Zeit * 10.) / Dauer
  Gruppe1.setAttribute("transform", "scale(" + Skalierung + ")")
  // Durchsichtigkeit ändern
  var Durchsichtigkeit = 0.5 + 0.5*Zeit / Dauer
  Gruppe1.setAttribute("stroke-opacity", Durchsichtigkeit)
  //Zeit vergrößern
  Zeit = Zeit + Zeitschritt
  //nach maximaler Zeit beenden
  if (Zeit &gt; Dauer) return
  // Skalieren nochmal aufrufen, Zeitschritt später.
  setTimeout("c1()", Zeitschritt)         
}    
</script>
</defs>  
</svg>

switch und script

Bearbeiten

Einfaches Beispiel, welches switch, Merkmalszeichenkette (featureString) und ecma-script verwendet. Die Anzeige des Dokumentes hängt ab von einer Merkmalszeichenkette und von ecma-script. Eine ausführliche Beschreibung gibt es wieder im Element desc.

Das ausgeführte Skript in diesem Dokument ist eine Änderung des Präsentationsattributes display auf none beziehungsweise auf block, um dem Nutzer zusätzliche Informationen zur Skriptausführung zu geben.

Bei SVG tiny 1.2 kann zusätzlich noch eine bedingte Verarbeitung davon abhängig gemacht werden, ob requiredFormats="application/ecmascript" zutrifft oder nicht, um herauszufinden, welche Skriptsprache verfügbar ist, wenn die Merkmalszeichenkette zu erkennen gibt, dass überhaupt eine Skriptinterpretation gegeben ist.

<?xml version="1.0" encoding="UTF-8" ?>
<svg viewBox="0 0 1000 600"
    xmlns="http://www.w3.org/2000/svg" version="1.1"
    xml:lang="de">
<title> switch und script </title>
<desc>
Einfaches Beispiel, welches switch, 
eine Merkmalszeichenkette (featureString) und ecmascript verwendet. 
Die Anzeige des Dokumentes hängt ab 
von einer Merkmalszeichenkette und von ecmascript.

Erster Fall: 
Merkmalszeichenkette gibt wahr für 'script' zurück 
und Skriptausführung ist aktiv - 
der Nutzer erhält eine Bitte, 
Skriptausführung abzuschalten, 
um mit dem Test fortzufahren.

Zweiter Fall: 
Merkmalszeichenkette gibt wahr für 'script' zurück 
und Skriptausführung ist deaktiviert -
der Nutzer bekommt eine Information über einen Fehler. 
Das Dokument enthält nur ecmascript. 
Falls das Darstellungsprogramm dies nicht interpretieren kann, 
sollte es falsch für die Merkmalszeichenkette 
und für dieses Dokument zurückgeben.

Dritter Fall: 
Merkmalszeichenkette gibt falsch für 'script' zurück 
und Skriptausführung ist trotzdem aktiv - 
der Nutzer sieht eine Information über den Fehler - 
Skriptausführung sollte generell nicht verfügbar sein, 
wenn die Merkmalszeichenkette falsch zurückgibt.

Vierter Fall: Merkmalszeichenkette gibt falsch für 'script' zurück 
und Skriptausführung ist deaktiviert - 
der Nutzer sieht einen Alternativtext. 
Zu beachten ist, 
dass die Merkmalszeichenkette eine Information darüber angibt, 
ob ein Merkmal unterstützt wird oder nicht, 
nicht ob es generell und irgendwelchen Umständen möglich ist, 
daß ein Merkmal unterstützt wird, in diesem Falle nur, 
ob Skriptausführung aktiviert ist. 
Falls die Merkmalszeichenketten irgendeinen Nutzen für Autoren haben sollen, 
muß der zurückgegebene Wert der Merkmalszeichenkette 
von den spezifischen aktuellen Bedingungen des entsprechenden Dokumentes 
und von den Voreinstellungen des Nutzers abhängen, 
nicht von einer generellen philosophischen Stellungnahme 
des Darstellungsprogrammes.  
</desc>  
<switch>
  <g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Script"
    fill="red" font-size="20" font-family="sans-serif">
    <text x="20" y="100" id="bsp0" display="block">
Nur sichtbar, falls Merkmalszeichenkette für 'script' wahr ist,
    </text>
    <text x="20" y="200" id="bsp1" display="block">
aber kein ecmascript interpretiert wird!
    </text>
    <text x="20" y="300" id="bsp2" display="block">
Allerdings enthält das Dokument keine andere Skriptsprache als ecmascript.
    </text>
    <text x="20" y="400" id="bsp3" fill="gray" display="none">
Skriptinterpretation aktiv!
    </text>
    <text x="20" y="500" id="bsp4" fill="black" display="none">
Bitte Skriptinterpretation deaktivieren und neuladen,
um mit dem Test fortzufahren!</text>
  </g>
  <g font-size="30" font-family="sans-serif" fill="#440">
    <text x="20" y="70" font-size="50">
Alternativtext
    </text>
    <text x="20" y="140" font-size="20">
Sichtbar, wenn keine Skriptinterpretation verfügbar ist.
    </text>
    <text x="20" y="210" font-size="20" fill="#66f" display="block">
Bei Bedarf Skriptinterpretation aktivieren und neuladen, 
um mit dem Test fortzufahren.
    </text>
 
    <text x="20" y="500" font-size="30" fill="red" display="none" id="bsp5">
Skriptinterpretation trotzdem aktiv? Fehler!
    </text>
 </g>
</switch>

<defs>
<script type="application/ecmascript">   
  document.getElementById("bsp0").setAttributeNS(null,"display",'none')
  document.getElementById("bsp1").setAttributeNS(null,"display",'none')
  document.getElementById("bsp2").setAttributeNS(null,"display",'none')
  document.getElementById("bsp3").setAttributeNS(null,"display",'block')
  document.getElementById("bsp4").setAttributeNS(null,"display",'block')
  document.getElementById("bsp5").setAttributeNS(null,"display",'block')
</script>
</defs>
</svg>

Titel und Beschreibung auslesen

Bearbeiten

Bei gängigen Darstellungsprogrammen ergibt sich das Problem, dass man, einmal abgesehen von der Einsicht direkt in den Quelltext, keinen direkten und kompletten Zugriff auf Titel und Beschreibung von Elementen hat. Mittels Skript ist es nun recht einfach möglich, diese Inhalte interaktiv und mit Bezug zur visuellen Präsentation der Elemente anzuzeigen. Dies ist in folgendem Beispiel umgesetzt. Es ist sogar möglich, ausgehend von einem Element im Dokument-Objekt-Modell zu navigieren und so Titel und Beschreibung von Eltern- und Kindelementen anzufordern. Damit ist das Beispiel gleichzeitig hilfreich, um zu erfahren, wie sich ein Autor in einem Dokument anhand des Dokument-Objekt-Modells ohne Vorwissen über die genaue Dokumentstruktur zurechtfinden kann. Zudem ist es ein Beispiel für die Behandlung von Textknoten, was innerhalb des Dokument-Objekt-Modells etwas mehr Aufwand erfordert als der Zugriff auf Elemente oder deren Attribute.

<?xml version="1.0" encoding="UTF-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
     xml:lang="de" viewBox="0 0 400 400">
<title>Titel und Beschreibung auslesen</title>
<desc>Per Klick auf ein Element kann dessen 
Titel und Beschreibung präsentiert werden.
Bei Bedarf wird auch das jeweilige Elternelement 
oder ein gegebenenfalls auswählbares Kindelement 
entsprechend behandelt.
So ist es möglich, 
schrittweise alle Titel und Beschreibungen 
im Dokument präsentiert zu bekommen.
</desc>
 
<defs>
<script type="application/ecmascript"> 
// auf click lauschen   
window.addEventListener("click", textalt, false)
function textalt (evt) {
  // welcher Knopf, welches Element?
  var ele = evt.target
  var knopf=evt.button

  // falls der linke Knopf gedrückt ist 
  // (mittlerer und rechter Knopf funktionieren 
  // bei einigen Programmen leider nicht)
  if (knopf==0) {
    var weiter=true
    var cp=''
    while (weiter) {
   
      if (ele.nodeName == 'svg') {
        var dummy=beschreibung(ele)
        dummy+='____________\n\n\n# Weitere Beschreibungen?'
        dummy+='\n Zahl: Kindelement\n'
        cp=prompt(dummy)
        if ((cp=='w') || (cp=='e')){
          weiter=false
          cp=''
        }
      } else {
        var dummy=beschreibung(ele)
        dummy+='____________\n\n\n# Weitere Beschreibungen?'
        dummy+='\n w: Wurzelelement\n e: Elternelement'
        dummy+='\n Zahl: Kindelement\n'  
        cp=prompt(dummy)
      }  
      if (cp=='w') {
        weiter==true
        ele=document.rootElement      
      } else if (cp=='e') {
        ele=ele.parentNode
        weiter==true
      } else {
        cp=Math.min(Math.max(0,parseInt(cp)),ele.childNodes.length-1)
        var test=ele.childNodes.item(cp)
        if (test.nodeType == 1) {
          ele=test
          weiter==true
        } else {
          weiter=false
        }   
      }
    }
  }
}   

function beschreibung(ele) {
   // erstes Kind ermitteln
   var child = ele.firstChild
   var ename=ele.nodeName
   // Text vorbereiten
   var title=''
   var desc=''
   var was=''
   var n=0
   var kinder=''
   //gucken was drin ist, wenn es Kinder gibt
   while (child != null) {
     // Kindelemente merken
     if (child.nodeType == 1) {
      kinder+=n+' - '+child.nodeName+'\n'
     }
     // falls title, Text extrahieren
     if (child.nodeName == "title") {
       title+=child.textContent+'\n\n'
       was+='t'
     // falls desc, Text extrahieren  
     } else if (child.nodeName == "desc") {
       desc+=child.textContent+'\n\n'
       was+='d'
     }
     // nächstes Kind
     n+=1
     child = child.nextSibling;
     // wenn title und desc gefunden, dann beenden
     // sie sollten zudem die ersten beiden sein, 
     // den Rest muß man dann nicht mehr ansehen
     // if ((was == 'td')||(was == 'dt')) { break }
   }
   var raus='Element '+ename+'\n\n\n# Titel:\n'+
       raus+=title+'____________\n\n# Beschreibung:\n'
       raus+=desc+'____________\n\n# Kinder:\n'+kinder
   return raus;
}
</script>
</defs>
 
<g>
  <title>Titel vom Element g</title>
  <desc>Beschreibung vom Element g</desc>

  <circle cx="120" cy="120" r="100" fill="green">
    <title>Ein grüner Kreis</title>
      <desc>Beschreibung des grünen Kreises  
         - weitere Textzeile zum Test</desc>
    <title>
    zusätzliches Element title - 
    nur zu Testzwecken, sollte man sonst so nicht notieren.
    </title>
  </circle>  
  <g>
    <title>Titel von noch einem Element g</title>
    <desc>Beschreibung von noch einem Element g 
       - enthält einen blauen Kreis</desc>
    <circle cx="280" cy="280" r="100" fill="blue">
      <title>Ein blauer Kreis</title>
      <desc>Beschreibung des blauen Kreises</desc>
      <animate attributeName="fill" values="#ccf;#004;#ccf" 
               dur="120" repeatDur="indefinite">
	<title>Titel der Animation</title>
	<desc>Endlose Animation der Füllung im blauen Bereich</desc>
      </animate>
    </circle>  
  </g> 
</g>
</svg>

Zugriff und Manipulation bei Pfaden

Bearbeiten

SVG hat ja viele Listen als Attributwerte. Der Zugriff allgemein über das normale Dokument-Objekt-Modell ist dabei oft schwierig, weil dieses keine speziellen Methode hat, um solche Listen zu verwalten. Daher hat SVG hier Erweiterungen, die man zusätzlich verwenden kann. Während man mit deklarativer Animation etwa immer nur den kompletten Attributwert ändern kann - bei Transformationen immerhin noch etwas anhängen, kann per Skript auf einzelne Listenpunkte zugegriffen werden.

Das Attribut d des Elementes path hat als Wert eine besonders komplizierte Liste. Folgendes Beispiel zeigt den Zugriff und die Manipulation des Attributes d.

<?xml version="1.0" encoding="UTF-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
     xml:lang="de" viewBox="-500 -500 1000 1000">
<title>Pfadsegmente</title>
<desc>
Bei einem Pfad, bestehend aus zwei geschlossenen kubischen Unterpfaden, 
werden zufällig einzelne Pfadsegmente gewechselt.

Per Klick auf die Figur kann die Änderung angehalten werden 
und mit einem weiteren Klick wieder fortgesetzt werden.

Zusätzlich wird bei jeder Änderung bei einem dunkelblauen Pfad 
ein horizontales oder vertikales relatives Segment zufällig angehängt 
- dieser Pfad wird bei jeder Fortsetzung neu angefangen.

Ein magenta Pfad eiert zudem grob im Kreis umher, um zu zeigen, 
wie man Segmente an einer bestimmten Stelle in einen Pfad einsetzt.
Auch dieser Pfad wird bei jeder Fortsetzung neu angefangen.

Sind der dunkelblaue und der magenta Pfad zudem lang genug, 
wird gelegentlich auch mal ein Pfadsegment entfernt.
</desc>
 
<defs>
<script type="application/ecmascript">
var svg = document.rootElement;
var path, seqs, repl, ti, aktiv, pfad, liste, seg, q, ql, qs
var qw=0
document.documentElement.addEventListener("SVGLoad", start, false);
   
function start () {
// Zu Beginn lauschen auf Klick einschalten, 
// Pfad identifizieren und Segmentliste und Segment anlegen
document.getElementById("p").addEventListener("click", stop, false)
path = document.getElementById('p')
segs = path.pathSegList
repl = path.pathSeg

// neuen Pfad anlegen
var pf = document.createElementNS('http://www.w3.org/2000/svg','path')
pf.setAttributeNS(null,'id', 'pfad');
pf.setAttributeNS(null,'fill', 'none');
pf.setAttributeNS(null,'stroke', '#004');
pf.setAttributeNS(null,'d', 'M0,0');
svg.insertBefore(pf,path);
pfad = document.getElementById('pfad')
liste = pfad.pathSegList
seg = pfad.pathSeg

// noch einen neuen Pfad anlegen
var qf = document.createElementNS('http://www.w3.org/2000/svg','path')
qf.setAttributeNS(null,'id', 'q');
qf.setAttributeNS(null,'fill', 'none');
qf.setAttributeNS(null,'stroke', '#a06');
qf.setAttributeNS(null,'d', 'M0,0 q-10,-10 10,10');
svg.insertBefore(qf,path);
q = document.getElementById('q')
ql = q.pathSegList
qs = q.pathSeg

// Status Animation
aktiv=1
// Animation starten
ti=window.setInterval(function () {wechsel()}, 50)
}

// Bei Mausklick
function stop() {
  if (aktiv==1){
    // falls Animation aktiv, abschalten
    window.clearInterval(ti)
    aktiv=0
    // wieviele Segmente sind im erzeugten Pfad?
    var val=liste.numberOfItems
    T=document.getElementById('T')
    T.textContent=val    
  } else {
    // angelegten blauen Pfad zurücksetzen
    //liste.clear() // komplett säubern (brauchen wir nicht)
    var x=0
    var y=0 
    seg=pfad.createSVGPathSegMovetoAbs(x,y)  
    // Listeninhalt durch Anfangssegment ersetzen
    liste.initialize(seg)
    // angelegten magenta Pfad zurücksetzen
    var x=0
    var y=0 
    qs=q.createSVGPathSegMovetoAbs(x,y)  
    // Listeninhalt durch Anfangssegment ersetzen
    ql.initialize(seg)    
    
    // Segmentangabe beseitigen
    T=document.getElementById('T')
    T.textContent=''    
    // falls Animation nicht aktiv, anschalten
    aktiv=1
    ti=window.setInterval(function () {wechsel()}, 50)
  }
}

// Zufälliges Wechseln von Pfadsegmenten
function wechsel() {
// erstmal nichts malen
var id = svg.suspendRedraw(100)

// Erzeugter dunkelblauer Pfad, Segment anhängen
var hov=rand(0,1)
var val=(2*rand(0,1)-1)*10
if (hov==0) {
seg=pfad.createSVGPathSegLinetoHorizontalRel(val)
} else {
seg=pfad.createSVGPathSegLinetoVerticalRel(val)
}
liste.appendItem(seg)

// gelegentlich mal einen Listenpunkt entfernen
var val = rand(1,100)
if (val &gt; 70) {
  var val=liste.numberOfItems
  if (val &gt; 20) {
    val=rand(1,val-10)
    // Entfernen eines vorhandenen Listenpunktes:
    liste.removeItem(val)  
  }
  var val=ql.numberOfItems
  if (val &gt; 20) {
    val=rand(1,val-2)
    // Entfernen eines vorhandenen Listenpunktes:
    ql.removeItem(val)  
  }    
}
// magenta Pfad erweitern
  qw+=0.1
  var x1=rand(400,550)*Math.cos(qw)
  var y1=rand(400,550)*Math.sin(qw)
  qw+=0.1  
  var x2=rand(350,450)*Math.cos(qw)
  var y2=rand(350,450)*Math.sin(qw)
  qs=q.createSVGPathSegCurvetoQuadraticAbs(x2,y2,x1,y1)
  // das würde deutlich wilder ablaufen:
  //qs=q.createSVGPathSegCurvetoQuadraticSmoothAbs(x2,y2)
  ql.insertItemBefore(qs,1)

// Gelbgrüner Pfad:
// zufällig ein Segment auswählen 
// - wobei einige nicht geändert werden sollen, etwa das z-Segment
// - zwei Segmente werden zudem durch andere Segmente bestimmt, 
// damit die Pfade ohne Knick geschlossen werden.
var lp=rand(0,1)*6+rand(0,3)

if (lp == 0) {
  // Pfadanfang
  var x=rand(-300,300)
  var y=rand(-300,300)
  repl=path.createSVGPathSegMovetoAbs(x,y)
  segs.replaceItem(repl,0)

  // auch das Ende des Unterpfades muß passend geändert werden
  var xx1=path.pathSegList.getItem(1).x1
  var yy1=path.pathSegList.getItem(1).y1

  var x2=2*x-xx1
  var y2=2*y-yy1
  // man beachte die Reihenfolge, 
  // abweichend von der Notation im d-Attribut 
  // - hier erst der neue Punkt, dann der Kontrollpunkt
  repl=path.createSVGPathSegCurvetoCubicSmoothAbs(x,y,x2,y2)
  segs.replaceItem(repl,4)

} else if (lp == 4) {
  // sollte nicht vorkommen, wird automatisch berechnet
} else if (lp == 5) {
  // sollte nicht vorkommen, ist immer 'z'
} else if (lp == 6) {
// Anfang Unterpfad
  var x=rand(-300,300)
  var y=rand(-300,300)
  repl=path.createSVGPathSegMovetoAbs(x,y)
  segs.replaceItem(repl,6)
  
  // auch das Ende des Unterpfades muß passend geändert werden
  var xx1=path.pathSegList.getItem(7).x1
  var yy1=path.pathSegList.getItem(7).y1

  var x2=2*x-xx1
  var y2=2*y-yy1

  repl=path.createSVGPathSegCurvetoCubicSmoothAbs(x,y,x2,y2)
  segs.replaceItem(repl,10)

} else if (lp ==1) {
  // erstes kubisches Pfadsegment
  var x1=rand(-300,300)
  var y1=rand(-300,300)
  var x2=rand(-300,300)
  var y2=rand(-300,300)
  var x=rand(-300,300)
  var y=rand(-300,300)

  repl=path.createSVGPathSegCurvetoCubicAbs(x,y,x1,y1,x2,y2)
  segs.replaceItem(repl,1)
  
  // auch das Ende des Unterpfades muß passend geändert werden
  var x0=path.pathSegList.getItem(0).x
  var y0=path.pathSegList.getItem(0).y

  var x2=2*x0-x1
  var y2=2*y0-y1

  repl=path.createSVGPathSegCurvetoCubicSmoothAbs(x0,y0,x2,y2)
  segs.replaceItem(repl,4)

} else if (lp ==7) {
  // erstes kubisches Pfadsegment des zweiten Unterpfades
  var x1=rand(-300,300)
  var y1=rand(-300,300)
  var x2=rand(-300,300)
  var y2=rand(-300,300)
  var x=rand(-300,300)
  var y=rand(-300,300)

  repl=path.createSVGPathSegCurvetoCubicAbs(x,y,x1,y1,x2,y2)
  segs.replaceItem(repl,7)
  
  // auch das Ende des Unterpfades muß passend geändert werden
  var x0=path.pathSegList.getItem(6).x
  var y0=path.pathSegList.getItem(6).y

  var x2=2*x0-x1
  var y2=2*y0-y1

  repl=path.createSVGPathSegCurvetoCubicSmoothAbs(x0,y0,x2,y2)
  segs.replaceItem(repl,10)

} else {
  // irgendein S-Segment ändern
  var x1=rand(-300,300)
  var y1=rand(-300,300)
  var x2=rand(-300,300)
  var y2=rand(-300,300)
  repl=path.createSVGPathSegCurvetoCubicSmoothAbs(x2,y2,x1,y1)
  segs.replaceItem(repl,lp)
}

// malen wieder erlauben
svg.unsuspendRedraw(id)

}   

// ganzzahlige Zufallszahl...
function rand (min, max) {
   return Math.floor(Math.random() * (max - min + 1)) + min;
}
 </script>
 </defs>
 
<path d="
M-400,0 
C -400,-100 -100,-400 0,-400 
S 400,-100 400,0 
S 100,400 0,400 
S -400,100 -400,0 
z
M-200,0 
C -200,100 -100,200 0,200 
S 200,100 200,0 
S 100,-200 0,-200 
S -200,-100 -200,0 
z" 
id="p" fill="#ffc" stroke="green" fill-opacity="0.5" />

<text id="T" x="0" y="490" 
  font-size="10" fill="black" 
  text-anchor="middle"></text>
</svg>

Parametereingabe

Bearbeiten

Zum Beispiel bei Spielen kann es nützlich sein, wenn der Spieler Parameter des Spiels selbst bequem über eine graphische Schnittstelle eingeben kann. Für ähnliche Interaktionen hat (X)HTML etwa Formulare, welche allerdings eigentlich dafür gedacht sind, Eingaben an ein Dienstprogramm zu senden. SVG 1.1 hat selbst keine solche Eingabemöglichkeiten, nicht einmal, um Eingaben an ein Dienstprogramm zu senden. Natürlich können dafür einfach GET-Parameter an eine URI angehängt werden, die der Dienstrechner auswerten kann, wenn die URI mit Parametern aufgerufen wird. SVG Tiny 1.2 hat allerdings die Möglichkeit, Textelemente als beschreibbar zu kennzeichnen. Ohne Skripteinsatz ist diese Funktion allerdings recht sinnlos, weil man diese Eingabe nicht an ein Dienstprogramm senden kann. Dieser Ansatz wurde also nicht zuende gedacht. Mit Skripteinsatz kann dieser vom Nutzer ausgefüllte Textinhalt natürlich ausgewertet werden. Selbstvertändlich ist es auch möglich, für Formulare in SVG Fragmente von XHTML oder XForms zu verwenden, meist ist es für eine weitgehende Interpretation durch Darstellungsprogramme allerdings eher sinnvoll, weder solche Fragmente einzubetten, noch sich darauf zu verlassen, dass ein Darstellungsprogramm die Texteingabe von SVG Tiny 1.2 korrekt interpretiert. Bei relevantem Inhalt wird man klassisch auf ein XHTML-Dokument mit Formular ausweichen und die Formulareingabe dann zu einem Dienstprogramm senden, welches das SVG als Ausgabe dynamisch erstellt.

Bei Skriptanwendungen wie Spielen ist es indessen oft nicht effektiv, Anfragen an ein Dienstprogramm zu senden. Solch interaktive Spiele funktionieren ohne Skriptanwendung ohnehin nicht. Von daher wird ein Autor für solche auch nur eine Textalternative bereitstellen, sofern eine Publikation erwogen wird. Eine solche kann natürlich auch problemlos eine Beschreibung des Einflusses von Parametern auf das Spiel beeinhalten. Parametereingaben im Falle einer Skriptinterpretation können recht einfach umgesetzt werden. Meistens wird es sich ohnehin um Zahlen handeln oder um eine feste Anzahl vordefinierter Alternativen, zwischen denen einfach ausgewählt werden kann. Daneben kann auch noch eine freie Texteingabe sinnvoll sein, dafür hat eine Skriptsprache wie JavaScript auch eine vordefinierte Methode, die im Beispiel zu Titel und Beschreibung auch bereits verwendet wurde.

Das folgende Beispiel zeigt ein paar graphische Umsetzungen solcher Eingaben. Dabei wurde darauf geachtet, dass die Anzahl gleichartiger Parametereingabemöglichkeiten leicht geändert werden kann, ebenso der Wertebereich. Die Beispiele zeigen somit, dass es ausreicht, Ereignisse für Gruppen gleichartiger Parametereingabemöglichkeiten zu behandeln, statt jede Parametereingabemöglichkeit einzeln zu behandeln.

Wie bei den vorherigen Beispielen findet sich eine genauere Beschreibung wieder im Element desc.

<?xml version="1.0" encoding="UTF-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
     xml:lang="de" viewBox="-40 -60 240 240">
<title>Parametereingabe</title>
<desc>
Einige Beispiele zur Parametereingabe per Skript.
Verschiedene Eingabetypen sind zu Gruppen zusammengefaßt, 
eine Gruppe kann bei Bedarf einfach erweitert werden.
Eine weitere einfache, hier nicht durchgeführte 
Verallgemeinerung besteht etwa darin, 
den Gültigkeitsbereich für jede Eingabe individuell anzupassen.
Die könnte mit weiteren Wertelisten im Skript erfolgen.

Beim Taster kann aus endlich vielen Werten gewählt werden.
Durch Klicken der Pfeiltasten auf oder ab 
wird jeweils zum nächsten Wert gewechselt.
Um den gewünschten Wert mit möglichst wenig Klicks zu erreichen, 
hat der Taster keinen Anschlag.

Beim Schieber wird dieser gedrückt und verschoben, 
dann losgelassen, um den neuen Wert einzustellen.
Mit einem Knopf daneben kann jeweils der Schiebertyp 
eingestellt werden: 
Linear, Exponentiell, Logarithmus, Quadratisch, Wurzel.
Der Knopf wird so oft gedrückt, 
bis der gewünschte Typ gewählt ist. 
Welcher ausgewählt ist, 
ergibt sich aus dem Titel-Attribut des Knopfes.
Durch eine nicht lineare Wahl ist es möglich, 
entweder einen kleinen oder einen großen Werte 
genauer einzustellen.

Bei der freien Texteingabe ist einfach 
für den gewünschten Parameter der Knopf zu drücken,
daraufhin öffnet sich ein Fenster mit einer Eingabezeile.

Bei Typ '0/1' kann für jeden Parameter 
nur zwischen ja und nein gewählt werden, 
grüner Knopf für ja, roter für nein.
Welcher Zustand ausgewählt ist, 
ergibt sich aus dem Titel-Attribut des kleinen, 
umgebenden Kastens.
Im Titel-Attribut des Knopfes 
steht immer der damit auswählbare Wert.
</desc>
 
<defs>
<script type="application/ecmascript">
// erst alles laden, dann starten  
document.documentElement.addEventListener("SVGLoad", start, false);

// Wurzelelement identifizieren
var svg = document.documentElement

// Werte für den weiteren Zugriff
tasterPar=new Array(10)
schieberPar=new Array(10)
schieberParTyp=new Array(10)
schieberTypText=new Array(4)
textPar=new Array(10)
janeinPar=new Array(10)
janeinParText=new Array(2)
tasterPar[1]=5
tasterPar[2]=5
tasterPar[3]=5
schieberPar[1]=0.1
schieberPar[2]=0.3
schieberPar[3]=0.9
schieberParTyp[1]=0
schieberParTyp[2]=0
schieberParTyp[3]=0
schieberTypText[0]='Linear'
schieberTypText[1]='Exponentiell'
schieberTypText[2]='Logarithmus'
schieberTypText[3]='Quadrat'
schieberTypText[4]='Wurzel'
textPar[1]='a'
textPar[2]='b'
textPar[3]='c'
janeinPar[1]=1
janeinPar[2]=1
janeinPar[3]=1
janeinParText[0]='nein'
janeinParText[1]='ja'

// Lauschen, ob und was in den Menüs passiert
function start () {
  document.getElementById("menuetaster").addEventListener("click", menuetaster, false)
  document.getElementById("menuetext").addEventListener("click", menuetext, false)
  document.getElementById("menueschieber").addEventListener("mousedown", menueschieber, false)
  document.getElementById("menueschieber").addEventListener("click", menueschieberklick, false)
  document.getElementById("menuejanein").addEventListener("click", menuejanein, false)
}

// Taster behandeln
function menuetaster(e) {
  // bei welchem Element ist das Ereignis eingetreten?
  var ele = e.target
  // auf oder ab?
  var klasse=ele.getAttributeNS(null,"class")
  // welcher Zähler ist zu ändern?
  var pare=ele.parentNode
  var p=pare.getAttributeNS(null,"id")
  var num=p.substr(1)
  var pn=tasterPar[num]
  // wo kommt der Text hin?
  var t = pare.firstChild
  while (t.nodeName != 'text') {
     t = t.nextSibling;
  }
  // neuen Wert setzen
  if (klasse=="auf") {
    pn=(pn+1)%10
    t.textContent=pn          
  } else if (klasse=="ab") {
    pn=(pn+9)%10
    t.textContent=pn          
  }
  tasterPar[num]=pn   
}

// Schiebertyp ändern
function menueschieberklick(e) {
  // bei welchem Element ist das Ereignis eingetreten?
  var ele = e.target
  if (ele.nodeName == 'circle') {
    // welcher Schieber ist zu ändern?
    var pare=ele.parentNode
    var p=pare.getAttributeNS(null,"id")
    var num=p.substr(1) 
    // Schiebertyp ändern
    schieberParTyp[num]=(schieberParTyp[num]+1)%5
    // wohin mit dem Text?
    var child = ele.firstChild
    while (child.nodeName != 'title') {
     child = child.nextSibling;
    }    
    child.textContent='Schiebertyp '+schieberTypText[schieberParTyp[num]]
  }
}

// gucken, ob der Schieber bewegt wird oder abzuschalten ist
function menueschieber(e) {
  // bei welchem Element ist das Ereignis eingetreten?
  var ele = e.target
  if (ele.nodeName == 'rect') {
    ele.addEventListener("mouseup", schieberaus, false)
    ele.addEventListener("mouseout", schieberaus, false)
    document.getElementById("menueschieber").addEventListener("mousemove", schieberbewegen, true)
  }
}

// Schieber abschalten
function schieberaus(e) {
  // bei welchem Element ist das Ereignis eingetreten? 
  var ele = e.target
  // Wenn Objekt verlassen wird, nicht mehr nach dem Schieber gucken
  ele.removeEventListener("mouseup", schieberaus, false)
  ele.removeEventListener("mouseout", schieberaus, false)
  document.getElementById("menueschieber").removeEventListener("mousemove", schieberbewegen, true)
}

// Schieberstand auswerten
function schieberbewegen(e) {
  // Koordinatenpaar für weitere Rechnungen definieren
  var pt = svg.createSVGPoint();
  // bei welchem Element ist das Ereignis eingetreten?
  var ele = e.target
  // Elternelement
  var pare=ele.parentNode
  // Erstes Rechteck im Elternelement soll geändert werden
  var sch = pare.firstChild
  while (sch.nodeName != 'rect') {
    sch = sch.nextSibling;
  }
  // welcher Zähler ist zu ändern?
  var pare=ele.parentNode
  var p=pare.getAttributeNS(null,"id")
  var num=p.substr(1)
  // wohin der Text?
  var t = pare.firstChild
  while (t.nodeName != 'text') {
    t = t.nextSibling;
  }   
  // gucken, wo der Mauszeiger gerade ist
  var w = parseFloat(ele.getAttributeNS(null,"x"))+4
  // gucken, wo die Maus in Bildschirmkoordinaten gerade ist
  pt.x = e.clientX; 
  pt.y = e.clientY; 
  // Mausposition in lokalen Koordinaten berechnen
  var p = pt.matrixTransform(ele.getScreenCTM().inverse())
  var dw = p.x -w
  dw=Math.min(100,Math.max(0,dw))
  // Schieber aktualisieren
  sch.setAttributeNS(null,'width', dw)
  // Je nach Schiebertyp Parameter bestimmen und ablegen
  dw=dw/100
  if (schieberParTyp[num]==0) {
    schieberPar[num] =dw
  } else if (schieberParTyp[num]==1) {
    schieberPar[num] =(Math.exp(dw) -1)/(Math.E -1)
  } else if (schieberParTyp[num]==2) {
    schieberPar[num] =Math.log(dw+1)/Math.log(2)
  } else if (schieberParTyp[num]==3) {
    schieberPar[num] =dw*dw
  } else if (schieberParTyp[num]==4) {
    schieberPar[num] =Math.sqrt(dw)
  }
  // Parameter zur Kontrolle ausgeben
  t.textContent=Math.round(schieberPar[num]*10000)/10000
}

// freie Texteingabe
function menuetext(e) {
  // bei welchem Element ist das Ereignis eingetreten?
  var ele = e.target
  // Aktion nur wenn ein Kreis angeklickert wurde
  if (ele.nodeName == 'circle') {
    // welcher Text ist zu ändern?
    var pare=ele.parentNode
    var p=pare.getAttributeNS(null,"id")
    var num=p.substr(1)
    var tp=prompt('Textparameter '+num+' eingeben')
    var t = pare.firstChild
    while (t.nodeName != 'text') {
      t = t.nextSibling;
    }
    t.textContent=tp
    textPar[num]=tp
  }
}

// Menü 0/1, ja oder nein
function menuejanein(e) {
  // bei welchem Element ist das Ereignis eingetreten?
  var ele = e.target
  // ja oder nein?
  var klasse=ele.getAttributeNS(null,"class")
  // welcher Knopf ist zu ändern?
  var pare=ele.parentNode
  var p=pare.getAttributeNS(null,"id")
  var num=p.substr(1)
  // ja/nein-Wechsel
  if (klasse=="ja") {
    janeinPar[num]=1
  } else if (klasse=="nein") {
    janeinPar[num]=0
  }
  // wo kommt der Text hin?
  var t = pare.firstChild
  while (t.nodeName != 'title') {
     t = t.nextSibling;
  }
  t.textContent=janeinParText[janeinPar[num]]
}
</script>
</defs>
<rect x="-40" y="-60" rx="5" width="240" height="240" fill="#caf" stroke="#204" />

<g id="menuetaster" font-size="10" text-anchor="middle" fill="#ffc" stroke="#040">
  <rect x="-30" y="-50" rx="5" width="160" height="90" fill="white" stroke="black" />
  <text x="50" y="-35" stroke="none" fill="black">Taster</text>
  
  <g id="p1">
    <text x="0" y="4" stroke="none" fill="black">5</text>  
    <path class="auf" d="M0,-30 -20,-10 20,-10z" /> 
    <path class="ab"  d="M0,30 -20,10 20,10z" /> 
  </g>  

  <g id="p2" transform="translate(50)">
    <text x="0" y="4" stroke="none" fill="black">5</text>  
    <path class="auf" d="M0,-30 -20,-10 20,-10z" /> 
    <path class="ab"  d="M0,30 -20,10 20,10z" /> 
  </g>

  <g id="p3" transform="translate(100)">
    <text x="0" y="4" stroke="none" fill="black">5</text>  
    <path class="auf" d="M0,-30 -20,-10 20,-10z" /> 
    <path class="ab"  d="M0,30 -20,10 20,10z" /> 
  </g>

</g>

<g id="menueschieber" transform="translate(-20,90)" font-size="10" text-anchor="middle">
  <rect x="-10" y="-40" rx="5" width="160" height="120" fill="white" stroke="black" />
  <text x="70" y="-25">Schieber</text>
  
  <g id="s1">  
    <text x="50" y="-4">0.1</text>
    <rect x="0" y="0" height="10" width="10" />
    <rect x="-4" y="-1" height="12" width="108" fill="blue" fill-opacity="0.2" />
    <circle cx="130" cy="5" r="6" fill="#ccf" stroke="#004">
      <title>Schiebertyp linear</title>
    </circle>
  </g>

  <g id="s2" transform="translate(0,30)">  
    <text x="50" y="-4">0.3</text>
    <rect x="0" y="0" height="10" width="30" />
    <rect x="-4" y="-1" height="12" width="108" fill="blue" fill-opacity="0.2" />
    <circle cx="130" cy="5" r="6" fill="#ccf" stroke="#004">
      <title>Schiebertyp linear</title>
    </circle>
  </g>

  <g id="s3" transform="translate(0,60)">  
    <text x="50" y="-4">0.9</text>
    <rect x="0" y="0" height="10" width="90" />
    <rect x="-4" y="-1" height="12" width="108" fill="blue" fill-opacity="0.2" />
    <circle cx="130" cy="5" r="6" fill="#ccf" stroke="#004">
      <title>Schiebertyp linear</title>
    </circle>
  </g>

</g>

<g id="menuetext" transform="translate(165,-20)" font-size="10" text-anchor="middle">
  <rect x="-25" y="-30" rx="5" width="50" height="140" fill="white" stroke="black" />
  <text x="0" y="-15">Text</text>
  
  <g id="t1">
    <circle r="10" fill="#ccf" stroke="blue" /> 
    <text y="25">a</text>
  </g>
  <g id="t2">
    <circle cy="40" r="10" fill="#cfc" stroke="green" />
    <text y="65">b</text>
  </g>
  <g id="t3">
    <circle cy="80" r="10" fill="#fcc" stroke="red" />
    <text y="105">c</text>
  </g>
</g>


<g id="menuejanein" transform="translate(140,100)" font-size="10">
  <rect x="0" y="0" rx="5" width="50" height="70" fill="white" stroke="black" />
  <text x="25" y="14" text-anchor="middle">0/1</text>    
  
  <g id="j1"><title>ja</title>
    <rect x="2" y="17" rx="5" width="46" height="15" fill="#ffa" />  
    <text x="5" y="30">a</text>
    <circle class="ja" cx="25" cy="25" r="5" fill="green" stroke="#040"><title>ja</title></circle>
    <circle class="nein" cx="40" cy="25" r="5" fill="#fcc" stroke="red"><title>nein</title></circle> 
  </g>  
  <g id="j2" transform="translate(0,18)"><title>ja</title>
    <rect x="2" y="17" rx="5" width="46" height="15" fill="#ffa" />
    <text x="5" y="30">b</text>
    <circle class="ja" cx="25" cy="25" r="5" fill="green" stroke="#040"><title>ja</title></circle>
    <circle class="nein" cx="40" cy="25" r="5" fill="#fcc" stroke="red"><title>nein</title></circle> 
  </g>  
  <g id="j3" transform="translate(0,36)"><title>ja</title>
    <rect x="2" y="17" rx="5" width="46" height="15" fill="#ffa" />
    <text x="5" y="30">c</text>
    <circle class="ja" cx="25" cy="25" r="5" fill="green" stroke="#040"><title>ja</title></circle>
    <circle class="nein" cx="40" cy="25" r="5" fill="#fcc" stroke="red"><title>nein</title></circle> 
  </g>
  
</g>

</svg>

Weiterführende Literatur

Bearbeiten