Houdini – sposób obalania mitów na wczesnym etapie

Czy zdarzyło Ci się zastanawiać się, ile pracy wykonuje CSS? Po zmianie pojedynczego atrybutu cała witryna nagle wyświetla się w innym układzie. To rodzaj magii. Jak dotąd tylko my, społeczność programistów stron internetowych, mogliśmy doświadczyć tej magii. A co, jeśli chcemy wymyślić naszą własną magię? A jeśli chcesz zostać magikiem?

Dołącz do Houdini!

Grupa zadaniowa Houdini składa się z inżynierów Mozilla, Apple, Opera, Microsoft, HP, Intela i Google, którzy wspólnie pracują nad udostępnianiem niektórych części mechanizmu CSS deweloperom stron internetowych. Grupa zadaniowa pracuje nad zbiorem wersji roboczych, aby zostały one zaakceptowane przez W3C i uruchomione rzeczywistymi standardami internetowymi. Ustalili kilka ogólnych celów, a następnie zmienili je w wersje robocze specyfikacji, co z kolei powstało zestaw pomocnych wersji roboczych specyfikacji niższego poziomu.

Gdy ktoś mówi o „Houdini”, to właśnie zbiorem tych wersji roboczych jest to, W tej chwili lista wersji roboczych jest niekompletna, a niektóre z nich są tylko obiektami zastępczymi.

Specyfikacja

Worklety (spec)

Same Worklety nie są zbyt przydatne. To pojęcie, które umożliwia utworzenie wielu późniejszych wersji roboczych. Jeśli przy lekturze „workletu” przyszło Ci do głowy użycie Web Worker, nie oznacza to, że się mylisz. Terminy w wielu kategoriach się pokrywają. Po co więc wciąż mamy pracowników?

Celem Houdini jest udostępnienie nowych interfejsów API, aby umożliwić programistom internetowym podłączanie własnego kodu do silnika CSS i otaczających systemów. Nierealistyczne jest zakładanie, że niektóre z tych fragmentów kodu będą musiały być uruchamiane w przypadku każdej pojedynczej klatki. Niektóre z nich muszą z definicji. Zacytuj specyfikację Web Worker:

Oznacza to, że pracownicy internetowi nie są w stanie zrealizować planów, jakie planuje Houdini. Dlatego powstały worklety. Worklety korzystają z klas ES2015 do definiowania metod, których podpisy są wstępnie zdefiniowane przez typ workletu. Są lekkie i krótkotrwałe.

CSS Paint API (spec)

Interfejs Paint API jest domyślnie włączony w Chrome 65. Przeczytaj szczegółowe wprowadzenie.

Worklet kompozytora

Opisany tu interfejs API jest nieaktualny. Worklet kompozytora został zmieniony i nosi obecnie nazwę „Worklet animacji”. Dowiedz się więcej o bieżącej iteracji interfejsu API.

Mimo że specyfikacja workletu kompozytora została przeniesiona do WICG i będzie poddawana cyklicznie, to ta specyfikacja najbardziej mnie fascynuje. Niektóre operacje są zlecane karcie graficznej komputera przez mechanizm CSS, choć zależy to zarówno od karty graficznej, jak i ogólnie od urządzenia.

Przeglądarka zwykle wybiera drzewo DOM i na podstawie określonych kryteriów nadaje niektórym gałęziom i poddrzewom własną warstwę. Te poddrzewali same się na nie malują (w przyszłości może to być malarz). Ostatnim etapem jest nałożenie poszczególnych warstw na siebie i ich ułożenie na sobie, respektując indeksy Z, przekształcenia 3D itd., tak aby uzyskać ostateczny obraz widoczny na ekranie. Ten proces nosi nazwę komponowania i jest wykonywany przez kompozytor.

Zaletą procesu komponowania jest to, że nie trzeba dopilnować, aby wszystkie elementy ponownie malowały się, gdy strona przewijała się choćby trochę. Zamiast tego możesz ponownie wykorzystać warstwy z poprzedniej ramki i jeszcze raz uruchomić kompozytor ze zaktualizowaną pozycją przewijania. Dzięki temu wszystko działa szybciej. Dzięki temu możemy osiągnąć 60 kl./s.

Worklet kompozytora.

Jak sama nazwa wskazuje, worklet kompozytora pozwala połączyć się z kompozytorem i wpływać na sposób umieszczania już namalowanej warstwy elementu oraz jej warstwy na innych warstwach.

Aby uzyskać bardziej szczegółowe informacje, możesz poinformować przeglądarkę, że chcesz dołączyć do procesu komponowania określonego węzła DOM i poprosić o dostęp do niektórych atrybutów, takich jak pozycja przewijania, transform lub opacity. Wymusza to utworzenie osobnej warstwy dla tego elementu, a kod jest wywoływany w każdej ramce. Możesz przesuwać warstwę, modyfikując jej przekształcanie warstw i zmieniając jej atrybuty (np. opacity). W ten sposób możesz wykonywać różne zadania przy maksymalnie 60 klatkach na sekundę.

