Czy zastanawiałeś/się kiedyś, ile pracy wykonuje usługa CSS? Zmieniasz jeden atrybut, a nagle cała witryna ma inny układ. To rodzaj magii. Do tej pory my, czyli społeczność programistów internetowych, mogliśmy tylko przyglądać się tej magii. A co, jeśli chcemy stworzyć własną magię? Co zrobić, jeśli chcemy stać się magami?
Wejdź do Houdiniego.
Zespół zadaniowy Houdini składa się z inżynierów z Mozilla, Apple, Opera, Microsoft, HP, Intel i Google, którzy wspólnie pracują nad udostępnieniem deweloperom webowym niektórych części silnika CSS. Grupa robocza pracuje nad kolekcją wersji roboczych, aby uzyskać ich akceptację przez W3C i ustanowić je jako standardy internetowe. Uczestnicy projektu wyznaczyli sobie kilka ogólnych celów, przekształcili je w szkice specyfikacji, które z kolei dały początek zestawowi wspierających szkiców specyfikacji na niższych poziomach.
Kolekcja tych wersji roboczych to to, co zwykle ma na myśli użytkownik, gdy mówi o „Houdinie”. W momencie pisania tego artykułu lista projektów jest niepełna, a niektóre z nich to tylko placeholdery.
Specyfikacje
Worklety (spec)
Same worklety nie są zbyt przydatne. Są to koncepcje wprowadzone, aby umożliwić tworzenie wielu późniejszych wersji. Jeśli po przeczytaniu słowa „worklet” pomyślałeś/pomyślałaś o Web Workers, masz rację. Te słowa mają wiele wspólnego. Dlaczego mamy wprowadzać nowe rozwiązanie, skoro mamy już pracowników?
Celem Houdini jest udostępnienie nowych interfejsów API, aby umożliwić deweloperom stron internetowych podłączenie ich własnego kodu do silnika CSS i otaczających go systemów. Można przypuszczać, że niektóre z tych fragmentów kodu będą musiały być wykonywane w każdej klatce. Niektóre z nich muszą być zdefiniowane. Cytując specyfikację Web Worker:
Oznacza to, że wątki internetowe nie nadają się do tego, co planuje Houdini. Dlatego wymyśliliśmy worklety. Worklety korzystają z klas ES2015 do definiowania zbioru metod, których sygnatury są wstępnie zdefiniowane przez typ workleta. Są lekkie i krótkotrwałe.
Interfejs API CSS Paint (specyfikacja)
Interfejs Paint API jest domyślnie włączony w Chrome 65. Przeczytaj szczegółowe wprowadzenie.
Worklet kompozytora
Opisany tutaj interfejs API jest nieaktualny. Element kompozytorski został przeprojektowany i jest teraz proponowany jako „Element kompozytorski animacji”. Dowiedz się więcej o obecnej wersji interfejsu API.
Mimo że specyfikacja workleta kompozytora została przeniesiona do WICG i będzie ulepszana, to właśnie ona najbardziej mnie ekscytuje. Niektóre operacje są zlecane przez silnik CSS do karty graficznej komputera, ale zależy to od karty graficznej i ogólnego stanu urządzenia.
Przeglądarka zwykle pobiera drzewo DOM i na podstawie określonych kryteriów decyduje, które gałęzie i poddrzewa mają mieć własną warstwę. Te poddrzewa nakładają się na siebie (być może za pomocą workletu do nakładania w przyszłości). W ostatnim kroku wszystkie te oddzielne, już namalowane warstwy są ułożone jedna na drugiej, z zachowaniem indeksów z i przekształceń 3D, aby uzyskać obraz widoczny na ekranie. Ten proces nazywa się kompozycją i jest wykonywany przez kompozytor.
Zaletą procesu kompozytowania jest to, że nie musisz ponownie renderować wszystkich elementów, gdy strona przesunie się o trochę w dół. Zamiast tego możesz ponownie użyć warstw z poprzedniego klatki i ponownie uruchomić kompozytor z aktualną pozycją przewijania. To przyspiesza działanie. Pomoże nam to osiągnąć 60 FPS.

