CSS Houdini Tutorial

Selamat siang teman!

Apa itu Houdini?


Houdini ( Houdini ) - kumpulan API peramban yang secara signifikan meningkatkan proses pengembangan web, termasuk pengembangan standar CSS. Pengembang akan dapat memperluas CSS menggunakan JavaScript, memengaruhi rendering CSS dan memberi tahu browser bagaimana gaya harus diterapkan. Ini akan memberikan peningkatan yang signifikan dalam kinerja dan stabilitas daripada penggunaan polyphiles.

Houdini terdiri dari dua grup API - API tingkat tinggi dan API tingkat rendah.

API tingkat tinggi dikaitkan dengan proses rendering (gaya - tata letak - gambar - komposisi). Grup ini termasuk:

  • Paint API - memungkinkan Anda untuk memperluas CSS pada langkah (yang berarti tahap rendering) dari rendering elemen visual (warna, latar belakang, batas, dll.).
  • Layout API - memungkinkan Anda untuk memperluas CSS pada langkah menentukan ukuran, posisi, dan perataan elemen.
  • API Animasi - "titik ekstensi" pada langkah menampilkan dan menggerakkan elemen.

API tingkat rendah adalah dasar untuk API tingkat tinggi dan termasuk:

  • API Model Objek yang Diketik
  • API Properti Nilai & Khusus
  • API Metrik Font
  • Worklets

Masa depan CSS


Houdini, tidak seperti CSS biasa, memungkinkan pengembang untuk memperluas CSS dengan cara yang lebih alami. Apakah ini berarti bahwa spesifikasi CSS akan berhenti berevolusi dan standar baru diadopsi? Tidak semuanya. Tujuan Houdini adalah untuk membantu mengembangkan fitur-fitur CSS baru melalui pembuatan prototipe kerja yang dapat dengan mudah distandarisasi.

Selain itu, pengembang dapat dengan mudah berbagi worklets CSS terbuka tanpa khawatir tentang kompatibilitas.

API Typed Object Model (TOM)


Sebelum Houdini, satu-satunya cara JS dan CSS berinteraksi adalah melemparkan CSS ke string dan memodifikasinya. Gaya parsing dan redefining secara manual dapat menjadi kompleks dan rentan kesalahan karena kebutuhan untuk konversi ganda dari tipe nilai (misalnya, dari angka ke string dan sebaliknya). Anda juga harus menentukan satuan ukuran untuk nilai baru secara manual.

selectedElement.style.fontSize = newFontSize + 'px' // newFontSize = 20
console.log(selectedElement.style.fontSize) // 20px

TOM memberi properti CSS makna yang lebih semantik dengan merepresentasikannya sebagai objek JS yang diketik. Ini sangat meningkatkan kinerja, stabilitas, dan memfasilitasi dukungan kode. Nilai-nilai CSS diwakili oleh antarmuka CSSUnitValue, yang terdiri dari nilai dan properti "unit ukuran".

{
  value: 20,
  unit: 'px'
}

Antarmuka ini dapat digunakan dengan fitur-fitur baru berikut:

  • computedStyleMap (): untuk parsing gaya yang dikomputasi (tidak built-in). Metode ini dipanggil sebelum parsing atau menggunakan metode lain.
  • attributeStyleMap: untuk parsing dan memodifikasi inline styles. Ini adalah properti item.

//       ( )
selectedElement.computedStyleMap().get('font-size') // { value: 20, unit: 'px' }

//   
selectedElement.attributeStyleMap.set('font-size', CSS.em(2))
selectedElement. attributeStyleMap.set('color', 'blue')

//    
selectedElement.computedStyleMap().get('font-size') // { value: 20, unit: 'px' }

//    
selectedElement.attributeStyleMap.get('font-size') // { value: 2, unit: 'em' }

Perhatikan bagaimana tipe CSS digunakan saat menetapkan nilai baru. Menggunakan sintaks ini menghindari masalah terkait tipe, dan kode yang dihasilkan menjadi lebih aman.

