読者です 読者をやめる 読者になる 読者になる

iQONのviewportを一気に書き換えた話

Web

f:id:vasilyjp:20160902113032j:plainフロントエンドエンジニアのnibaです。 先日、iQONのスマホページでviewportの改善を行いました。 その際の技術選定や工夫について述べていきたいと思います。

viewportについて

まず初めにviewportに関して説明します。 viewportはHTMLメタ要素の一つです。これを指定することにより、スマホ/タブレットで表示される際の描画領域幅やスケールを決定できます。
viewportは以下のようなタグで指定できます。

<meta name='viewport' content='width=device-width,initial-scale=1'>

上の例では、描画領域幅widthにデバイス幅を意味するdevice-width、スケールinitial-scaleに1が指定されています。widthには980のようなピクセル固定値やdevice-widthを指定できます。固定値の場合には描画領域が画面幅に合うように自動でスケーリングされます。

昔は幅320pxのデバイスが多かったため、widthに320を指定するケースが多くありました。 しかし、レスポンシブデザインがスタンダードになってきている今、widthに固定値ではなくdevice-widthを指定することをGoogleも推奨しています。 Google Adsenseをはじめとした多くの広告もdevice-widthを前提として作られています。

今回やったこと

iQONでは、1年前のリニューアル時にiPhone6のRetinaディスプレイの実ピクセル数に合わせて viewportのwidthに750を指定して実装しました。 しかし、これは現在のスタンダードにそぐわない上にデバイス幅基準の広告にも合いません。 そのため、8月にviewportのwidthにdevice-widthを指定する改修を行いました。 次に実際にどのように改修を行ったかについて述べていきます。

具体的にどのように改修を行ったか

実装で用いる単位の検討

今回の改修では、device-widthによらずレイアウトは一定にすることにしました。 そのため、絶対値のpxを相対値の単位に変更する必要があったので、CSSの相対単位の比較検討を行いました。 以下にCSSの相対単位の代表例を示します。

単位名 定義 対応デバイス
rem htmlタグに指定されたfont-sizeを1remとする iOS5~
Android2.1~
em 親要素のfont-sizeを1emとする 全て
vw ビューポートの幅に対する百分率。 iOS6.1~
Android4.4~
vh ビューポートの高さに対する百分率。 iOS6.1~
Android4.4~

最終的に、iQONでは以下の理由でremを採用することにしました。

  • 1remの大きさを自由に調整できる。
  • 要素の包含関係に依存しないので、pxと同様に扱える。
  • iQONでサポートしているAndroid4.0~で使用できる。

1remの大きさの設定

1remの大きさを決定するにあたり、以下の2つを満たすような実装を考えました。

  1. 各デバイスで表示が変わらないようにする
  2. 幅750pxのデザインデータから直感的にコーディング出来るようにする
    (デザイン上の100px=1remなど)

1.の条件を満たすために、画面幅基準の相対単位vwで1remの大きさを指定するようにしました。 さらに、1remに13.33vwを指定することでデザイン上の100px=1remの関係になり、2.の条件を満たすことができました。

改修前

h1 {
  font-size: 20px;
  width: 700px;
}

改修後

html {
  font-size: 13.33vw; /*  1[vw] = 7.5[px];  100[px] / 7.5[px/vw] = 13.33vw;  */
}

h1 {
  font-size: 0.2rem;
  width: 7rem;
}

デザイン上の1px=1remデザイン上の10px=1remがより直感的ですが、実際にブラウザでレンダリングされる際に1remが10px未満になってしまいます。10px未満のフォントサイズはブラウザで正しくレンダリングされない為、デザイン上の100px=1remにしています。

ここまでvwを用いて1remの大きさを設定してきましたが、vwはAndroid4.4以上しか対応していません。 しかし、Android4.3以前の端末からのアクセスの場合でも、以下のJavaScriptコードで対応できます。

document.querySelector('html').style.fontSize = 100 * window.innerWidth + MOCKUP_VIEWPORT + 'px'

これで、CSSの改修を単純な置換作業に落とし込むことができました。 幾つかハマった点もありましたが、単純な置換作業のみで大方問題なく改修出来ました。 次に、ハマった点をいくつかピックアップしたいと思います。

改修でハマった点

remはレンダリングの過程でpxに変換され、この際に小数が発生してしまいます。小数の処理はブラウザに大きく依存します。そのため、小さな値をremで指定した場合に不具合が発生することがあります。 ここでは実例を3つ紹介したいと思います。

ボーダーが消えてしまうことがある

border-widthをremで指定するとデバイスによって1px未満の値になってしまい、 最悪の場合ボーダーが消えます。iQONでは、デバイスごとに表示が変わってしまうことを前提にborder-widthはpx指定を残しました。

border-radiusで描いた円が歪む

ブラウザの小数点以下の処理により正円で無くなってしまうことがあります。 デバイスごとに表示が変わってしまうことを前提にpx指定を残しました。

f:id:vasilyjp:20160901162345p:plain

子要素が微妙に隠れてしまう

HTMLでは親要素は子要素に合わせて拡大されます。 しかし、子要素のサイズが小数点を含む場合、親要素が小数点以下を切り捨てられたサイズまでしか 広がらず、子要素の一部が隠れることがあります。対応方法はケースバイケースですが、子要素に指定していたスタイルを親要素に移すなどして対応しました。

子要素

f:id:vasilyjp:20160901162354p:plain

親要素
f:id:vasilyjp:20160901162357p:plain

まとめ

CSSの相対単位は、レンダリングの過程で小数のpxに変換された際に表示がおかしくなる場合があるので 注意が必要な場合があります。 しかし、remを工夫して用いることによりviewportのdevice-widthへの改修を最小限のコストで行うことができました。

最後に

VASILYでは、Webサービスを自らの手で創りたいハングリーな仲間を募集しています。 ご興味のある方は以下のリンク先をご覧ください。

www.wantedly.com