SVG in Polymer

TL;DR

SVG in Polymer ist kaputt und Google sollte das beheben, da es an sich eine schöne Möglichkeit wäre, SVG Darstellungen im Browser zu modularisieren.

Einleitung

Ich habe in letzter Zeit beruflich viel mit SVG in Polymer-Elementen zu tun. Die Situation ist da leider recht unbefriedigend.

Wir bauen eine relativ komplexe Visualisierunge-Anwendung in Polymer, für die wir ein ebenfalls komplexes SVG Template benutzen. Der erste Prototyp der Anwendung war in Angular geschrieben und hat zur Visualisierung eine Angular-SVG Komponente benutzt. Wir haben die Anwendung dann für den zweite Prototypen auf Polymer 0.5 umgestellt und die Visualisierungs-Komponente mehr schlecht als recht portiert.

Da wir zur Laufzeit viele Parameter der Visualisierung ändern und an vielen Stellen Events abfangen müssen, wollte ich gerne eine modularere Lösung haben. Ich habe also begonnen die Visualisierung in Polymer-Komponenten nachzubauen. In meiner naiven Weltsicht hätten wir so eine ganze Menge Probleme auf einen Schlag beheben können:

Probleme

Leider habe ich relativ schnell feststellen müssen, dass Polymer nicht dazu gedacht ist gut mit SVG zusammen zu arbeiten. Das liegt vor allem daran, dass SVG erwartet, dass Kindelemente Instanzen von SVGNode sind, Polymer beim Templating jedoch von HTMLElement vererbt.

Mit nativen WebComponents funktioniert das im Übrigen. Erst wenn man Polymer benutzt, geht es kaputt. Hoffentlich etwas, was in den nächsten Polymer Versionen gefixed wird.

Es gibt für das Problem ein Workaround. Spoiler: Nur für Polymer 0.5, in Polymer 1.0 funktioniert dieser nicht mehr:
Wenn man seine Custom Elements in <foreignObject> packt und den Inhalt der Custom Elements in <svg>, funktioniert das Ganze ein bisschen.

Irritierend ist, dass <foreignObject> und <svg> auf Maus-Interaktionen reagieren, Tap-Events abfangen und sich selektieren lassen. Das kann man ihnen aber relativ leicht im CSS mit pointer-events: none; abgewöhnen.

Das nächste Problem ist, dass <foreignObject> nicht wie gewohnt mit ihren Eltern-Elementen größer wird. Hierdurch erzeugt man zwar gültige SVG-Elemente, die jedoch auf der Seite nicht sichtbar sind, da sie außerhalb der Ausmaße ihres umgebenden <foreignObject> liegen. Wenn man die Ausmaße des <foreignObject> nun auf width="100%" height="100%" setzt, scheint das Problem erstmal behoben zu sein.

Für viele Anwendungsfälle, sollte diese Lösung zufriedenstellend funktionieren, nicht jedoch für unsere Visualisierungs-Komponente.

Die Komponente ist im Grunde ein großer SVG-Canvas, den man mit der Maus Zoomen und bewegen kann. Die Funktionalität habe ich mit einer alle Elemente umgebenden SVG Group und einer Abbildungsmatrix gelöst: <g id="viewport" transform="matrix(scale, 0, 0, scale, x, y)>...</g>".

Hier hat mich nun mein <foreignObject>-Hack eingeholt. Die Lösung funktioniert super, solange alle Elemente im positiven Bereich der Koordinaten bleiben. Bewegt man sie links oder über [0,0] verschwinden sie einfach. Auch dieses Problem ist einfach erklärt: Die von mir definierten <foreignObject> Elemente haben zwar Ausmaße von 100% in jede Richtung, beginnen für den Browser jedoch erst bei [0,0].

Resignation

Leider habe ich für dieses Problem auch keine andere Lösung gefunden, als die Bewegung des Canvas über eine Bewegung aller Elemente zu verwirklichen, um dafür zu sorgen, dass alle Koordinaten positiv bleiben. Alle zu erwartenden Performance-Einbußen werden dabei wahr, weshalb ich diese Lösung wegwerfen konnte.

Fazit

SVG in Polymer ist kaputt - in Polymer 1.0 noch mehr als in 0.5.
Der Ansatz seine SVG Graphen in Polymer darzustellen ist reizvoll, weshalb ich hoffe, dass das Team um Polymer diese Einschränkung bald behebt. Im Moment ist es jedoch eher ein Gehacke und Gefrickel als das Erzeugen eleganter Lösungen.

Was ist aus unserer Visualisierungs-Komponente geworden? Wir lösen die Darstellung mit der Virtual DOM in React Komponenten, was erstaunlich schnell und schmerzfrei ist.

Show Comments