API yang dipertimbangkan termasuk tidak hanya mendapatkan dan mengatur, tetapi juga metode lain, misalnya:

  • hapus: hapus semua gaya sebaris
  • delete: menghapus properti CSS tertentu dan nilainya
  • has: mengembalikan true / false tergantung pada ketersediaan properti yang ditentukan
  • append: menambahkan nilai ekstra ke properti yang mendukung banyak nilai

Deteksi


let selectedElement = document.getElementById('example')

if(selectedElement.attributeStyleMap){
  // ...
}

if(selectedElement.computedStyleMap){
  // ...
}

Status Spesifikasi


Draf kerja : diposting untuk diskusi komunitas.

Dukung


ChromeTepiOperaFirefoxSafari
Didukung olehDidukung olehDidukung olehTidak didukungDukungan sebagian

API Properti dan Nilai Kustom


API ini memungkinkan pengembang untuk memperluas variabel CSS dengan menentukan jenis, nilai awal, dan warisan. Untuk mendefinisikan properti khusus, perlu mendaftarkannya menggunakan metode registerProperty. Metode ini menentukan bagaimana browser harus menerapkan properti dan menangani kesalahan.

CSS.registerProperty({
  name: '--colorPrimary',
  syntax: '<color>',
  inherits: false,
  initialValue: 'blue',
})

Objek dengan properti berikut diteruskan ke metode ini sebagai argumen:

  • nama: nama properti khusus
  • sintaks: instruksi untuk parsing. Nilai yang ditentukan sebelumnya adalah: <color>, <integer>, <number>, <length>, <percentage>, dll.
  • initialValue: nilai default (sebelum mengganti, serta ketika kesalahan terjadi)

Dalam contoh di atas, properti khusus tipe <color> didefinisikan. Properti ini akan digunakan untuk menentukan gradien. CSS reguler tidak mendukung transisi gradien. Perhatikan bagaimana properti khusus akan digunakan untuk menentukan transisi.

.gradient-box {
  background: linear-gradient(45deg, rgba(255, 255, 255, 1) 0% var(--colorPrimary) 60%);
  transition: --colorPrimary 0.5s ease;
  ...
}

.gradient-box:hover {
  --colorPrimary: red;
  ...
}

Browser tidak tahu bagaimana melakukan transisi untuk gradien, tetapi ia tahu bagaimana melakukannya untuk warna. Itu sebabnya kami mendefinisikan tipe properti sebagai <color>. Di browser yang mendukung Houdini, perubahan gradien akan terjadi saat Anda mengarahkan kursor. Posisi gradien, diukur dalam persen, juga dapat diubah menggunakan properti khusus CSS (terdaftar sebagai <persentase>).

Mungkin di masa mendatang akan dimungkinkan untuk mendaftarkan properti khusus secara langsung di CSS.

@property --colorPrimary {
  syntax: '<color>';
  inherits: false;
  initial-value: blue;
}

Contoh


Contoh sederhana ini menunjukkan bagaimana Anda dapat mengubah warna dan titik kontrol gradien menggunakan properti CSS kustom. Contoh kode dapat ditemukan di sini .



Deteksi


if(CSS.registeredProperty) {
  // ...
}

Status Spesifikasi


Draf kerja : diposting untuk diskusi komunitas.

Dukung


ChromeTepiOperaFirefoxSafari
Didukung olehDidukung olehDidukung olehTidak didukungTidak didukung

API Metrik Font


API ini berada pada tahap awal pengembangan, sehingga spesifikasi dapat berubah secara dramatis di masa mendatang. Saat ini ia menyediakan metode untuk mengukur ukuran elemen teks yang ditampilkan di layar, memungkinkan pengembang untuk mempengaruhi rendering karakter. Fitur CSS yang ada tidak memungkinkan Anda untuk bekerja dengan nilai-nilai ini atau membuat pekerjaan ini sangat sulit. Salah satu contoh penggunaan API ini adalah pemotongan dinamis multi-baris teks.

Status Spesifikasi


Kumpulan ide : konsep tidak dipublikasikan.

Browser tidak didukung.

Vorkleta


