子像素渲染与 CSS 对象模型
在 Windows 8 中,对于浏览网页的设备,您将面临前所未有的宽泛选择,从大型台式计算机屏幕到小型平板电脑不等。为了适应这一系列设备,浏览器必须能够以众多不同的屏幕尺寸和维度来缩放并布局网页内容。我们此前曾发布过一些博客,探讨了 IE 中支持这些应用场景的某些功能。(文本和布局的)子像素定位是美化网页,并使网页内容可在任何缩放比例下一致显示的一项核心平台技术。
本篇博文将介绍我们为使 IE10 更好地利用 CSS-OM 支持子像素定位而开展的改进工作。
Web 开发人员可通过众多平台技术来构建美观的布局。一般来说,开发人员将使用 CSS 样式表来描述网站的布局。在某些应用场景中,Web 开发人员还将使用 JavaScript 代码来衡量、校准或定位包含网页元素(pixel-perfect
精度)。例如,某些在线编辑器可将一个编辑框精准地放置于现有内容的上方,因此其显示效果可让您感觉就像是在直接编辑现有内容。类似这样的应用场景包括使用 CSS 对象模型 (CSS-OM) API 来读取和/或设置元素的位置。CSS-OM 是一套用于对 CSS 进行编程性操作的 JavaScript API。
使用 CSS-OM API 来衡量并校准布局元素可能将引发许多问题,这是因为这些 API 的运行方式可将子像素的定位值四舍五入或截断成全像素值。
有关完美像素布局的简易介绍
一般来说,Web 上完美像素的布局有可能与启用可访问、兼容、且自适应的内容的目标相冲突,因此这并不是一项最佳做法。以下拼贴画显示了一些错误,当 Web 设计人员试图创建一项完美像素的设计,而意外的 Web 平台差异导致其设计出错时,即有可能发生这些错误。
完美像素设计出错的示例
当使用 CSS-OM 来动态生成布局时,Web 开发人员应允许一小部分像素出现潜在错误。更妙的是,IE10 可提供数个全新的布局选项,开发人员可使用这些选项来更好地实现这些所需的布局,而无需使用 CSS-OM 进行完美像素调整。
说明
为了说明 CSS-OM API 可能以何种方式产生细微的定位问题,请考虑一个简单的例子。子像素定位可让四个框均匀地分布于容器内,无论该容器的尺寸数值能否被四整除。
请考虑以下 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
所返回的数值求和而得出的 offsetWidth 总和与实际容器的宽度相差两个像素,这是由于个别 div
元素的 offsetWidth
中所进行的四舍五入而造成的。
其他浏览器中的结果也将出现类似的差异,尽管有时候总数将小于实际值,而有时又将大于实际值。
当四舍五入/截断导致总和超出容器尺寸时(如图所示),那么网页内容将开始换行,并出现不必要的滚动条。此外,许多容器的尺寸是根据文本和文本呈现所用的字体设计而成,而这些字体的度量可能会根据浏览器的不同而各有差异,或者当所需的字体不可用时,容器将选择备用字体。
offsetWidth
API 以及许多其他广泛使用的 CSS-OM 属性(最早可追溯到 1997 年的 IE4)可提供一个方便、快捷的方式从众多不同的坐标系统中为某一元素提取整数像素值。所有主流的浏览器均实施了这些 API 中的大部分内容,以获得兼容性,而且它们同时是 W3C 草案标准 CSS-OM 视图模块中的一部分。
浏览器中的新功能将继续表现出将 CSS-OM 的属性限定为全数值像素的弊端。举例来说,诸如 SVG 和 CSS 2D/ 3D 转换等众多功能可让元素的维度在两个像素间轻易下降。
解决问题
由于(部分)认识到了这一缺陷,W3C CSS-OM 视图规格可将通过 getBoundingClientRect()
API 所返回的坐标描述为 floats
;换句话说,该坐标能以可体现子像素精度的数值呈现。(getBoundingClientRect()
是另一个 CSS-OM API,其可通过使用与 offsetWidth
API 相同的源来提供某一元素的边界框位置和维度。)
在 IE10 中,我们更新了 getBoundingClientRect()
API,以便在默认情况下以 IE10 标准模式返回子像素的分辨率,进而实现与其他浏览器进行互操作,并遵从 W3C 标准。
通过更新上述示例来报告 getBoundingClientRect()
所返回的 width
部分,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
为了保持我们与旧式网页(一般情况下,可能尚未准备使用 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 也可让 Web 开发人员从以上所列 CSS-OM 属性中启用子像素的定位值。使用该特殊功能需要网站在 IE10 标准模式下运行,并通过在文件对象上设置以下属性来选择加入:
document.msCSSOMElementFloatMetrics = true;
当通过将 document.msCSSOMElementFloatMetrics
设置为 true
而启用 CSS-OM API 时,此前所列的所有 CSS-OM API 都将开始以子像素的精度报告其数值,从而准确反映布局和渲染引擎内部进行的计算。请注意,JavaScript 将把 1.00 转换成 1,因此在返回数值中您不一定总能看到小数点。
返回我们的示例,并将 document.msCSSOMElementFloatMetrics
设置为 true
可导致 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 属性进行完美像素布局计算)。 使用 CSS-OM 时,您应了解这些 API 的全像素报告特征。无论您是使用 CSS 还是 CSS-OM API 来设计布局,都请确保可适应一些像素偏差,从而确保提高网站在多个浏览器和/或设备中的可靠性。
—Internet Explorer 项目经理 Travis Leithead