Renderização subpixel e o modelo de objeto CSS

Com o Windows 8, você tem uma inédita variedade de opções de dispositivos para a navegação na Web que inclui telas grandes de desktops e pequenos slates. A fim de se adaptar a essa gama de dispositivos, o navegador precisa ser capaz de ajustar a escala e o layout da Web em vários tamanhos e dimensões de tela diferentes. Já escrevemos sobre alguns dos recursos do IE que dão suporte a esses cenários. O posicionamento subpixel (de texto e layout) é uma das tecnologias de plataforma básicas que permitem que as páginas da Web fiquem bonitas e consistentes em qualquer escala.

Nesta postagem, descrevemos as alterações feitas no IE10 para dar um melhor suporte ao posicionamento subpixel por meio do CSS-OM.

Os desenvolvedores da Web podem criar lindos layouts por meio de várias tecnologias de plataforma. Em geral, os desenvolvedores usam as folhas de estilo CSS para descrever o layout de um site. Em alguns cenários, os desenvolvedores da Web também dependem do código JavaScript para medir, alinhar ou posicionar os elementos de uma página da Web com precisão pixel-perfect. Por exemplo, alguns editores online posicionam uma caixa de edição de forma cuidadosa exatamente sobre o conteúdo existente, para que pareça que a edição do conteúdo existente é feita diretamente. Cenários como esse podem usar as APIs do CSS-OM (modelo de objeto CSS) para ler e/ou definir a posição dos elementos. O CSS-OM é um conjunto de APIs JavaScript para a manipulação programática da CSS.

Medir e alinhar elementos de layout usando as APIs do CSS-OM pode ser problemático devido à forma como essas APIs arredondam ou truncam valores subpixel posicionados para números de pixels inteiros.

Uma nota rápida sobre layouts de pixels perfeitos

Em geral, os layouts de pixels perfeitos na Web costumam conflitar com as metas de conteúdo acessível, compatível e adaptável e, portanto, não são uma prática recomendada. A colagem a seguir ilustra alguns dos bugs que podem ocorrer quando o designer da Web tenta criar um design de pixels perfeitos, mas diferenças inesperadas na plataforma da Web causam erros nos designs.

Exemplos de erro em designs de pixels perfeitos
Exemplos de erro em designs de pixels perfeitos

Ao usar o CSS-OM para gerar um layout de forma dinâmica, os desenvolvedores da Web devem levar em conta alguns pixels com possíveis erros. Melhor do que isso, o IE10 oferece várias novas opções de layout que os desenvolvedores podem usar para melhor conseguir muitos desses layouts desejados sem a necessidade de alinhamento de pixels perfeitos com o uso do CSS-OM.

Uma ilustração

Para ilustrar como as APIs do CSS-OM podem causar pequenos problemas de posicionamento, considere um exemplo simples. O posicionamento subpixel permite que quatro caixas sejam distribuídas uniformemente no contêiner, apesar de o tamanho do contêiner não ser divisível por quatro.

Considere este fragmento de marcação HTML:

<footer>

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

</footer>

com esta marcação CSS parcial:

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; }

Agora, vamos adicionar uma função que é executada no carregamento e relata as larguras dos itens:

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;

}

O resultado da execução dessa marcação e código no IE9 é algo semelhante a isto:

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

Observe que a soma dos valores retornados pelo offsetWidth das APIs do CSS-OM equivale ao cálculo do offsetWidth total com a diferença de dois pixels de largura do contêiner atual, devido ao arredondamento que ocorre no offsetWidth dos elementos div individuais.

Os resultados em outros navegadores mostram uma discrepância semelhante, embora, às vezes, o total seja maior ou menor do que o atual.

Quando o arredondamento/trucamento causa uma soma total que excede o tamanho do contêiner (como ilustrado), o conteúdo começa a quebrar ou a causar o aparecimento de barras de rolagem indesejadas. Além disso, muitos contêineres são dimensionados de acordo com o texto e fonte usados para renderizar o texto, e as métricas dessas fontes podem mudar de um navegador para outro ou fontes alternativas podem ser selecionadas se a fonte solicitada não estiver disponível.

