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

:information_source: 概要 アドミンが定義した HTML と CSS を使用してスプラッシュ画面をカスタマイズできる Discourse プラグインです。
:hammer_and_wrench: リポジトリリンク https://github.com/VaperinaDEV/custom-splash-html-builder
:heart: 役に立ちましたか? > ./support --coffee
:open_book: インストールガイド Discourse にプラグインをインストールする方法

こんにちは :waving_hand:

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

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

より洗練されたアニメーション付きのスプラッシュ画面を作りたいと思っていましたが、現在の Discourse コアのスプラッシュ実装がサポートしている SVG アニメーションは、モバイルデバイスで意外と問題を引き起こすことがあることに気づきました。

デスクトップではアニメーションが完全にスムーズに見えても、モバイルでは明らかにカクつく、フレームが落ちる、遅延が生じる、あるいはアニメーション中に止まっているように見えることがありました。

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

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

これにより、ブラウザはデバイスのグラフィックスハードウェアを使用して、アニメーションをコンポジット操作として処理する機会を大きく得られます。

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

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

Before(アニメーション付き SVG:アニメーションが遅延し、止まっている)

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


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

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

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

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

モバイルデバイスでは、これが特に顕著になることがあります。

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

  • 明らかにカクつく
  • 一時的にフリーズする
  • 止まっているように見える
  • デスクトップよりもはるかに悪い動作をする

興味深いのは、実際にアニメーション化されているものによって、同じ視覚的アニメーションでも動作が非常に異なる場合があるという点です。


アニメーションを HTML レイヤーに移動する

はるかにうまくいったアプローチは、SVG 自体を静的なままにしておき、通常の HTML 要素の中に配置することです。

例えば:

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

SVG をアニメーションさせるのではなく、コンテナにアニメーションを適用します:

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

SVG 自体は変更されません。

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

これにより、モバイルデバイス上で劇的にスムーズな結果が得られました。

したがって、重要な区別は以下の通りです:

コアのアプローチ:

SVG
 └── SVG アニメーション
      └── SVG の内容がアニメーション化される

vs:

カスタムアプローチ:

HTML レイヤー
 └── SVG
      └── HTML レイヤーへの CSS トランスフォーム
           └── コンポジットしやすいアニメーション

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


Custom Splash HTML Builder を作成した理由

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

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

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

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

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

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

splash_custom_html

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

splash_custom_css

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

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


内蔵アドミンエディタ

このプラグインは、カスタムスプラッシュを管理するための小さな内蔵アドミンエディタも提供します。

Discourse のアドミンインターフェースに専用の Splash HTML Builder セクションを追加し、以下を個別に編集できます:

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

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

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

  • splash_custom_html
  • splash_custom_css

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

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


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

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

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

そして、各レイヤーに独自のアニメーションを適用できます:

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

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

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


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

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

単純なアプローチとしては、以下をオーバーライドし、

app/views/common/_discourse_splash.html.erb

現在の Discourse 実装をプラグインにコピーする方法があります。

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

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

その結果、以下が発生する可能性があります:

  • 新しいコアの変更を取りこぼす
  • パフォーマンス改善を取りこぼす
  • Discourse アップデート後に動作が壊れる
  • 毎回アップデート後に、プラグインのテンプレートとコアを手動で比較する必要がある

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


コアへのフォールバック

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

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

以下の場合:

SiteSetting.splash_custom_html.present?

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

カスタム HTML が空の場合

カスタムスプラッシュが設定されていない場合、プラグインは現在の 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 を使用する

例えば:

#d-splash .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 レイヤーをアニメーション化することで、モバイルでのパフォーマンスがはるかに優れたカスタムアニメーション付きスプラッシュを構築する方法を提供することです。

「いいね!」 8

こんにちは :waving_hand:

#d-splash セクションに data-color-scheme を追加したので、ライトとダークスキームのスプラッシュロゴを簡単に設定できるようになりました。 DEV: Implement dynamic color scheme for splash section · VaperinaDEV/custom-splash-html-builder@ba6641b · GitHub

<%- splash_forced_scheme = (dark_color_scheme? || forced_dark_mode?) ? "dark" : (forced_light_mode? ? "light" : nil) %>

<section id="d-splash"<%= " data-color-scheme=\"#{splash_forced_scheme}\"".html_safe if splash_forced_scheme %>>

これにより、ライトまたはダークスキームが明示的に強制されている場合、#d-splash には data-color-scheme="dark" / "light" 属性が付与され、両方のスキームが有効で OS に判断を委ねる場合のみ、属性は付与されません。

修正点は、カスタム CSS でメディアクエリよりもこの属性を優先させ、属性が存在しない場合にのみ prefers-color-scheme にフォールバックすることです。

例:

カスタム HTML

<!-- LIGHT MODE -->
<div class="custom-splash-light">
  <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>

<!-- DARK MODE -->
<div class="custom-splash-dark">
  <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>

カスタム CSS

#d-splash .custom-splash-dark {
  display: none;
}

/* OS が判断 — 強制スキームがない場合のみ */
@media (prefers-color-scheme: dark) {
  #d-splash:not([data-color-scheme]) .custom-splash-light {
    display: none;
  }
  #d-splash:not([data-color-scheme]) .custom-splash-dark {
    display: block;
  }
}

/* 強制スキームは OS に関係なく常に優先される */
#d-splash[data-color-scheme="light"] .custom-splash-dark {
  display: none;
}
#d-splash[data-color-scheme="dark"] .custom-splash-light {
  display: none;
}
#d-splash[data-color-scheme="dark"] .custom-splash-dark {
  display: block;
}

属性が存在すると :not([data-color-scheme]) は単に一致しなくなるため、2つのルールセット間で特異性の競合は発生せず、強制スキームが常に優先されます。

「いいね!」 2

これは素晴らしいですね。私は何年も前から、接続速度が遅い場合にブランドや画像を制御して、ユーザーが方向性を把握し、安定感を持てるようにする仕組みを提案したり探したりしていました。

一見するだけで、私が想像していたものよりもさらに優れた機能であることがわかります。ぜひいつか試してみたいと思います。素晴らしい仕事ですね!

「いいね!」 2