Dartiagram: Meine erste Dart Library

Nach den ersten Schritten mit Dart gestern, möchte ich mich heute an einem ersten kleinen eigenen Projekt versuchen. Eine einfache Library, die es erleichtert einfache Diagramme zu erstellen. Es gibt schon großartige Librarys wie etwa chart.js, welche auch in Dart portiert wurde, aber mir geht es noch nicht um den Produktiveinsatz, sondern um das Experimentieren mit Dart.

Planung

Als erstes habe ich ein Skizze gemacht, um im Blick zu haben, wo ich hin möchte. Ein einfaches Säulendiagramm. In der Minimalversion sollte es ausreichen, wenn man die Werte der Balken übergibt. Für das Beispiel könnte das so aussehen: ‘dartiagram (35 20 28);’. Oder mit Beschriftung: ‘dartiagram (35 20 28, ‘Kategorie 1′ ‘Kategorie 2′ ‘Kategorie 3′, ‘x-Beschriftung’, ‘y-Beschriftung’, ‘Titel’). Ich sehe schon jetzt viele mögliche Probleme und werde mich daher heute auf die erstem Möglichkeit, allerdings mit Beistrichen, damit da nicht eine zusätzliche Fehlerquelle reinkommt. Also: dartiagram (num a, num b, num c). Wobei mir da dann unklar ist, wie ich erweiterbar machen kann. Während ich bei der Möglichkeit ohne Beistrichen nur dartiagram (string a) habe und den dann bei den Leerzeichen splitten und in Numbers parsen könnte. Fühlt sich komisch, aber sinnvoller an. Und irgendwann kann man das dann über if Abfragen erweitern, damit man entweder Werte direkt oder etwa als json übergeben kann.

Nächstes Problem: Wie fange ich an? Erst dachte ich, dass ich direkt ein Pub Package erstelle, aber nachdem ich mich etwas herumgeklickt habe, scheint es mir sinnvoller, wenn ich erst eine Demo-Webapp erstelle und aus dieser dann den Code für ein Package abstrahiere.

Ein paar Stufen tiefer. Wie soll das ganze funktionieren?

  • Einbindung im code durch ‘dartiagram (string chart_data)’.
  • Variable chart_data wird in Einzeldatenpunkte zerteilt. -> bars_data = Liste mit den einzelnen Datenpunkten als Numbers
  • Säulendiagramm berechnen und ausgeben
    • Canvasgröße per Default 500x500px
    • 100% Höhe des Säulendiagramms = höchster Wert + 10%
    • Breite eines Balken = 80% von (Canvasgröße-100px für Beschriftung / Anzahl der Datenpunkte)
    • Höhe der Balken = höchster Wert 100%, Rest prozentuell Wert/(Höhe/100)
    • Wert des Balken über dem Balken anzeigen

Ich merke, dass ich mein Mathematikwissen wieder einmal auffrischen sollte. Aber der grundsätzliche Plan steht.

Coding

Neues Projekt, sample web application. Dann hübsch das pubspec.yaml ausfüllen.
Dart Editor 2014-07-24 14.30.56

Beim Editieren des Beispiel-HTMLs fällt mir auf, dass Dart ja gar nicht direkt im HTML aufgerufen werden kann. Kurz recherchieren. Maximal eine Dart-Datei und keine Inline-Aufrufe. Ich bin kurz überfragt. Wie übergebe ich dann Werte über das HTML? Erst verrückte Idee: <canvas class="dartiagram" id="35_20_28"></canvas>. Eigentlich kann ich mich darum später kümmern. Also erstmal schauen, dass ich es schaffe, dass die main() Funktion irgendwas ins Canvas malt. Übergabe der Werte kann ich später implementieren, erstmal hardcoden.

Während dem coden habe ich immer wieder in Chapter 2 der Dart Tour und den dart:html API Docs nachgelesen sowie ein paar Zeilen aus dem sunflower Beispiel übernommen.

Statt Schritt für Schritt vorzugehen und dazwischen immer wieder zu testen, ob es so funktioniert, wie ich mir das vorstelle, habe ich den Grundcode in einem Durchgang geschrieben. Für einen Anfänger wie mich, wahrscheinlich nicht ideal. So sieht es aus:
dartiagram_v0

