サブピクセル レンダリングと CSS オブジェクト モデル

Windows 8 では、大型のデスクトップ画面から小型のスレートまで、かつてないほど多様なデバイスが Web のブラウズに使われます。ブラウザーがこのように多岐にわたるデバイスに対応するには、画面のサイズや縦横比が変わっても、表示を調整して Web ページを適切にレイアウトできる必要があります。以前、ブログで、このようなシナリオをサポートする IE の機能 いくつかを紹介しました。(テキストやレイアウトの) サブピクセル配置 (英語) は、どのようなスケールでも Web ページを美しく、一貫性を保って表示できるようにする主要なプラットフォーム テクノロジの 1 つです。

この記事では、CSS-OM によるサブピクセル配置のサポートを強化するために、IE10 で実施された変更について説明します。

Web 開発者は、さまざまなプラットフォーム テクノロジを利用して、美しいレイアウトを構成します。通常、Web サイトのレイアウトの記述には、CSS スタイル シートが使われます。場合によっては、JavaScript コードを駆使して、1 ピクセルのずれもない (ピクセル パーフェクトな) 精度で Web ページの要素を計測、整列、または配置 (英語) することもあります。たとえば、一部のオンライン エディターでは、寸分の違いなく既存のコンテンツに編集ボックスを重ねて配置し、既存のコンテンツを直接編集しているように見せる場合があります。このようなシナリオでは、要素の配置の読み取りや設定に、CSS オブジェクト モデル (CSS-OM) API を使用できます。CSS-OM は、プログラムによって CSS を操作するための JavaScript API のセットです。

CSS-OM API を使用してレイアウト要素を計測および整列する場合、この API によって、位置を示すサブピクセル レベルの値が四捨五入または切り捨てられて整数のピクセル値になるために、問題が起きる可能性があります。

ピクセル パーフェクトなレイアウトについて一言

通常、Web のピクセル パーフェクトなレイアウトは、アクセス可能で互換性があり、移植可能なコンテンツを実現するという目標に反する傾向があるため、ベスト プラクティスではありません。ピクセル パーフェクトなデザインを制作しようとして、予期しなかった Web プラットフォームの違いのためにデザインが崩れたことが原因で発生する可能性のあるバグの例を、いくつか次の図に示します。

ピクセル パーフェクトなデザインが崩れた例
ピクセル パーフェクトなデザインが崩れた例

CSS-OM を使って動的にレイアウトを生成する場合は、数ピクセルの誤差が発生しても影響がないようにする必要があります。できれば、CSS-OM を使用してピクセル パーフェクトな整列をしなくても、目的のレイアウトの多くをより正確に実現できるいくつかの 新しい (英語) レイアウト オプションが IE10 には用意されているので、それらを使用するとよいでしょう。

例を使った説明

CSS-OM API によって、どのように配置のずれが生じるかを説明するために、簡単な例を使って考えてみましょう。サブピクセル配置では、コンテナーのサイズが 4 等分できないとしても、コンテナーの中に 4 個の枠を均等に配置することができます。

次の HTML マークアップ フラグメントと、

<footer>

<div>content 1</div><div>content 2</div><div>content 3</div><div>content 4</div>

</footer>

次の部分的な CSS マークアップについて考てみましょう。

footer { width: 554px; border: 1px solid black; text-align: center; }

footer div { display: inline-block; width: 25%; }

footer div:nth-child(even) { background-color: Red; }

footer div:nth-child(odd) { background-color: Orange; }

今度は、読み込み時に実行されて、各アイテムの幅を返す関数を追加しましょう。

onload = function () {

var footerBoxes = document.querySelectorAll("footer div");

var s = "";

var totalSize = 0;

for (var i = 0; i < footerBoxes.length; i++) {

// Reporting

var offsetWidth = footerBoxes[i].offsetWidth;

s += "content " + (i + 1) + " offsetWidth = " + offsetWidth + "px" + "<br />";

totalSize += offsetWidth;

}

s += "Total <i>calculated</i> offsetWidth = " + totalSize + "px" + "<br />";

s += "Container width = " + document.querySelector("footer").clientWidth + "px" + "<br />";

document.querySelector("#message").innerHTML = s;

}

このマークアップとコードを IE9 で実行すると、次のようになります。

content 1content 2content 3content 4 content 1 offsetWidth = 139content 2 offsetWidth = 139content 3 offsetWidth = 139content 4 offsetWidth = 139Total calculated offsetWidth = 556Actual container width = 554

CSS-OM API の offsetWidth によって返される値を合計すると、各 div 要素で offsetWidth が実行されるときに四捨五入が行われるため、計算された offsetWidth の合計値と実際のコンテナーの幅には 2 ピクセルの誤差が生じます。

合計値が実際の値より少ない場合も多い場合もありますが、他のブラウザーで実行しても同様の誤差が発生します。

四捨五入や切り捨てによって、合計値がコンテナーのサイズを超える場合 (図を参照)、コンテンツが折り返されるか、不要なスクロール バーが表示されます。また、多くのコンテナーでは、テキストやテキストのレンダリングに使われるフォントを基にサイズが決まりますが、ブラウザーによってフォント メトリックが異なることや、要求されたフォントを使用できなかった場合に別のフォントが使われることが考えられます。

