Tutorial do CSS Houdini

Bom dia amigos

O que é um Houdini?


Houdini ( Houdini ) - uma coleção de APIs de navegadores que melhoram significativamente o processo de desenvolvimento da web, incluindo o desenvolvimento de padrões CSS. Os desenvolvedores poderão estender o CSS usando JavaScript, influenciando a renderização do CSS e informando ao navegador como os estilos devem ser aplicados. Isso fornecerá um aumento significativo no desempenho e na estabilidade do que o uso de polifilos.

Houdini consiste em dois grupos de APIs - APIs de alto nível e APIs de baixo nível.

APIs de alto nível estão associadas ao processo de renderização (estilo - layout - desenho - composição). Este grupo inclui:

  • API do Paint - permite estender o CSS na etapa (significando o estágio de renderização) da renderização de elementos visuais (cor, plano de fundo, bordas etc.).
  • API de layout - permite estender o CSS na etapa de determinação do tamanho, posição e alinhamento dos elementos.
  • API de animação - “ponto de extensão” na etapa de exibição e animação de elementos.

APIs de baixo nível são a base para APIs de nível superior e incluem:

  • API de modelo de objeto digitado
  • API de propriedades e valores personalizados
  • API de métricas de fonte
  • Worklets

Futuro do CSS


Houdini, diferentemente do CSS comum, permite que os desenvolvedores estendam o CSS de uma maneira mais natural. Isso significa que as especificações CSS deixarão de evoluir e novos padrões serão adotados? De modo nenhum. O objetivo de Houdini é ajudar a desenvolver novos recursos de CSS através da criação de protótipos funcionais que podem ser facilmente padronizados.

Além disso, os desenvolvedores podem compartilhar facilmente worklets CSS abertos sem se preocupar com a compatibilidade.

API do Modelo de Objeto Digitado (TOM)


Antes do Houdini, a única maneira de o JS e o CSS interagirem era converter o CSS em uma string e modificá-lo. A análise manual e a redefinição de estilos podem ser complexas e propensas a erros devido à necessidade de conversão dupla do tipo de valor (por exemplo, de um número para uma sequência de caracteres e vice-versa). Você também deve especificar manualmente as unidades de medida para o novo valor.

selectedElement.style.fontSize = newFontSize + 'px' // newFontSize = 20
console.log(selectedElement.style.fontSize) // 20px

O TOM atribui às propriedades CSS um significado mais semântico, representando-as como objetos JS digitados. Isso melhora muito o desempenho, a estabilidade e facilita o suporte ao código. Os valores CSS são representados pela interface CSSUnitValue, que consiste no valor e na propriedade "unit of measure".

{
  value: 20,
  unit: 'px'
}

Essa interface pode ser usada com os seguintes novos recursos:

  • computedStyleMap (): para analisar estilos computados (não integrados). Este método é chamado antes de analisar ou usar outros métodos.
  • attributeStyleMap: para analisar e modificar estilos embutidos. Esta é uma propriedade do item.

//       ( )
selectedElement.computedStyleMap().get('font-size') // { value: 20, unit: 'px' }

//   
selectedElement.attributeStyleMap.set('font-size', CSS.em(2))
selectedElement. attributeStyleMap.set('color', 'blue')

//    
selectedElement.computedStyleMap().get('font-size') // { value: 20, unit: 'px' }

//    
selectedElement.attributeStyleMap.get('font-size') // { value: 2, unit: 'em' }

Observe como os tipos de CSS são usados ​​ao atribuir um novo valor. O uso dessa sintaxe evita os problemas associados aos tipos e o código resultante se torna mais seguro.

A API em consideração inclui não apenas get e set, mas também outros métodos, por exemplo:

  • limpar: remove todos os estilos embutidos
  • delete: exclui uma propriedade CSS específica e seu valor
  • has: retorna verdadeiro / falso, dependendo da disponibilidade da propriedade especificada
  • anexar: adiciona um valor extra a uma propriedade que suporta vários valores

Detecção


let selectedElement = document.getElementById('example')

if(selectedElement.attributeStyleMap){
  // ...
}

if(selectedElement.computedStyleMap){
  // ...
}

Status da especificação


Rascunho de trabalho : publicado para discussão da comunidade.

Apoio, suporte


cromadaBeiraÓperaRaposa de fogoSafári
Apoiado porApoiado porApoiado porNão suportadoSuporte parcial

API de propriedades e valores personalizados


