Houdini - Mengupas Tuntas CSS

Pernahkah Anda berpikir tentang besarnya pekerjaan yang dilakukan CSS? Anda mengubah satu atribut, dan tiba-tiba seluruh situs Anda muncul dalam tata letak yang berbeda. Ini seperti sulap. Sejauh ini, kami—komunitas developer web—hanya dapat menyaksikan dan mengamati keajaibannya. Bagaimana jika kita ingin menghasilkan keajaiban kita sendiri? Bagaimana jika kita ingin menjadi pesulap?

Masuki Houdini!

Satuan tugas Houdini terdiri atas engineer dari Mozilla, Apple, Opera, Microsoft, HP, Intel, dan Google yang bekerja sama untuk mengekspos bagian-bagian tertentu dari mesin CSS kepada developer web. Satuan tugas sedang mengerjakan koleksi draf dengan tujuan membuat draf tersebut diterima oleh W3C menjadi standar web yang sebenarnya. Mereka menetapkan sendiri beberapa tujuan tingkat tinggi, mengubahnya menjadi draf spesifikasi yang kemudian melahirkan serangkaian draf spesifikasi yang mendukung pada level yang lebih rendah.

Kumpulan draf ini biasanya dimaksudkan saat seseorang berbicara tentang "Houdini". Pada saat artikel ini ditulis, daftar draf belum lengkap dan beberapa draf hanyalah placeholder.

Spesifikasi

Worklet (spec)

Worklet sendiri tidak terlalu berguna. Mereka adalah konsep yang diperkenalkan untuk mewujudkan banyak draf selanjutnya. Jika Anda memikirkan Web Worker saat membaca "worklet", Anda tidak salah. Mereka memiliki banyak tumpang tindih konseptual. Jadi mengapa harus ada hal baru ketika kita sudah memiliki pekerja?

Tujuan Houdini adalah mengekspos API baru yang memungkinkan developer web menghubungkan kode mereka sendiri ke mesin CSS dan sistem di sekitarnya. Mungkin mustahil untuk mengasumsikan bahwa beberapa fragmen kode ini harus dijalankan setiap. single. frame. Beberapa dari mereka harus sesuai dengan definisi. Mengutip spesifikasi Web Worker:

Itu berarti pekerja web tidak layak untuk hal-hal yang rencananya akan dilakukan Houdini. Oleh karena itu, worklet ditemukan. Worklet menggunakan class ES2015 untuk menentukan kumpulan metode, yang tanda tangannya telah ditentukan sebelumnya oleh jenis worklet. Bahan ini ringan dan berumur pendek.

CSS Paint API (spec)

Paint API diaktifkan secara default di Chrome 65. Baca pengantar terperinci.

Worklet compositor

API yang dijelaskan di sini sudah tidak berlaku. Worklet compositor telah didesain ulang dan sekarang diusulkan sebagai "Worklet Animasi". Baca selengkapnya tentang iterasi API saat ini.

Meskipun spesifikasi worklet compositor telah dipindahkan ke WICG dan akan diiterasi, namun spesifikasi inilah yang paling menarik bagi saya. Beberapa operasi dialihdayakan ke kartu grafis komputer Anda oleh mesin CSS, meskipun hal ini bergantung pada kartu grafis dan perangkat Anda secara umum.

Browser biasanya mengambil hierarki DOM dan, berdasarkan kriteria tertentu, memutuskan untuk memberikan lapisannya sendiri kepada beberapa cabang dan subpohon. Sub-pohon ini melukis dirinya sendiri di atasnya (mungkin menggunakan worklet cat di masa mendatang). Sebagai langkah terakhir, semua lapisan individual yang kini telah digambar ditumpuk dan diposisikan di atas satu sama lain, dengan memperhatikan indeks z, transformasi 3D, dan sedemikian rupa sehingga menghasilkan gambar akhir yang terlihat di layar. Proses ini disebut pengomposisian dan dijalankan oleh compositor.

Keuntungan dari proses komposisi adalah Anda tidak perlu membuat semua elemen dilukis ulang sendiri saat halaman di-scroll sedikit. Sebagai gantinya, Anda dapat menggunakan kembali lapisan dari frame sebelumnya dan menjalankan kembali compositor dengan posisi scroll yang diperbarui. Hal ini membuat semuanya menjadi cepat. Ini membantu kami mencapai 60 fps.

Worklet compositor.

Seperti namanya, worklet compositor memungkinkan Anda mengaitkan ke compositor dan memengaruhi cara lapisan elemen, yang telah dicat, diposisikan dan dilapisi di atas lapisan lainnya.

Agar lebih spesifik, Anda dapat memberi tahu browser bahwa Anda ingin terhubung ke proses pengomposisian untuk node DOM tertentu dan dapat meminta akses ke atribut tertentu seperti posisi scroll, transform atau opacity. Tindakan ini memaksa elemen ini ke lapisannya sendiri dan pada setiap frame, kode Anda akan dipanggil. Anda dapat memindahkan lapisan dengan memanipulasi transformasi lapisan dan mengubah atributnya (seperti opacity) yang memungkinkan Anda melakukan berbagai hal rumit pada 60 fps.

