Costruttore HTML personalizzato per schermi di avvio

:information_source: Riepilogo Plugin per Discourse che consente di personalizzare la schermata di avvio (splash screen) utilizzando HTML e CSS definiti dall’amministratore.
:hammer_and_wrench: Link al Repository https://github.com/VaperinaDEV/custom-splash-html-builder
:open_book: Guida all’installazione Come installare i plugin in Discourse

Ciao :waving_hand:

Ho creato un piccolo plugin per Discourse che permette di personalizzare la schermata di avvio utilizzando HTML e CSS definiti dall’amministratore, senza dover mantenere una copia modificata del modello core di Discourse per la splash screen.

La motivazione originale per la creazione di questo plugin era in realtà le prestazioni sui dispositivi mobili.

Volevo creare una schermata di avvio animata più sofisticata, ma ho scoperto che le animazioni SVG supportate dall’implementazione core attuale della splash screen di Discourse potevano diventare sorprendentemente problematiche sui dispositivi mobili.

Su desktop, l’animazione poteva apparire perfettamente fluida, mentre su mobile poteva diventare visibilmente scattante, perdere frame, subire ritardi o persino sembrare bloccata durante l’animazione.

Dopo aver sperimentato diversi approcci, ho scoperto che spostare l’animazione dall’SVG stesso a un elemento HTML circostante, come un <div>, faceva una differenza molto significativa.

Invece di animare continuamente i contenuti SVG, l’SVG può rimanere statico mentre il browser anima lo strato HTML contenitore utilizzando trasformazioni CSS.

Questo offre al browser una opportunità molto migliore per gestire l’animazione come operazione di composizione utilizzando l’hardware grafico del dispositivo.

Il risultato è stato un’animazione molto più fluida sui dispositivi mobili, senza gli scatti e i blocchi che osservavo con l’approccio basato su SVG.

Questo è stato il motivo principale per cui è stato creato questo plugin.

Prima (SVG animato: l’animazione subisce ritardi e si blocca)

Dopo (HTML animato: animazione fluida)


Il problema dell’animazione degli SVG

L’implementazione originale della splash screen va benissimo per un logo semplice o un’animazione relativamente leggera.

Tuttavia, una volta che l’animazione diventa più complessa, il rendering SVG può diventare costoso.

Ad esempio, un’animazione applicata direttamente a un SVG o ai suoi elementi interni può richiedere al browser di elaborare o ridipingere ripetutamente parti dell’SVG durante l’animazione.

Sui dispositivi mobili, questo può diventare particolarmente evidente.

Durante i test, ho visto casi in cui l’animazione:

  • diventava visibilmente scattante
  • si bloccava temporaneamente
  • sembrava fermarsi
  • si comportava significativamente peggio rispetto al desktop

La parte interessante era che la stessa animazione visiva poteva comportarsi in modo molto diverso a seconda di cosa veniva effettivamente animato.


Spostare l’animazione su uno strato HTML

L’approccio che ha funzionato molto meglio è stato mantenere l’SVG stesso statico e inserirlo all’interno di un normale elemento HTML.

Ad esempio:

<div class="logo-layer">
  <svg viewBox="0 0 500 500">
    ...
  </svg>
</div>

Invece di animare l’SVG, l’animazione viene applicata al contenitore:

.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);
  }
}

L’SVG stesso non cambia.

Il browser può quindi gestire la trasformazione dello strato HTML in modo molto più efficiente e, nei casi supportati, promuoverlo a uno strato composto gestito dall’hardware grafico.

Questo ha prodotto un risultato drammaticamente più fluido sui dispositivi mobili.

La distinzione importante è quindi:

Approccio core:

SVG
 └── Animazione SVG
      └── I contenuti SVG sono animati

vs:

Approccio personalizzato:

Strato HTML
 └── SVG
      └── Trasformazione CSS sullo strato HTML
           └── Animazione compatibile con il compositor

Questo non è una garanzia che ogni animazione sarà accelerata dalla GPU. Il browser decide in definitiva come comporre un’animazione, ma nei miei test la differenza era molto evidente.


Perché ho creato il Custom Splash HTML Builder

Una volta che questo approccio funzionava, avevo anche bisogno di un modo per costruire effettivamente la schermata di avvio intorno ad esso.

Il modello standard della splash screen non offre flessibilità sufficiente per questo tipo di implementazione.

Per un’animazione più complessa, potrei aver bisogno di:

  • più strati SVG
  • più contenitori HTML
  • elementi animati in modo indipendente
  • keyframe CSS personalizzati
  • tempi di animazione diversi
  • posizionamento personalizzato
  • colori sensibili al tema
  • markup completamente diverso rispetto alla splash screen predefinita

Quindi, invece di creare un’altra implementazione di splash screen con codice fisso, ho deciso di esporre la parte visiva attraverso due impostazioni del sito.

Il plugin aggiunge:

