Cordova iOS アプリで -webkit-overflow-scrolling:touch を使った時の備忘録
Cordova アプリはデフォルトで DisallowOverscroll
が false
、つまりオーバースクロールする状態になっている。これにより慣性スクロールが効くわけだが、慣性スクロールによってページ外の背景色が見えてしまうのが気持ち悪い。この背景色を指定できればまだ良いのだが、どうも背景色を設定する方法はないらしい。
これを回避するために試行錯誤したので、その記録を紹介する。
DisallowOverscroll は true に設定する
まず DisallowOverscroll
は true
に設定し、デフォルトのオーバースクロールをさせないようにする。
<!-- config.xml -->
<preference name="DisallowOverscroll" value="true" />
html・body 要素はスクロールさせない
次に、html・body 要素はスクロールさせず、画面いっぱいに表示領域を広げておく。
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* スクロールをさせない */
}
慣性スクロールをさせるラッパーを作る
html・body 要素がスクロールしなくなったので、内部のコンテンツをスクロールできるようにしてやらなくてはならない。そのために、通常であれば body 要素に相当するページ全体をスクロールするラッパーを作る。
<div id="wrapper-outer">
<!-- ココにコンテンツ -->
</div>
#wrapper-outer {
width: 100%;
height: 100%;
overflow-y: scroll;
-webkit-overflow-scrolling: touch; /* 慣性スクロールを付ける */
background-color: #9cf; /* オーバースクロール時にこの色が見えるようにしたい */
}
これでコンテンツが多い場合は #wrapper-outer
が慣性スクロールしてくれるようになる。
しかし、コンテンツが少ない場合はスクロールが発生せず、「何もないけどオーバースクロールする」というあの妙に気持ち良い動きをしてくれなくなってしまう。
また、WebView の問題なのか分からないが、内側のコンテンツが margin
を消すなど #wrapper-outer
要素のオーバースクロールする領域に関わってくると、途端に background-color
指定が効かなくなってしまうというバグに遭遇した。
このバグの発生要因はイマイチ分からず、ただ linear-gradient
を使用した子要素が居るだけでも発生したり、しなかったり、オーバースクロールする度に背景色が白と黒とで変わったりとおかしな挙動をしていた。
というワケで、コンテンツが少ない場合もオーバースクロールさせ、かつ指定した background-color
が必ず見えるように細工していく。
必ずスクロールが発生するように内側のラッパーを作る
コンテンツが少ない場合もオーバースクロールさせるには、必ず overflow-y: scroll;
でスクロールバーが有効な状態を作り出してやれば良い。そこで、#wrapper-outer
の内側に #wrapper-inner
を作り、こいつに必ず overflow が発生するように指定してやる。
<div id="wrapper-outer">
<!-- inner を追加する -->
<div id="wrapper-inner">
<!-- ココにコンテンツ -->
</div>
</div>
/* 新たに inner の指定を作る */
#wrapper-inner {
width: 100%;
min-height: 100%; /* 最小の高さを 100% とする */
margin-bottom: 1px; /* 余分な margin により overflow を発生させる */
}
これでコンテンツ量が少ない場合もオーバースクロールさせられるようになった。
これでも上手くいく場合が多いのだが、これだと #wrapper-inner
直下の要素が持つ margin-top
が効いてしまい、うまくオーバースクロール時の背景色が有効にならない場合がある。
そこで以下のようにする。
#wrapper-inner {
width: 100%;
min-height: 100%;
padding-top: .5px; /* こうする */
padding-bottom: .5px; /* こうする */
}
親要素に padding
があると子要素の margin
が相殺されなくなるようなので、padding-top
と padding-bottom
で合計 1px 分増やして常にスクロールバーを表示させるようにした。
オーバースクロール時の背景色を正しく認識させる
内側のコンテンツの margin
指定などにより、#wrapper-outer
の background-color
の色ではなく、黒や白の背景色がオーバースクロールで表示されてしまう、WebView のバグっぽい挙動を解消する。
以下の StackOverflow にそれっぽい解決策があった。
#wrapper-outer
の CSS を以下のように修正する。
#wrapper-outer {
width: 100%;
height: 100%;
margin-top: -1px; /* コレを追加 */
padding-top: 1px; /* コレを追加 */
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
background-color: #9cf;
}
ネガティブマージンで要素を画面外に食い込ませ、その分の padding を指定して相殺している。
たったこれだけなのだが、「画面外にも対象の要素が存在する」状態を作り出すと、その要素の背景色を利用してオーバースクロールしてくれるようになるようだ。
コレがスターターセット
そんなこんなで、コレがスターターセットになった。
<!-- config.xml -->
<preference name="DisallowOverscroll" value="true" />
/* ページ全体はスクロールさせない */
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
/* オーバースクロールさせる要素 */
#wrapper-outer {
width: 100%;
height: 100%;
margin-top: -1px;
padding-top: 1px;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
background-color: #9cf; /* オーバースクロールで見える背景色 */
}
/* 常にスクロールバーを表示させる */
#wrapper-inner {
width: 100%;
min-height: 100%;
padding-top: .5px;
padding-bottom: .5px;
/* background を指定して #wrapper-outer と背景色を変えることも可能 */
}
<div id="wrapper-outer">
<div id="wrapper-inner">
<!-- ココにコンテンツ -->
</div>
</div>
以上。