Uma visão prática do CSS Houdini

Publicados: 2022-03-10
Resumo rápido ↬ Houdini, um termo abrangente para a coleção de APIs de navegadores, visa trazer melhorias significativas para o processo de desenvolvimento web e o desenvolvimento de padrões CSS em geral. Os desenvolvedores frontend poderão estender o CSS com novos recursos usando JavaScript, conectar-se ao mecanismo de renderização CSS e informar ao navegador como aplicar CSS durante um processo de renderização. O suporte ao navegador do Houdini está melhorando e algumas APIs estão disponíveis para uso hoje, então é um bom momento para se familiarizar com elas e experimentar. Vamos dar uma olhada em cada parte do Houdini, seu suporte atual ao navegador e ver como eles podem ser usados ​​hoje usando aprimoramento progressivo.

Leva muito tempo para um novo recurso CSS ou melhoria progredir de um rascunho inicial para um recurso CSS totalmente compatível e estável que os desenvolvedores podem usar. Polyfills baseados em JavaScript podem ser usados ​​como um substituto para a falta de suporte do navegador para usar novos recursos CSS antes de serem implementados oficialmente. Mas eles são falhos na maioria dos casos. Por exemplo, scrollsnap-polyfill é um dos vários polyfills que podem ser usados ​​para corrigir inconsistências de suporte do navegador para a especificação CSS Scroll Snap. Mas mesmo essa solução tem algumas limitações, bugs e inconsistências.

A desvantagem potencial de usar polyfills é que eles podem ter um impacto negativo no desempenho e são difíceis de implementar corretamente. Essa desvantagem está relacionada ao DOM e CSSOM do navegador. Browser cria um DOM (Document Object Model) a partir da marcação HTML e, da mesma forma, criou o CSSOM (CSS Object Model) a partir da marcação CSS. Essas duas árvores de objetos são independentes uma da outra. JavaScript funciona no DOM e tem acesso muito limitado ao CSSOM.

As soluções JavaScript Polyfill são executadas somente após a conclusão do ciclo de renderização inicial, ou seja, quando o DOM e o CSSOM foram criados e o documento terminou de carregar. Depois que o Polyfill faz alterações nos estilos no DOM (ao inline-los), ele faz com que o processo de renderização seja executado novamente e toda a página seja renderizada novamente. O impacto negativo no desempenho fica ainda mais aparente se eles contarem com o método requestAnimationFrame ou dependerem de interações do usuário, como eventos de rolagem.

Outro obstáculo no desenvolvimento web são as várias restrições impostas pelos padrões CSS . Por exemplo, há apenas um número limitado de propriedades CSS que podem ser animadas nativamente. CSS sabe como animar cores nativamente, mas não sabe como animar gradientes. Sempre houve a necessidade de inovar e criar experiências na web impressionantes, ultrapassando os limites, apesar das limitações tecnológicas. É por isso que os desenvolvedores geralmente tendem a usar soluções alternativas ou JavaScript menos do que ideais para implementar estilos e efeitos mais avançados que atualmente não são suportados pelo CSS, como layout de alvenaria, efeitos 3D avançados, animação avançada, tipografia fluida, gradientes animados, elementos de select com estilo, etc.

Parece impossível que as especificações CSS acompanhem as várias demandas de recursos da indústria, como mais controle sobre animações, truncamento de texto aprimorado, melhor opção de estilo para elementos de input e select , mais opções de display , mais opções de filter etc.

Qual poderia ser a solução potencial? Dê aos desenvolvedores uma maneira nativa de estender CSS usando várias APIs . Neste artigo, veremos como os desenvolvedores front-end podem fazer isso usando APIs Houdini, JavaScript e CSS. Em cada seção, examinaremos cada API individualmente, verificaremos o suporte do navegador e o status atual da especificação e veremos como elas podem ser implementadas hoje usando o aprimoramento progressivo.

Mais depois do salto! Continue lendo abaixo ↓

O que é Houdini?