splash_custom_html

Il markup HTML/SVG renderizzato all’interno della schermata di avvio.

splash_custom_css

Il CSS utilizzato dalla splash screen personalizzata, incluse animazioni, keyframe, posizionamento e comportamento reattivo.

Questo rende la splash screen effettivamente personalizzabile senza dover modificare il codice sorgente del plugin ogni volta che cambia l’animazione.


Editor admin integrato

Il plugin fornisce anche un piccolo editor admin integrato per gestire la splash screen personalizzata.

Aggiunge una sezione dedicata Splash HTML Builder nell’interfaccia admin di Discourse, con editor separati per:

  • HTML personalizzato
  • CSS personalizzato

Le modifiche possono essere salvate direttamente dall’interfaccia admin senza modificare manualmente le impostazioni del sito corrispondenti.

Le impostazioni sottostanti sono comunque:

  • splash_custom_html
  • splash_custom_css

L’editor è semplicemente un’interfaccia più conveniente per gestirle.

Questo significa anche che il plugin non richiede la modifica dei file sorgente del plugin ogni volta che l’animazione della splash screen deve essere cambiata.


Esempio

Una splash screen personalizzata può contenere più strati indipendenti:

<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>

E ogni strato può avere la propria animazione:

.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);
  }
}

Gli SVG rimangono statici mentre gli strati HTML circostanti sono animati.

Questo rende possibile creare animazioni di splash screen considerevolmente più complesse mantenendo il lavoro di animazione costoso al di fuori dell’SVG stesso.


Perché non sovrascrivere semplicemente il modello core della splash screen?

Un altro obiettivo importante era evitare di mantenere una copia del modello core della splash screen di Discourse.

Un approccio diretto sarebbe stato sovrascrivere:

app/views/common/_discourse_splash.html.erb

e copiare l’implementazione attuale di Discourse nel plugin.

Il problema è che questo crea un onere di manutenzione.

Se Discourse cambia la sua implementazione della splash screen in una versione futura, il plugin conterrebbe comunque la vecchia versione.

Questo potrebbe potenzialmente risultare in:

  • mancato aggiornamento delle nuove modifiche core
  • mancato aggiornamento dei miglioramenti delle prestazioni
  • comportamento interrotto dopo un aggiornamento di Discourse
  • la necessità di confrontare manualmente il modello del plugin con il core dopo ogni aggiornamento

Volevo evitare completamente questo scenario.


Fallback core

Il plugin supporta quindi una splash screen personalizzata con un fallback core.

HTML personalizzato configurato

Se:

SiteSetting.splash_custom_html.present?

allora il plugin renderizza la splash screen personalizzata.

HTML personalizzato vuoto

Se non è stata configurata una splash screen personalizzata, il plugin ricade al modello core attuale della splash screen di Discourse.

Il plugin individua il file core effettivo dall’installazione di Discourse in esecuzione:

Rails.root/app/views/common/_discourse_splash.html.erb

e renderizza quella implementazione.

Concettualmente:

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

Questo significa che il plugin non contiene una seconda copia del modello core della splash screen.


Considerazioni sulle prestazioni

Il plugin non pretende che ogni animazione CSS diventi magicamente accelerata dalla GPU.

Il browser decide ancora come vengono renderizzate e composte le singole animazioni.

L’obiettivo è invece fornire al browser una struttura molto più favorevole per la composizione accelerata dall’hardware:

  • mantenere i contenuti SVG statici
  • isolare gli elementi animati in modo indipendente
  • animare gli strati HTML
  • preferire transform per movimento/ridimensionamento/rotazione
  • evitare operazioni di ridisegno inutilmente costose
  • utilizzare will-change dove appropriato

Ad esempio:

.ring-layer {
  will-change: transform;
  animation: rotate 2.2s linear infinite;
}

Questo approccio ha funzionato particolarmente bene per il mio caso d’uso ed ha eliminato gli scatti mobili che osservavo con l’animazione SVG originale.


Abilitare o disabilitare la splash screen personalizzata

Il plugin fornisce anche un’impostazione del sito custom_splash_html_builder_enabled.

Quando disabilitato, viene utilizzata la schermata di avvio standard di Discourse indipendentemente dal fatto che siano stati configurati HTML o CSS personalizzati.

Questo fornisce un interruttore di sicurezza aggiuntivo per disabilitare temporaneamente la splash screen personalizzata senza eliminare l’HTML/CSS salvato.

La splash screen personalizzata viene renderizzata solo quando entrambi:

custom_splash_html_builder_enabled = true
splash_custom_html non è vuoto

Altrimenti, viene utilizzata la splash screen core attuale di Discourse.


Soprattutto, fornisce un modo per creare una splash screen animata personalizzata che performa molto meglio sui dispositivi mobili animando gli strati HTML intorno a contenuti SVG statici invece di animare direttamente l’SVG stesso.

1 Mi Piace