iOS10から、Safariで viewport
の user-scalable=no
が効かなくなりました。
ブラウザゲームなどを開発してる場合、ダブルタップで拡大されてしまうためクリティカルな問題になります。
Appleへの恨みを込めつつ、対策方法を調べました。
なお、
参考サイト: http://stackoverflow.com/questions/37808180/disable-viewport-zooming-ios-10-safari
に近いですが、このままではうまくいかなかったので、ちょっとアレンジしています。
複数指で拡大縮小が出来てしまうのを防ぐ
touchstart
のイベントを見て防ぎます。
なお、子要素で、stopPropagation()
をしてる場合、バブリングフェーズでpreventDefault()
をしても効果がなくなるため、意図的にキャプチャフェーズでpreventDefault()
をしています。
(addEventListener
の第三引数がtrue
になります。)
document.addEventListener('touchstart', event => {
if (event.touches.length > 1) {
event.preventDefault();
}
}, true);
ダブルタップを防ぐ
touchend
のイベントを見て防ぎます。
こちらも同様に、意図的にキャプチャフェーズでpreventDefault()
をしています。
(addEventListener
の第三引数がtrue
になります。)
なお、いくつかのサンプルサイトでは0.3秒の判定で対策されてましたが、私の環境では0.45秒は必要でした。
バッファも考慮し、0.5秒(500ms)としました。
時間測定もあえてDateTime
ではなく、performance.now()
を使っています。
let lastTouch = 0;
document.addEventListener('touchend', event => {
const now = window.performance.now();
if (now - lastTouch <= 500) {
event.preventDefault();
}
lastTouch = now;
}, true);
もしくは、setTimeoutを使ってもいいです。
let flag = false;
document.addEventListener('touchend', event => {
if (flag) {
event.preventDefault();
} else {
flag = true;
setTimeout(() => {
flag = false;
}, 500);
}
}, true);
フォームにカーソル移動時に拡大される
この問題はuser-scalable=no
とは関係ありませんでした。
情報提供を頂いた harapeko_wktk さんありがとうございました。
iOS10未満はviewportにinitial-scale=1, minimum-scale=1, maximum-scale=1でも拡大防止できていましたが、できなくなったようです。
iOS10からはfont-sizeを16px以上にするかuser-scalable=noが必要という結果でした。
16px
未満のフォントサイズのときに拡大がされてしまうようになりました。
こちらに関しては、16px
以上にfont-size
を指定する事で拡大を阻止出来ます。
16px
未満のまま防止する方法はもしかするとあるかもしれませんが、Safariの仕様変更の意図を考えるとあまり16px
以下に拘る必要性は無いかなと個人的には解釈しています。
困った問題
さて、これで基本的には解決するはずですが、解決しないこともあります。
- 時々ダブルタップをすると、
lastTouch
の値が2000を超え、上記処理では拡大を防ぐことができない。 - 時々ダブルタップをすると、touchendイベントが1回しかこないのにダブルタップ扱いになる。
もうこれ完全にSafariのバグだろと思いつつ、
私はいろいろ試行錯誤したあげく、最終的に以下の手段を取りました。
(最終手段) 拡大されたくない要素に対して、touchendイベントをキャンセルする
element.addEventListener('touchend', event => {
event.preventDefault();
}, false);
これをすると、いくら連打してもダブルタップしてもズームはされなくなりました。
但し、touchendイベント自体がキャンセルされてしまうため、この後にaddEventListener
でイベントが登録できなくなります。
preventDefault
をするのは処理の最後に書きましょう。
(追記)cssで対応する
コメント欄で教えていただきました。これがもっとも対応簡単だと思います。
touch-action: manipulation;