In einem Projekt von queo nutze ich die Vorteile von SVG Grafiken aus. In der Webanwendung soll die SVG nicht nur angezeigt werden, sondern auch mit ihr interagiert werden können. Dazu zählen das Hinein- und Herauszoomen, das Verschieben der SVG innerhalb eines Containers und auch das Anklicken von Punkten innerhalb der SVG. Diese Aktionen sollen sowohl mittels Gestensteuerung auf dem Tablet funktionieren als auch mit der Maus. Weiterhin soll das Zoomen auch über Buttons möglich sein.

Eine SVG ist eine zweidimensionale Vektorgrafik und sie basiert auf XML. Während jeder moderne Browser SVGs anzeigen kann, benötigen nur sehr alte Browser, wie zum Beispiel der IE8, ein zusätzliches Plugin um SVGs zu rendern. Der Vorteil von SVGs gegenüber anderen Bildformaten (oder auch dem HTML-Canvas-Element) ist, dass sie mit CSS gestylt werden können und im Javascript auf jedes Element der SVG zugegriffen werden kann.

Um zu verstehen wie so etwas umgesetzt werden kann, sollte der Leser JS-/HTML- und CSS-Kenntnisse vorweisen. Wer sich ohne Programmierkenntnisse auf Entdeckungstour begeben möchte, dem seien folgende Seiten ans Herz gelegt:

Hier finden sich ganz viele Beispiele, was alles mit SVGs im Browser möglich ist.

Anzeigen der SVG in einem Container

Bevor überhaupt eine Interaktion mit der SVG erfolgen kann, muss die mitunter sehr große Grafik in einem Container angezeigt werden.  Die Originalgröße der SVG wird dabei beibehalten und sie wird innerhalb des Containers zentriert. Wenn die Grafik jedoch kleiner ist, dann muss sie auf die Größe des Containers skaliert werden.

Dazu wird folgende HTML-Struktur verwendet.




<div class="container">
  <svg>
     <g></g>
  </svg>
</div>



Damit die SVG nur innerhalb des Containers zu sehen ist, muss dieser noch einen CSS-Style erhalten.




<style>
  .container{ 
    overflow: hidden; 
    width: 500px; 
    height: 400px; 
  } 
</style>



Mit Hilfe eines Services, der als Eingabeparameter die Höhe und Breite des Containers und der Grafik bekommt, wird die Zentrierung bzw. das Skalieren bei kleineren Grafiken wie folgt realisiert.

if(heightOfContainer > heightOfGraphic && widthOfContainer > widthOfGraphic){
    var startWithHeight = false;
    if(svgHeight > svgWidth){
        startWithHeight = true;
    }
    
    if(startWithHeight){
        /*
        * Höhe der SVG = Höhe des Containers
        * Berechne die Breite der SVG
        * Wenn die Breite größer als die des Containers ist, 
        * dann wird doch als erstes die Breite angepasst
        * und dann die Höhe berechnet.
        */
    }
    else{
        /*
        * Breite der SVG = Breite des Containers
        * Berechne die Höhe der SVG
        * Wenn die Breite größer als die des Containers ist, 
        * dann wird doch als erstes die Höhe angepasst
        * und dann die Breite berechnet.
        */
    }
    
    if(heightOfContainer > heightOfGraphic){
        //Höhe des Containers = Höhe der Grafik
    }
    if(widthOfContainer > widthOfGraphic){
        //Breite der Grafik = Breite des Containers
    }
    
    if(heightOfGraphic > heightOfContainer){
        /*
        * Verschieben der SVG-Grafik mit margin-top um die negative 
        * halbe Differenz der beiden Werte
        */
    }
    if(widthOfGraphic > widthOfContainer){
        /*
        * Verschieben der SVG-Grafik mit margin-left um die negative 
        * halbe Differenz der beiden Werte
        */
    }
}

 

SVG-Interaktion

