ブラウザの小数点以下のピクセル(サブピクセル)レンダリングまとめ

公開

更新

意図せずに出てしまった小数点以下のピクセル(以下サブピクセル)で表示ミスしてしまったので、2014/06/05時点での私が用意できる最新ブラウザ※におけるレンダリングされ方についてまとめてみました。

※Chrome(35.0.1916.114), Firefox(29.0.1), Opera(22.0.1471.50),Safari(7.0.4(9537.76.4)),IE(11.0.9600.17031), mobile safari(7.0),Android Chrome(34.0.1847.114), Androidブラウザ(4.2.2-FJLjpkdi.20131219.1833),Chrome,Firefox,OperaはOSX上のもの、IEは仮想環境(modernIE)上のものです。

※2014-06-06 15:20 FirefoxにおけるRetinaディスプレイ時のレンダリングについて追記と修正しました

子要素の幅の合計が親要素の幅と一致する場合

例えば、あるボックスのなかに4つのボックスを均等に並べたいときは下記のように、子要素の幅の合計が100%になるようにwidthを指定するかと思います。

HTML
<div class="wrap">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>
CSS
.wrap {
  overflow: hidden;
  background: red;
}
.wrap div {
  float: left;
  width: 25%;
  background: blue;
}

デモページ

この場合.wrapwidth値が4で割り切れる値であればいいのですが、例えば50pxなどの割り切れない値が出る場合、子要素のwidthは12.5pxとサブピクセルの値となってしまいます。この場合に、各ブラウザによって挙動が異なりレイアウトが崩れてしまったり、意図しない隙間が出来てしまいます。

Chrome, Firefox, Opera, IEの挙動

今日時点で上記のブラウザの最新版は同じ挙動を示します。例えば、Chromeでは下の図のように赤い背景の親要素に隙間なく青い背景の子要素が収まってレンダリングされます。

Chromeでのレンダリング例

上記のブラウザは、いずれもサブピクセルレンダリングに対応しているため、いい感じに埋めてくれるのですが、上記以外のブラウザについてはそうではありませんでした。

Safari(OS X&iOS),Androidブラウザの挙動 + IE7の挙動

いずれもWebkitをレンダリングエンジンに採用しているブラウザですが、上記の3つのブラウザは共通して下記のような挙動を示します。今回はSafari(OSX)の例です。

Safari(OS X)でのレンダリング例

これらのブラウザでは、12.5pxの小数点以下を切り捨ててしまうため、12px * 4 = 48pxとなり、結果右端に2pxの隙間が生まれ親要素の赤が見えてしまいます。この問題についてはjQueryを用いてゴリゴリ解決する方法もあるようです。Fixing Subpixel Layout Rendering in Safari

なお参考までにIE7以下では、四捨五入で繰り上げてレンダリングするため、今回のケースでは一番右の子要素がカラム落ちしてしまいます。

IE7でのレンダリング例

小数点以下の数値が親要素に満たないかはみ出した場合

先の章では、合計が100%であるが表示上100%にならない場合をとりあげましたが今度は、例えば幅が50pxのボックスの中に49.5pxや50.5pxの幅を持ったボックスがある場合はどうなるでしょうか。

HTML
<div class="fill99">
	<div>99%</div>
</div>
<div class="fill101">
	<div>101%</div>
</div>
CSS
div[class^="fill"] {
  width: 50px;
  height: 50px;
  background: red;
}
.div[class^="fill"] div {
  width: 99%;
  background: blue;
}
.fill101 div {
  width: 101%;
}

デモページ

Safari(OS X&iOS),Androidブラウザの挙動

先にWebkit系の挙動について説明します。先にも上げたとおり、Webkit系のブラウザは小数点以下を切り捨てて表示させるため、49.5pxは49pxに、50.5pxは50pxとなり、満たない場合は少し隙間ができ、はみ出した場合はちょうど収まるようにレンダリングされます。

Safari(OS X)でのレンダリング例

Chrome(mobile), Opera, IEの挙動

