Houdini: desmistificando o CSS

Você já pensou na quantidade de trabalho que o CSS faz? Você altera uma única e, de repente, todo o seu site aparece em um layout diferente. Está um tipo de mágica. Até agora, a comunidade de desenvolvedores Web só foi capaz de testemunhar e observar a magia. E se quisermos elaborar nossa própria magia? E se quisermos ser mágicos?

Aí sim para Houdini!

A força-tarefa da Houdini é composta por engenheiros da Mozilla, Apple, Opera, Microsoft, HP, Intel e Google trabalham juntos para expor certas partes da mecanismo CSS para desenvolvedores Web. A força-tarefa está trabalhando em uma coleção de rascunhos com o objetivo de fazer com que eles sejam aceitos pelo W3C para que se tornem de fato na Web. de conformidade. Eles definiram algumas metas gerais, as transformaram rascunhos de especificação que, por sua vez, deram à luz um conjunto de rascunhos de especificação de nível inferior.

O acervo desses rascunhos é o que significa quando alguém fala sobre "Houdini". No momento em que este artigo foi escrito, a lista de rascunhos está incompletos e alguns dos rascunhos são meros espaços reservados.

As especificações

Worklets (spec)

Os Worklets em si não são muito úteis. Eles são um conceito introduzido e viabilizar muitos dos rascunhos posteriores. Se você pensou nos web workers quando ler "worklet", não está certo. Há muita sobreposição conceitual. Por que algo novo quando já temos workers?

O objetivo da Houdini é expor novas APIs para permitir que os desenvolvedores Web conectem seu próprio código ao mecanismo CSS e o sistemas circundantes. Não é realista supor que algumas dessas os fragmentos de código terão que ser executados todas. solteiro. frame. Alguns deles precisam por definição. Com a especificação do Web Worker:

Isso significa que os web workers não são viáveis para o que Houdini planeja fazer. Por isso, os worklets foram inventados. Os Worklets usam classes ES2015 para definir uma coleção de métodos com assinaturas predefinidas o tipo de worklet. Eles são leves e de curta duração.

API CSS Paint (especificação)

A API Paint está ativada por padrão no Chrome 65. Leia o introdução detalhada.

Objeto de composição

A API descrita aqui está obsoleta. O worklet do criador tem foi reformulado e agora é proposto como um "worklet de animação". Leia mais na iteração atual da API.

Embora a especificação do worklet do compositor tenha sido movida para o WICG e iterado, é a especificação que mais me empolga. Algumas de serviço são terceirizadas para a placa de vídeo do computador pelo CSS embora isso dependa da placa de vídeo e do dispositivo, geral.

Um navegador geralmente pega a árvore do DOM e, com base em critérios específicos, decide dar a algumas ramificações e subárvores sua própria camada. Essas subárvores se pintam sobre ela (talvez usando um worklet de tinta futuro). Por fim, todas essas camadas individuais, agora pintadas, são empilhadas e posicionados uns sobre os outros, respeitando os índices z, as transformações 3D e para produzir a imagem final visível na tela. Esse processo é chamada composição e é executada pelo compositor.

A vantagem do processo de composição é que você não precisa fazer todas os elementos são repintados quando a página rola um pouco. Em vez disso, pode reutilizar as camadas do frame anterior e apenas executar novamente o compositor com a posição de rolagem atualizada. Isso torna tudo mais rápido. Isso nos ajuda a atingir 60 fps.

Objeto de composição.

Como o nome sugere, o worklet do compositor permite conectar ao compositor. e influenciar a forma como a camada de um elemento, que já foi pintada, é posicionadas e sobrepostas às outras camadas.

Para receber um pouco mais específica, você pode informar ao navegador que deseja vincular à composição processo para um determinado nó do DOM e pode solicitar acesso a certos atributos, como a posição de rolagem, transform ou opacity. Isso força o elemento a ser camada própria e, em cada frame, seu código é chamado. É possível mover a camada manipulando as camadas de transformação e alterando os atributos delas (como opacity). permitindo que você faça coisas incríveis a 60 QPS.

Confira uma implementação completa para rolagem de paralaxe usando o compositor. worklet.

// main.js
window.compositorWorklet.import('worklet.js')
    .then(function() {
    var animator = new CompositorAnimator('parallax');
    animator.postMessage([
        new CompositorProxy($('.scroller'), ['scrollTop']),
        new CompositorProxy($('.parallax'), ['transform']),
    ]);
    });

// worklet.js
registerCompositorAnimator('parallax', class {
    tick(timestamp) {
    var t = self.parallax.transform;
    t.m42 = -0.1 * self.scroller.scrollTop;
    self.parallax.transform = t;
    }

    onmessage(e) {
    self.scroller = e.data[0];
    self.parallax = e.data[1];
    };
});

Robert Flack escreveu um polyfill para um worklet compositor para testar, obviamente com uma impacto no desempenho.

Objeto de layout (spec)

O primeiro rascunho de especificação real foi proposto. Implementação é bom quando está longe.

