カスタムスプラッシュHTMLビルダー

:information_source: 概要 管理者が定義した HTML と CSS を使用してスプラッシュ画面をカスタマイズできる Discourse プラグインです。
:hammer_and_wrench: リポジトリリンク https://github.com/VaperinaDEV/custom-splash-html-builder
:open_book: インストールガイド Discourse でのプラグインのインストール方法

こんにちは :waving_hand:

Discourse コアのスプラッシュテンプレートの修正版をメンテナンスする必要なく、管理者が定義した HTML と CSS を使用してスプラッシュ画面をカスタマイズできる小さな Discourse プラグインを作成しました。

このプラグインを作成した当初の動機は、実はモバイル端末でのパフォーマンスでした。

より洗練されたアニメーション付きのスプラッシュ画面を作成したかったのですが、現在の Discourse コアのスプラッシュ実装がサポートしている SVG アニメーションが、モバイル端末で予期せぬ問題を引き起こす可能性があることに気づきました。

デスクトップではアニメーションが完璧にスムーズに見えても、モバイル端末では明らかにカクつき、フレーム落ち、ラグ、あるいはアニメーション中に止まるように見える場合がありました。

さまざまなアプローチを試した結果、アニメーションを SVG 自体から <div> などの surrounding HTML 要素に移すことで、非常に大きな違いが生まれることがわかりました。

SVG コンテンツを継続的にアニメーションさせる代わりに、SVG は静的なままにし、ブラウザが CSS トランスフォームを使用してコンテナとなる HTML レイヤーをアニメーションさせます。

これにより、ブラウザはデバイスのグラフィックスハードウェアを使用してアニメーションを合成操作として処理する機会が大幅に向上します。

その結果、SVG ベースのアプローチで見られたカクつきやフリーズなしで、モバイル端末でのアニメーションがはるかにスムーズになりました。

これがこのプラグインが作成された主な理由です。

変更前(アニメーション付き SVG: アニメーションがラグし、停止)

変更後(アニメーション付き HTML: スムーズなアニメーション)


SVG のアニメーション化の問題点

元のスプラッシュ実装は、シンプルなロゴや比較的軽量なアニメーションには完全に問題ありません。

しかし、アニメーションが複雑になると、SVG のレンダリングコストが高くなります。

例えば、SVG またはその内部要素に直接適用されたアニメーションは、アニメーション中にブラウザが SVG の一部を繰り返し処理したり再描画したりする必要がある場合があります。

モバイル端末では、これが特に顕著になります。

テスト中、アニメーションが以下のような状態になるケースを目にしました。

  • 目に見えてカクつく
  • 一時的にフリーズする
  • 停止しているように見える
  • デスクトップよりもはるかに劣る挙動を示す

興味深いのは、同じ視覚的なアニメーションでも、実際にアニメーション化されている対象によって挙動が大きく異なるということです。


アニメーションを HTML レイヤーへ移動

はるかに効果的だったアプローチは、SVG 自体を静的なままにし、通常の HTML 要素内に配置することでした。

例えば:

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

SVG をアニメーション化する代わりに、アニメーションはコンテナに適用されます:

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

SVG 自体は変化しません。

したがって、ブラウザは HTML レイヤーの変換をより効率的に処理でき、サポートされている場合はグラフィックスハードウェアが処理する合成レイヤーに昇格させることができます。

これにより、モバイル端末での結果が劇的にスムーズになりました。

重要な区別は以下の通りです:

コアのアプローチ:

SVG
 └── SVG アニメーション
      └── SVG コンテンツがアニメーション化される

vs:

カスタムアプローチ:

HTML レイヤー
 └── SVG
      └── HTML レイヤーへの CSS トランスフォーム
           └── 合成に優しいアニメーション

これはすべてのアニメーションが GPU によって加速されるという保証ではありません。ブラウザは最終的にアニメーションがどのように合成されるかを決定しますが、私のテストではその違いは非常に顕著でした。


カスタムスプラッシュ HTML ビルダーを作成した理由

このアプローチが機能するようになった後、実際にスプラッシュ画面を構築する方法も必要でした。

標準のスプラッシュテンプレートは、この種の実装には十分な柔軟性を提供していません。

より複雑なアニメーションの場合、以下が必要になる可能性があります:

  • 複数の SVG レイヤー
  • 複数の HTML コンテナ
  • 独立してアニメーション化される要素
  • カスタム CSS キーフレーム
  • 異なるアニメーションタイミング
  • カスタム位置指定
  • テーマに合わせた色
  • デフォルトのスプラッシュとは完全に異なるマークアップ

そのため、もう一つのハードコードされたスプラッシュ実装を作成する代わりに、視覚的な部分を 2 つのサイト設定を通じて公開することにしました。

このプラグインは以下を追加します:

splash_custom_html