Houdini, um termo abrangente para a coleção de APIs de navegadores, visa trazer melhorias significativas para o processo de desenvolvimento web e o desenvolvimento de padrões CSS em geral. Os desenvolvedores poderão estender o CSS com novos recursos usando JavaScript, conectar-se ao mecanismo de renderização de CSS e informar ao navegador como aplicar CSS durante um processo de renderização. Isso resultará em desempenho e estabilidade significativamente melhores do que usar polyfills regulares.

A especificação Houdini consiste em dois grupos de APIs - APIs de alto nível e APIs de baixo nível .

As APIs de alto nível estão intimamente relacionadas ao processo de renderização do navegador (estilo → layout → pintura → composição). Isso inclui:

  • API de pintura
    Um ponto de extensão para a etapa de renderização de pintura do navegador onde as propriedades visuais (cor, plano de fundo, borda etc.) são determinadas.
  • API de layout
    Um ponto de extensão para a etapa de renderização de layout do navegador onde são determinadas as dimensões, a posição e o alinhamento do elemento.
  • API de animação
    Um ponto de extensão para a etapa de renderização composta do navegador, onde as camadas são desenhadas na tela e animadas.

As APIs de baixo nível formam uma base para APIs de alto nível. Isso inclui:

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

Algumas APIs Houdini já estão disponíveis para uso em alguns navegadores com outras APIs para seguir o exemplo quando estiverem prontas para lançamento.

O Futuro do CSS

Ao contrário das especificações de recursos CSS regulares que foram introduzidas até agora, o Houdini se destaca por permitir que os desenvolvedores estendam o CSS de uma maneira mais nativa. Isso significa que as especificações CSS vão parar de evoluir e nenhuma nova implementação oficial de recursos CSS será lançada? Bem, esse não é o caso. O objetivo de Houdini é ajudar o processo de desenvolvimento de recursos CSS, permitindo que os desenvolvedores criem protótipos funcionais que podem ser facilmente padronizados.

Além disso, os desenvolvedores poderão compartilhar os Worklets CSS de código aberto com mais facilidade e com menos necessidade de correções de bugs específicas do navegador.

API de modelo de objeto digitado

Antes da introdução do Houdini, a única maneira de JavaScript interagir com CSS era analisando o CSS representado como valores de string e modificando-os. Analisar e substituir estilos manualmente pode ser difícil e propenso a erros devido ao tipo de valor que precisa ser alterado para frente e para trás e a unidade de valor precisa ser anexada manualmente ao atribuir um novo valor.

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

A API Typed Object Model (Typed OM) adiciona mais significado semântico aos valores CSS, expondo-os como objetos JavaScript tipados. Melhora significativamente o código relacionado e o torna mais eficiente, estável e de fácil manutenção. Os valores CSS são representados pela interface CSSUnitValue que consiste em um valor e uma propriedade de unidade.

 { value: 20, unit: "px" }

Essa nova interface pode ser usada com as seguintes novas propriedades:

  • computedStyleMap() : para analisar estilos computados (não embutidos). Este é um método do elemento selecionado que precisa ser invocado antes de analisar ou usar outros métodos.
  • attributeStyleMap : para analisar e modificar estilos embutidos. Esta é uma propriedade que está disponível em um elemento selecionado.
 // Get computed styles from stylesheet (initial value) selectedElement.computedStyleMap().get("font-size"); // { value: 20, unit: "px"} // Set inline styles selectedElement.attributeStyleMap.set("font-size", CSS.em(2)); // Sets inline style selectedElement.attributeStyleMap.set("color", "blue"); // Sets inline style // Computed style remains the same (initial value) selectedElement.computedStyleMap().get("font-size"); // { value: 20, unit: "px"} // Get new inline style selectedElement.attributeStyleMap.get("font-size"); // { value: 2, unit: "em"}

Observe como tipos específicos de CSS estão sendo usados ​​ao definir um novo valor numérico. Ao usar essa sintaxe, muitos possíveis problemas relacionados ao tipo podem ser evitados e o código resultante é mais confiável e livre de bugs.