Am einfachsten umzusetzen, ist die Funktion um Punkte in der SVG anzuklicken. Hierfür wird einfach allen Punkten (SVG Element <circle>) ein Click-Eventhandler hinzugefügt, welcher dann die Attribute des Punktes so verändert, dass er markiert erscheint.

Eine Übersicht, wie und was man an SVG-Elemente so alles mittels CSS oder Attribut-Eigenschaften beeinflussen kann findet sich im Self-HTML-Wiki.

Zoomen und Verschieben mit der Maus

Für die Interaktion mit der SVG nutzen wir das Framework d3js, weil es bereits Funktionen zum Zoomen und Verschieben einer SVG bereitstellt. Mit der Maus ist wie gewohnt über das Scrollrad das Rein- und Rauszoomen möglich und mit Drag und Drop lässt sich die SVG verschieben.

Hierfür wird zunächst ein Zoom-Verhalten initialisiert.

var zoomBehaviour = d3.behavior.zoom().scaleExtent([scaleStart, scaleEnd]);

Dabei muss festgelegt werden, in wie vielen Stufen herein- oder herausgezoomt kann. Dies wird über den Skalierungsfaktor festgelegt (scaleStart, scaleEnd). In unsere Anwendung wird scaleStart abhängig von der Größe der SVG berechnet, damit wir bei der maximal herausgezoomten Stufe die komplette SVG sehen.

zoomBehaviour.on('zoom', zoomFunc);

Anschließend wird dem Zoomverhalten noch eine Eventhandler-Callback-Funktion zugewiesen, die aufgerufen wird, wenn die Gesten- und Mausevents zum Verschieben oder Zoomen gefeuert werden. Folgende Werte sind dabei bereits innerhalb der Bibliothek d3.js berechnet worden.

  • [x,y] Verschiebe um x an der x-Achse und/oder um y an der y-Achse
  • scale Skalieriungsfaktor

Diese Werte können nun in das Attribut transform des root-Elements der SVG geschrieben werden, um eine Transformation auszulösen.

var translate =  d3.event.translate;
var scale = d3.event.scale;
d3ViewContainer = d3.select('.svg-container'); 
d3ViewContainer.select('g')
.attr('transform', 'translate(' + translate + ') scale(' + scale + ')');

Falls die SVG kein root-Element besitzt und zum Beispiel mehrere g-Tags auf der obersten Ebene liegen, müssen diese alle transformiert werden. Hierzu wir die folgende Funktion verwendet.

d3ViewContainer.selectAll('g').attr(…)
Wir finden individuelle Lösungen
Planungs- und Entscheidungsprozesse in Unternehmen basieren auf zahlreichen Daten und deren Gewichtung. Wir finden individuelle Lösungen, die Ihren speziellen Anforderungen entsprechen und binden bestehende Systeme optimal ein.

Inkonsistente Gestensteuerung

Leider verhält sich die Gestensteuerung mit d3.js auf dem Tablet unterschiedlich, je nachdem, welchen Browser man verwendet:

  • Chrome: Normales Zoomen über Pinch-Gesten und Verschieben mit Wischen
  • Firefox: Zoomen über Hoch und Runter-Wischen und Verschieben mit Wischen
  • Internet Explorer/ Edge: Standardmäßig gar keine Gesten verfügbar
  • Safari: nur das Verschieben ist möglich

Alle Browser wurden auf dem Surface Pro 3 getestet.

Für den Internetexplorer (oder auch Edge) muss folgendes Style-Attribut für das svg-tag gesetzt werden, damit zumindest das Verschieben funktioniert:

-ms-touch-action: none; 

Dadurch werden sämtliche Touch-Gesten ausgestellt und bei einem Touch werden stattdessen die Mausevents gefeuert. Folglich ist ein Zoomen so nicht möglich.

Als Alternative haben wir für den Internet Explorer die d3.js Bibliothek komplett deaktiviert und das Zoomen über CSS-Eigenschaften des div-tags, welches die SVG-Grafik umschließt, ermöglicht.