スプラッシュ画面内でレンダリングされる HTML/SVG マークアップ。

splash_custom_css

カスタムスプラッシュで使用される CSS。アニメーション、キーフレーム、位置指定、レスポンシブな動作を含む。

これにより、アニメーションが変更されるたびにプラグインのソースコードを修正することなく、スプラッシュ画面を実質的にカスタマイズできます。


組み込みの管理者用エディタ

このプラグインは、カスタムスプラッシュの管理用の小さな組み込み管理者用エディタも提供します。

Discourse 管理インターフェースに専用のスプラッシュ HTML ビルダーセクションを追加し、以下を別々に編集できます:

  • カスタム HTML
  • カスタム CSS

変更は、対応するサイト設定を手動で編集することなく、管理インターフェースから直接保存できます。

基となる設定は依然として以下の通りです:

  • splash_custom_html
  • splash_custom_css

エディタは、これらを管理するためのより便利なインターフェースに過ぎません。

これは、スプラッシュアニメーションを変更するたびにプラグインのソースファイルを修正する必要がないことを意味します。


カスタムスプラッシュには、複数の独立したレイヤーを含めることができます:

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

そして、各レイヤーは独自のアニメーションを持つことができます:

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

SVG は静的なままになり、周囲の HTML レイヤーがアニメーション化されます。

これにより、高コストなアニメーション処理を SVG 自体の外に保ちながら、はるかに複雑なスプラッシュアニメーションを作成することが可能になります。


なぜコアのスプラッシュテンプレートを単純にオーバーライドしないのか

n
もう一つの重要な目標は、Discourse コアのスプラッシュテンプレートのコピーをメンテナンスすることを避けることでした。

straightforward なアプローチは、以下をオーバーライドすることです:

app/views/common/_discourse_splash.html.erb

そして、現在の Discourse 実装をプラグインにコピーします。

問題は、これによってメンテナンスの負担が生じることです。

Discourse が将来のリリースでスプラッシュ実装を変更した場合、プラグインは依然として古いバージョンを含んだままになります。

これにより、以下が潜在的に発生する可能性があります:

  • 新しいコアの変更が反映されない
  • パフォーマンスの向上が反映されない
  • Discourse の更新後に動作が破綻する
  • 各更新後にプラグインのテンプレートとコアを手動で比較する必要がある

これを完全に避けたいと考えました。


コアフォールバック

したがって、このプラグインはカスタムスプラッシュとコアフォールバックをサポートしています。

カスタム HTML が設定されている場合

もし:

SiteSetting.splash_custom_html.present?

であれば、プラグインはカスタムスプラッシュをレンダリングします。

カスタム HTML が空の場合

custom splash が設定されていない場合、プラグインは現在の Discourse コアのスプラッシュテンプレートにフォールバックします。

プラグインは、実行中の Discourse インストールから実際のコアファイルを検索します:

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

そしてその実装をレンダリングします。

概念的には:

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

これは、プラグインがコアスプラッシュテンプレートの二重のコピーを保持していないことを意味します。


パフォーマンスに関する考慮事項

このプラグインは、すべての CSS アニメーションが魔法のように GPU 加速されるという主張をしているわけではありません。

ブラウザは依然として個々のアニメーションがどのようにレンダリングされ、合成されるかを決定します。

目標は、代わりにハードウェア加速された合成に対してブラウザにより有利な構造を提供することです:

  • SVG コンテンツを静的に保つ
  • 独立してアニメーション化される要素を分離する
  • HTML レイヤーをアニメーション化する
  • 移動/スケーリング/回転には transform を優先する
  • 不要な高コストな再描画操作を避ける
  • 適切な箇所で will-change を使用する

例えば:

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

このアプローチは私のユースケースで特に効果的であり、元の SVG アニメーションで見られたモバイル端末でのカクつきを解消しました。


カスタムスプラッシュの有効化/無効化

このプラグインは、custom_splash_html_builder_enabled というサイト設定も提供します。

無効にすると、カスタム HTML または CSS が設定されているかどうかに関係なく、標準の Discourse スプラッシュ画面が使用されます。

これにより、保存された HTML/CSS を削除することなく、カスタムスプラッシュを一時的に無効にするための追加の安全スイッチが提供されます。

カスタムスプラッシュは、以下の両方が満たされている場合にのみレンダリングされます:

custom_splash_html_builder_enabled = true
splash_custom_html が空ではない

それ以外の場合、現在の Discourse コアスプラッシュが使用されます。


最も重要なのは、SVG 自体を直接アニメーション化するのではなく、静的な SVG コンテンツの周囲の HTML レイヤーをアニメーション化することで、モバイル端末ではるかにパフォーマンスの良いカスタムアニメーション付きスプラッシュを構築する方法を提供することです。

「いいね!」 1