DHTML, JavaScript
Linie, Ellipse, Kreis, Rechteck, Polygon zeichnen.

Schnelle JavaScript Vektorgraphik Bibliothek.

Entwickler: Walter Zorn
 
"JavaScript ist eine Programmiersprache ohne Grafik -Fähigkeit" gilt nicht. Diese JavaScript-Vektorgrafikbibliothek implementiert Funktionen für Grafik-Grundelemente, d.h. kann Linien, Kreise, Ellipsen (Ovale), Polygone (auch Dreiecke, Rechtecke) und Polylinien (geknickte Linien) dynamisch in eine Webseite zeichnen. Die Verwendung dieser Vector-Grafik -Bibliothek ist selbst bei nicht vorhandener JavaScript-Erfahrung einfach - siehe Dokumentation weiter unten auf dieser Seite. Ziel der Entwicklung war nebenbei eine Lösung mit optimierter Performance und optimal gezeichneten Pixeltreppchen (Pixeloptimierung).
 
 
Hier testen:
x1  y1  x2  y2 
x  y  w  h 
x  y  w  h 
x y
x  y  w  h 
x  y  w  h 
setColor("#") setStroke() setStroke(Stroke.DOTTED)
Test-Zeichenfläche
 
 
 
Performance
In HTML selbst gibt es keine schrägen Linien, Kreise, Ellipsen oder andere nicht-rechteckig begrenzten Elemente. Als Work-around könnte man entsprechend viele kleine farbige DIV-Elemente (Layer) erzeugen und passend aneinanderreihen[1]. Da jedes DIV zwangsläufig seinen gesamten browserinternen Objekt-Overhead mitbringt, wäre es allerdings suboptimal, für jedes Pixel ein eigenes DIV zu erzeugen.
 
Diese Vektorgrafik-Bibliothek versucht nun, diesen Overhead möglichst gering zu halten. Sie verwendet neben schnellen Algorithmen zur Berechnung der Formen (hauptsächlich aufgebaut auf Bresenham-Algorithmen) den Trick, jeweils möglichst viele Pixel zu einem DIV zusammenzufassen. Die Anzahl benötigter Layer sinkt[2] (bei 1 Pixel breiten bzw. geraden Linien) auf einen pro Pixeltreppchenstufe, wie in der Grafik links anhand eines Kreissegments im Maßstab 11/1 veranschaulicht, und die Performance steigt entsprechend, i.d.R. sogar überproportional. Nebenbei wird dafür gesorgt, dass sich die DIV-Elemente (bei 1 Pixel breiten Linien) nur Ecke an Ecke berühren. Dadurch erscheinen die Linien dem Auge als gleichmäßig dick.
 
Trotzdem: Die Performance kann nicht mit Java oder einem Stand-Alone-Programm vergleichbar sein! Layer (DIV-Elemente) zu erzeugen ist langsamer als direkt Pixel einzufärben. Mit diesem JavaScript-Programm habe ich lediglich versucht, aus dieser webtechnologie-bedingten Einschränkung das Bestmögliche an Performance herauszuholen. Anhaltspunkt: Zu zeichnende Formen sollten nicht in beiden Dimensionen größer als 600 bis 1000 Pixel sein (genauer: die Summe der Pixeltreppchenstufen aller Elemente einer Grafik sollte nicht größer als 1000 sein).
 
Alternative zu SVG? Derzeit sind SVG-fähige Browser, bzw. Browser, in die ein SVG-Plugin korrekt eingebunden wurde, noch rar. Diese Vektorgraphik -Bibliothek dürfte als nicht-proprietäre Alternative für kleine oder wenig komplexe Grafikelemente in den Browsern von mehr als 95% aller Internetbesucher laufen. Außerdem kann sie an beliebigen Stellen in ein HTML-Dokument zeichnen, weil die Grafik über den Rand von "Zeichenflächen" hinausfließen darf.
 
 
 
 
Welche Browser?
 
 
 
 
 
jsGraphics-Bibliothek einbinden
1. Bibliothek in die HTML-Datei einbinden
Folgenden Codeschnipsel am besten im Kopfbereich der HTML-Datei (irgendwo zwischen <head> und </head>) einfügen:
<script type="text/javascript" src="wz_jsgraphics.js">
</script>
 
