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

Czy zdarzyło Ci się zastanawiać się nad ilością pracy wykonywanej w ramach usługi porównywania cen? Gdy zmieniasz pojedynczy atrybut, cała witryna ma inny układ. To coś w rodzaju magii. Do tej pory to my, czyli społeczność programistów stron internetowych, mogliśmy dostrzec tę magię. A jeśli chcemy wymyślić własną magię? A jeśli chcemy zostać iluzjonistą?

Czas na Houdini!

Grupa zadaniowa Houdini składa się z inżynierów z firm Mozilla, Apple, Opera, Microsoft, HP, Intel i Google, którzy wspólnie pracują nad udostępnieniem niektórym części mechanizmu CSS programistom stron internetowych. Grupa zadaniowa pracuje nad zbiorem wersji roboczych, aby sprawić, że zostaną one zaakceptowane przez W3C i staną się rzeczywistymi standardami internetowymi. Określili sobie kilka ogólnych celów i przekształcili je w wersje robocze, które z kolei stworzyły szereg dodatkowych, niższego poziomu wersji roboczych specyfikacji.

Zbiór tych wersji roboczych ma zazwyczaj znaczenie, gdy ktoś mówi o „Houdinim”. W chwili pisania lista wersji roboczych jest niepełna, a niektóre z nich są tylko miejscami zastępczymi.

Specyfikacje

Worklety (spec)

Worklety nie są raczej przydatne. Stanowią one koncepcję, która umożliwia utworzenie wielu późniejszych wersji roboczych. Jeśli czytasz „worklet”, to nie masz racji. Pod względem koncepcyjnym te pojęcia bardzo się pokrywają. Dlaczego więc nic nowego, skoro mamy już pracowników?

Celem Houdini jest udostępnienie nowych interfejsów API, dzięki którym programiści stron internetowych mogą podłączać własny kod do silnika CSS i systemów otaczających. Prawdopodobnie nierealistyczne jest zakładanie, że niektóre z tych fragmentów kodu będą musiały być uruchamiane w każdej klatce. Niektóre muszą z definicji. Cytując specyfikację usługi Web Worker:

Oznacza to, że pracownicy sieci nie nadają się do tego, co planuje Houdini. Dlatego wynaleźli Worklety. Worklety korzystają z klas ES2015 do definiowania zbioru metod, których podpisy są wstępnie zdefiniowane dla typu 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 przestarzały. Worklet kompozytora został przeprojektowany i jest teraz proponowany jako „Worklet animacji”. Więcej informacji o bieżącej iteracji interfejsu API.

Chociaż specyfikacja Worklet kompozytora została przeniesiona do WICG i będzie powtarzana, to właśnie ta specyfikacja mnie najbardziej podoba. Niektóre operacje są powierzane do karty graficznej komputera przez mechanizm usługi porównywania cen, chociaż w zasadzie zależy to zarówno od karty graficznej, jak i od urządzenia.

Przeglądarka zwykle pobiera drzewo DOM i na podstawie określonych kryteriów decyduje o tym, że wybrane gałęzie i poddrzewa mają własną warstwę. Te podrzędne drzewa malują się na nim (w przyszłości być może za pomocą farby). Na koniec wszystkie te warstwy są układane w stos i rozmieszczane na sobie, z uwzględnieniem indeksów Z i przekształceń 3D. W ten sposób powstaje obraz, który widać na ekranie. Ten proces nazywa się komponowaniem i jest wykonywany przez kompozytora.

Zaletą procesu komponowania jest to, że nie musisz ponownie malować wszystkich elementów przy maleńkim przewijaniu strony. Zamiast tego możesz ponownie użyć warstw z poprzedniej klatki i ponownie uruchomić kompozytor ze zaktualizowaną pozycją przewijania. Przyspiesza to pracę. Pomaga to osiągnąć prędkość 60 kl./s.

Worklet kompozytora.

Jak sama nazwa wskazuje, Worklet kompozytora umożliwia zaczepienie się kompozytora i wpływ na to, jak warstwa elementu, która została już wymalowana, jest umieszczana i nakładana na inne warstwy.

Żeby bardziej sprecyzować, możesz wskazać przeglądarce, że chcesz podłączyć się do procesu komponowania określonego węzła DOM i poprosić o dostęp do określonych atrybutów, takich jak pozycja przewijania, transform czy opacity. Powoduje to, że element zostaje umieszczony we własnej warstwie, a w każdej ramce wywoływany jest kod. Możesz przesuwać warstwę, modyfikując warstwy i zmieniając jej atrybuty (np. opacity). Dzięki temu możesz tworzyć fantazyjne efekty z szybkością do 60 klatek na sekundę.