Berikut adalah implementasi lengkap untuk scroll paralaks, menggunakan workstation compositor.

// 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 telah menulis polyfill untuk worklet compositor sehingga Anda dapat mencobanya – yang jelas dengan dampak performa yang jauh lebih tinggi.

Worklet tata letak (spec)

Draf spesifikasi nyata pertama telah diusulkan. Implementasi dapat dilakukan dalam waktu dekat.

Sekali lagi, spesifikasi untuk hal ini praktis kosong, tetapi konsepnya menarik: tulis tata letak Anda sendiri. Worklet tata letak seharusnya memungkinkan Anda melakukan display: layout('myLayout') dan menjalankan JavaScript untuk mengatur turunan node di kotak node.

Tentu saja, menjalankan implementasi JavaScript penuh dari tata letak flex-box CSS akan lebih lambat daripada menjalankan implementasi native yang setara, tetapi mudah untuk membayangkan skenario saat pemotongan sudut dapat menghasilkan peningkatan performa. Bayangkan sebuah situs web yang hanya terdiri dari ubin, seperti Windows 10 atau tata letak bergaya masonry. Pemosisian absolut dan tetap tidak digunakan, begitu juga z-index, dan elemen juga tidak pernah tumpang tindih atau memiliki jenis batas atau tambahan apa pun. Mampu melewati semua pemeriksaan ini saat menata ulang tata letak dapat menghasilkan peningkatan performa.

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

WebView Berjenis (spec)

WebView Bertipe (CSS Object Model atau Cascading Style Sheets Object Model) mengatasi masalah yang mungkin kita semua pernah alami dan baru saja belajar untuk menerimanya. Izinkan saya menggambarkan dengan baris JavaScript:

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

Kita melakukan penghitungan, mengonversi angka menjadi string untuk menambahkan unit agar browser mengurai string tersebut dan mengubahnya kembali menjadi angka untuk mesin CSS. Situasi ini akan menjadi semakin jelek jika Anda memanipulasi transformasi dengan JavaScript. Tidak lagi! Sebentar lagi CSS akan mengetik.

Draf ini adalah salah satu draf yang lebih matang dan polyfill sedang diproses. (Pernyataan penyangkalan: menggunakan polyfill tentu akan menambahkan overhead komputasi lebih banyak. Intinya adalah menunjukkan seberapa nyaman API tersebut.)

Sebagai ganti string, Anda akan mengerjakan StylePropertyMap elemen, dengan setiap atribut CSS memiliki kuncinya sendiri dan jenis nilai yang sesuai. Atribut seperti width memiliki LengthValue sebagai jenis nilainya. LengthValue adalah kamus dari semua unit CSS seperti em, rem, px, percent, dan seterusnya. Menetapkan height: calc(5px + 5%) akan menghasilkan LengthValue{px: 5, percent: 5}. Beberapa properti seperti box-sizing hanya menerima kata kunci tertentu sehingga memiliki jenis nilai KeywordValue. Selanjutnya, validitas atribut tersebut dapat diperiksa saat runtime.

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

Properti dan nilai

(spec)

Apakah Anda mengetahui Properti Khusus CSS (atau alias tidak resminya "Variabel CSS")? Ini dia, tapi dengan jenis! Sejauh ini, variabel hanya bisa memiliki nilai {i>string<i} dan menggunakan pendekatan {i>search and replace<i} sederhana. Dengan draf ini, Anda tidak hanya dapat menentukan jenis untuk variabel, tetapi juga menentukan nilai default dan memengaruhi perilaku pewarisan menggunakan JavaScript API. Secara teknis, hal ini juga akan memungkinkan properti kustom dianimasikan dengan transisi dan animasi CSS standar, yang juga dipertimbangkan.

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

Metrik font

Metrik font persis seperti namanya. Apa yang dimaksud dengan kotak pembatas (atau kotak pembatas) saat saya merender string X dengan font Y berukuran Z? Bagaimana jika saya menggunakan anotasi ruby? Hal ini sering diminta dan Houdini akhirnya harus mewujudkan keinginannya.

Tunggu, masih ada lagi!

Ada lebih banyak spesifikasi dalam daftar draf Houdini, tetapi masa depannya tidak pasti dan tidak lebih dari placeholder untuk ide. Contohnya mencakup perilaku tambahan kustom, API ekstensi sintaksis CSS, ekstensi perilaku scroll native, dan hal-hal yang juga ambisius, semuanya memungkinkan hal-hal di platform web yang sebelumnya tidak mungkin dilakukan.

Demo

Saya telah membuat kode untuk demo menjadi open source (demo langsung menggunakan polyfill).