Visual Viewport API
主にiPhone/iPadで、
スクリーンキーボードの上にツールバーやステータスバーを実装するのに使う
iOSはスクリーンキーボードを開いても window.innerHeight
が変化しない
position: fixed
で画面の下端にくっつけている要素がキーボードで隠れてしまう
キーボードを背景透過させてwebページがぼけて見える仕様のせい
ボケすぎてて何も見えないけど
iOSでスクリーンキーボードの上、画面下端にツールバーをくっつけたかった
Androidではまったく不要
CSSの position: fixed
と bottom: 0
でいい
Androidはスクリーンキーボードを開くと window.innerHeight
が縮むので
ドキュメント
対応ブラウザ
iOS 13から使える
API
visualViewport.height
や visualViewport.offsetTop
スクリーンキーボードを除いた大きさと位置が取得できる
window.height
や window.offsetTop
と同じような感じ
CSSではなくJavaScriptで値を取得し、要素を移動させなければならない
Reactだとこんな感じだが
jsconst offsetTop = visualViewport.height - 40 + visualViewport.offsetTop;
const style = {
transform: `translate(${visualViewport.offsetLeft}px, ${offsetTop}px) scale(${1/visualViewport.scale})` // CSS書いて
}
return (
<div className='bottom-toolbar' style={style}> // styleにセット
画面スクロールなど、適切なイベントを使ってstyleを再セットし続け、ずっとrenderしなおす必要がある
調査した
Androidはすばらしい出来
全ての visualViewport
の値がfloatでリアルタイムに更新される
iOSはひどい
textareaにカーソルを入れ、スクリーンキーボードを表示している間は
スクロールを完全に止めるまで、scrollイベントが発火しなくなる
visualViewport.offsetTop
の反映が遅れる
0.5刻みで、Androidより精度が低い
window.scrollY
は遅延がない
しかしこちらはIntegerなのでガタガタになる
なぜ1px未満の細かいスクロールができる端末で、開発者が取得できる座標系が整数なんだろう?
Androidは window.scrollY
もfloatなのに
特にiOS safariでは、上下スクロールによってアドレスバーの高さが変化するのだが
それが visualViewport.height
に反映されるタイミングが一瞬遅い
スクリーンキーボードを開いていない場合は反応が速い
iPad
Safari Chrome共通
visualViewport.offsetTop
の反映が遅い。iOSと同じ
Chrome
スクリーンキーボードの上のパスワードボタンの高さが visualViewport.height
に含まれていない
これはSafariでは問題ない
結論
こういうのはiOSでは絶対に作れない
スクリーンキーボードの上にツールバーを常に表示し
上下スクロールしてもスクリーンキーボードにぴったりくっついてくるUI
やるならこう
スクロール中は非表示
スクロールが終わったらツールバーを表示する
windowのscrollやresizeイベントを見て、500 msecぐらい静かであったらスクロール完了と見なす
visualViewport.height
と visualViewport.offsetTop
を使って表示位置を計算する
iOSのために作られたAPIだが、iOSはまともに実装されていない
Androidではまったく必要の無いAPIだが、完璧に実装されている