Essa API permite que os desenvolvedores estendam variáveis ​​de CSS definindo tipo, valor inicial e herança. Para definir uma propriedade customizada, é necessário registrá-la usando o método registerProperty. Este método determina como os navegadores devem aplicar a propriedade e manipular erros.

CSS.registerProperty({
  name: '--colorPrimary',
  syntax: '<color>',
  inherits: false,
  initialValue: 'blue',
})

Um objeto com as seguintes propriedades é passado para esse método como argumento:

  • nome: nome da propriedade customizada
  • sintaxe: instrução para análise. Os valores predefinidos são: <color>, <integer>, <number>, <length>, <percentage> etc.
  • initialValue: valor padrão (antes da substituição, bem como quando ocorrem erros)

No exemplo acima, uma propriedade customizada do tipo <color> foi definida. Esta propriedade será usada para determinar o gradiente. CSS regular não suporta transições de gradiente. Observe como a propriedade customizada será usada para definir a transição.

.gradient-box {
  background: linear-gradient(45deg, rgba(255, 255, 255, 1) 0% var(--colorPrimary) 60%);
  transition: --colorPrimary 0.5s ease;
  ...
}

.gradient-box:hover {
  --colorPrimary: red;
  ...
}

O navegador não sabe como fazer a transição para o gradiente, mas sabe como fazer para a cor. Por isso, definimos o tipo de propriedade como <color>. Em um navegador que suporta o Houdini, uma alteração de gradiente ocorrerá quando você passar o mouse. A posição do gradiente, medida em porcentagem, também pode ser alterada usando a propriedade customizada CSS (registrada como <percentage>).

Provavelmente, no futuro, será possível registrar uma propriedade customizada diretamente no CSS.

@property --colorPrimary {
  syntax: '<color>';
  inherits: false;
  initial-value: blue;
}

Exemplo


Este exemplo simples mostra como você pode alterar a cor e os pontos de controle de um gradiente usando propriedades CSS personalizadas. Um código de exemplo pode ser encontrado aqui .



Detecção


if(CSS.registeredProperty) {
  // ...
}

Status da especificação


Rascunho de trabalho : publicado para discussão da comunidade.

Apoio, suporte


cromadaBeiraÓperaRaposa de fogoSafári
Apoiado porApoiado porApoiado porNão suportadoNão suportado

API de métricas de fonte


Essa API está em um estágio inicial de desenvolvimento, portanto, a especificação pode mudar drasticamente no futuro. Atualmente, ele fornece métodos para medir o tamanho dos elementos de texto exibidos na tela, permitindo que os desenvolvedores influenciem a renderização dos caracteres. Os recursos CSS existentes não permitem que você trabalhe com esses valores ou dificulte esse trabalho. Um exemplo do uso dessa API é o truncamento dinâmico de texto em várias linhas.

Status da especificação


Coleção de idéias : rascunho não publicado.

Navegadores não são suportados.

Vorkleta


Antes de passar para as seguintes APIs, você precisa entender o conceito de worklets. Vorklets são scripts que são executados durante a renderização e são independentes do código JS subjacente. Eles expandem os recursos do mecanismo de renderização, são projetados para execução paralela (2 ou mais instâncias), não bloqueiam o encadeamento principal, têm acesso limitado ao escopo global e são chamados pelo mecanismo conforme necessário. Os vorklets podem ser executados apenas por HTTPS (em produção) ou por host local (para fins de desenvolvimento e teste).

Houdini inclui os seguintes worklets que estendem o mecanismo de renderização do navegador:

  • Paint Worklet - API do Paint
  • Worklet de animação - API de animação
  • Worklet de layout - API de layout

API do Paint


A API Paint permite que os desenvolvedores usem funções JS para pintar o plano de fundo, bordas ou conteúdo de um elemento usando o Contexto de renderização 2D, que é um subconjunto da API Canvas do HTML5. A API do Paint usa o Paint Worklet para desenhar uma imagem que depende de alterações no CSS (como alterações nas variáveis ​​do CSS). Aqueles familiarizados com a API do Canvas se sentirão em casa com a API do Paint.

A criação de um Worklet de pintura consiste em várias etapas:

  1. Escreva e registre um worklet usando a função registerPaint
  2. Chame um worklet em HTML ou JS usando CSS.paintWorklet.addModule
  3. Use o método paint () em CSS junto com o nome do vorklet e os argumentos passados

Vejamos a função registerPaint, usada para registrar e determinar a funcionalidade do Paint Worklet.

registerPaint('paintWorkletExample', class {
  static get inputProperties() { return ['--myVariable']; }
  static get inputArguments() { return ['<color>']; }
  static get contextOptions() { return {alpha: true} }

  paint(ctx, size, properties, args) {
    // ...
  }
})