Sebelum pindah ke API berikut, Anda harus memahami konsep worklets. Vorklets adalah skrip yang dijalankan selama rendering dan independen dari kode JS yang mendasarinya. Mereka memperluas kemampuan mesin rendering, dirancang untuk eksekusi paralel (2 instance atau lebih), tidak memblokir utas utama, memiliki akses terbatas ke ruang lingkup global dan dipanggil oleh mesin sebagaimana diperlukan. Vorklets hanya dapat dijalankan melalui HTTPS (dalam produksi) atau melalui localhost (untuk tujuan pengembangan dan pengujian).

Houdini termasuk worklets berikut yang memperpanjang mesin rendering browser:

  • Paint Worklet - Paint API
  • Worklet Animasi - API Animasi
  • Layout Worklet - Layout API

API Cat


Paint API memungkinkan pengembang untuk menggunakan fungsi JS untuk melukis latar belakang, perbatasan, atau konten elemen menggunakan Konteks Rendering 2D, yang merupakan subset dari HTML5 Canvas API. Paint API menggunakan Paint Worklet untuk menggambar gambar yang tergantung pada perubahan pada CSS (seperti perubahan pada variabel CSS). Mereka yang terbiasa dengan Canvas API akan merasa betah dengan Paint API.

Membuat Paint Worklet terdiri dari beberapa langkah:

  1. Tulis dan daftarkan worklet menggunakan fungsi registerPaint
  2. Panggil worklet dalam HTML atau JS menggunakan CSS.paintWorklet.addModule
  3. Gunakan metode paint () dalam CSS bersama dengan nama vorklet dan argumen yang disampaikan

Mari kita lihat fungsi registerPaint, yang digunakan untuk mendaftar dan menentukan fungsionalitas Paint Worklet.

registerPaint('paintWorkletExample', class {
  static get inputProperties() { return ['--myVariable']; }
  static get inputArguments() { return ['<color>']; }
  static get contextOptions() { return {alpha: true} }

  paint(ctx, size, properties, args) {
    // ...
  }
})

Fungsi registerPaint terdiri dari bagian-bagian berikut:

  • inputProperties: Array properti CSS kustom yang diamati worklet. Array ini mewakili dependensi
  • inputArguments: array argumen yang dapat diteruskan dari suatu fungsi ke CSS eksternal
  • contextOptions: transparansi warna. Jika salah, semua warna akan sepenuhnya buram
  • paint: fungsi utama mengambil argumen berikut:

    • ctx: konteks gambar 2D yang hampir identik dengan konteks kanvas 2D API Kanvas
    • size: sebuah objek dengan lebar dan tinggi elemen. Nilai tergantung pada proses rendering tata letak. Ukuran kanvas sama dengan ukuran item sebenarnya
    • properti: variabel yang terkandung dalam inputProperties
    • args: array argumen yang dilewatkan ke fungsi paint

Setelah mendaftarkan worklet, itu harus dipanggil dalam HTML, yang menunjukkan path ke file.

CSS.paintWorklet.addModule('path/to/worklet/file.js')

Vorklets dapat ditambahkan dari sumber eksternal apa pun (misalnya, dari CDN), yang membuatnya modular dan dapat digunakan kembali.

CSS.paintWorklet.addModule('https://url/to/worklet/file.js')

Setelah memanggil worklet, ia dapat digunakan dalam CSS menggunakan fungsi "paint". Fungsi ini, sebagai parameter pertama, mengambil nama terdaftar dari vorklet dan semua argumen yang ditentukan dalam inputArguments. Mulai saat ini, browser tahu kapan harus memanggil worklet dan tindakan pengguna apa yang menyebabkan perubahan nilai-nilai tertentu dari properti kustom CSS.

.example-element {
  // paintWorkletExample -  
  // blue - ,  
  background: paint(paintWorkletExample, blue);
}

Contoh


Contoh berikut menunjukkan penggunaan Paint API, serta modularitas dan penggunaan kembali worklets. Itu menggunakan worklet riak dari repositori Google Chrome Labs . Lihat contoh kode di sini .



Deteksi


if(‘paintWorklet’ in CSS){
  // …
}

@supports(background: paint(paintWorkletExample)){
  // …
}

Status Spesifikasi


Rekomendasi : Draf kerja yang stabil, siap digunakan.

Dukung


