【ブラウザーがクラッシュ!】ダークモードを実装する時の注意点と知っておきたいポイント
このブログサイトのダークモード切り替え時に、スマートフォンのブラウザーがクラッシュしていていました。
使っていたスマホは iPhone 11 & 15 と、Androidのグーグルピクセルです。この問題を特定するまでに数時間ハマってしまいました!
使用ブラウザ:
– Safari
– Chrome
– FireFox
この記事を読むことで、ダークモード実装で切り替え時になんか重いな、ダークモード中にアプリ全体の動きがもたつくなどの問題があれば、その解決に至るかもしれません。
原因はCSS!!
結論から言うと、 CSS のfilter: brightness();
が原因でした。
filter
プロパティはGPUを利用して処理されることが多いです。
適用範囲が広大な場合や複雑なフィルターが多用されると、特にタブレットやスマホデバイスではGPUのリソースを過度に消費し、パフォーマンスの低下やクラッシュを引き起こす可能性が高まってしまいます。
ダークモード中に、色々な箇所で色の変数を定義して使うかと思います。その中でも特に画像やタイトル、テキストなど白を使うことが多いと思います。
その際にそれらの明度を落とすために 上記のCSSをアプリ中のあちこちに多用すると思いますが、それが災いしてレンダリング時に負荷をかけるようです…
僕はダークモードでこのブログ記事の h2 タグと画像に明度を落とすようにしていたのですが、h2タグにかかっていた filter: brightness(); を外すだけで、
ブラウザークラッシュがなくなりました。
ブラウザは透明物を表示するのにCPUを使う?
透明度(opacity
)やフィルタ(filter
)などのグラフィックス効果は、ブラウザがGPU(グラフィックス処理ユニット)ではなくCPU(中央処理ユニット)を使ってレンダリングすることがあります。
特に複雑なページや多数の要素にこれらの効果を適用すると、パフォーマンスに悪い影響を与える可能性が考えられます。
透明度やフィルタ効果がCPUを多用する原因は、これらの効果がピクセルレベルでの画像処理を必要とするからです。
ブラウザは各ピクセルに対して計算を行い、最終的な色や明るさを決定する必要があります。
これが多数の要素に対して行われると、特にタブレットや、スマホなどのCPUの処理能力が限られている環境では、ページの応答性が悪くなったりクラッシュの原因になり得ます。
パフォーマンスを最適化するためには、不必要なフィルタ効果の使用を避けCSSの will-change
プロパティを活用してブラウザに前もって、その要素が変化することを通知することで、ブラウザがより効率的にレンダリングを行えます。
他にも、CSSアニメーションやトランジションに代わる
- WebGL
- Canvas
これらを使用することで、GPU の力を活用できる場合があります。
他にも何か注意することは?
JavaScriptでスタイルを切り替えている
僕の場合は useContext を使い、localStorage の darkmode の状態の True/False を監視して切り替えていたのですが、これがメモリーをよく使ってしまうようです。
あまり切り替えるコンポーネントが多くない場合は問題ないのですが、数が多いと負担になってしまいます。
ですので、グローバルCSSファイル内に CSS変数を定義して切り替えることが推奨されています。
グローバルCSSファイル内に CSS変数を定義
コードの例は下記です:
globals.css
:root {
/* LIGHT MODE */
--colorTextHeader: rgba(60, 60, 60, 1);
}
[data-theme = "dark"] {
--colorTextHeader: rgba(255, 255, 255, 0.8);
}
カスタム属性を定義してください⬇︎
(darkTheme は localStorage から ダークモードの状態である true / false を取得しています)
<main data-theme={darkTheme ? "dark" : "light"}>
<h1>TITLE</h1>
</main>
上記で定義されたものを使用するコード⬇︎
CSSファイル内
h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--colorTextHeader);
}
JavaScriptの過剰なDOM操作
ダークモードの切り替えの際に、大量のDOM要素に対するスタイルの変更やクラスの追加・削除を行うと、レンダリングの負荷が高まり、ブラウザーのクラッシュにつながることがあります。
メモリリーク
ダークモードの切り替え処理において不適切なイベントハンドラの管理、大量のデータをメモリに保持し続けるなど、メモリリークが発生している可能性があります。
メモリリークは時間とともにシステムのパフォーマンスを低下させ、最終的にはクラッシュを引き起こします。
不適切なリソース管理
ダークモードの切り替えに伴うリソース(画像、文字フォントなど)の動的な読み込みが多い場合、下記のような挙動を起こします。
- リソースの取得に失敗
- 読み込みが遅延
- ブラウザーのクラッシュ
解決策
最適化とシンプル化: CSSとJavaScriptのコードを最適化し、できるだけシンプルに保ちます。filter
プロパティの使用を最小限にし、必要なDOM操作のみを行うようにします。
適切なイベントハンドリング: イベントハンドラが不要になったら適切に削除して、メモリリークを防ぎます。
プログレッシブエンハンスメント: 基本的な機能はシンプルなCSSで実装するようにし、そこにJavaScriptを使って機能を強化。
初期読み込み時のパフォーマンスを向上させましょう。
パフォーマンステスト: 異なるデバイスやブラウザでのパフォーマンステストを行い、問題を特定して修正します。
ダークモードの切り替え機能を実装する際には、特にモバイル環境でのパフォーマンスと安定性に注意を払うことが重要です。
ある程度のサイトのパフォーマンスは下記のサイトで測ることができます。
PageSpeed Insights: https://pagespeed.web.dev
このブログに使われている言語は?
このブログはいわゆる JamStack で、ワードプレスをバックエンドにし、フロントに JavaScript を使って構築しています。
使っている内容は以下です:
- Next.js
- TypeScript
- WordPress
- GraphQL
サーバーはレンタルサーバーを使用しています。
フロントエンドの制作は一からなので大変でしたが、バックエンドとフロントエンドを切り分けることでセキュリティー性も増しました。それに加えフロントはSPAで実装できています。
人気記事
Kazu
オーストラリア在住歴7年。現地で英語学習後に日本語教師 → 工場就職 → 海外WEB開発ディプロマを取得→フリーランス。現在は余った時間をプログラミング&ブログに費やして生きています。
89