A função registerPaint consiste nas seguintes partes:

  • inputProperties: Uma matriz de propriedades CSS customizadas que o worklet está observando. Essa matriz representa as dependências do worklet.
  • inputArguments: uma matriz de argumentos que podem ser passados ​​de uma função para CSS externo
  • contextOptions: transparência de cores. Se for falso, todas as cores serão completamente opacas
  • paint: a função principal usando os seguintes argumentos:

    • ctx: um contexto de desenho 2D quase idêntico ao contexto de tela 2D da API do Canvas
    • tamanho: um objeto com a largura e altura do elemento. Os valores dependem do processo de renderização do layout. O tamanho da tela é igual ao tamanho real do item
    • properties: variáveis ​​contidas em inputProperties
    • args: uma matriz de argumentos passados ​​para a função paint

Após registrar o worklet, ele deve ser chamado em HTML, indicando o caminho para o arquivo.

CSS.paintWorklet.addModule('path/to/worklet/file.js')

Os vorklets podem ser adicionados a partir de qualquer fonte externa (por exemplo, da CDN), o que os torna modulares e reutilizáveis.

CSS.paintWorklet.addModule('https://url/to/worklet/file.js')

Após chamar o worklet, ele pode ser usado em CSS usando a função "paint". Essa função, como o primeiro parâmetro, leva o nome registrado do vorklet e todos os argumentos especificados em inputArguments. A partir desse momento, o navegador sabe quando chamar o worklet e quais ações do usuário levam à alteração de certos valores das propriedades customizadas do CSS.

.example-element {
  // paintWorkletExample -  
  // blue - ,  
  background: paint(paintWorkletExample, blue);
}

Exemplo


O exemplo a seguir demonstra o uso da API do Paint, bem como a modularidade e a reutilização de worklets. Ele usa o worklet ripple do repositório do Google Chrome Labs . Veja o código de exemplo aqui .



Detecção


if(‘paintWorklet’ in CSS){
  // …
}

@supports(background: paint(paintWorkletExample)){
  // …
}

Status da especificação


Recomendação : Um esboço de trabalho estável, pronto para uso.

Apoio, suporte


cromadaBeiraÓperaRaposa de fogoSafári
Apoiado porApoiado porApoiado porNão suportadoNão suportado

API de animação


Essa API estende as animações da web através do processamento de vários eventos (rolagem, pairar, clicar etc.) e melhora o desempenho ativando animações em seu próprio fluxo através do worklet de animação.

Como qualquer outro worklet, uma animação de treino deve primeiro ser registrada.

registerAnimation(‘animationWorkletExample’, class {
  constructor(options){
    // …
  }
  animate(currentTime, effect){
    // …
  }
})

Esta classe inclui duas funções:

  • construtor: chamado quando uma nova instância é criada. Usado para configuração geral.
  • animar: A principal função que contém a lógica da animação. Os seguintes parâmetros são aceitos:

    • currentTime: registros de data e hora em uma linha do tempo específica
    • effect: uma matriz de efeitos usados ​​na animação

Após o registro, o worklet é incluído no arquivo JS principal, a animação (elemento, quadros, configurações) é adicionada e anexada à linha do tempo. O conceito de marcas da linha do tempo e os fundamentos da animação na Web estão na próxima seção.

//   
await CSS.animationWorklet.addModule(‘path/to/worklet/file.js’)

//    
const elementExample = document.getElementById(‘element-example’)