Super finde ich die kleinen Symbole auf der linken Seite, die erscheinen, wenn man in einem Kommentar ‘TODO’ schreibt. Vermutlich Standard in vielen Editoren, aber ich habe es bisher noch nicht gesehen.

Natürlich bekam ich beim ersten Laufen lassen eine Fehlermeldung. Erst nach etwas Suchen fand ich einen Hinweis, mit dem ich etwas anfangen konnte.
dartiagram_error

Anscheinend bekommt die Funktion das Canvas Element mit der ID dartiagram nicht zum greifen. Nachdem ich erst glaubte, dass Dart ausgeführt wird bevor die Seite ausreichend geladen ist, was Unsinn ist, weil Dart aktuell immer erst nach dem vollständigen Laden ausgeführt wird. Tatsächlich habe ich versehentlich einmal #dartiagram und einmal #dartiagramm geschrieben.

Nächster Versuch. Neuer Fehler: “Breaking on exception: type ‘List’ is not a subtype of type ‘num’ of ‘other’.”. Schon etwas vertrauter mit dem Debugger habe ich mir angeschaut, was die letzte funktionierende Ausführung war und konnte schnell sehen, dass ich vergessen habe statt der Liste selbst die Länge der Liste abzufragen. Korrekt: “i < bars_data.length"

dartiagram error list

Nächster Fehler: “NoSuchMethodError: method not found: ‘lenght’”. Habe ich mich verschrieben? Nein. Oh. Weiter unten, bei dem Ausrechnen der Säulenbreite habe ich mich verschrieben.

Nächster Fehler: “type ‘double’ is not a subtype of type ‘int’ of ‘bar_start’.”. Ich habe versucht einen Integer mit einem Double zu addieren. Gefällt Dart wohl nicht. Also ‘int bar_start’ in ‘num bar_start’ geändert.

Keine Fehler mehr. Juhu. Und wie sieht es aus?

Dartiagramtmp - Chromium 2014-07-24 18.59.45

Säulen sind da. Aber statt, dass sie unterschiedlich hoch sind, unterscheiden sie sich in der Breite. Mal bar_width und bar_height vertauschen: “..rect(bar_start, 0, bar_height, bar_width)”.

Dartiagramtmp - Chromium 2014-07-24 19.03.35

Sieht schon besser aus. Aber eigentlich wollte ich sie von unten nach oben. Dass 0,0 links oben ist, hätte ich mir denken können. Weiter geht es etwas mit Versuchen das ganze zum funktionieren zu bekommen. Höhe und Breite negativ und den Starpunkt nicht auf 0 sondern eben über die Breite des ersten Balkens. Sonst würde es negativ nicht funktionieren. Ganz durchschaue ich den Code im Moment nicht mehr, aber er funktioniert.

Dartiagram

Und jetzt noch der Test, ob es auch mit mehreren Datenpunkten funktioniert: “var bars_data = [35, 20, 28, 20, 11, 45, 4, 12];”
Dartiagramtmp - Chromium 2014-07-24 19.21.51

Funktioniert. Natürlich fallen gleich zwei Probleme auf. Ich berechne die Breite zwar über die Anzahl der Säulen, aber füge immer 10px Abstand hinzu. Wenn man das auch prozentual über die Anzahl macht, dann würde der Platz nicht irgendwann aus sein. Ebenfalls ist aktuell die Höhe noch absolut festgelegt anstatt über die höchste Säule. Und man könnte auch in Frage stellen, ob Canvas das ideale Mittel für sowas ist. Einerseits kann das Diagramm somit einfach als .png gespeichert werden, aber es dürfte schwierig werden es interaktiv zumachen.

Und hier noch der Code.


wallpaper-1019588
The Brilliant Healer’s New Life in the Shadows – Neue Details zum Anime bekannt + Trailer
wallpaper-1019588
3 für 2 Aktion bei Anime Planet gestartet
wallpaper-1019588
Hotel Inhumans – Manga erhält eine Anime-Adaption
wallpaper-1019588
Manga-ka Shuzo Oshimi kündigt neues Werk an