Oto pełna implementacja przewijania paralaksy za pomocą workletu kompozytora.

// 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 napisał polyfill dla workletu kompozytora, więc możesz go wypróbować – z większym wpływem na wydajność.

Worklet układu (spec)

Zaproponowano pierwszą rzeczywistą wersję roboczą specyfikacji. a wdrażanie to jest już trochę czasu.

Specyfikacja jest prawie pusta, ale sama koncepcja jest intrygująca: napisz własny układ. Worklet układu powinien umożliwiać Ci wykonanie polecenia display: layout('myLayout') i uruchomienie kodu JavaScript, aby umieścić elementy podrzędne węzła w polu węzła.

Oczywiście pełna implementacja JavaScriptu układu flex-box CSS jest wolniejsza niż równoważna implementacja natywna. Łatwo sobie jednak wyobrazić, że skrócenie tekstu może zwiększyć wydajność. Wyobraź sobie witrynę składającą się tylko z kafelków, na przykład Windows 10 lub układ w stylu betonowym. Nie jest używane pozycjonowanie bezwzględne ani stałe, ani z-index. Elementy nigdy nie nakładają się na siebie ani nie są w żaden sposób przepełnione. Możliwość pominięcia wszystkich tych kontroli przy ponownym układzie może zwiększyć wydajność.

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

Typ CSSOM (spec)

Typowy model CSSOM (CSS Object Model lub Cascading StyleSheet Object Model) rozwiązuje problem, który prawdopodobnie wszyscy znamy i który właśnie nauczyliśmy się rozwiązywać. Przedstawię to za pomocą wiersza kodu JavaScript:

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

Przeprowadzamy obliczenia matematyczne. Przekształcamy liczbę w ciąg znaków, aby dołączyć jednostkę po to, aby przeglądarka przeanalizowała ten ciąg znaków i przekształciła go z powrotem w liczbę na potrzeby silnika CSS. Sprawia się to jeszcze bardziej, jeśli manipulujesz przekształceniami za pomocą JavaScriptu. To już koniec! CSS zacznie wkrótce wpisywać się w zapytaniu.

To jedna z najstarszych wersji roboczych. Obecnie pracujemy nad kodem polyfill. (Wyłączenie odpowiedzialności: użycie kodu polyfill spowoduje oczywiście jeszcze większy nakład obliczeniowy). Chodzi o to, żeby pokazać, jak wygodny jest interfejs API).

Zamiast ciągów znaków będziesz pracować na StylePropertyMap elementu, gdzie każdy atrybut CSS ma własny klucz i odpowiadający mu typ wartości. Typ wartości atrybutów, takich jak width, to LengthValue. LengthValue to słownik wszystkich jednostek CSS, takich jak em, rem, px, percent itd. Ustawienie height: calc(5px + 5%) zwróciło LengthValue{px: 5, percent: 5}. Niektóre właściwości, takie jak box-sizing, akceptują tylko określone słowa kluczowe i dlatego mają typ wartości KeywordValue. Prawidłowość tych atrybutów będzie można sprawdzać w czasie działania aplikacji.

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

Właściwości i wartości

(spec)

Czy znasz właściwości niestandardowe CSS (lub ich nieoficjalny alias „zmienne CSS”)? To właśnie oni, ale z typami! Do tej pory zmienne mogły mieć tylko wartości w postaci ciągów znaków i wykorzystywały prostą metodę „wyszukaj i zastąp”. Dzięki temu będzie można nie tylko określić typ zmiennych, ale też zdefiniować wartość domyślną i wpłynąć na działanie dziedziczenia za pomocą interfejsu JavaScript API. Technicznie rzecz biorąc, umożliwia to również animowanie właściwości niestandardowych za pomocą standardowych przejść i animacji CSS.

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

Dane dotyczące czcionek

Dane dotyczące czcionek są zgodne z opisem. Czym są ramki ograniczające, gdy renderuję ciąg znaków X z czcionką Y w rozmiarze Z? A jeśli użyję adnotacji ruby? Wiele osób prosiło o to i Houdini powinni w końcu spełnić te życzenia.

Poczekaj, to jeszcze nie wszystko.

Na liście wersji roboczych Houdini znajduje się jeszcze więcej danych, ale ich przyszłość jest raczej niepewna i nie są one czymś więcej niż tylko symbolami zastępczymi koncepcji. Są to na przykład niestandardowe ustawienia nadmiaru danych, interfejs API rozszerzenia składni CSS, rozszerzenie natywnego przewijania i podobne elementy umożliwiające obsługę funkcji, które wcześniej nie były możliwe.

Przykłady

Jestem udostępniony na licencji open source kodu wersji demonstracyjnej (demonstracja na żywo z użyciem kodu polyfill).