Bei der Umsetzung komplizierter, grafisch aufwendiger Webinterfaces — wie beispielsweise interaktiver Infografiken — kommt man mit klassischen Browserbordmitteln und HTML-Elementen schnell zu einem Punkt, an dem man sein eigenes Entwickler-Dasein in Frage stellt.
p5.js – eine OpenSource JavaScript-Libary mit Fokus auf Kunstschaffende und Designende – kann uns hierbei unterstützen und das »Zeichnen auf dem Bildschirm« erleichtern. Diese hat vor allem durch die YouTube Videos von ›The Coding Train‹ einiges an Bekanntheit erlangt.
In diesem Blogbeitrag werde ich anhand einer Radar-Chart-React-Komponenten die Vorteile und Anwendungszweckevon p5.js genauer beleuchten und auf meine Learnings beim Arbeiten mit der Libary eingehen.
Good to know
Mein Experiment bezieht sich auf React in TypeScript. Natürlich kann p5 aber auch in klassischen React Projekten oder sogar in Plain-HTML Seiten verwendet werden. Links hierzu findest du am Ende des Artikels. Um p5.js in unser React-Project einzubinden verwende ich die Libary react-p5 selbstverständlich gibt es hierzu auch einige Alternativen. Die einzelnen Codeschnipsel und das Demoprojekt (React-Komponente mit TypeScript) findet sich in unserem GitHub Repository
How it works
Schauen wir uns als erstes einmal den Grundaufbau eines p5 Projektes an. Dieses besteht aus zwei Hauptmethoden: setup() und draw()
setup() wird bei vor dem ersten Zeichnen des p5-Sketches ausgeführt. Das heißt alle Dinge, die nur einmal passieren müssen, werden aus Performance-Gründen hier ausgeführt. Mindestens aber muss ein Canvas erstellt werden:
p5.createCanvas(canvasSize, canvasSize).parent(canvasParentRef);
Des Weiteren setzen wir in unserem Beispiel den angleMode und die textSize
p5.angleMode("degrees");
p5.textSize(14);
Um schließlich das eigentliche Interface zu zeichnen, verwenden wir draw() Diese Methode wird bei jedem Zeichenvorgang ausgeführt; in der Standardeinstellung passt sich die Framerate dabei an die, des Bildschirms an. Dementsprechend wird der Code auf den meisten Computern 60-mal in der Sekunde ausgeführt.
Randnotiz: In manchen Anwendungsfällen kann es sinnvoll sein, dass man die Framerate absichtlich anpasst. Beispielsweise bei statischen Grafiken, die auch nicht von der Größe des Bildschirms abhängen und deswegen nicht so oft neu gezeichnet werden müssen. Hierzu kann einfach die Framerate in setup() gesetzt werden:
frameRate(fr);
Außerdem ist wichtig, dass draw() stringent von oben nach unten durchläuft und alle weiteren Elemente über die vorherigen Elemente zeichnet. Das bedeutet, wenn Elemente optisch hinter anderen liegen sollen, müssen diese zuerst gezeichnet werden. In unserem Beispiel zeichne ich daher zuerst die Ringe, um diese unter den eigentlichen Graph zu legen.
Auch bedeutet dies, dass immer die zuletzt gesetzten Attribute verwendet werden.
- Farbe auf Grün setzen
- Ellipse zeichnen
- Farbe auf Rot setzen
- Rechteck zeichnen
Dieser Pseudocode produziert beispielsweise eine grüne Ellipse und ein rotes Rechteck.
→ Beim Zeichnen von Elementen müssen keine Attribute wie Farbe oder Border angegeben werden, weil diese vom Kontext und den aktuell gesetzten Werten definiert werden. Dadurch kann viel Boilerplate-Code reduziert werden.
Aber wie zeichnet man denn jetzt ein Element?
p5.ellipse(x, y, width, height);
p5.rect(x, y, width, height);
p5.text("text", x, y);
ja, es ist so simpel. Selbst das Zeichnen eines Freiform-Vektors ist relativ selbsterklärend:
p5.beginShape();
p5.vertex(x, y); // Hinzufügen eines Punktes
p5.vertex(x1, y1); // Hinzufügen eines weiteren Punktes
p5.endShape(p5.CLOSE);
Setzten wir nun den draw()-Ablauf der einzelnen Elemente unseres Spider-Graphs in die richtige Reihenfolge:
- Ringe zeichnen
- Linien nach außen zeichnen
- Texte zeichnen
- Spinnennetz zeichnen
- Spinnennetzpunkte zeichnen
Dabei müssen wir an einigen Stellen den xOffset und yOffset basierend auf dem Mittelpunkt, dem Radius und dem Winkel ausrechnen, um die Koordinaten eines Punktes mit Abstand r in einem bestimmten Winkel zum Mittelpunkt zu bekommen. Dafür verwende ich einfach klassische Dreiecksberechnungen. ›The Coding Train‹ hat dazu ein aufschlussreiches Tutorial. Um die Texte optisch ansprechend um den Graphen herum zu positionieren habe ich des Weiteren ein einfaches if-else Konstrukt geschrieben. Dieses setzt basierend auf dem Winkel bezogen zur Mitte den textAlign . Dieser bestimmt wo die x und y Koordinate bei dem Text anliegen.
p5.textAlign(p5.CENTER, p5.CENTER);
würde zum Beispiel aussagen, dass die beim Zeichnen angegebenen Koordinaten den Mittelpunkt beschreiben.
function setTextAlignBasedOnAngle(p5: p5Types, angle: number)
{
if (angle >= -10 && angle < 10) {
p5.textAlign(p5.LEFT, p5.CENTER);
} else if (angle >= 10 && angle < 80) {
...
} else {
p5.textAlign(p5.CENTER, p5.CENTER);
}
}
Mit react-p5 müssen wir schließlich nur noch setup() und draw() an die Komponente übergeben und der p5.js Sketch wird gezeichnet:
<Sketch setup{setup} draw{draw} />;
Die Learnings für die Interhyp
p5.js ist ein unglaublich mächtiges Tool um grafische Interfaces mit einem bisschen Spice zu versehen. Es ist nicht unwahrscheinlich, dass wir beim Gestalten von neuen Infografiken und Statistiken in unseren Produkten in Zukunft auf p5.js zurückgreifen. Vor allem die Benutzerfreundlichkeit und die geringe Menge an Code — sogar bei komplizierten Interfaces — zeichnen die Libary meiner Meinung nach aus.
Links
Wenn dich das Thema p5 interessiert, findest du hier weitere Ressourcen: