Mermaid図のモバイル表示で、拡大縮小まわりの不具合を直しました。
小さい修正ですが、原因の見立てを一度外したので、失敗談として残しておきます。
起きたこと
ブログ内のMermaid図で、iPhone 16eだけ変な動きになりました。
+を押すと拡大ではなく縮小する-を押しても縮小する- 2本指で広げても縮小する
単純に + と - の処理が逆なら、- では拡大するはずです。
でも実際には、どの操作でも小さくなりました。
最初の見立て
最初は、iOS Safari系の resize 挙動が原因だと思いました。
iPhoneでは、アドレスバーや下部ツールバーの表示変化で、画面回転していなくても resize が発火することがあります。
そのため、ズーム後に初期表示へ戻っているのではないかと考えました。
そこで、resize のたびに無条件で再フィットする処理をやめました。
幅が変わったときだけ再フィットし、高さだけの変化ではズーム状態を維持するようにしました。
それでも直らなかった
PlaywrightのChromiumで、iPhone Safari相当のUAを使った確認では正常に見えました。
しかし、実機のiPhone 16eでは直っていませんでした。
ここが今回の失敗です。
UAをiPhone Safari風にしても、実際のiOS Safariの確認にはなりません。
特に、画像サイズ、SVG、タッチ操作、viewportまわりは、ChromiumとiOS Safariで差が出ます。
本当の原因
問題は、ズーム計算の基準サイズでした。
当初の実装では、ズームするたびに画像サイズを取り直していました。
const getImageSize = () => ({
width: sourceImage.naturalWidth || sourceImage.width || 1,
height: sourceImage.naturalHeight || sourceImage.height || 1,
});
そして、取得した幅にズーム倍率をかけていました。
sourceImage.style.width = `${width * currentZoom}px`;
このとき、iOS Safari側で sourceImage.width が現在表示中の縮小済みサイズとして扱われると、ズームの基準が壊れます。
本来は、元のSVG幅を基準にして、そこへ倍率をかけるべきです。
しかし、現在の表示幅を基準にしてしまうと、+ でも - でもピンチアウトでも小さくなります。
つまり、ズーム方向が逆だったのではなく、ズーム計算の基準値が壊れていました。
最終的な修正
Mermaid画像ごとに、基準サイズを一度だけ固定するようにしました。
baseImageWidthbaseImageHeight
この2つを画像ロード後に確定し、ズーム操作中は更新しません。
ズーム時は、常に固定した基準サイズから計算します。
sourceImage.style.width = `${baseImageWidth * currentZoom}px`;
これで、現在の表示幅を再利用してしまう問題を避けられました。
その後、iPhone 16e実機で確認したところ、+、-、2本指ピンチが正常に動くようになりました。
学び
+と-の両方で縮小するなら、単純な符号反転ではない- ズーム処理では、基準サイズを最初に固定した方が安全
img.widthは自然サイズではなく、現在の表示サイズになり得る- iPhone Safari系の不具合は、ChromiumのUA偽装だけでは確認不足
- 実機確認は、最後の確認ではなく原因特定の材料になる
小さいUI不具合でも、ブラウザ差分が絡むと見立てを外します。
今回は、実機で「まだ直っていない」と確認できたことで、原因を絞り直せました。