1. はじめに(実機テストで起きる絶望)
Webアプリ開発において、「画面内の特定のDOMを画像化して保存・シェアさせる機能」は定番ですが、この実装にはしばしば悪夢が伴います。
PC上のChromeやAndroidの実機テストでは完璧に画像化できているのに、いざiOS(Safari)の実機でテストした瞬間、画像の一部が真っ白に抜け落ちたり、不自然に見切れたりする現象に遭遇したことはないでしょうか?
結論、これは書いたコードが悪いわけではありません。
画像化ライブラリの裏側で動いている「SVG (foreignObject)」という仕組みと、Safariのレンダリングエンジンの致命的なバグ(相性問題) が原因です。
本記事では、このiOS Safari特有の「真っ白・見切れ」バグに対する泥沼のデバッグ録と、そこから導き出した最終的な解決策を共有します。
2. 泥沼のデバッグ:何と戦っていたのか?(仮説と検証)
バグに直面した当初、私は自分のDOM操作やタイミング制御が悪いのだと思い込み、以下のようないくつかの仮説を立てて泥臭いハックを積み上げていました。
仮説①:DOMの展開が間に合っていないのでは?
Safariは描画処理が遅い(あるいはタイミングが違う)のではないかと考えました。そこで、画像キャプチャの瞬間だけCSSの制限(overflow: hidden など)を解除してDOMを全開にする「まばたきハック」や、requestAnimationFrame を使ってブラウザのペイント完了を待機する処理を実装しました。しかし、結果は失敗(真っ白)でした。
仮説②:Safariの「タイルレンダリング」のせいでは?
テスト中、「複数枚の描画対象画像のうち、2枚目だけは正常に写る」という謎の挙動に気づいていました。ここから「画面外のDOMの描画をサボるSafari特有のタイルレンダリング仕様が原因では?」と疑いました。
スクロール位置の強制リセットや、transform: translateZ(0) を当ててGPUに強制描画させるハックも試しましたが、これでも白抜けは直りませんでした。
【結論】
描画タイミングのズレ対策でも、タイルレンダリングによるサボり対策でも解消せず、最終的には根本的な「レンダリングの仕組み」そのものに問題があったようです。
3. 真の原因:html-to-image と Safariの「相性問題」
DOM画像化の定番ライブラリである html-to-image(や dom-to-image)は、対象のDOMを一度「SVG(foreignObject)」に変換し、それをブラウザ自身のネイティブエンジンに描画させるというアーキテクチャを採用しています。
しかし、iOS Safariのエンジンは、この foreignObject 内に複雑なCSSが組み込まれた際の描画において、Chromeなど他ブラウザとは異なる挙動を示すことが多々あります。
具体的には、-webkit-line-clamp(複数行の末尾省略記号)、角丸(border-radius)、backdrop-filter(すりガラス効果)といったモダンなスタイルが含まれると、正常にレンダリングが完走せず、画像が真っ白になったり一部が見切れたりするのです。
結果として、これが「Safari固有の描画問題(ライブラリとの致命的な相性問題)」を引き起こしていました。
つまり、JavaScript側でどれだけDOMを操作して「描画するタイミング」を完璧に調整したところで、Safariのネイティブエンジンと html-to-image のアプローチ(SVG変換)そのものの相性が悪いため、絶対に根本的な解決には至らなかったというわけです。
4. 究極の解決策:「html2canvas(-pro)」+「オフスクリーンDOM」
この泥沼から抜け出すための究極の解決策は、ブラウザのネイティブ描画(SVG)に依存するのをやめることです。
具体的には、JavaScriptでDOMツリーを走査し、自力でCanvasに模写していくアプローチをとる html2canvas にエンジンをすげ替えるのが大前提となります。(※モダンCSS環境で html2canvas を使う際の注意点は、別の記事:html2canvasで「oklch」エラーが出る原因と対策 もご参照ください。上位互換の html2canvas-pro の使用を推奨します)。
このJS模写アプローチ(html2canvas)を採用することで、以下のようなブレイクスルーが起きます。
オフスクリーンDOMでの描画が可能に
html2canvas なら、Safariの気まぐれな描画サボり(SVGバグ)を完全に回避できます。そのため、仮説①で試行錯誤したような複雑な「まばたきハック」やタイミング調整はすべて不要になります。
画面の裏側(position: absolute; left: -9999px; など)に配置したユーザーからは見えない「オフスクリーンDOM」であっても、JSが正確にスタイルを読み取ってくれるため、iOS Safari環境でも完璧な画像を安定して生成できるようになります。
5. おわりに
Safariの不可解な描画バグと戦う時は、html-to-image のような「ネイティブエンジン(SVG)に依存するライブラリ」を早々に見切る決断が必要でした。
仮説に基づいて、複雑なDOM操作やスクロール制御といった泥臭いハックを積み上げた結果、最終的には**「画像生成のアプローチ(SVG変換 vs JS模写)そのものを疑うこと」**。
これこそが、バグ解消への最短ルートでした。
iOS SafariでのDOM画像化バグに沼っている方の参考になれば幸いです。