Os métodos get e set são apenas um pequeno subconjunto de todos os métodos disponíveis definidos pela API Typed OM. Alguns deles incluem:

  • clear : remove todos os estilos embutidos
  • delete : remove uma propriedade CSS especificada e seu valor dos estilos embutidos
  • has : retorna um booleano se uma propriedade CSS especificada estiver definida
  • append : adiciona um valor adicional a uma propriedade que suporta vários valores
  • etc.

Detecção de recursos

 var selectedElement = document.getElementById("example"); if(selectedElement.attributeStyleMap) { /* ... */ } if(selectedElement.computedStyleMap) { /* ... */ }

Status da Especificação W3C

  • Rascunho de trabalho: publicado para revisão pela comunidade

Suporte ao navegador

Google Chrome Microsoft borda Navegador Opera Raposa de fogo Safári
Compatível Compatível Compatível Não suportado Suporte parcial (*)

* compatível com “Recursos da Plataforma Web Experimental” ou outro sinalizador de recurso ativado.

Fonte de dados: Houdini já está pronto?

API de propriedades e valores personalizados

A API de propriedades e valores CSS permite que os desenvolvedores estendam variáveis ​​CSS adicionando um tipo, valor inicial e definam herança. Os desenvolvedores podem definir propriedades personalizadas de CSS registrando-as usando o método registerProperty , que informa aos navegadores como fazer a transição e lidar com o fallback em caso de erro.

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

Este método aceita um argumento de entrada que é um objeto com as seguintes propriedades:

  • name : o nome da propriedade customizada
  • syntax : informa ao navegador como analisar uma propriedade personalizada. Esses são valores predefinidos como <color> , <integer> , <number> , <length> , <percentage> , etc.
  • inherits : informa ao navegador se a propriedade customizada herda o valor de seu pai.
  • initialValue : informa o valor inicial que é usado até que seja substituído e isso é usado como fallback em caso de erro.

No exemplo a seguir, a propriedade personalizada do tipo <color> está sendo definida. Essa propriedade customizada será usada na transição de gradiente. Você pode estar pensando que o CSS atual não suporta transições para gradientes de fundo e você estaria correto. Observe como a própria propriedade personalizada está sendo usada em transition , em vez de uma propriedade de plano de background que seria usada para transições regulares background-color .

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

O navegador não sabe como lidar com a transição de gradiente, mas sabe como lidar com as transições de cores porque a propriedade personalizada é especificada como tipo <color> . Em um navegador compatível com Houdini, uma transição de gradiente ocorrerá quando o elemento estiver sendo posicionado sobre ele. A porcentagem da posição do gradiente também pode ser substituída pela propriedade personalizada CSS (registrada como tipo <percentage> ) e adicionada a uma transição da mesma maneira que no exemplo.

Se registerProperty for removido e uma propriedade customizada CSS regular for registrada em um seletor :root , a transição de gradiente não funcionará. É necessário que registerProperty seja usado para que o navegador saiba que deve tratá-lo como cor.

Na futura implementação desta API, seria possível registrar uma propriedade customizada diretamente no CSS.

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

Exemplo

Este exemplo simples mostra a cor do gradiente e a transição de posição no evento hover usando propriedades personalizadas CSS registradas para cor e posição, respectivamente. O código-fonte completo está disponível no repositório de exemplo.

Cor e posição de gradiente animadas usando a API de propriedades e valores personalizados. Atraso para cada propriedade adicionada para efeito na propriedade de transição CSS. (Visualização grande)

Detecção de recursos

 if (CSS.registerProperty) { /* ... */ }

Status da Especificação W3C

  • Rascunho de trabalho: publicado para revisão pela comunidade

Suporte ao navegador

Google Chrome Microsoft borda Navegador Opera Raposa de fogo Safári
Compatível Compatível Compatível Não suportado Não suportado

