| Résumé | Plugin Discourse permettant de personnaliser l’écran de démarrage (splash screen) à l’aide de HTML et CSS définis par l’administrateur. | |
| Lien du dépôt | https://github.com/VaperinaDEV/custom-splash-html-builder | |
| Vous l’avez trouvé utile ? | > ./support --coffee | |
| Guide d’installation | Comment installer des plugins dans Discourse |
Bonjour ![]()
J’ai créé un petit plugin Discourse qui permet de personnaliser l’écran de démarrage à l’aide de HTML et CSS définis par l’administrateur, sans avoir à maintenir une copie modifiée du modèle de splash core de Discourse.
La motivation initiale pour créer ce plugin était en réalité les performances mobiles.
Je voulais créer un écran de démarrage animé plus sophistiqué, mais j’ai constaté que les animations SVG prises en charge par l’implémentation actuelle du splash core de Discourse pouvaient devenir problématique sur les appareils mobiles.
Sur ordinateur, l’animation pouvait paraître parfaitement fluide, tandis que sur mobile, elle pouvait devenir visiblement saccadée, perdre des images, subir des retards ou même sembler s’arrêter pendant l’animation.
Après avoir expérimenté différentes approches, j’ai découvert que déplacer l’animation de l’élément SVG lui-même vers un élément HTML environnant, tel qu’un <div>, faisait une très grande différence.
Au lieu d’animer en continu le contenu du SVG, le SVG peut rester statique tandis que le navigateur anime la couche HTML conteneur à l’aide de transformations CSS.
Cela donne au navigateur une bien meilleure opportunité de traiter l’animation comme une opération de composition en utilisant le matériel graphique de l’appareil.
Le résultat a été une animation beaucoup plus fluide sur mobile, sans les à-coups et les figements que je constatais avec l’approche basée sur le SVG.
C’était la raison principale pour laquelle ce plugin a été créé.
Avant (SVG animé : l’animation est en retard et s’arrête)
Après (HTML animé : animation fluide)
Le problème avec l’animation des SVG
L’implémentation originale du splash est parfaitement adaptée pour un logo simple ou une animation relativement légère.
Cependant, une fois que l’animation devient plus complexe, le rendu SVG peut devenir coûteux.
Par exemple, une animation appliquée directement à un SVG ou à ses éléments internes peut exiger du navigateur de traiter ou de repeindre répétitivement des parties du SVG pendant l’animation.
Sur les appareils mobiles, cela peut devenir particulièrement notable.
Pendant les tests, j’ai constaté des cas où l’animation :
- devenait visiblement saccadée
- se figeait temporairement
- semblait s’arrêter
- se comportait beaucoup moins bien que sur ordinateur
La partie intéressante était que la même animation visuelle pouvait se comporter très différemment selon ce qui était réellement animé.
Déplacer l’animation vers une couche HTML
L’approche qui a beaucoup mieux fonctionné a été de garder le SVG lui-même statique et de le placer à l’intérieur d’un élément HTML normal.
Par exemple :
<div class="logo-layer">
<svg viewBox="0 0 500 500">
...
</svg>
</div>
Au lieu d’animer le SVG, l’animation est appliquée au conteneur :
#d-splash .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);
}
}
Le SVG lui-même ne change pas.
Le navigateur peut donc traiter la transformation de la couche HTML beaucoup plus efficacement, et dans les cas pris en charge, la promouvoir en une couche composite gérée par le matériel graphique.
Cela a produit un résultat nettement plus fluide sur les appareils mobiles.
La distinction importante est donc la suivante :
Approche core :
SVG
└── Animation SVG
└── Le contenu du SVG est animé
vs :
Approche personnalisée :
Couche HTML
└── SVG
└── Transformation CSS sur la couche HTML
└── Animation compatible avec le compositeur
Ce n’est pas une garantie que chaque animation sera accélérée par le GPU. Le navigateur décide finalement comment une animation est composée, mais dans mes tests, la différence était très notable.
Pourquoi j’ai créé le Custom Splash HTML Builder
Une fois que j’avais cette approche fonctionnelle, j’avais aussi besoin d’un moyen de construire réellement l’écran de démarrage autour de celle-ci.
Le modèle de splash standard ne fournit pas assez de flexibilité pour ce type d’implémentation.
Pour une animation plus complexe, je pourrais avoir besoin de :
- plusieurs couches SVG
- plusieurs conteneurs HTML
- des éléments animés indépendamment
- des keyframes CSS personnalisées
- des minuteries d’animation différentes
- un positionnement personnalisé
- une balise (markup) complètement différente de celle du splash par défaut
Ainsi, au lieu de créer une autre implémentation de splash codée en dur, j’ai décidé d’exposer la partie visuelle via deux paramètres de site.
Le plugin ajoute :
splash_custom_html
Le balisage HTML/SVG rendu à l’intérieur de l’écran de démarrage.
splash_custom_css
Le CSS utilisé par le splash personnalisé, y compris les animations, les keyframes, le positionnement et le comportement responsive.
Cela rend le splash effectivement personnalisable sans avoir à modifier le code source du plugin chaque fois que l’animation change.
Éditeur d’administration intégré
Le plugin fournit également un petit éditeur d’administration intégré pour gérer le splash personnalisé.
Il ajoute une section dédiée Splash HTML Builder dans l’interface d’administration de Discourse, avec des éditeurs séparés pour :
- HTML personnalisé
- CSS personnalisé
Les modifications peuvent être enregistrées directement depuis l’interface d’administration sans avoir à modifier manuellement les paramètres de site correspondants.
Les paramètres sous-jacents restent :
splash_custom_htmlsplash_custom_css
L’éditeur est simplement une interface plus pratique pour les gérer.
Cela signifie également que le plugin ne nécessite pas de modifier les fichiers source du plugin chaque fois que l’animation du splash doit être modifiée.
Exemple
Un splash personnalisé peut contenir plusieurs couches indépendantes :
<div class="ring-layer">
<svg viewBox="0 0 500 500">
...
</svg>
</div>
<div class="logo-layer">
<svg viewBox="0 0 500 500">
...
</svg>
</div>
Et chaque couche peut avoir sa propre animation :
#d-splash .ring-layer {
animation: rotate 2.2s linear infinite;
will-change: transform;
}
#d-splash .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);
}
}
Les SVG restent statiques tandis que les couches HTML environnantes sont animées.
Cela permet de créer des animations de splash considérablement plus complexes tout en gardant le travail d’animation coûteux hors du SVG lui-même.
Pourquoi ne pas simplement remplacer le modèle de splash core ?
Un autre objectif important était d’éviter de maintenir une copie du modèle de splash core de Discourse.
Une approche simple consisterait à remplacer :
app/views/common/_discourse_splash.html.erb
et à copier l’implémentation actuelle de Discourse dans le plugin.
Le problème est que cela crée une charge de maintenance.
Si Discourse modifie son implémentation de splash dans une future version, le plugin contiendrait toujours l’ancienne version.
Cela pourrait potentiellement entraîner :
- l’absence de nouvelles modifications core
- l’absence d’améliorations de performance
- un comportement cassé après une mise à jour de Discourse
- avoir à comparer manuellement le modèle du plugin avec le core après chaque mise à jour
Je voulais éviter cela entièrement.
Repli sur le core (Core fallback)
Le plugin prend donc en charge un splash personnalisé avec un repli sur le core.
Le HTML personnalisé est configuré
Si :
SiteSetting.splash_custom_html.present?
alors le plugin rend le splash personnalisé.
Le HTML personnalisé est vide
Si aucun splash personnalisé n’a été configuré, le plugin bascule sur le modèle de splash core actuel de Discourse.
Le plugin localise le fichier core réel depuis l’installation Discourse en cours d’exécution :
Rails.root/app/views/common/_discourse_splash.html.erb
et rend cette implémentation.
Conceptuellement :
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
Cela signifie que le plugin ne porte pas une seconde copie du modèle de splash core.
Considérations de performance
Le plugin ne prétend pas que chaque animation CSS deviendra magiquement accélérée par le GPU.
Le navigateur décide toujours comment les animations individuelles sont rendues et composées.
L’objectif est plutôt de donner au navigateur une structure beaucoup plus favorable pour la composition accélérée par le matériel :
- garder le contenu SVG statique
- isoler les éléments animés indépendamment
- animer les couches HTML
- privilégier
transformpour les déplacements/mises à l’échelle/rotations - éviter les opérations de repeinture inutiles et coûteuses
- utiliser
will-changelorsque c’est approprié
Par exemple :
#d-splash .ring-layer {
will-change: transform;
animation: rotate 2.2s linear infinite;
}
Cette approche a particulièrement bien fonctionné pour mon cas d’utilisation et a éliminé les à-coups mobiles que je constatais avec l’animation SVG originale.
Activer ou désactiver le splash personnalisé
Le plugin fournit également un paramètre de site custom_splash_html_builder_enabled.
Lorsqu’il est désactivé, l’écran de démarrage standard de Discourse est utilisé, qu’il y ait ou non du HTML ou du CSS personnalisé configuré.
Cela fournit un interrupteur de sécurité supplémentaire pour désactiver temporairement le splash personnalisé sans supprimer le HTML/CSS enregistré.
Le splash personnalisé n’est rendu que lorsque :
custom_splash_html_builder_enabled = true
splash_custom_html n'est pas vide
Sinon, le splash core actuel de Discourse est utilisé.
Le plus important, c’est qu’il fournit un moyen de construire un splash animé personnalisé qui fonctionne beaucoup mieux sur mobile en animant des couches HTML autour de contenu SVG statique au lieu d’animer directement le SVG lui-même.