Jak sama nazwa wskazuje, moduł kompozytorski umożliwia Ci podłączenie się do kompozytora i wpływanie na sposób, w jaki warstwa elementu, który został już namalowany, jest umieszczana i umieszczana na innych warstwach.
Aby uzyskać nieco bardziej szczegółowe informacje, możesz poinformować przeglądarkę, że chcesz w jakimś procesie tworzenia kompozycji zaczepić się do określonego węzła DOM, a także poprosić o dostęp do określonych atrybutów, takich jak pozycja zwinięcia, transform
lub opacity
. Wymusza to użycie tego elementu na jego własnej warstwie i w każdej klatce wywołuje kod. Możesz przesuwać warstwę, manipulować jej transformacją i zmieniać jej atrybuty (np. opacity
), co pozwala na wykonywanie skomplikowanych działań z prędkością 60 FPS.
Poniżej znajduje się pełna implementacja przewijania paralaksy z wykorzystaniem 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 workleta kompozytora, więc możesz go wypróbować – oczywiście przy znacznie większym wpływie na wydajność.
Worklet układu (spec)
Zaproponowano pierwszy prawdziwy projekt specyfikacji. Wdrożenie jest już w drodze.
Ponownie specyfikacja jest praktycznie pusta, ale pomysł jest intrygujący: napisz własny układ. Worklet układu ma umożliwić wykonanie display: layout('myLayout')
i uruchomienie kodu JavaScript, aby uporządkować elementy podrzędne węzła w polu węzła.
Oczywiście uruchamianie pełnej implementacji JavaScript układu flex-box
w CSS jest wolniejsze niż uruchamianie równoważnej implementacji natywnej, ale łatwo wyobrazić sobie scenariusz, w którym pominięcie niektórych elementów może przynieść wzrost wydajności. Wyobraź sobie stronę internetową składającą się wyłącznie z płytek, tak jak w Windows 10 lub w układzie typu „kamień”. Nie używa się pozycjonowania bezwzględnego ani stałego, ani z-index
. Elementy nie mogą się na siebie nakładać ani mieć żadnych ramek ani przepełnienia. Możliwość pominięcia wszystkich tych kontroli podczas ponownego układania może przynieść wzrost wydajności.
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
};
}
});
Typowany obiekt CSSOM (spec)
Typowany model obiektów CSSOM (model obiektów arkuszy stylów kaskadowych) rozwiązuje problem, z którym prawdopodobnie wszyscy się spotkaliśmy i zwykliśmy go po prostu tolerować. Pokażę to na przykładzie linii kodu JavaScript:
$('#someDiv').style.height = getRandomInt() + 'px';
Wykonujemy obliczenia, konwertując liczbę na ciąg znaków, aby dodać jednostkę, a następnie przeglądarka przeanalizuje ten ciąg znaków i konwertuje go z powrotem na liczbę dla silnika CSS. Sytuacja staje się jeszcze gorsza, gdy zmieniasz transformacje za pomocą JavaScripta. Nie ma już tego problemu. Kod CSS będzie teraz wpisywany.
Ten projekt jest jednym z bardziej dopracowanych, a praca nad polyfillem jest już w toku. (Uwaga: używanie polyfilla będzie oczywiście jeszcze bardziej obciążać procesor. Chodzi o to, aby pokazać, jak wygodna jest ta usługa API.)
Zamiast ciągów tekstowych będziesz pracować nad elementem StylePropertyMap
, gdzie każdy atrybut CSS ma swój klucz i odpowiadający mu typ wartości. Atrybuty takie jak width
mają typ wartości LengthValue
. LengthValue
to słownik wszystkich jednostek CSS, takich jak em
, rem
, px
, percent
itd. Ustawienie height: calc(5px + 5%)
da wynik LengthValue{px: 5, percent: 5}
. Niektóre właściwości, np. box-sizing
, akceptują tylko określone słowa kluczowe, dlatego mają typ wartości KeywordValue
. W tym celu można sprawdzić ich ważność w czasie działania.
<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 wiesz, czym są właściwości niestandardowe w kodzie CSS (lub ich nieoficjalny alias „zmienniki w kodzie CSS”)? To są one, ale z typami. Do tej pory zmienne mogły mieć tylko wartości ciągu znaków i wykorzystywały proste wyszukiwanie i zastępowanie. Ten projekt umożliwiłby nie tylko określenie typu zmiennych, ale też zdefiniowanie wartości domyślnej i wpływanie na zachowanie dziedziczenia za pomocą interfejsu JavaScript API. Teoretycznie umożliwiłoby to również animowanie właściwości niestandardowych za pomocą standardowych przejść i animacji CSS, co również jest rozważane.
["--scale-x", "--scale-y"].forEach(function(name) {
document.registerProperty({
name: name,
syntax: "<number>",
inherits: false,
initialValue: "1"
});
});
Dane czcionki
Dane dotyczące czcionek to dokładnie to, co sugeruje nazwa. Co to jest ramka ograniczająca (lub ramki ograniczające) podczas renderowania ciągu X za pomocą czcionki Y w rozmiarze Z? Co się stanie, jeśli użyję adnotacji Ruby? Wiele osób prosiło o to, więc Houdini w końcu spełnił ich życzenie.
Poczekaj, to jeszcze nie wszystko.
Na liście projektów w Houdinim jest jeszcze więcej specyfikacji, ale ich przyszłość jest raczej niepewna i są one raczej tylko miejscem na pomysły. Przykłady obejmują niestandardowe zachowania przepełnienia, interfejs API rozszerzeń składni CSS, rozszerzenia zachowania natywnego przewijania i podobnie ambitne rozwiązania, które umożliwiają wykonywanie na platformie internetowej czynności, które wcześniej były niemożliwe.
Przykłady
Udostępniłem w źródle kod wersji demonstracyjnej (demo na żywo korzystające z polyfill).