ChromeTepiOperaFirefoxSafari
Didukung olehDidukung olehDidukung olehTidak didukungTidak didukung

API Animasi


API ini memperluas animasi web melalui pemrosesan berbagai peristiwa (menggulir, melayang, mengklik, dll.) Dan meningkatkan kinerja dengan meluncurkan animasi dalam alirannya sendiri melalui worklet animasi.

Seperti worklet lainnya, animasi latihan harus didaftarkan terlebih dahulu.

registerAnimation(‘animationWorkletExample’, class {
  constructor(options){
    // …
  }
  animate(currentTime, effect){
    // …
  }
})

Kelas ini mencakup dua fungsi:

  • constructor: dipanggil ketika instance baru dibuat. Digunakan untuk pengaturan umum.
  • animate: Fungsi utama yang berisi logika animasi. Parameter berikut diterima:

    • currentTime: cap waktu pada timeline tertentu
    • efek: berbagai efek yang digunakan dalam animasi

Setelah pendaftaran, worklet dimasukkan dalam file JS utama, animasi (elemen, bingkai, pengaturan) ditambahkan dan dilampirkan ke timeline. Konsep tanda waktu dan dasar-dasar animasi web ada di bagian selanjutnya.

//   
await CSS.animationWorklet.addModule(‘path/to/worklet/file.js’)

//    
const elementExample = document.getElementById(‘element-example’)