Novamente, a especificação para isso está praticamente vazia, mas o conceito é intrigante: escreva seu próprio layout! A worklet de layout deve permitir para fazer display: layout('myLayout') e executar o JavaScript para organizar um objeto filhos na caixa do nó.

Obviamente, executar uma implementação completa de JavaScript do layout flex-box do CSS é mais lento do que executar uma implementação nativa equivalente, mas é fácil imagine um cenário em que cortar aspectos pode gerar um ganho de desempenho. Imagine um site com apenas blocos, como Windows 10 ou um sistema de alvenaria o mesmo layout organizacional. O posicionamento absoluto e fixo não é usado, nem o z-index, nem se sobrepõem ou têm qualquer tipo de borda ou estouro. Poder pular todas essas verificações no novo layout podem gerar um ganho de desempenho.

registerLayout('random-layout', class {
    static get inputProperties() {
        return [];
    }
    static get childrenInputProperties() {
        return [];
    }
    layout(children, constraintSpace, styleMap) {
        const width = constraintSpace.width;
        const height = constraintSpace.height;
        for (let child of children) {
            const x = Math.random()*width;
            const y = Math.random()*height;
            const constraintSubSpace = new ConstraintSpace();
            constraintSubSpace.width = width-x;
            constraintSubSpace.height = height-y;
            const childFragment = child.doLayout(constraintSubSpace);
            childFragment.x = x;
            childFragment.y = y;
        }

        return {
            minContent: 0,
            maxContent: 0,
            width: width,
            height: height,
            fragments: [],
            unPositionedChildren: [],
            breakToken: null
        };
    }
});

CSSOM digitado (spec)

O CSSOM (modelo de objeto CSS ou modelo de objeto de folhas de estilo em cascata) tipado aborda um um problema que todos nós já enfrentamos e acabamos de aprender a lidar com isso. Vou ilustrar com uma linha de JavaScript:

    $('#someDiv').style.height = getRandomInt() + 'px';

Estamos fazendo cálculos matemáticos, convertendo um número em uma string para acrescentar uma unidade o navegador analisa essa string e a converte de volta em um número para o mecanismo CSS. Isso fica ainda mais complicado quando você manipula transformações com JavaScript. Não há mais! O CSS está prestes a começar a digitar.

Este rascunho é um dos mais maduros e um polyfill é que já estamos desenvolvendo. Exoneração de responsabilidade: usar o polyfill obviamente adicionar ainda mais sobrecarga computacional. O objetivo é mostrar como o API é.

Em vez de strings, você trabalhará na StylePropertyMap de um elemento, em que cada atributo CSS tem sua própria chave e tipo de valor correspondente. Atributos como width, têm LengthValue como tipo de valor. Um LengthValue é um dicionário de todas as unidades CSS, como em, rem, px, percent e assim por diante. Ambiente height: calc(5px + 5%) resultaria em um LengthValue{px: 5, percent: 5}. Algumas propriedades como box-sizing aceitam determinadas palavras-chave e, portanto, têm Tipo de valor KeywordValue. A validade desses atributos pode ser verificada durante a execução.

<div style="width: 200px;" id="div1"></div>
<div style="width: 300px;" id="div2"></div>
<div id="div3"></div>
<div style="margin-left: calc(5em + 50%);" id="div4"></div>
var w1 = $('#div1').styleMap.get('width');
var w2 = $('#div2').styleMap.get('width');
$('#div3').styleMap.set('background-size',
    [new SimpleLength(200, 'px'), w1.add(w2)])
$('#div4')).styleMap.get('margin-left')
    // => {em: 5, percent: 50}

Propriedades e valores

(especificação)

Você conhece as propriedades personalizadas do CSS (ou o alias não oficial "Variáveis CSS")? Estes são eles, mas com tipos! Até agora, as variáveis só podiam ter valores de string e usou uma abordagem simples de pesquisa e substituição. Esse rascunho permitiria apenas especificar um tipo para suas variáveis, mas também definir um valor padrão e influenciar o comportamento de herança usando uma API JavaScript. Tecnicamente, isso também permite que propriedades personalizadas sejam animadas com transições CSS padrão e animações, que também estão sendo consideradas.

["--scale-x", "--scale-y"].forEach(function(name) {
document.registerProperty({
    name: name,
    syntax: "<number>",
    inherits: false,
    initialValue: "1"
    });
});

Métricas de fonte

As métricas de fonte são exatamente o que parecem. O que é a caixa delimitadora (ou o caixas delimitadoras) quando renderizo a string X com a fonte Y no tamanho Z? E se eu usar anotações Ruby? Isso foi muito solicitado, e Houdini finalmente e realizar esses desejos.

Mas não é só isso.

Há ainda mais especificações na lista de rascunhos de Houdini, mas o futuro delas incerto e não são muito mais do que espaços reservados para ideias. Exemplos incluem comportamentos de estouro personalizados, API de extensão de sintaxe CSS, extensão do comportamento de rolagem nativo e de coisas igualmente ambiciosas, que permitem coisas na plataforma da web que não eram possíveis antes.

Demonstrações

Abri o código do código para a demonstração (demonstração ao vivo usando polyfill).