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?
Witaj Houdini!
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ą projektów, których celem jest uzyskanie akceptacji W3C i ustanowienie ich jako standardów internetowych. 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 „Houdini”. W momencie pisania tego artykułu lista projektów jest niekompletna, 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ązania, 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 usługi porównywania cen (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ą workleta do nakładania w przyszłości). W ostatnim kroku wszystkie te pojedyncze, już namalowane warstwy są ułożone jedna na drugiej, z zachowaniem indeksów z-, przekształceń 3D itp., 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 niewielką odległość. Zamiast tego możesz ponownie użyć warstw z poprzedniego kadru 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 wstawić kod do procesu tworzenia kompozycji w przypadku określonego węzła DOM, i poprosić o dostęp do określonych atrybutów, takich jak pozycja kółka przewijania, 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ę, manipulując jej transformacją, oraz zmieniać jej atrybuty (np. opacity
). Dzięki temu możesz wykonywać skomplikowane czynności z częstotliwoś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 workletu 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ż blisko.
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 masonry. 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 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
};
}
});
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 tolerować. Pokażę to na przykładzie 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 już się rozpoczęła. (Oświadczenie: używanie polyfilla będzie oczywiście jeszcze bardziej obciążać procesor. Chodzi o to, aby pokazać, jak wygodnie można korzystać z interfejsu 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 CSS (lub ich nieoficjalny alias „zmienniki w CSS”)? Oto 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 jest również rozważane.
["--scale-x", "--scale-y"].forEach(function(name) {
document.registerProperty({
name: name,
syntax: "<number>",
inherits: false,
initialValue: "1"
});
});
Dane czcionki
Dane 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? Wielu użytkowników prosiło o to, a Houdini w końcu spełniło ich życzenie.
Poczekaj, to jeszcze nie wszystko.
Na liście projektów w Houdini 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 rozszerzenia składni CSS, rozszerzenie zachowania natywnego przewijania i podobnie ambitne funkcje, które umożliwiają wykonywanie na platformie internetowej czynności, które wcześniej były niemożliwe.
Prezentacje
Udostępniłem w źródle kod wersji demonstracyjnej (demo na żywo korzystające z polyfill).