//  
const effectExample = new KeyframeEffect(
  elementExample, //  
  [ // … ], //  
  { // … } //  - , ,    ..
)

//        
new WorkletAnimation(
  ‘animationWorkletExample’ //  
  effectExample, //  
  document.timeline, //  
  {},  //   
).play()

Prangko waktu


Animasi web didasarkan pada cap waktu - tonggak untuk efek pada garis waktu animasi. Sebagai contoh, kami menganalisis animasi linier berulang, yang terdiri dari tiga frame (awal, tengah, akhir), dimulai 1 detik setelah halaman dimuat penuh (penundaan) dan berlangsung selama 4 detik.

Stempel waktu efek akan terlihat seperti ini (untuk animasi yang berlangsung 4 detik dan tanpa penundaan):

Prangko waktuBingkai animasi
0msBingkai pertama - awal animasi
2000 msBingkai kedua - tengah animasi
4000 msFrame terakhir - akhir animasi atau reset ke frame pertama

effect.localTime dengan nilai 3000ms (diberi penundaan 1000ms) mengikat animasi ke frame rata-rata pada timeline (1000ms delay + frame rata-rata 2000ms). Efek yang sama akan tercapai ketika mengatur 7000ms dan 11000ms, karena animasi berulang setiap 4000ms.

animate(currentTime, effect){
  effect.localTime = 3000 // 1000ms  + 2000ms  
}

Dengan nilai efek.localTime yang konstan, animasi akan dikunci pada bingkai tertentu. Oleh karena itu, nilai effect.localTime harus berubah. Nilai ini harus berupa fungsi yang terikat ke currentTime atau variabel lain.

Seperti apa bentuk kode animasi linier:

animate(currentTime, effect){
  effect.localTime = currentTime // y = x  
}

Timeline (document.timeline)Stempel waktuBingkai
startTime + 0ms (waktu yang berlalu)startTime + 0msPertama
startTime + 1000ms (waktu yang berlalu)startTime + 1000ms (tunda) + 0msPertama
startTime + 3000ms (waktu yang berlalu)startTime + 1000ms (delay) + 2000msTengah
startTime + 5000ms (waktu yang berlalu)startTime + 1000ms (tunda) + 4000msTerakhir / pertama
startTime + 7000ms (waktu yang berlalu)startTime + 1000ms (tunda) + 6000msTengah
startTime + 9000ms (waktu yang berlalu)startTime + 1000ms (tunda) + 8000msTerakhir / pertama

Prangko waktu tidak terbatas pada 1: 1. API Animasi memungkinkan pengembang untuk memanipulasi tanda melalui fungsi bernyawa, menggunakan fungsi JS standar untuk membuat efek kompleks. Animasi juga dapat bervariasi di setiap iterasi (dengan animasi yang berulang).

Animasi dapat diikat tidak hanya pada pemuatan dokumen, tetapi juga pada tindakan pengguna. Tindakan pengguna seperti menggulir halaman dapat digunakan dalam animasi melalui objek ScrollTimeline. Misalnya, animasi dapat dimulai ketika Anda menggulir 200px dan berakhir saat Anda menggulir 800px.

const scrollTimelineExample = new ScrollTimeline({
  scrollSource: scrollElement, // ,     
  orientation: ‘vertical’, //  
  startScrollOffset: ‘200px’, //  
  endScrollOffset: ‘800px’, //  
  timeRange: 1200, //  
  fill: ‘forwards’ //  
})

Animasi secara otomatis beradaptasi dengan kecepatan gulir, dengan tetap halus dan responsif. Karena animasi berjalan di alirannya sendiri dan terhubung ke mesin rendering browser, itu dimulai dengan lancar dan tidak mempengaruhi kinerja.

Contoh


Contoh berikut menunjukkan animasi non-linear. Ia menggunakan fungsi Gaussian dengan rotasi waktu yang sama di sana dan kembali. Lihat contoh kode di sini .



Deteksi


if(CSS.animationWorklet){
  // …
}

Status Spesifikasi


Draf kerja publik pertama : siap untuk diskusi komunitas, dapat berubah di masa depan.

Dukung


ChromeTepiOperaFirefoxSafari
Dukungan sebagianDukungan sebagianDukungan sebagianTidak didukungTidak didukung

Layout API


Layout API memungkinkan pengembang untuk memperpanjang proses rendering tata letak dengan mendefinisikan modul baru untuk digunakan dalam properti "tampilan" CSS. API ini memperkenalkan konsep-konsep baru, sangat kompleks dan menawarkan sejumlah besar pengaturan untuk mengembangkan algoritma khusus untuk bekerja dengan tata letak halaman.

Hal pertama yang pertama, worklet harus didaftarkan.

registerLayout(‘exampleLayout’, class{
  static get inputProperties() { return [‘--example-variable’] }

  static get childrenInputProperties() { return [‘--exampleChildVariable’] }

  static get layoutOptions(){
    return {
      childDisplay: ‘normal’,
      sizing: ‘block-like’
    }
  }

  intrinsicSizes(children, edges, styleMap){
    // …
  }

  layout(children, edges, constraints, styleMap, breakToken){
    // …
  }
})

Mendaftarkan worklet mencakup metode berikut:

  • inputProperties: array properti CSS khusus yang ditonton oleh worklet dan milik orang tua, elemen yang menyebabkan tata letak untuk di-render. Array ini mewakili dependensi tata letak
  • childrenInputProperties: array properti CSS khusus yang ditonton oleh widget dan milik keturunan
  • layoutOptions: mendefinisikan properti tata letak berikut:

    • childDisplay: Nilai yang ditentukan sebelumnya adalah blok dan normal. Menentukan bagaimana item ditampilkan (blok atau garis)
    • sizing: Nilai-nilai yang ditentukan sebelumnya adalah seperti blok dan manual. Menentukan kebutuhan untuk perhitungan awal ukuran elemen (jika tidak ditentukan)
  • intrinsicSizes: mendefinisikan bagaimana wadah atau isinya ditampilkan dalam konteks tata letak:

    • children: child dari elemen yang menyebabkan tata letak halaman di-render
    • tepi: batas wadah
    • styleMap: model objek gaya wadah yang diketik
  • tata letak: fungsi utama untuk bekerja dengan tata letak:

    • anak-anak: elemen anak
    • ujung: batas
    • kendala: kendala yang dipaksakan oleh tata letak induk
    • styleMap: model objek gaya wadah yang diketik
    • breakToken: breakpoint untuk pagination atau print layout splitting

Selanjutnya, worklet ditambahkan ke file HTML atau JS.

CSS.layoutWorklet.addModule(‘path/to/worklet/file.js’)

Kami membuat tautan ke worklet dalam file dengan gaya.

.example-element {
  display: layout(exampleLayout)
}

Cara Layout API bekerja dengan tata letak


Pada contoh sebelumnya, kita mendefinisikan exampleLayout.

Elemen .example disebut tata letak induk, termasuk indentasi, batas, dan bilah geser. Tata letak induk terdiri dari elemen anak-anak yang disebut tata letak saat ini. Layout saat ini adalah elemen target yang layoutnya “dikustomisasi” menggunakan Layout API. Misalnya, ketika menggunakan "display: flex", keturunan elemen disusun ulang sesuai dengan tata letak yang fleksibel. Ini mirip dengan operasi Layout API.

Setiap tata letak saat ini terdiri dari tata letak anak yang berisi algoritma untuk merender tata letak elemen anak - LayoutChild (termasuk pseudo-class :: before dan :: after). LayoutChild - wadah yang dihasilkan oleh alat CSS yang berisi data tentang gaya (tanpa data tentang tata letak). Elemen LayoutChild secara otomatis dibuat oleh browser pada tahap penerapan gaya. Tata letak anak dapat membuat Fragmen yang berisi instruksi untuk merender tata letak.

Contoh


Contoh ini juga menggunakan repositori Google Chrome Labs , tetapi teks diganti dengan gambar. Lihat contoh kode di sini .



Deteksi


if(CSS.layoutWorklet){
  // …
}

Status Spesifikasi


Draf kerja publik pertama : siap untuk diskusi komunitas, dapat berubah di masa depan.

Dukung


ChromeTepiOperaFirefoxSafari
Dukungan sebagianDukungan sebagianDukungan sebagianTidak didukungTidak didukung

Houdini dan peningkatan progresif


Terlepas dari kenyataan bahwa Houdini saat ini tidak memiliki dukungan browser yang optimal, Houdini dapat digunakan untuk peningkatan progresif. Jika Anda tidak terbiasa dengan konsep peningkatan progresif, saya sarankan Anda untuk melihat artikel ini . Saat menggunakan Houdini, hal-hal berikut harus dipertimbangkan:

Identifikasi selalu dukungan untuk menghindari kesalahan.


Setiap Houdini API dan Worklet memiliki cara mudah untuk memeriksa aksesibilitas. Ini menghindari masalah menggunakan Houdini di browser yang belum mendukung teknologi ini.

Gunakan Houdini untuk meningkatkan tampilan dan visualisasi.


Pengguna yang menggunakan browser yang tidak mendukung Houdini harus memiliki akses ke konten dan fungsionalitas dasar situs. Pengalaman pengguna dan tampilan konten tidak boleh bergantung pada Houdini.

Gunakan CSS standar sebagai alternatif


Misalnya, properti CSS khusus dapat digunakan sebagai alternatif untuk API properti dan nilai kustom.

Fokus pada pengembangan aplikasi yang produktif dan andal, menggunakan Houdini untuk tujuan dekoratif hanya sebagai peningkatan progresif.

Kesimpulan


Houdini memungkinkan pengembang untuk menggunakan kode JS untuk bekerja dengan gaya dalam proses rendering, meningkatkan kinerja dan stabilitas aplikasi. Kemampuan untuk menyematkan kode dalam proses rendering memungkinkan Anda membuat polifah CSS yang mudah dibagikan kepada orang lain, berlaku, dan mungkin bahkan termasuk dalam spesifikasi. Selain itu, Houdini memungkinkan pengembang dan perancang untuk tidak terlalu bergantung pada pembatasan CSS saat bekerja dengan gaya, tata letak, dan animasi.

Houdini dapat digunakan sekarang, tetapi hanya sebagai peningkatan progresif. Ini akan memungkinkan browser yang tidak mendukung Houdini untuk menampilkan halaman tanpa kesalahan, memberikan pengalaman pengguna yang optimal.

Saya tidak bisa menunggu sampai komunitas pengembangan dapat sepenuhnya menikmati kemampuan Houdini. Berikut adalah beberapa contoh:

CSS Houdini Expirements
Pengantar Interaktif untuk CSS Houdini Houdini
Contoh dari Google Chrome Labs

Referensi


W3C Draft Spesifikasi Animasi Houdini
Houdini (Chrome Dev Summit 2018)
Worklet - Google Developers

Terima kasih atas perhatian Anda.

All Articles