Fonte de dados: Houdini já está pronto?

API de métricas de fonte

A API Font Metrics ainda está em um estágio muito inicial de desenvolvimento, portanto, sua especificação pode mudar no futuro. Em seu rascunho atual, a API Font Metrics fornecerá métodos para medir as dimensões dos elementos de texto que estão sendo renderizados na tela para permitir que os desenvolvedores afetem como os elementos de texto estão sendo renderizados na tela. Esses valores são difíceis ou impossíveis de medir com os recursos atuais, portanto, essa API permitirá que os desenvolvedores criem recursos CSS relacionados a texto e fontes com mais facilidade. O truncamento de texto dinâmico de várias linhas é um exemplo de um desses recursos.

Status da Especificação W3C

  • Coleção de Ideias: nenhum projeto de especificação enviado no momento

Suporte ao navegador

Google Chrome Microsoft borda Navegador Opera Raposa de fogo Safári
Não suportado Não suportado Não suportado Não suportado Não suportado

Fonte de dados: Houdini já está pronto?

Worklets

Antes de passar para as outras APIs, é importante explicar o conceito de Worklets. Worklets são scripts executados durante a renderização e são independentes do ambiente JavaScript principal. Eles são um ponto de extensão para mecanismos de renderização. Eles são projetados para paralelismo (com 2 ou mais instâncias) e agnósticos de thread, têm acesso reduzido ao escopo global e são chamados pelo mecanismo de renderização quando necessário. Worklets podem ser executados apenas em HTTPS (em ambiente de produção) ou em localhost (para fins de desenvolvimento).

Houdini apresenta os seguintes Worklets para estender o mecanismo de renderização do navegador:

  • Worklet de pintura - API de pintura
  • Worklet de animação - API de animação
  • Worklet de layout - API de layout

API de pintura

A API do Paint permite que os desenvolvedores usem funções JavaScript para desenhar diretamente no plano de fundo, na borda ou no conteúdo de um elemento usando o Contexto de renderização 2D, que é um subconjunto da API HTML5 Canvas. A API do Paint usa o Paint Worklet para desenhar uma imagem que responde dinamicamente às alterações no CSS (alterações nas variáveis ​​do CSS, por exemplo). Qualquer pessoa familiarizada com a API do Canvas se sentirá em casa com a API do Paint da Houdini.

Existem várias etapas necessárias para definir um Paint Worklet:

  1. Escreva e registre um Paint Worklet usando a função registerPaint
  2. Chame o Worklet no arquivo HTML ou no arquivo JavaScript principal usando a função CSS.paintWorklet.addModule
  3. Use a função paint() em CSS com um nome de Worklet e argumentos de entrada opcionais.