Oto pełna implementacja przewijania paralaksy przy użyciu listy 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ł kod polyfill dla Workletu kompozytora, więc możesz go wypróbować – oczywiście ze znacznie większym wpływem na wydajność.

Worklet układu (spec)

Zaproponowano pierwszą wersję roboczą rzeczywistej specyfikacji. Implementacja nie wymaga czasu.

Specyfikacja jest praktycznie pusta, ale koncepcja jest intrygująca: napisz własny układ. Worklet układu powinien umożliwiać wykonywanie czynności display: layout('myLayout') i uruchamianie kodu JavaScript w celu rozmieszczenia elementów podrzędnych węzła w polu węzła.

Wykonanie pełnej implementacji JavaScriptu układu flex-box CSS jest oczywiście wolniejsze niż zastosowanie równoważnej implementacji natywnej, ale łatwo jest wyobrazić sobie scenariusz, w którym użycie nowych rozwiązań może zwiększyć wydajność. Wyobraź sobie witrynę składającą się wyłącznie z płytek, np. Windows 10 lub układ w stylu kamienia. Pozycjonowanie bezwzględne i stałe nie jest stosowane. Nie stosuje się również parametru z-index. Elementy nigdy nie nakładają się na siebie ani nie mają żadnych obramowania ani nadmiaru. Możliwość pominięcia wszystkich tych testów podczas przekazywania 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
        };
    }
});

Wpisany CSSOM (spec)

Wpisany model CSSOM (CSS Object Model lub Cascading Style Arkusze Object Model) rozwiązuje problem, który prawdopodobnie wszyscy natrafiliśmy na problem i właśnie go nauczyliśmy. Przeanalizujmy to za pomocą wiersza JavaScript:

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

Przeprowadzamy obliczenia matematyczne – konwertujemy liczbę na ciąg znaków, aby dodać jednostkę, a przeglądarka tylko przeanalizuje ten ciąg znaków i przekonwertuje go z powrotem na liczbę dla silnika CSS. Funkcja staje się jeszcze bardziej efektowna, gdy manipulujesz przekształceniami za pomocą JavaScriptu. To już wszystko. CSS za chwilę zacznie pisać.

Ta wersja robocza jest jedną z bardziej dopracowanych wersji, nad którą właśnie pracujemy nad polyfill. (Wyłączenie odpowiedzialności: użycie polyfill oczywiście spowoduje jeszcze większe obciążenie obliczeniowe). Chodzi o pokazanie, jak wygodny jest interfejs API).

Zamiast ciągów będziesz pracować nad właściwością StylePropertyMap elementu, gdzie każdy atrybut CSS ma własny klucz i odpowiedni typ wartości. Atrybuty takie jak width mają LengthValue jako typ wartości. LengthValue to słownik wszystkich jednostek CSS, takich jak em, rem, px, percent i tak dalej. Ustawienie height: calc(5px + 5%) zwraca wartość 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 można sprawdzić 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 usługi niestandardowe CSS (lub ich nieoficjalny alias „Zmienne CSS”)? To tylko te, ale z różnymi typami! Dotychczas zmienne mogły mieć tylko ciągi znaków i wykorzystywały prostą metodę „wyszukaj i zastąp”. Ta wersja robocza pozwoliłaby nie tylko określić typ zmiennych, ale też zdefiniować wartość domyślną i wpływać na dziedziczenie za pomocą interfejsu JavaScript API. Technicznie rzecz biorąc, umożliwiłoby to również animowanie właściwości niestandardowych z użyciem standardowych przejść i animacji CSS, które także są brane pod uwagę.

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

Dane dotyczące czcionek

Dokładnie widać dane czcionek. Jaka jest ramka ograniczająca, gdy renderuję ciąg znaków X z czcionką Y w rozmiarze Z? Co, jeśli zacznę używać adnotacji rubinowych? Otrzymywaliśmy wiele próśb i Hudini powinna w końcu spełnić te życzenia.

Poczekaj, to jeszcze nie wszystko.

Lista wersji roboczych Houdiniego zawiera jeszcze więcej specyfikacji, ale ich przyszłość jest raczej niepewna. Nie są one raczej obiektami zastępczymi pomysłów. Przykłady: niestandardowe zachowania nadmiarowe, interfejs API rozszerzenia składni CSS, rozszerzenie natywnego zachowania przewijania i podobne ambitne zadania, które umożliwiają na platformie internetowej realizację działań, które wcześniej nie były możliwe.

Przykłady

Udostępniam kod wersji demonstracyjnej na zasadach open source (prezentacja na żywo z użyciem kodu polyfill).