Субпиксельная отрисовка и объектная модель CSS

Windows 8 предоставляет пользователю огромный выбор устройств для просмотра веб-страниц в Интернете, начиная с больших настольных мониторов и заканчивая компактными планшетами. Чтобы обеспечить соответствие всему этому диапазону устройств, браузер должен поддерживать масштабирование и изменять макет веб-страниц в соответствии с размерами экрана и измерениями его элементов. Мы уже писали о некоторых возможностях в Internet Explorer, поддерживающих эти сценарии. Субпиксельная отрисовка (текста и макета) — это одна из базовых технологий платформы, благодаря которой веб-страницы выглядят красиво и согласованно при любом масштабе отображения.

В данной статье мы расскажем об изменениях, внесенных в Internet Explorer 10, которые позволяют улучшить поддержку субпиксельной отрисовки с помощью объектной модели CSS-OM.

Веб-разработчики могут создавать красивые макеты с помощью различных платформенных технологий. Обычно разработчики используют таблицы стилей CSS для описания макета веб-сайта. В некоторых случаях веб-разработчики также применяют код JavaScript для измерения, выравнивания или размещения элементов на веб-странице с точностью pixel-perfect. Например, некоторые редакторы публикуемых в Интернете материалов аккуратно размещают поле ввода точно над имеющимся содержимым, чтобы оно появлялось только при непосредственном изменении содержимого. В таких сценариях для считывания элементов или задания их положения можно использовать интерфейсы API объектной модели CSS (CSS-OM). CSS-OM — это набор интерфейсов API JavaScript для программного управления CSS-стилями.

Определение размеров и выравнивание элементов макета с помощью интерфейсов API модели CSS-OM может оказаться проблематичным из-за того, что такие API округляют и усекают субпиксельные значения координат до целых чисел.

Краткая заметка о макетах с точностью Pixel-perfect

В общем случае макеты с точностью pixel-perfect при использовании в Интернете часто конфликтуют с целями создания доступного, совместимого и адаптируемого содержимого, поэтому использовать их не рекомендуется. На следующем рисунке показаны некоторые ошибки, которые могут возникать, когда веб-дизайнер пытается создать макет с точностью pixel-perfect, но непредвиденные отличия в веб-платформе приводят к искажению его замысла.

Примеры искажения макетов с точностью pixel-perfect
Примеры искажения макетов с точностью pixel-perfect

При использовании CSS-OM для динамического создания макетов веб-разработчики должны быть готовы к смещению элементов на несколько пикселей и учитывать эту потенциальную ошибку в своей работе. Однако в Internet Explorer 10 предлагается несколько новых параметров макетирования, которые позволяют создавать нужные макеты без необходимости выравнивания с точностью pixel-perfect с помощью модели CSS-OM.

Пример

Чтобы понять, как интерфейсы API модели CSS-OM могут вызывать проблемы с расположением элементов, рассмотрим простой пример. Субпиксельная отрисовка позволяет равномерно расположить четыре поля внутри контейнера, несмотря на то, что размер контейнера даже не делится на четыре без остатка.

Рассмотрим следующий фрагмент 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;

}

Результат обработки этой разметки и выполнения кода в Internet Explorer 9 будет выглядеть примерно так:

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

Обратите внимание, что сумма значений, возвращенных свойством offsetWidth API модели CSS-OM, дает итоговое расчетное значение offsetWidth, отличающееся от фактической ширины контейнера на два пикселя из-за округления, выполняемого в свойстве offsetWidth отдельных элементов div.

В других браузерах наблюдается аналогичное расхождение результатов, иногда сумма оказывается меньше фактического значения, а иногда больше.

Когда округление или усечение ведет к тому, что итоговая сумма превышает размер контейнера (как показано на рисунке), содержимое будет переноситься на другую строку либо будут появляться нежелательные полосы прокрутки. Кроме того, размер многих контейнеров устанавливается в соответствии с текстом и шрифтом, используемым для отображения этого текста. Однако метрики этих шрифтов могут в различных браузерах отличаться друг от друга; если же запрошенный шрифт отсутствует, вместо него могут выбираться альтернативные шрифты.