offsetWidth API は、他に広く使用されている多くの CSS-OM プロパティ (最も古くは 1997 年の IE4 にさかのぼる) と同様に、さまざまな座標系から、便利にすばやく要素の整数のピクセル値を抽出する手段を提供します。一般的なブラウザーはどれも、互換性確保のためにこれらの API の大部分を実装していますし、W3C のドラフト標準の CSS-OM View (英語) モジュールにも参加しています。

新しいブラウザー機能でも、CSS-OM プロパティが整数のピクセル値しか使用できないことによる問題は再現されます。たとえば、SVG や CSS 2D (英語) / 3D 変換 (英語) などの機能では、要素のサイズが 1 ピクセル未満の端数のある値になりがちです。

解決策

この制限 (一部) を認識して、W3C CSS-OM View (英語) の仕様では、getBoundingClientRect() API から返される座標を浮動小数点数で表すように規定しています。つまり、サブピクセルの精度で表すことができる値になります (getBoundingClientRect() も、offsetWidth API と同じ原点を使用して、要素の境界ボックスの位置とサイズを提供できる CSS-OM API です)。

IE10 では、他のブラウザーとの相互運用性を確保し、W3C 標準に準拠するために、getBoundingClientRect() API を更新して、IE10 の標準モードでは既定でサブピクセルの結果が返されるようにしています。

四角形の width コンポーネントが getBoundingClientRect() によって返されるように上記の例を更新すると、IE10 では次のような小数値が返されます。

content 1content 2content 3content 4 content 1 offsetWidth = 139, getBoundingClientRect().width = 138.5 content 2 offsetWidth = 139, getBoundingClientRect().width = 138.5 content 3 offsetWidth = 139, getBoundingClientRect().width = 138.5 content 4 offsetWidth = 139, getBoundingClientRect().width = 138.5 Total calculated offsetWidth = 556 Total calculated getBoundingClientRect().width = 554 Actual container width = 554

どこででもサブピクセルの値を

getBoundingClientRect だけでなく、マウス/ポインター イベントの位置も IE10 の標準モードでは既定でサブピクセルで返されるようにしています。ただし、このような値が返されるのは、拡大/縮小率が 100% 以外の値に設定されているときに、マウス/ポインターがピクセルとピクセルの間に置かれた場合のみです。具体的に影響を受けるマウス/ポインター API は次のとおりです。

  • MouseEvent.offsetX/Y
  • MouseEvent.layerX/Y
  • MouseEvent.clientX/Y
  • MouseEvent.pageX/Y
  • MouseEvent.x/y

レガシ Web ページ (概して CSS-OM からのサブピクセル値を処理できる状態になっていない可能性があるページ) との互換性を維持するために、IE10 では、他の CSS-OM プロパティについては、引き続き既定で整数単位のピクセル値を返します。これに該当する API は次のとおりです。

  • Element.clientHeight
  • Element.clientWidth
  • Element.clientLeft
  • Element.clientTop
  • Element.scrollTop
  • Element.scrollLeft
  • Element.scrollWidth
  • Element.scrollHeight
  • HTMLElement.offsetWidth
  • HTMLElement.offsetHeight
  • HTMLElement.offsetTop
  • HTMLElement.offsetLeft
  • TextRange.offsetLeft
  • TextRange.offsetTop

ただし、IE10 では必要に応じて、上記の CSS-OM プロパティでもサブピクセル レベルで位置を表す値を使用できるようになりました。この特殊機能を使用するには、IE10 標準モードであること、また、サイトの document オブジェクトで次のようにプロパティを設定して、この機能を使用できるようにする必要があります。

document.msCSSOMElementFloatMetrics = true;

document.msCSSOMElementFloatMetricstrue に設定して有効化されると、上記のすべての CSS-OM API から、レイアウトおよびレンダリング エンジンによって内部で使用される計算と正確に一致する、サブピクセルの精度で表された値が返されるようになります。ただし、JavaScript では 1.00 が 1 に変換されるため、必ずしも返された値に小数点が含まれるとは限りません。

例に戻って、document.msCSSOMElementFloatMetricstrue に設定すると、IE10 では次のような結果になります。

content 1content 2content 3content 4 content 1 offsetWidth = 138.5, getBoundingClientRect().width = 138.5 content 2 offsetWidth = 138.5, getBoundingClientRect().width = 138.5 content 3 offsetWidth = 138.5, getBoundingClientRect().width = 138.5 content 4 offsetWidth = 138.5, getBoundingClientRect().width = 138.5 Total calculated offsetWidth = 554 Total calculated getBoundingClientRect().width = 554 Actual container width = 554

offsetWidth によって返される小数値とその合計値が一致するようになりました。

まとめ

CSS-OM プロパティを使用して、ピクセル パーフェクトなレイアウトの計算を実現できることが役立つ場合もあります (まれにそれが必要な場合もあります)。CSS-OM を使用する場合、これらの API では整数の値が返されることに注意してください。CSS または CSS-OM API を使用してレイアウトをデザインする場合は、ブラウザーやデバイスが変わってもレイアウトに影響を受けないサイトになるように、数ピクセルの誤差を吸収できるようにしてください。

- Internet Explorer 担当プログラム マネージャー Travis Leithead