2. DIV-Bereich(e) als Fläche(n) zum Zeichnen

Dieser Schritt ist nicht erforderlich, wenn beim Laden der Seite direkt in das Dokument gezeichnet werden soll. Ansonsten: Sowohl relativ (räumliche Eingliederung dort, wo im HTML-Quelltext definiert) als auch absolut positionierte DIV-Bereiche sind geeignet. Die zum Zeichnen vorgesehenen DIV-Bereiche müssen je eine eindeutige (nur einmal vergebene) ID haben:
<div id="myCanvas" style="position:relative;height:250px;width:100%;"></div>
...
<div id="anotherCanvas" style="position:relative;height:100px;width:300px;"></div>
 
3. Bibliothek speichern

Die JavaScript-Datei downloaden und entzippen. Entweder im gleichen Verzeichnis wie die HTML-Datei speichern, oder den Pfad src="wz_jsgraphics.js" im HEAD-Bereich der HTML-Datei anpassen.
 
 
 
 
 
 
Dokumentation: Verwendung der Zeichenmethoden
1. jsGraphics-Objekt erzeugen
 
a) In Layer (DIV) zeichnen:
Ist auch nach Abschluss des Ladevorgangs möglich, funktioniert aber nicht im Netscape 4 und Opera älter als Version 7. Irgendwo nach dem DIV-Bereich, aber noch vor dem schließenden </body>-Tag, mittels folgendem Code ein jsGraphics-Objekt instanzieren. Dabei muss die ID des auserkorenen DIV-Bereichsn, in Anführungszeichen oder Hochkommata gesetzt, der Funktion new jsGraphics() übergeben werden:
<script type="text/javascript">
<!--

var jg = new jsGraphics("myCanvas");

//-->
</script>
Falls Sie mehrere getrennte DIV-Bereiche als Zeichenflächen definieren wollen, müssen Sie für jeden ein eigenes jsGraphics-Objekt erzeugen:
<script type="text/javascript">
<!--

var jg = new jsGraphics("myCanvas");
var jg2 = new jsGraphics("anotherCanvas");

//-->
</script>
 
 
b) Beim Laden direkt in das Dokument zeichnen:
Funktioniert auch im Netscape 4 und Opera 5/6. Übergeben Sie dem Konstuktor anstatt einer DIV-ID einfach nichts (oder den Wert null).
<script type="text/javascript">
<!--

var jg_doc = new jsGraphics();

//-->
</script>
Statt jg, jg2 oder jg_doc können Sie Ihre jsGraphics-Instanzen natürl�h auch anders nennen, sofern die Regeln für Variablennamen eingehalten werden.
 
 
 
2. Zeichenmethoden aufrufen

Nachdem Sie nun eine Objekt-Instanz beispielsweise namens jg erzeugt haben, können Sie die Methoden jederzeit, auch nach Abschluss des Ladevorgangs, wie in folgendem funktionsfähigen Beispiel verwenden (Anmerkung: Wenn die Objekt-Instanz außerhalb von Funktionen (global) deklariert wurde, bleibt sie erhalten, solange die Webseite im Browser geladen ist):
<script type="text/javascript">
<!--
function myDrawFunction()
{
  jg_doc.setColor("#00ff00"); // grün
  jg_doc.fillEllipse(100, 200, 100, 180); // Koordinaten auf document bezogen
  jg_doc.setColor("maroon");
  jg_doc.drawPolyline(new Array(50, 10, 120), new Array(10, 50, 70));
  jg_doc.paint(); // zeichnet in diesem Fall direkt in's document

  jg.setColor("#ff0000"); // rot
  jg.drawLine(10, 113, 220, 55); // Koordinaten auf Zeichenfläche bezogen
  jg.setColor("#0000ff"); // blau
  jg.fillRect(110, 120, 30, 60);
  jg.paint();

  jg2.setColor("#0000ff"); // blau
  jg2.drawEllipse(10, 50, 30, 100);
  jg2.drawRect(400, 10, 100, 50);
  jg2.paint();
}

var jg_doc = new jsGraphics(); // direkt in's document zeichnen
var jg = new jsGraphics("myCanvas");
var jg2 = new jsGraphics("anotherCanvas");