.svg-container{
  overflow: auto;
  -ms-content-zooming: zoom;
  -ms-scroll-rails: none;
  -ms-content-zoom-limit-min: 50%;
  -ms-content-zoom-limit-max: 500%;
}

Nun ist es möglich in der SVG ganz normal mittels Pinch-Geste rein und raus zu zoomen und mit Wischen die Grafik zu verschieben. Leider kann das Zoomen mit Buttons dadurch nicht mehr unterstützt werden, denn es gibt keine Möglichkeit herauszufinden, in welchem Zoomlevel der Container sich gerade befindet.

Zoomen mit Buttons

Beim Zoomen mit Hilfe von Buttons muss die Berechnung eigenhändig implementiert werden, da d3.js hierfür keine Funktionen bereitstellt. Da es aber möglich ist, dass der Nutzer zwischen den beiden Interaktionsmöglichkeiten wechselt, muss weiterhin mit dem Zoomverhalten, dass für das Zoomen mittels Gesten/Maus verantwortlich ist, gearbeitet werden.

Als erstes müssen sich der aktuelle scale-Wert und die beiden translate-Werte vom Zoom-Verhalten geholt werden. Danach erfolgt die Berechnung folgendermaßen.

if (zoomType == "zoomIn") {
    if (scale <= scaleEnd * 0.8) { scale = scale * 1.25; x = ((x - (factor * (widthOfSvg / 2))) * 1.25) + (factor * widthOfSvg / 2); y = ((y - (heightOfSvg / 2)) * 1.25) + height / 2; } } else { if (zoomListener.scale() >= scaleStart*1.25) {
        scale = scale * 0.8;
        x = ((x - (factor * (widthOfSvg / 2))) * 0.8) + (factor * widthOfSvg / 2);
        y = ((y - (heightOfSvg / 2)) * 0.8) + heightOfSvg / 2;
    }
}

Die neuen Werte werden dann verwendet um manuell ein Zoom-Event beim Zoomverhalten auszulösen.

zoomBehaviour
  .scale(scale)
  .translate([x, y]);
zoomBehaviour.event(d3ViewContainer.select('svg'));

 Die Skalierung und das Verschieben erfolgt dann wieder in der Eventhandler-Callback-Funktion (zoomFunc).

Zusammenfassung und Ausblick

Dieser  Blogeintrag sollte einen ersten Einblick verschaffen, inwieweit es möglich ist die Interaktion mit SVGs innerhalb einer Webseite umzusetzen. Die Manipulation von SVG-Elementen ist sehr einfach umsetzbar, da sie wie andere HTML-Tags behandelt werden können. Jedoch ist aufgefallen, dass komplexe Manipulationen wie das Zoomen oder Verschieben gerade auf mobilen Geräten noch schwierig zu realisieren sind. Vorhandene Bibliotheken weisen hier noch mangelnde Funktionalitäten auf. Denn auch bei anderen Bibliotheken (wie z.B. SVGPan oder svg-pan-zoom)  ist die Gestensteuerung auf dem Tablet unzureichend umgesetzt oder gar nicht möglich. d3.js bietet hierbei noch den größten Funktionsumfang. Da in unserem Projekt nur mit dem Internet Explorer auf dem Tablet gearbeitet wird, brauchten wir nicht auf einer konsistente Umsetzung der Gestensteuerung achten. Ich denke jedoch, dass diese sich durchaus umsetzen lässt, wenn man sich mit dem Quellcode von d3.js auseinandersetzt und ihn so anpasst, dass der Nutzer in allen Browser die gleichen Gesten verwenden kann.

Falls Sie das Zoomen oder Verschieben einer SVG selbst ausprobieren wollen, finden Sie hier ein Beispiel, welches mit d3.js umgesetzt wurde.

Diskutieren Sie mit!

Es steht Ihnen frei einen Kommentar zu hinterlassen. Sie finden Informationen zur Verwendung Ihrer Daten in unserer Datenschutzerklärung.

Alle mit einem markierten Felder sind Pflichtfelder und müssen ausgefüllt werden