Vamos dar uma olhada na função registerPaint que é usada para registrar um Paint Worklet e definir sua funcionalidade.

 registerPaint("paintWorketExample", 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 em várias partes:

  • inputProperties :
    Uma matriz de propriedades customizadas de CSS que o Worklet acompanhará. Essa matriz representa as dependências de um worklet de pintura.
  • inputArguments :
    Uma matriz de argumentos de entrada que podem ser passados ​​da função paint de dentro do CSS.
  • contextOptions : permite ou não a opacidade das cores. Se definido como false , todas as cores serão exibidas com total opacidade.
  • paint : a função principal que fornece os seguintes argumentos:
    • ctx : contexto de desenho 2D, quase idêntico ao contexto de desenho 2D da API Canvas.
    • size : um objeto contendo a largura e a altura do elemento. Os valores são determinados pelo processo de renderização do layout. O tamanho da tela é o mesmo que o tamanho real do elemento.
    • properties : variáveis ​​de entrada definidas em inputProperties
    • args : uma matriz de argumentos de entrada passados ​​na função paint em CSS

Após o Worklet ter sido registrado, ele precisa ser invocado no arquivo HTML simplesmente fornecendo um caminho para o arquivo.

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

Qualquer Worklet também pode ser adicionado a partir de uma URL externa (de uma Content Delivery Network, por exemplo) o que os torna modulares e reutilizáveis.

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

Após a chamada do Worklet, ele pode ser usado dentro do CSS usando a função paint . Esta função aceita o nome registrado do Worklet como um primeiro argumento de entrada e cada argumento de entrada que o segue é um argumento personalizado que pode ser passado para um Worklet (definido dentro de inputArguments do inputArguments ). A partir desse ponto, o navegador determina quando chamar o Worklet e quais ações do usuário e valores de propriedades customizadas CSS mudam para responder.

 .exampleElement { /* paintWorkletExample - name of the worklet blue - argument passed to a Worklet */ background-image: paint(paintWorketExample, blue); }

Exemplo

O exemplo a seguir mostra a API do Paint e a reutilização e modularidade geral do Worklet. Ele está usando o Ripple Worklet diretamente do repositório do Google Chrome Labs e é executado em um elemento diferente com estilos diferentes. O código-fonte completo está disponível no repositório de exemplo.

Exemplo de efeito de ondulação (usa Ripple Worklet do Google Chrome Labs) (visualização grande)

Detecção de recursos

 if ("paintWorklet" in CSS) { /* ... */ } @supports(background:paint(paintWorketExample)){ /* ... */ }

Status da Especificação W3C

  • Recomendação do candidato: projeto de trabalho estável pronto para implementação

Suporte ao navegador

Google Chrome Microsoft borda Navegador Opera Raposa de fogo Safári
Compatível Compatível Compatível Não suportado Não suportado

Fonte de dados: Houdini já está pronto?

API de animação

A API de animação estende as animações da Web com opções para ouvir vários eventos (rolagem, foco, clique etc.) e melhora o desempenho executando animações em seu próprio encadeamento dedicado usando um Worklet de animação. Ele permite que a ação do usuário controle o fluxo de animação que é executado de maneira performática e sem bloqueio.

Como qualquer Worklet, o Worklet de Animação precisa ser registrado primeiro.

 registerAnimator("animationWorkletExample", class { constructor(options) { /* ... */ } animate(currentTime, effect) { /* ... */ } });

Esta classe consiste em duas funções:

  • constructor : chamado quando uma nova instância é criada. Usado para configuração geral.
  • animate : a função principal que contém a lógica da animação. Fornece os seguintes argumentos de entrada:
    • currentTime : o valor de tempo atual da linha do tempo definida
    • effect : uma matriz de efeitos que esta animação usa

Após o Worklet de Animação ter sido registrado, ele precisa ser incluído no arquivo JavaScript principal , a animação (elemento, quadros-chave, opções) precisa ser definida e a animação é instanciada com a linha do tempo selecionada. Conceitos de linha do tempo e noções básicas de animação da web serão explicados na próxima seção.

 /* Include Animation Worklet */ await CSS.animationWorklet.addModule("path/to/worklet/file.js");; /* Select element that's going to be animated */ const elementExample = document.getElementById("elementExample"); /* Define animation (effect) */ const effectExample = new KeyframeEffect( elementExample, /* Selected element that's going to be animated */ [ /* ... */ ], /* Animation keyframes */ { /* ... */ }, /* Animation options - duration, delay, iterations, etc. */ ); /* Create new WorkletAnimation instance and run it */ new WorkletAnimation( "animationWorkletExample" /* Worklet name */ effectExample, /* Animation (effect) timeline */ document.timeline, /* Input timeline */ {}, /* Options passed to constructor */ ).play(); /* Play animation */

Mapeamento da linha do tempo

A animação da Web é baseada em linhas de tempo e mapeamento da hora atual para uma linha de tempo da hora local de um efeito . Por exemplo, vamos dar uma olhada em uma animação linear repetida com 3 quadros-chave (início, meio, último) que é executada 1 segundo após o carregamento de uma página (atraso) e com duração de 4 segundos.

A linha do tempo do efeito do exemplo ficaria assim (com a duração de 4 segundos sem atraso):

Linha do tempo do efeito (duração de 4s) Quadro-chave
0ms Primeiro quadro-chave - a animação começa
2.000 ms Quadro-chave intermediário - animação em andamento
4000ms Último quadro-chave - a animação termina ou é redefinida para o primeiro quadro-chave

Para entender melhor effect.localTime , definindo seu valor para 3000ms (levando em conta o atraso de 1000ms), a animação resultante será bloqueada para um quadro-chave intermediário na linha de tempo do efeito (atraso de 1.000ms + 2.000ms para um quadro-chave intermediário). O mesmo efeito acontecerá definindo o valor para 7000ms e 11000ms porque a animação se repete em intervalos de 4000ms (duração da animação).

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

Nenhuma animação acontece ao ter um valor effect.localTime constante porque a animação está bloqueada em um quadro-chave específico. Para animar adequadamente um elemento, seu effect.localTime precisa ser dinâmico. É necessário que o valor seja uma função que dependa do argumento de entrada currentTime ou alguma outra variável.

O código a seguir mostra uma representação funcional do mapeamento 1:1 (função linear) de uma linha do tempo para afetar a hora local.

 animate(currentTime, effect) { effect.localTime = currentTime; // y = x linear function }
Linha do tempo ( document.timeline ) Tempo local de efeito mapeado Quadro-chave
startTime + 0ms (tempo decorrido) startTime + 0ms Primeiro
startTime + 1000ms (tempo decorrido) startTime + 1000ms (atraso) + 0ms Primeiro
startTime + 3000ms (tempo decorrido) startTime + 1000ms (atraso) + 2000ms Meio
startTime + 5000ms (tempo decorrido) startTime + 1000ms (atraso) + 4000ms Os últimos, Primeiro
startTime + 7000ms (tempo decorrido) startTime + 1000ms (atraso) + 6000ms Meio
startTime + 9000ms (tempo decorrido) startTime + 1000ms (atraso) + 8000ms Os últimos, Primeiro

A linha do tempo não está restrita ao mapeamento 1:1 para a hora local do efeito. A API de animação permite que os desenvolvedores manipulem o mapeamento da linha do tempo na função de animate usando funções JavaScript padrão para criar linhas do tempo complexas. A animação também não precisa se comportar da mesma forma em cada iteração (se a animação for repetida).

A animação não precisa depender da linha do tempo do documento, que só começa a contar milissegundos a partir do momento em que é carregado. As ações do usuário, como eventos de rolagem, podem ser usadas como uma linha do tempo para animação usando um objeto ScrollTimeline . Por exemplo, uma animação pode começar quando um usuário rolar até 200 pixels e terminar quando um usuário rolar até 800 pixels em uma tela.

 const scrollTimelineExample = new ScrollTimeline({ scrollSource: scrollElement, /* DOM element whose scrolling action is being tracked */ orientation: "vertical", /* Scroll direction */ startScrollOffset: "200px", /* Beginning of the scroll timeline */ endScrollOffset: "800px", /* Ending of the scroll timeline */ timeRange: 1200, /* Time duration to be mapped to scroll values*/ fill: "forwards" /* Animation fill mode */ }); ...

A animação se adaptará automaticamente à velocidade de rolagem do usuário e permanecerá suave e responsiva. Como os Worklets de Animação estão sendo executados no encadeamento principal e conectados ao mecanismo de renderização de um navegador, a animação que depende da rolagem do usuário pode ser executada sem problemas e ter muito desempenho.

Exemplo

O exemplo a seguir mostra como uma implementação de linha do tempo não linear. Ele usa a função Gaussiana modificada e aplica a animação de translação e rotação com a mesma linha do tempo. O código-fonte completo está disponível no repositório de exemplo.

Animação criada com a API de animação que está usando o mapeamento de tempo da função gaussiana modificada (visualização grande)

Detecção de recursos

 if (CSS.animationWorklet) { /* ... */ }

Status da Especificação W3C

  • Primeiro rascunho de trabalho público: pronto para revisão da comunidade, propenso a alterações de especificação

Suporte ao navegador

Google Chrome Microsoft borda Navegador Opera Raposa de fogo Safári
Suporte parcial (*) Suporte parcial (*) Suporte parcial (*) Não suportado Não suportado

* compatível com o sinalizador "Recursos da plataforma da Web experimental" ativado.

Fonte de dados: Houdini já está pronto?

API de layout

A API de Layout permite que os desenvolvedores estendam o processo de renderização de layout do navegador definindo novos modos de layout que podem ser usados ​​na propriedade CSS de display . A API de layout apresenta novos conceitos, é muito complexa e oferece muitas opções para o desenvolvimento de algoritmos de layout personalizados.

Da mesma forma que outros Worklets, o Worklet de layout precisa ser registrado e definido primeiro.

 registerLayout('exampleLayout', class { static get inputProperties() { return ['--exampleVariable']; } 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 worklet contém os seguintes métodos:

  • inputProperties :
    Uma matriz de propriedades personalizadas CSS que o Worklet acompanhará e que pertence a um elemento Layout pai, ou seja, o elemento que chama esse layout. Essa matriz representa as dependências de um Worklet de Layout.
  • childrenInputProperties :
    Uma matriz de propriedades personalizadas CSS que o Worklet acompanhará e que pertencem a elementos filho de um elemento Layout pai, ou seja, os filhos dos elementos que definem esse layout.
  • layoutOptions : define as seguintes propriedades de layout:
    • childDisplay : pode ter um valor pré-definido de block ou normal . Determina se as caixas serão exibidas como blocos ou em linha.
    • sizing : pode ter um valor pré-definido de block-like ou manual . Ele diz ao navegador para pré-calcular o tamanho ou não pré-calcular (a menos que um tamanho seja definido explicitamente), respectivamente.
  • intrinsicSizes : define como uma caixa ou seu conteúdo se encaixa em um contexto de layout.
    • children : elementos filhos de um elemento Parent Layout, ou seja, os filhos do elemento que chama este layout.
    • edges : Layout Bordas de uma caixa
    • styleMap : estilos OM digitados de uma caixa
  • layout : a principal função que executa um layout.
    • children : elementos filhos de um elemento Parent Layout, ou seja, os filhos do elemento que chama este layout.
    • edges : Layout Bordas de uma caixa
    • constraints : restrições de um layout pai
    • styleMap : estilos OM digitados de uma caixa
    • breakToken : token de quebra usado para retomar um layout em caso de paginação ou impressão.

Como no caso de uma API do Paint, o mecanismo de renderização do navegador determina quando o Worklet do paint está sendo chamado. Ele só precisa ser adicionado a um arquivo HTML ou JavaScript principal.

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

E, finalmente, ele precisa ser referenciado em um arquivo CSS

 .exampleElement { display: layout(exampleLayout); }

Como a API de layout executa o layout

No exemplo anterior, exampleLayout foi definido usando a API Layout.

 .exampleElement { display: layout(exampleLayout); }

Este elemento é chamado de Layout Pai que é incluído com Layout Edges que consiste em preenchimentos, bordas e barras de rolagem. O Layout Pai consiste em elementos filho que são chamados de Layouts Atuais . Layouts atuais são os elementos de destino reais cujo layout pode ser personalizado usando a API de layout. Por exemplo, ao usar display: flex; em um elemento, seus filhos estão sendo reposicionados para formar o layout flexível. Isso é semelhante ao que está sendo feito com a API de layout.

Cada Layout Atual consiste em Layout Filho , que é um algoritmo de layout para o LayoutChild (elemento, ::before e ::after pseudo-elementos) e LayoutChild é uma caixa gerada por CSS que contém apenas dados de estilo (sem dados de layout). Os elementos LayoutChild são criados automaticamente pelo mecanismo de renderização do navegador na etapa de estilo. Layout Child pode gerar um Fragment que realmente executa ações de renderização de layout.

Exemplo

Da mesma forma que o exemplo da API do Paint, este exemplo está importando um Worklet de layout de alvenaria diretamente do repositório do Google Chrome Labs, mas neste exemplo, ele é usado com conteúdo de imagem em vez de texto. O código-fonte completo está disponível no repositório de exemplo.

Exemplo de layout de alvenaria (usa o Masonry Worklet do Google Chrome Labs (visualização grande)

Detecção de recursos

 if (CSS.layoutWorklet) { /* ... */ }

Status da Especificação W3C

  • Primeiro rascunho de trabalho público: pronto para revisão da comunidade, propenso a alterações de especificação

Suporte ao navegador

Google Chrome Microsoft borda Navegador Opera Raposa de fogo Safári
Suporte parcial (*) Suporte parcial (*) Suporte parcial (*) Não suportado Não suportado

* compatível com o sinalizador "Recursos da plataforma da Web experimental" ativado.

Fonte de dados: Houdini já está pronto?

Houdini e aprimoramento progressivo

Embora o CSS Houdini ainda não tenha um suporte ideal para navegadores, ele pode ser usado hoje com aprimoramento progressivo em mente. Se você não estiver familiarizado com o aprimoramento progressivo, vale a pena conferir este artigo útil que explica muito bem. Se você decidir implementar o Houdini em seu projeto hoje, há algumas coisas a serem lembradas:

  • Use a detecção de recursos para evitar erros.
    Cada API e Worklet Houdini oferece uma maneira simples de verificar se está disponível no navegador. Use a detecção de recursos para aplicar os aprimoramentos do Houdini apenas a navegadores que o suportem e evite erros.
  • Use-o apenas para apresentação e aprimoramento visual.
    Os usuários que estão navegando em um site em um navegador que ainda não suporta Houdini devem ter acesso ao conteúdo e à funcionalidade principal do site. A experiência do usuário e a apresentação do conteúdo não devem depender dos recursos do Houdini e devem ter um fallback confiável.
  • Faça uso de um fallback CSS padrão.
    Por exemplo, propriedades personalizadas de CSS regulares podem ser usadas como um substituto para estilos definidos usando a API de propriedades e valores personalizados.

Concentre-se no desenvolvimento de uma experiência de usuário de site confiável e de alto desempenho primeiro e, em seguida, use os recursos do Houdini para fins decorativos como um aprimoramento progressivo.

Conclusão

As APIs Houdini finalmente permitirão que os desenvolvedores mantenham o código JavaScript usado para manipulação de estilo e decoração mais próximo do pipeline de renderização do navegador, resultando em melhor desempenho e estabilidade. Ao permitir que os desenvolvedores se conectem ao processo de renderização do navegador, eles poderão desenvolver vários polyfills CSS que podem ser facilmente compartilhados, implementados e, potencialmente, adicionados à própria especificação CSS. O Houdini também tornará os desenvolvedores e designers menos limitados pelas limitações de CSS ao trabalhar em estilo, layouts e animações, resultando em novas e agradáveis ​​experiências na web.

Os recursos do CSS Houdini podem ser adicionados aos projetos hoje, mas estritamente com aprimoramento progressivo em mente. Isso permitirá que os navegadores que não suportam os recursos do Houdini renderizem o site sem erros e ofereçam uma experiência de usuário ideal.

Será empolgante ver o que a comunidade de desenvolvedores apresentará à medida que Houdini ganha força e melhor suporte ao navegador. Aqui estão alguns exemplos incríveis de experimentos da API Houdini da comunidade:

  • Experiências CSS Houdini
  • Introdução interativa ao CSS Houdini
  • Amostras de Houdini do Google Chrome Labs

Referências

  • Rascunhos de Especificação W3C Houdini
  • Estado de Houdini (Chrome Dev Summit 2018)
  • Worklet de animação de Houdini - Google Developers
  • Introdução interativa ao CSS Houdini