myDrawFunction();

//-->
</script>
Wie man sieht, sollte zuerst für jede Fläche eine Malfarbe gesetzt werden, andernfalls wird alles in der Standardfarbe Schwarz gezeichnet. Die Koordinaten beziehen sich im Modus "In document zeichnen" auf die linke obere Ecke des Dokuments, im Modus "In DIV zeichnen" auf die linke obere Ecke des Layers. Da die Zeichenmethoden die Grafik zunächst intern erzeugen, muss zur Ausgabe in die jeweilige Malfläche explizit die Methode paint() aufgerufen werden.
 
Name der Methode
(Bildchen selbstverständlich von der Vektorgraphik-Bibliothek in die Tabelle gezeichnet)
Codebeispiel (am Beispiel von jg)
Allgemeine Hinweise
 
1.) Zahlen, die den Funktionen übergeben werden, müssen Ganzzahlen (Integers) sein. Nicht-Ganzzahlen (Fließkommazahlen) und Zeichenketten (Strings) müssen mit den JavaScript-Standardfunktionen parseInt() oder Math.round() in Integers umgewandelt werden. JavaScript-Berechnungen beispielsweise haben überwiegend Fließkommazahlen als Ergebnis, Werte von Formulareingabefeldern sind immer Strings, auch wenn Zahlen eingegeben wurden. Codebeispiel:
jg.setStroke(parseInt(document.MyForm.Linewidth.value));
 
2.) Falls Sie gezeichnete Elemente sehr genau positionieren müssen/wollen: Beachten Sie, dass Koordinaten zwischen Pixeln liegen, nicht auf ihnen, und dass der "Malstift" die Pixel rechts unterhalb des durch die Koordinaten vorgegebenen Pfades färbt, auch bei Linien dicker als 1 px.
setColor("#Hexfarbwert");
 
Setzt Malfarbe für die nachfolgend aufgerufenen Zeichenmethoden, und zwar so lange, bis sie durch einen erneuten Aufruf von setColor() geändert wird. Der Farbwert muss in Anführungszeichen stehen. Mit Rücksicht auf Cross-Browser-Funktionalität sollte er hexadezimal, nach dem in HTML üblichen Schema "#rrggbb", angegeben werden. Erlaubt sind aber auch die in HTML zulässigen Farbnamen.
jg.setColor("#ff0000");
 
oder mit identischem Ergebnis
 
jg.setColor("red");
setStroke(Zahl);
 
Kann optional aufgerufen werden und setzt die Liniendicke für die nachfolgend aufgerufenen Zeichenmethoden, und zwar so lange, bis sie durch einen erneuten Aufruf von setStroke() geändert wird. Voreingestellte Dicke, solange .setStroke() nicht aufgerufen wurde, ist 1 px.
 
Um eine gepunktete Linie (gültig auch für Umrandungen von Ellipsen, Polygonen etc.) zu erzeugen, können Sie setStroke() als Parameter die Konstante Stroke.DOTTED übergeben. Die Liniendicke wird automatisch auf 1 Pixel gesetzt, andere Dicken sind nicht möglich.
jg.setStroke(3);
 
oder
 
jg.setStroke(Stroke.DOTTED)
drawLine(X1, Y1, X2, Y2);
1 Pixel breite Linie (Gerade) von Anfangs- zu Endkoordinaten. Um die Linienstärke zu ändern, kann zuvor die Methode .setStroke(Zahl) aufgerufen werden. Diese Einstellung bleibt für alle nachfolgend aufgerufenen Linienmethoden erhalten, bis .setStroke() erneut aufgerufen wird.
jg.drawLine(0, 11, 40, 0);
drawPolyline(X-Werte Anfang/Knicke/Ende, Y-Werte);
(Mehrfach) geknickte Linie, beginnend beim ersten X- und Y-Wert. X- und Y-Werte sind Arrays. Sie können entweder direkt im Methodenaufruf definiert, oder vorher nach folgendem Schema angelegt werden:
var xWerte = new Array(x1,x2,x3,x4,x5);
var yWerte = new Array(y1,y2,y3,y4,y5);

 
Liniendicke entweder, wie per .setStroke() eingestellt, oder 1px.
jg.drawPolyline(new Array(10,85,93,60), new Array(50,10,105,87));
 
