Android4.2と4.3で-webkit-transitionのwidthにpxとパーセントで指定できない際の代替案:メモ

悩み
悩み

根本的には解決策が見つかっていませんが、とりあえず代替案ができたのでメモ。

ただ、実際のところ動く方がおかしいのか動かない方がおかしいのかよく分かっていません。

仕様の詳細や策定の流れを追えば確かなことがわかるとは思いますが、代替案の検討だけで力つきました…。

今回はバッドノウハウぽい感じです。

Android4.2と4.3で -webkit-transition の width に px と パーセント で指定できない

タイトルだとちょっと分かりにくいので、状況を箇条書にします。

  • 対象要素のwidthの初期値はpx指定の固定値(仮に100pxとする)
  • cssのtransitionを使いwidthの横幅を100%(コンテンツ幅いっぱい)に広げる
  • Android4.2でも動作させたい
  • 動作のトリガーは「画面内に表示されたら」でjQueryでクラス(仮に.ativeとする)を付与する

ここで重要なのは、cssの以下の指定です。


div{
 width:100px;
 -webkit-transition: width 5s;/*5秒間でwidthを操作する*/
            transition: width 5s;
}
div.active{/*画面内に表示されたら.activeを付与する*/
 width:100%;
}

iPhone4以上やIEやChormeやFirefoxなどのモダンブラウザでは上記が動作しますが、Android4.3以下ではアニメーションによる変化も無く、一瞬で横幅が100%になります。

原因の調査

調べた所、どうやらAndroid4.3と4.2では以下のような状況でした。

  • 値の単位が揃っていないと動かない
  • 例1:pxで統一(初期値:100px / 動作後:1000px)
  • 例2:%で統一(初期値:10% / 動作後:100%)

アニメーションしなかったのは数値を認識できなかったためのようで、単位さえ揃えれば動きます。

補足資料

transitionに用いる値に関しては、「CSS Transitions W3C Working Draft 19 November 2013」の「transitions」のページにある「7.1. Properties from CSS」に以下のように示されています(※引用元は表形式のため引用の都合上一部表記を変更しています)。

width: as length, percentage, or calc

上記のように、widthは実数指定(pxなど)とパーセンテージ(%)とcalcとなっています。

更に詳細が同ページの「6. Animation of property types 」にある「length, percentage, or calc」に書かれていますので、更に引用します。

length, percentage, or calc: when both values are lengths, interpolated as lengths; when both values are percentages, interpolated as percentages; otherwise, both values are converted into a ‘calc()’ function that is the sum of a length and a percentage (each possibly zero), and these ‘calc()’ functions have each half interpolated as real numbers.

上記を読むと「両方の値に」とあり、基本的には用いる値の双方に同じ単位を用いる模様です。

calc()に関しては、pxとパーセントの指定を合算してくれるようですが、「 (each possibly zero)…」以降がうまく訳せず理解できていません…。

そこで、日本語訳が集められている「CSS3の日本語訳集」「CSS Transitions 日本語訳」の「CSS Transitions 日本語訳」から同一箇所の訳を引用します。

長さ, 百分率, または calc 式( length, percentage, or calc )
両者の値が長さである場合は 長さとして補間され,両者の値が百分率である場合は 百分率として補間される。 他の場合の両者の値は、[[ 長さと百分率(いずれもゼロをとり得る) ]の和である calc() 関数 ]に変換された上で、それらの calc() 関数がとる各 片割れ 【長さ どうし, および 百分率 どうし】 が,実数として補間される。

「each possibly zero」は「いずれもゼロをとり得る」でした。…英語が得意でないといろいろ厳しいですね。

しかし上記を読みますと、両方の単位が違う場合でも「calc() 関数」に変換されてから実数として補間されると書かれていますので、仕様上はpxとパーセントを使っても問題ないはずです。

原因の推測

仕様上動くのに動かない、となったのでとりあえずcalc()関数で調べてみますと以下のサイトで情報を発見。

CSSで計算式を可能にするcalc()ファンクション

該当箇所を引用します。

Android4.4よりサポート

仕様書に定められているcalc()関数がそもそも使えない、ということで、どうやらこれが原因の模様です。

なお、cssの仕様で「length, percentage, or calc」とされているものは恐らくすべて同じですから、animationも同様に動きません。

とりあえずの対応策

関数が使えない時点で、javascriptなどを使うか諦めるかの2択になりそうでしたが、半ば無理矢理対応する方法を考えました。

参考にしたのは「stackoverflow」の以下のページです。

Transition pixel to percent

このページに書かれていること自体は試してもうまくいかなかったのですが、min-widthを使う方法に着目し、以下のように2つの方法を作成しました。

対策1


div{
  max-width: 30%;/*最小コンテンツ幅を300pxと想定*/
  width: 100px;
  -webkit-transition: max-width 5s;
          transition: width 5s;
}
div.active{
  max-width: 100%;
  width: 100%;
}

Andorid4.3以下でもtransition自体は動作するので、webkitベンダープレフィックを付けたtransitionの方にwidthではなくmax-widthを指定し、パーセント指定で無理矢理揃えた形です。

加えて、モダンブラウザやiPhoneには適用したくなく、これだけのためにいちいち4.3以下のみを選別して指定する仕組みを取り入れたくなかったので、思い切ってtransitionのみの方はwidthを指定し、通常の方法で記述しています。

問題点は以下の通り。

  • パーセント指定のためコンテンツ幅によっては最小値が幅が意図した通りにならない
  • ベンダープレフィックスなしの時のためのwidthのせいで、ベンダープレフィックスありの時に動作が一瞬おかしくなる(max-widthの初期値とwidthの初期値の実数が合致しないため)

少し問題点が多いので、次の対策2の方がよさそうです。

対策2


div{
  max-width: 100px;
  -webkit-transition: max-width 5s;
          transition: max-width 5s;
}
div.active{
  max-width: 800px;/*コンテンツの横幅の最大値が800pxと仮定*/
}

視点を変え、コンテンツの最大横幅をmax-widthに指定する方法です。

ただ、この方法にも問題点があります。

  • ブラウザ幅一杯に伸ばす場合には使えない(100%と記述できないため)
  • 最大値まで伸びると仮定した際の速度が適用される

1目も問題ですが、2つ目もそれなりに気になる点ではあります。

  • max-width以下の幅しかなくとも、最大幅まで伸びると仮定した速度になる
  • つまり、親要素の幅がmax-widthより大きくても小さくても速度が同じになる

このような動作を求めていれば良いのですが、そうではなく、「指定の幅まで◯秒で」という意図でしたらメディアクエリで細かくしていする形になりそうです。

結び

最初は調べても分からず、総当たりで試して原因を推測し、そこから方針を固めて再調査し、対策を検討。

原因はシンプルなものだったようでその点はクリアになった気はしますが、相当時間がかかりました。

日本語で情報がありそうなものですが、こういう仕様書を読めば分かるという部分で躓く方はいないのか、広く周知の事柄なのか、見つかりませんでした。
検索能力が不足し過ぎなのかもしれません。

0人がこの記事を評価

役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。

連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。