//  
const effectExample = new KeyframeEffect(
  elementExample, //  
  [ // … ], //  
  { // … } //  - , ,    ..
)

//        
new WorkletAnimation(
  ‘animationWorkletExample’ //  
  effectExample, //  
  document.timeline, //  
  {},  //   
).play()

Carimbos de hora


As animações da Web são baseadas em registros de data e hora - marcos para efeitos na linha do tempo da animação. Por exemplo, analisamos uma animação linear de repetição, que consiste em três quadros (início, meio, fim), inicia 1 segundo após a página estar totalmente carregada (atraso) e dura 4 segundos.

Os carimbos de hora dos efeitos terão a seguinte aparência (para uma animação com duração de 4 segundos e sem demora):

Carimbos de horaQuadro de animação
0msO primeiro quadro - o início da animação
2000msO segundo quadro - o meio da animação
4000msÚltimo quadro - final da animação ou redefinir para o primeiro quadro

effect.localTime com um valor de 3000ms (dado um atraso de 1000ms) vincula a animação ao quadro médio na linha do tempo (atraso de 1000ms + quadro médio de 2000ms). O mesmo efeito será alcançado ao definir 7000ms e 11000ms, pois a animação se repete a cada 4000ms.

animate(currentTime, effect){
  effect.localTime = 3000 // 1000ms  + 2000ms  
}

Com um valor constante de effect.localTime, a animação será bloqueada em um quadro específico. Portanto, o valor de effect.localTime deve mudar. Este valor deve ser uma função vinculada a currentTime ou outra variável.

Aqui está a aparência do código de animação linear:

animate(currentTime, effect){
  effect.localTime = currentTime // y = x  
}

Linha do tempo (document.timeline)Carimbo de horaQuadro, Armação
startTime + 0ms (tempo decorrido)startTime + 0msO primeiro
startTime + 1000ms (tempo decorrido)startTime + 1000ms (atraso) + 0msO primeiro
startTime + 3000ms (tempo decorrido)startTime + 1000ms (atraso) + 2000msMeio
startTime + 5000ms (tempo decorrido)startTime + 1000ms (atraso) + 4000msOs últimos, Primeiro
startTime + 7000ms (tempo decorrido)startTime + 1000ms (atraso) + 6000msMeio
startTime + 9000ms (tempo decorrido)startTime + 1000ms (atraso) + 8000msOs últimos, Primeiro

Os carimbos de hora não se limitam a 1: 1. A API de animação permite que os desenvolvedores manipulem marcas através da função animar, usando funções JS padrão para criar efeitos complexos. As animações também podem variar a cada iteração (com animações repetíveis).

A animação pode ser vinculada não apenas ao carregamento do documento, mas também às ações do usuário. Uma ação do usuário, como rolar uma página, pode ser usada na animação por meio de um objeto ScrollTimeline. Por exemplo, uma animação pode começar quando você rola 200 pixels e termina quando você rola 800 pixels.

const scrollTimelineExample = new ScrollTimeline({
  scrollSource: scrollElement, // ,     
  orientation: ‘vertical’, //  
  startScrollOffset: ‘200px’, //  
  endScrollOffset: ‘800px’, //  
  timeRange: 1200, //  
  fill: ‘forwards’ //  
})

A animação se adapta automaticamente à velocidade de rolagem, mantendo-se suave e responsiva. Como a animação é executada em seu próprio fluxo e se conecta ao mecanismo de renderização do navegador, ela inicia sem problemas e não afeta o desempenho.

Exemplo


O exemplo a seguir demonstra animação não linear. Ele usa a função Gaussiana com a mesma rotação de tempo para lá e para trás. Veja o código de exemplo aqui .



Detecção


if(CSS.animationWorklet){
  // …
}

Status da especificação


Primeiro esboço de trabalho público : pronto para discussão da comunidade, sujeito a alterações no futuro.

Apoio, suporte


cromadaBeiraÓperaRaposa de fogoSafári
Suporte parcialSuporte parcialSuporte parcialNão suportadoNão suportado

API de layout


A API de layout permite que os desenvolvedores estendam o processo de renderização de layout definindo novos módulos para uso na propriedade "display" do CSS. Essa API apresenta novos conceitos, é muito complexa e oferece um grande número de configurações para o desenvolvimento de algoritmos personalizados para trabalhar com o layout da página.

Primeiramente, um worklet precisa ser registrado.

registerLayout(‘exampleLayout’, class{
  static get inputProperties() { return [‘--example-variable’] }

  static get childrenInputProperties() { return [‘--exampleChildVariable’] }

  static get layoutOptions(){
    return {
      childDisplay: ‘normal’,
      sizing: ‘block-like’
    }
  }

  intrinsicSizes(children, edges, styleMap){
    // …
  }

  layout(children, edges, constraints, styleMap, breakToken){
    // …
  }
})

O registro de um worklet inclui os seguintes métodos:

  • inputProperties: uma matriz de propriedades CSS personalizadas que o worklet está observando e que pertencem ao pai, o elemento que causou a renderização do layout. Essa matriz representa as dependências do layout
  • childrenInputProperties: uma matriz de propriedades CSS personalizadas que são assistidas pelo widget e que pertencem ao descendente
  • layoutOptions: define as seguintes propriedades de layout:

    • childDisplay: os valores predefinidos são block e normal. Define como o item é exibido (bloco ou linha)
    • dimensionamento: os valores predefinidos são do tipo bloco e manuais. Determina a necessidade de cálculo preliminar dos tamanhos dos elementos (se não especificado)
  • intrinsicSizes: define como o contêiner ou seu conteúdo é exibido no contexto do layout:

    • filhos: filho do elemento que causou o layout da página
    • bordas: bordas do contêiner
    • styleMap: modelo de objeto de estilo de contêiner digitado
  • layout: a principal função para trabalhar com o layout:

    • children: elemento filho
    • bordas: bordas
    • restrições: restrições impostas pelo layout pai
    • styleMap: modelo de objeto de estilo de contêiner digitado
    • breakToken: ponto de interrupção para paginação ou divisão do layout de impressão

Em seguida, o worklet é adicionado ao arquivo HTML ou JS.

CSS.layoutWorklet.addModule(‘path/to/worklet/file.js’)

Criamos um link para o worklet no arquivo com estilos.

.example-element {
  display: layout(exampleLayout)
}

Como a API de layout funciona com o layout


No exemplo anterior, definimos exampleLayout.

Um elemento .example é chamado de layout pai, incluindo recuos, bordas e controles deslizantes. Um layout pai consiste em elementos filhos chamados layouts atuais. Os layouts atuais são elementos de destino cujos layouts são "personalizados" usando a API de layout. Por exemplo, ao usar "display: flex", os descendentes do elemento são reorganizados de acordo com o layout flexível. Isso é semelhante à operação da API de layout.

Cada layout atual consiste em layouts filhos que contêm algoritmos para renderizar o layout do elemento filho - LayoutChild (incluindo as pseudo-classes :: antes e :: depois). LayoutChild - um contêiner gerado por ferramentas CSS contendo dados sobre estilos (sem dados sobre o layout). Os elementos LayoutChild são criados automaticamente pelo navegador na fase de aplicação dos estilos. Um layout filho pode criar um fragmento contendo instruções para renderizar o layout.

Exemplo


Este exemplo também usa o repositório do Google Chrome Labs , mas o texto é substituído por imagens. Veja o código de exemplo aqui .



Detecção


if(CSS.layoutWorklet){
  // …
}

Status da especificação


Primeiro esboço de trabalho público : pronto para discussão da comunidade, sujeito a alterações no futuro.

Apoio, suporte


cromadaBeiraÓperaRaposa de fogoSafári
Suporte parcialSuporte parcialSuporte parcialNão suportadoNão suportado

Houdini e melhoria progressiva


Apesar de o Houdini atualmente não possuir suporte ideal para o navegador, ele pode ser usado para melhorias progressivas. Se você não estiver familiarizado com o conceito de melhoria progressiva, recomendo que você dê uma olhada neste artigo . Ao usar o Houdini, o seguinte deve ser considerado:

Sempre identifique o suporte para evitar erros.


Cada API e Worklet da Houdini tem uma maneira fácil de verificar a acessibilidade. Isso evita os problemas do uso do Houdini em navegadores que ainda não oferecem suporte a essa tecnologia.

Use o Houdini para melhorar a exibição e a visualização.


Os usuários que usam navegadores que não oferecem suporte ao Houdini devem ter acesso ao conteúdo e à funcionalidade básica do site. A experiência do usuário e a exibição do conteúdo não devem depender do Houdini.

Use CSS padrão como alternativa


Por exemplo, propriedades CSS personalizadas podem ser usadas como uma alternativa às API de propriedades e valores customizados.

Concentre-se no desenvolvimento de aplicativos produtivos e confiáveis, usando o Houdini apenas para fins decorativos como uma melhoria progressiva.

Conclusão


Houdini permite que os desenvolvedores usem o código JS para trabalhar com estilos no processo de renderização, aumentando o desempenho e a estabilidade do aplicativo. A capacidade de incorporar código no processo de renderização permite criar polifiles CSS fáceis de compartilhar com outras pessoas, aplicar e possivelmente até incluir na especificação. Além disso, o Houdini permite que desenvolvedores e designers sejam menos dependentes das restrições de CSS ao trabalhar com estilos, layouts e animações.

Houdini pode ser usado agora, mas apenas como uma melhoria progressiva. Isso permitirá que navegadores que não suportam o Houdini exibam páginas sem erros, proporcionando uma experiência ideal ao usuário.

Mal posso esperar até que a comunidade de desenvolvedores possa aproveitar totalmente os recursos do Houdini. Aqui estão alguns exemplos:

Expiração de CSS Houdini:
uma introdução interativa ao CSS Houdini Houdini
Exemplos do Google Chrome Labs

Referências


Rascunhos do W3C da especificação Houdini
Houdini (Chrome Dev Summit 2018)
Worklet de animação - Google Developers

Agradecemos sua atenção.

All Articles