oder alternativ
 
var xWerte = new Array(10,85,93,60);
var yWerte = new Array(50,10,105,87);
jg.drawPolyline(xWerte,yWerte);
drawRect(X,Y,Breite,Höhe);
Rechteck, nicht gefüllt, aus 1 px dicken Linien. Koordinaten beziehen sich auf die linke obere Ecke. Die Liniendicke kann mit .setStroke(Dicke) geändert werden.
jg.drawRect(0, 0, 40, 11);
fillRect(X,Y,Breite,Höhe);
Gefülltes Rechteck. Koordinaten beziehen sich auf linke obere Ecke.
jg.fillRect(0, 0, 40, 11);
drawPolygon(X-Werte Ecken, zugehörige Y-Werte);
Vieleck. X- und Y-Werte sind Arrays. Sie können entweder direkt im Methodenaufruf definiert, oder vorher nach folgendem Schema angelegt werden:
var xWerte = new Array(x1,x2,x3,x4,x5);
var yWerte = new Array(y1,y2,y3,y4,y5);

Falls das letzte X-/Y-Wertepaar nicht identisch mit dem ersten ist, schließt das Programm von sich aus das Polygon, indem es eine Linie vom letzten zum ersten Koordinatenpaar zieht.
 
Liniendicke entweder, wie per .setStroke() eingestellt, oder 1px.
jg.drawPolygon(new Array(10,85,93,60), new Array(50,10,105,87));
 
oder alternativ
 
var xWerte = new Array(10,85,93,60);
var yWerte = new Array(50,10,105,87);
jg.drawPolygon(xWerte,yWerte);

Statt "xWerte" bzw. "yWerte" können Sie natürlich andere Variablennamen verwenden.
fillPolygon(X-Werte Ecken, zugehörige Y-Werte);
Gefülltes Polygon. Parameter analog zu drawPolygon()
jg.fillPolygon(new Array(10,85,93,60), new Array(50,10,105,87));
drawEllipse(X,Y,Breite,Höhe);
Ellipse. Liniendicke entweder, wie per .setStroke() eingestellt, oder 1px. Koordinaten beziehen sich auf das virtuelle Rechteck, das die Ellipse (genauer: den Pfad, dem der Malstift folgt) umfasst.
jg.drawEllipse(0, 0, 40, 12);
oder
jg.drawOval(0, 0, 40, 12);
fillEllipse(X, Y, Breite, Höhe);
Gefüllte Ellipse. Koordinaten beziehen sich auf das virtuelle Rechteck, das die Ellipse exakt umfasst.
jg.fillEllipse(0, 0, 41, 13);
oder
jg.fillOval(0, 0, 41, 13);
drawString("Text", X, Y);
Text an definierte Position schreiben. Die Koordinaten X und Y beziehen sich auch hier, anders als bei Java, auf die linke obere Ecke der (ersten) Textzeile. Der Text muss in Anführungszeichen gesetzt werden. Enthaltene HTML-Tags werden interpretiert. Ein (nicht maskiertes) <br> würde also tatsächlich einen Zeilenumbruch bewirken.
 
setFont("Schiftfamilie", "Größe+Einheit", Stil);
 
Damit können Sie zuvor auch die Schriftart und -Größe setzen oder ändern. Schriftfamilie und Größenangabe in Anführungszeichen.
 
Mögliche Stilangaben:
Font.PLAIN für normale Schrift
Font.BOLD für fette Schrift
Font.ITALIC für Kursivschrift
Font.ITALIC_BOLD oder Font.BOLD_ITALIC, um letztere zu kombinieren
jg.setFont("arial","15px",Font.BOLD);
jg.drawString("Geschwalle",20,50);
drawStringRect("Text", X, Y, Breite, Ausrichtung);
Wie drawString. Zusätzlich können die Breite des Text-"Containers" und die Textausrichtung innerhalb dieses Rechtecks festgelegt werden. Textausrichungswert muss ein String sein (d.h. in Anführungszeichen oder Apostrophe eingeschlossen sein) und kann entweder "left", "center", "right" or "justify" sein.
jg.setFont("verdana","11px",Font.BOLD);
jg.drawStringRect("Text",20,50,300,"right");
drawImage("Pfad", X, Y, Breite, Höhe);
Bild exakt an definierter Position in angegebener Größe darstellen.
 
