| Zusammenfassung | Discourse-Plugin, das die Anpassung des Splash-Screens mit vom Administrator definiertem HTML und CSS ermöglicht. | |
| Repository-Link | https://github.com/VaperinaDEV/custom-splash-html-builder | |
| Installationsanleitung | Plugins in Discourse installieren |
Hallo ![]()
Ich habe ein kleines Discourse-Plugin erstellt, mit dem sich der Splash-Screen mit vom Administrator definiertem HTML und CSS anpassen lässt, ohne eine modifizierte Kopie der Kern-Splash-Vorlage von Discourse pflegen zu müssen.
Die ursprüngliche Motivation für die Erstellung dieses Plugins war tatsächlich die Mobile-Leistung.
Ich wollte einen ausgefeilteren animierten Splash-Screen erstellen, stellte aber fest, dass die SVG-Animationen, die von der aktuellen Kern-Splash-Implementierung von Discourse unterstützt werden, auf mobilen Geräten überraschende Probleme verursachen können.
Auf dem Desktop kann die Animation perfekt flüssig aussehen, auf mobilen Geräten kann sie jedoch deutlich ruckeln, Frames auslassen, verzögern oder sogar während der Animation stehen bleiben.
Nachdem ich verschiedene Ansätze ausprobiert hatte, stellte ich fest, dass das Verschieben der Animation vom SVG selbst auf ein umschließendes HTML-Element wie ein <div> einen sehr deutlichen Unterschied machte.
Anstatt den SVG-Inhalt kontinuierlich zu animieren, kann das SVG statisch bleiben, während der Browser die umschließende HTML-Ebene mit CSS-Transformationen animiert.
Dies gibt dem Browser eine viel bessere Möglichkeit, die Animation als Kompositionsvorgang mit der Grafikhardware des Geräts zu verarbeiten.
Das Ergebnis war eine deutlich flüssigere Animation auf mobilen Geräten, ohne das Ruckeln und Einfrieren, das ich beim SVG-basierten Ansatz beobachtet hatte.
Das war der Hauptgrund für die Erstellung dieses Plugins.
Vorher (Animiertes SVG: Die Animation ruckelt und bleibt stehen)
Nachher (Animiertes HTML: Fließende Animation)
Das Problem mit der Animation von SVGs
Die ursprüngliche Splash-Implementierung ist für ein einfaches Logo oder eine relativ leichte Animation völlig in Ordnung.
Sobald die Animation jedoch komplexer wird, kann das SVG-Rendering teuer werden.
Beispielsweise kann eine Animation, die direkt auf ein SVG oder seine internen Elemente angewendet wird, erfordern, dass der Browser Teile des SVGs während der Animation wiederholt verarbeitet oder neu zeichnet.
Auf mobilen Geräten kann dies besonders auffällig sein.
Während der Tests habe ich Fälle gesehen, in denen die Animation:
- sichtbar ruckelte
- vorübergehend einfroren
- stehen zu bleiben schien
- sich deutlich schlechter verhielt als auf dem Desktop
Der interessante Teil war, dass sich dieselbe visuelle Animation sehr unterschiedlich verhalten konnte, je nachdem, was tatsächlich animiert wurde.
Die Animation in eine HTML-Ebene verschieben
Der Ansatz, der viel besser funktionierte, bestand darin, das SVG selbst statisch zu halten und in ein normales HTML-Element zu setzen.
Zum Beispiel:
<div class="logo-layer">
<svg viewBox="0 0 500 500">
...
</svg>
</div>
Anstatt das SVG zu animieren, wird die Animation auf den Container angewendet:
.logo-layer {
animation: pulse 1.8s ease-in-out infinite;
will-change: transform;
}
@keyframes pulse {
0%,
100% {
transform: scale(0.8);
}
50% {
transform: scale(0.85);
}
}
Das SVG selbst ändert sich nicht.
Der Browser kann die Transformation der HTML-Ebene daher viel effizienter verarbeiten und sie in unterstützten Fällen in eine kompositorische Ebene hochstufen, die von der Grafikhardware verarbeitet wird.
Dies führte zu einem dramatisch flüssigeren Ergebnis auf mobilen Geräten.
Die wichtige Unterscheidung ist daher:
Kern-Ansatz:
SVG
└── SVG-Animation
└── SVG-Inhalte werden animiert
vs:
Benutzerdefinierter Ansatz:
HTML-Ebene
└── SVG
└── CSS-Transformation auf HTML-Ebene
└── compositorfreundliche Animation
Dies ist keine Garantie dafür, dass jede Animation GPU-beschleunigt wird. Der Browser entscheidet ultimately, wie eine Animation kompositorisch verarbeitet wird, aber in meinen Tests war der Unterschied sehr deutlich.
Warum ich den Custom Splash HTML Builder erstellt habe
Sobald dieser Ansatz funktionierte, brauchte ich auch eine Möglichkeit, den Splash-Screen darum herum zu erstellen.
Die Standard-Splash-Vorlage bietet nicht genug Flexibilität für diese Art von Implementierung.
Für eine komplexere Animation könnte ich benötigen:
- mehrere SVG-Ebenen
- mehrere HTML-Container
- unabhängig animierte Elemente
- benutzerdefinierte CSS-Keyframes
- verschiedene Animationstimmings
- benutzerdefinierte Positionierung
- themenbewusste Farben
- völlig anderen Markup als der Standard-Splash
Anstatt also eine weitere hartcodierte Splash-Implementierung zu erstellen, entschied ich mich, den visuellen Teil über zwei Site-Einstellungen freizugeben.
Das Plugin fügt hinzu:
splash_custom_html
Der HTML/SVG-Code, der im Splash-Screen gerendert wird.
splash_custom_css
Das CSS, das vom benutzerdefinierten Splash verwendet wird, einschließlich Animationen, Keyframes, Positionierung und responsivem Verhalten.
Dies macht den Splash effektiv anpassbar, ohne den Plugin-Quellcode jedes Mal ändern zu müssen, wenn sich die Animation ändert.
Eingebauter Admin-Editor
Das Plugin bietet auch einen kleinen eingebauten Admin-Editor zur Verwaltung des benutzerdefinierten Splash-Screens.
Es fügt einen eigenen Abschnitt Splash HTML Builder in die Discourse-Admin-Oberfläche hinzu, mit separaten Editoren für:
- Benutzerdefiniertes HTML
- Benutzerdefiniertes CSS
Die Änderungen können direkt aus der Admin-Oberfläche gespeichert werden, ohne die entsprechenden Site-Einstellungen manuell bearbeiten zu müssen.
Die zugrunde liegenden Einstellungen sind immer noch:
splash_custom_htmlsplash_custom_css
Der Editor ist einfach eine bequemere Oberfläche zur Verwaltung dieser Einstellungen.
Dies bedeutet auch, dass das Plugin keine Änderungen an den Plugin-Quelldateien erfordert, wenn die Splash-Animation geändert werden muss.
Beispiel
Ein benutzerdefinierter Splash kann mehrere unabhängige Ebenen enthalten:
<div class="splash-logo-container">
<div class="ring-layer">
<svg viewBox="0 0 500 500">
...
</svg>
</div>
<div class="logo-layer">
<svg viewBox="0 0 500 500">
...
</svg>
</div>
</div>
Und jede Ebene kann ihre eigene Animation haben:
.ring-layer {
animation: rotate 2.2s linear infinite;
will-change: transform;
}
.logo-layer {
animation: pulse 1.8s ease-in-out infinite;
will-change: transform;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes pulse {
0%,
100% {
transform: scale(0.8);
}
50% {
transform: scale(0.85);
}
}
Die SVGs bleiben statisch, während die umschließenden HTML-Ebenen animiert werden.
Dies macht es möglich, erheblich komplexere Splash-Animationen zu erstellen, während die aufwendigen Animationen außerhalb des SVGs selbst bleiben.
Warum nicht einfach die Kern-Splash-Vorlage überschreiben?
Ein weiteres wichtiges Ziel war es, die Wartung einer Kopie der Kern-Splash-Vorlage von Discourse zu vermeiden.
Ein direkter Ansatz wäre es, Folgendes zu überschreiben:
app/views/common/_discourse_splash.html.erb
und die aktuelle Discourse-Implementierung in das Plugin zu kopieren.
Das Problem ist, dass dies einen Wartungsaufwand erzeugt.
Wenn Discourse seine Splash-Implementierung in einer zukünftigen Version ändert, würde das Plugin immer noch die alte Version enthalten.
Das könnte potenziell zu Folgendem führen:
- fehlende neue Kern-Änderungen
- fehlende Leistungsverbesserungen
- fehlerhaftes Verhalten nach einem Discourse-Update
- manuelles Vergleichen der Plugin-Vorlage mit dem Kern nach jedem Update
Das wollte ich vollständig vermeiden.
Kern-Fallback
Das Plugin unterstützt daher einen benutzerdefinierten Splash mit einem Kern-Fallback.
Benutzerdefiniertes HTML ist konfiguriert
Wenn:
SiteSetting.splash_custom_html.present?
dann rendert das Plugin den benutzerdefinierten Splash.
Benutzerdefiniertes HTML ist leer
Wenn kein benutzerdefinierter Splash konfiguriert wurde, fällt das Plugin auf die aktuelle Discourse-Kern-Splash-Vorlage zurück.
Das Plugin findet die tatsächliche Kern-Datei aus der laufenden Discourse-Installation:
Rails.root/app/views/common/_discourse_splash.html.erb
und rendert diese Implementierung.
Konzeptionell:
core_splash_path = Rails.root.join("app", "views", "common", "_discourse_splash.html.erb")
if File.exist?(core_splash_path)
render inline: File.read(core_splash_path), type: :erb
end
Das bedeutet, dass das Plugin nicht eine zweite Kopie der Kern-Splash-Vorlage enthält.
Leistungsaspekte
Das Plugin behauptet nicht, dass jede CSS-Animation magischerweise GPU-beschleunigt wird.
Der Browser entscheidet immer noch, wie einzelne Animationen gerendert und kompositorisch verarbeitet werden.
Das Ziel ist es stattdessen, dem Browser eine viel günstigere Struktur für hardwarebeschleunigte Komposition zu geben:
- SVG-Inhalte statisch halten
- unabhängig animierte Elemente isolieren
- HTML-Ebenen animieren
transformfür Bewegung/Scaling/Rotation bevorzugen- unnötig aufwendige Neuzeichnungsoperationen vermeiden
will-changedort verwenden, wo es angemessen ist
Zum Beispiel:
.ring-layer {
will-change: transform;
animation: rotate 2.2s linear infinite;
}
Dieser Ansatz funktionierte besonders gut für meinen Anwendungsfall und eliminierte das mobile Ruckeln, das ich bei der ursprünglichen SVG-Animation beobachtete.
Den benutzerdefinierten Splash aktivieren oder deaktivieren
Das Plugin bietet auch eine Site-Einstellung custom_splash_html_builder_enabled.
Wenn deaktiviert, wird der Standard-Discourse-Splash-Screen verwendet, unabhängig davon, ob benutzerdefiniertes HTML oder CSS konfiguriert wurde.
Dies bietet einen zusätzlichen Sicherheitsschalter zum vorübergehenden Deaktivieren des benutzerdefinierten Splash-Screens, ohne das gespeicherte HTML/CSS zu löschen.
Der benutzerdefinierte Splash wird nur gerendert, wenn beide Bedingungen erfüllt sind:
custom_splash_html_builder_enabled = true
splash_custom_html ist nicht leer
Andernfalls wird der aktuelle Discourse-Kern-Splash verwendet.
Wichtig ist vor allem, dass es eine Möglichkeit bietet, einen benutzerdefinierten animierten Splash zu erstellen, der auf mobilen Geräten viel besser performt, indem HTML-Ebenen um statische SVG-Inhalte herum animiert werden, anstatt das SVG selbst direkt zu animieren.