それでは、先の例ではうまい具合に調整してくれたChromeなどのブラウザの挙動はどうなるでしょうか。結論から言うと通常Firefox以外は49.5px以上50.5未満なら50pxとしてレンダリングされます。とすると、デモページの例で言うと49.5pxの場合はうまく収まり、50.5pxの場合は1pxはみ出して表示されます。付け加えると、49.5未満の場合は、1px隙間があるように見えます。下記はOperaでのレンダリング例です。

Operaでのレンダリング例

また上記のブラウザでも、それぞれ認識できる桁数が違うため例えば、width: 100.998877%などとすると細かく違ってきます。認識できる桁数については、[CSSの小数点以下の数値を各ブラウザはどのように解釈するか(http://unformedbuilding.com/articles/after-the-decimal-point-in-css/)]で詳しく解説されています。

Firefoxの挙動

この場合、Firefoxのみディスプレイのピクセル密度にあわせてサブピクセルまでレンダリングされます。下の画像はRetinaディスプレイの際とピクセル密度86ppiの普通のPCディスプレイの際のFirefoxでのレンダリング例です。

FirefoxでRetinaディスプレイと普通のPCディスプレイでレンダリング例

普通のディスプレイの方では、chromeやOperaと同様の表示なのですが、Retinaディスプレイだとサブピクセルも忠実にレンダリングし、0.5px分隙間が見えたり、はみ出て見えると思います。ちょっと上記の画像だと分かりにくいので、49.5pxの方にborder-rightを追加してみると分かるのですが、ボーダーが.5px分だけはみ出てちょうど1pxボーダーの真ん中に親要素の境界が来る感じになります。

Firefoxでborder-right:1pxを追加してレンダリングした例

まだ0.5px程度だと分かるんですが、それ以下だとなかなか注意してみないと気付かったりするので意図してない場合は注意が必要ですね。

ブラウザのズーム機能利用時について

これまでいずれも、ブラウザの表示倍率が100%の際のレンダリング例を上げてきましたがズーム機能を利用するとどう変わってくるのでしょうか。

Safari(OS X&iOS),Androidブラウザの挙動

Webkit系のブラウザには、iPhoneに搭載されているSafariやAndroidブラウザが含まれているためズーム機能を使うユーザがデスクトップ版に比べて多いのでは無いでしょうか。これらの場合、切り捨てているため、表示倍率が変わってもレイアウトが変わることはありません。詳しく調べていませんが、むしろこのためにズバッと切り捨てているのでは無いかと思います。ただ難点が、最初の子要素の幅の合計が親要素の幅と一致する場合のHTMLをズームしてレンダリングすると要素間にぼんやりと隙間があるように見える時があります。下記はSafariでのレンダリング例です。

Safari(OS X)ズーム時でのレンダリング例

Chrome, Opera, IEの挙動

表示倍率が100%の際は、±1pxの間は整数の値としてレンダリングしていたChrome,Opera,IEのブラウザは、表示倍率を変えるとまた違ってレンダリングするようになります。例えば下記はIE11で表示倍率を200%にした例ですが、width: 99% = 49.5pxの場合も隙間があるようにレンダリングされます。

IE11でズームした際のレンダリング例

ここもズーム機能について詳しく調べていませんが、49.5px * 200% = 99pxとレンダリングされるのではないかと思います。これは整数にならなくても±1pxの範囲を超えるとそのようにレンダリングされますので、表示倍率150%の際も同様です。

Firefoxの挙動

Firefoxは表示倍率100%の際と変わらずにレンダリングします。

まとめ

上記で上げた記事や、文末に掲載する参考記事でも述べられていますが特定の幅のみに対応するのではなく、未知のデバイスのディスプレイサイズへの対応を考えたレスポンシブデザインでは、リキッドデザインが用いられますが、その場合多くのレイアウトが小数点の数値を持つ可能性があります。その場合は、上記の挙動をよく理解することが重要な気がしています。

またいわゆるデスクトップのサイズのディスプレイにのみ対応する場合は、むやみにサブピクセルが発生するレイアウトの仕方をせず、きっちり整数のpx単位でレイアウトを組むことが大事では無いでしょうか。ちなみに私は今回はこれでミスをして調べ始めました。

参考