"Pfad"-Angabe wie in HTML. Selbstverständlich ist die Größenangabe (nahezu) beliebig und erlaubt verzerrte Darstellung.
 
Optional kann als fünfter Parameter ein Eventhandler definiert werden, der dann automatisch in das erzeugte <img>-Tag eingefügt wird. Beispiel: jg.drawImage('anImg.jpg',8,5,95,70,'onmouseover="YourFunc()"');
jg.drawImage("DerTutNix.jpg", 20,50,100,150);
paint();
 
Die mit den obigen Methoden zunächst nur intern erzeugte Grafik wird ausgegeben. Für optimale Performance empfiehlt es sich, paint() nicht unnötig oft (in unnötig kleinen Intervallen zwischen den einzelnen Zeichenmethoden) aufzurufen, sondern erst zum Abschluss der internen Grafikerzeugung.
 
Vermeiden Sie also
jg.drawEllipse(0, 0, 100, 100);
jg.paint();
jg.drawLine(200, 10, 400, 40);
jg.paint();
...

 
sondern schreiben Sie besser
jg.drawEllipse(0, 0, 100, 100);
jg.drawLine(200, 10, 400, 40);
/* ...weitere Zeichenmethoden... */
jg.paint();
jg.paint();
clear();
 
Alle vom jsGraphics-JavaScript gezeichneten Inhalte der betreffenden Malfläche werden gelöscht. Der ursprüngliche Inhalt bleibt aber erhalten, also Text oder Bilder innerhalb des <div>-Elements, die im HTML-Quelltext definiert waren.
jg.clear();
 
Alle script-generierten Inhalte in "myCanvas" werden gelöscht.
setPrintable(true);
 
Normalerweise werden die gezeichneten Elemente beim Drucken nicht sichtbar, weil die Browser unter den Standard-Druckereinstellungen keine Hintergrundfarben mitdrucken. Mit setPrintable() und dem Parameter true werden die nachfolgend gezeichneten Elemente drucker-wiedergabefähig (zumindest im Mozilla/Netscape 6+ und IE). Allerdings auf Kosten der Darstellungsgeschwindigkeit auf dem Bildschirm, die je nach Browser um 10% bis 25% langsamer wird.
jg.setPrintable(false);
 
Setzt die Zeichenmethoden auf "nicht-druckerfähig" zurück.
 
 
 
 
 
 
Download
 
 

 
 
 
 
Fußnoten
[1]
Eine Lösung mit farbigen 1x1-Pixel-Bildchen würde die Farbauswahl einschränken, es sei denn, beim Seitenaufruf würden 16777216 verschiedenfarbige Bildchen (656 MB Datenvolumen) für 24 Bit Farbauswahl mitgeladen, oder die Bildchen lägen wenigstens auf dem Server bereit, oder das Script selbst würde sich jedesmal, wenn eine Malfarbe definiert wird, ein PNG schreiben...
Zurück
 
[2]
Unter der Annahme, dass alle Linienwinkel auf dem Bildschirm gleich wahrscheinlich sind und die Längenverteilung der Linien gleichmäßig ist, gilt für 1 px breite Linien:
Die Lösung "Pro Pixel ein Layer" erfordert im Durchschnitt sin(45°) / (1 - sin(45°)) = 2.41 mal mehr Layer als die Lösung "Pro Pixeltreppchenstufe ein Layer". Dies gilt unter der Voraussetzung, dass sich in beiden Fällen die Pixeltreppchenstufen Ecke an Ecke berühren. Dann bleibt die Zahl einzufärbender Pixel minimal, und damit auch die Zahl der Layer, die von der "Pro Pixel ein Layer"-Lösung erzeugt werden muss; andernfalls wäre ihr Performance-Nachteil noch größer.
Zurück

Walter Zorn, München, 2005
 Dank an Walter Zorn, der mit den Scripts die Grundlagen für die Darstellung geschaffen hat.