Свойство offsetWidth API вместе со многими другими широко используемыми свойствами CSS-OM (большинство из них появилось еще в Internet Explorer 4 в 1997 г.) предлагает удобный и быстрый способ извлечь целые значения пикселей для элемента из множества разных координатных систем. Во всех основных браузерах реализовано большинство этих API в целях обеспечения совместимости. Они также являются частью модуля CSS-OM View, который предложен консорциумом W3C в качестве проекта стандарта.

Новые функции браузеров по-прежнему демонстрируют недостаток ограничения свойств CSS-OM целочисленными значениями пикселей. Например, такие функции, как SVG и CSS 2D/ 3D Transforms, допускают, чтобы измерения элементов определялись дробными значениями пикселей.

Решение проблемы

Осознавая это ограничение (частично), консорциум W3C предложил спецификацию CSS-OM View, где координаты, возвращаемые через метод getBoundingClientRect() API, имеют тип floats; другими словами, значения координат могут представлять субпиксельную точность. (getBoundingClientRect() — является другим API модели CSS-OM, представляющим расположение и измерение ограничивающего прямоугольника элемента с таким же началом координат, что и у свойства offsetWidth.)

В Internet Explorer 10 мы обновили метод getBoundingClientRect() API, и теперь он соответствует стандарту консорциума W3C и по умолчанию возвращает субпиксельное разрешение в стандартном режиме Internet Explorer 10 для обеспечения возможностей взаимодействия с другими браузерами.

Если обновить пример выше и задать возвращение компонента width прямоугольника, возвращаемого методом getBoundingClientRect(), то Internet Explorer 10 теперь возвращает следующие дробные значения:

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 мы также по умолчанию возвращаем субпиксельное положение для событий мыши/указателя в стандартном режиме Internet Explorer 10. Указатель мыши может оказаться в положении between (между) пикселями только в случае, когда коэффициент масштаба имеет значение, отличное от 100 %. В особенности это касается следующих API мыши и указателя:

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

Мы также продолжаем поддерживать совместимость с устаревшими веб-страницами (которые в общем случае невозможно адаптировать для обработки субпиксельных значений из модели CSS-OM). Поэтому по умолчанию Internet Explorer 10 продолжает возвращать целочисленные пиксельные значения для других свойств 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

Тем не менее, при необходимости Internet Explorer 10 позволяет веб-разработчикам также использовать субпиксельные значения позиционирования для вышеуказанных свойств CSS-OM. Для использования этой специальной возможности требуется перевести работу сайта в стандартный режим Internet Explorer 10 и задать следующее свойство объекта документа:

document.msCSSOMElementFloatMetrics = true;

При включении этой возможности посредством задания для document.msCSSOMElementFloatMetrics значения true, все API модели CSS-OM в предыдущем списке начнут возвращать значения с субпиксельной точностью, которая в точности отражает вычисления, производимые обработчиком макетов и механизмом визуализации. Обратите внимание, что JavaScript будет преобразовывать 1.00 в 1, поэтому в возвращенных значениях не всегда может присутствовать десятичный разделитель.

Вернемся к нашему примеру и присвоим свойству document.msCSSOMElementFloatMetrics значение true, что приведет к получению следующих результатов в Internet Explorer:

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 для расчета значений макета с точностью pixel-perfect. При использовании модели CSS-OM помните, что ее API возвращают целочисленные значения. При создании макетов с помощью CSS или интерфейсов API модели CSS-OM оставляйте несколько пикселей допуска, чтобы сайт отображался одинаково правильно в разных браузерах и на разных устройствах.

—Трэвис Лейтхед (Travis Leithead), руководитель программы, Internet Explorer