A API offsetWidth, juntamente com várias outras propriedades do CSS-OM amplamente usadas (a maioria remonta ao IE4, em 1997), oferece uma maneira rápida e prática de extrair valores de pixel inteiros para um elemento de uma variedade de sistemas coordenados diferentes. Todos os principais navegadores implementam a maioria dessas APIs para fins de compatibilidade e também fazem parte do módulo CSS-OM View (Exibição do CSS-OM), um padrão de rascunho do W3C.

Novos recursos de navegador continuam demonstrando a desvantagem da limitação das propriedades do CSS-OM a pixels de valor inteiro. Por exemplo, recursos como SVG e CSS 2D/ 3D Transforms permitem que as dimensões de um elemento se encaixem facilmente entre os pixels.

Lidando com o problema

Reconhecendo essa limitação (em parte), a especificação W3C CSS-OM View descreve as coordenadas retornadas pela API getBoundingClientRect() como floats; isto é, valores que podem representar a precisão subpixel. (getBoundingClientRect() é outra API do CSS-OM que fornece uma dimensão e posição da caixa delimitadora de um elemento usando a mesma origem da API offsetWidth.)

No IE10, atualizamos a API getBoundingClientRect() para que retornasse a resolução subpixel por padrão no modo de padrões do IE10 para que pudesse funcionar com outros navegadores e alinhar-se com o padrão W3C.

Atualizando o nosso exemplo acima para que relatasse o componente width do retângulo retornado por getBoundingClientRect(), o IE10 agora relata estes valores fracionários:

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

Levando os valores subpixel a todos os lugares

Além da getBoundingClientRect, nós também relatamos a posição subpixel de eventos do mouse/ponteiro no modo de padrões do IE10 por padrão. Somente é possível que o mouse/ponteiro se posicione between pixels quando o valor do fator zoom não é definido como 100%. Especificamente, as APIs do mouse/ponteiro afetadas são:

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

Para mantermos o nosso compromisso com a compatibilidade com páginas da Web herdadas (páginas que talvez não estejam preparadas para lidar com valores subpixel do CSS-OM em geral), o IE10 continua relatando unidades de pixel de valor inteiro por padrão para as outras propriedades do CSS-OM. Essas APIs são:

  • 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

Entretanto, se necessário, o IE10 agora também permite aos desenvolvedores da Web habilitar valores subpixel posicionados das propriedades do CSS-OM relacionadas acima. O uso desse recurso especial exige o modo de padrões do IE10 e que o site o aceite definindo esta propriedade no objeto de documento:

document.msCSSOMElementFloatMetrics = true;

Quando habilitadas pela definição de document.msCSSOMElementFloatMetrics como true, todas as APIs do CSS-OM da lista acima começarão a relatar seus valores em precisão subpixel que refletirá os cálculos exatos usados internamente pelo mecanismo de renderização e layout. Observe que o JavaScript converte 1,00 em 1, portanto, talvez você nem sempre veja um ponto decimal nos valores retornados.

Voltando ao nosso exemplo e definindo document.msCSSOMElementFloatMetrics como true, teremos este resultado no 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

Observe os valores fracionários retornados por offsetWidth e que todos os totais agora são correspondentes.

Resumo

Às vezes, é útil (e, em casos raros, necessário) poder usar as propriedades do CSS-OM para o cálculo de layouts de pixels perfeitos. Ao usar o CSS-OM, considere as características de relatório de pixel inteiro dessas APIs. Ao criar layouts com APIs da CSS ou do CSS-OM, lembre-se de incluir alguns pixels de tolerância para garantir um site mais robusto em vários navegadores e/ou dispositivos.

—Travis Leithead, Gerente de programas, Internet Explorer