ภาพรวมเชิงปฏิบัติของ CSS Houdini
เผยแพร่แล้ว: 2022-03-10ใช้เวลานานสำหรับคุณลักษณะ CSS ใหม่หรือการปรับปรุงเพื่อดำเนินการจากแบบร่างเริ่มต้นเป็นคุณลักษณะ CSS ที่ได้รับการสนับสนุนอย่างเต็มที่และมีเสถียรภาพซึ่งนักพัฒนาสามารถใช้ได้ polyfills ที่ใช้ JavaScript สามารถใช้แทนการขาดการสนับสนุนเบราว์เซอร์เพื่อใช้คุณสมบัติ CSS ใหม่ก่อนที่จะนำไปใช้อย่างเป็นทางการ แต่มีข้อบกพร่องในกรณีส่วนใหญ่ ตัวอย่างเช่น scrollsnap-polyfill เป็นหนึ่งในหลาย ๆ polyfill ที่สามารถใช้เพื่อแก้ไขความไม่สอดคล้องของการสนับสนุนเบราว์เซอร์สำหรับข้อกำหนด CSS Scroll Snap แต่ถึงกระนั้นวิธีแก้ปัญหานั้นก็มีข้อจำกัด ข้อบกพร่อง และไม่สอดคล้องกันบางประการ
ข้อเสียที่อาจเกิดขึ้นจากการใช้โพลีฟิลล์คือสามารถส่งผลเสียต่อประสิทธิภาพและยากที่จะนำไปใช้อย่างเหมาะสม ข้อเสียนี้เกี่ยวข้องกับ DOM และ CSSOM ของเบราว์เซอร์ เบราว์เซอร์สร้าง DOM (Document Object Model) จากมาร์กอัป HTML และสร้าง CSSOM (CSS Object Model) จากมาร์กอัป CSS ในทำนองเดียวกัน ต้นไม้วัตถุทั้งสองนี้เป็นอิสระจากกัน JavaScript ทำงานบน DOM และเข้าถึง CSSOM ได้อย่างจำกัด
โซลูชัน JavaScript Polyfill ทำงานหลังจากรอบการแสดงผลเริ่มต้นเสร็จสิ้นเท่านั้น เช่น เมื่อสร้างทั้ง DOM และ CSSOM และเอกสารโหลดเสร็จแล้ว หลังจากที่ Polyfill ทำการเปลี่ยนแปลงกับสไตล์ใน DOM (โดยใส่ไว้ใน DOM) จะทำให้กระบวนการแสดงผลทำงานอีกครั้งและแสดงผลทั้งหน้าใหม่ ผลกระทบด้านประสิทธิภาพเชิงลบจะชัดเจนยิ่งขึ้นหากพวกเขาอาศัย requestAnimationFrame
หรือขึ้นอยู่กับการโต้ตอบของผู้ใช้ เช่น เหตุการณ์การเลื่อน
อุปสรรคอีกประการหนึ่งในการพัฒนาเว็บคือ ข้อจำกัดต่างๆ ที่กำหนดโดยมาตรฐาน CSS ตัวอย่างเช่น มีพร็อพเพอร์ตี้ CSS ในจำนวนจำกัดที่สามารถเคลื่อนไหวโดยกำเนิดได้ CSS รู้วิธีทำให้สีเคลื่อนไหวโดยกำเนิด แต่ไม่รู้วิธีทำให้การไล่ระดับสีเคลื่อนไหว มีความจำเป็นเสมอที่จะคิดค้นและสร้างประสบการณ์เว็บที่น่าประทับใจด้วยการก้าวข้ามขอบเขตแม้จะมีข้อจำกัดทางเทคโนโลยีก็ตาม นั่นคือเหตุผลที่นักพัฒนามักจะมุ่งไปที่การใช้วิธีแก้ปัญหาที่น้อยกว่าอุดมคติหรือ JavaScript เพื่อปรับใช้สไตล์และเอฟเฟกต์ขั้นสูงที่ CSS ยังไม่รองรับในปัจจุบัน เช่น เลย์เอาต์ก่ออิฐ เอฟเฟกต์ 3D ขั้นสูง แอนิเมชั่นขั้นสูง การพิมพ์แบบไหล การไล่ระดับสีแบบเคลื่อนไหว องค์ประกอบที่ select
ที่มีสไตล์ ฯลฯ
ดูเหมือนว่า เป็นไปไม่ได้ที่ข้อกำหนด CSS จะรักษา ความต้องการคุณลักษณะต่างๆ จากอุตสาหกรรม เช่น การควบคุมภาพเคลื่อนไหว การตัดข้อความที่ได้รับการปรับปรุง ตัวเลือกการจัดสไตล์ที่ดีขึ้นสำหรับการ input
และ select
องค์ประกอบ ตัวเลือกการ display
ที่มากขึ้น ตัวเลือกตัว filter
ที่มากขึ้น เป็นต้น
อะไรคือทางออกที่เป็นไปได้? ให้นักพัฒนามี วิธีดั้งเดิมในการขยาย CSS โดยใช้ API ต่างๆ ในบทความนี้ เราจะมาดูกันว่านักพัฒนาส่วนหน้าสามารถทำได้โดยใช้ Houdini APIs, JavaScript และ CSS ได้อย่างไร ในแต่ละส่วน เราจะตรวจสอบ API แต่ละรายการ ตรวจสอบการรองรับเบราว์เซอร์และสถานะข้อกำหนดปัจจุบัน และดูวิธีการปรับใช้ในวันนี้โดยใช้การเพิ่มประสิทธิภาพแบบก้าวหน้า
ฮูดินี่คืออะไร?
Houdini เป็นคำที่ใช้เรียกรวมของ API ของเบราว์เซอร์ มีจุดมุ่งหมายเพื่อนำการปรับปรุงที่สำคัญมาสู่กระบวนการพัฒนาเว็บและการพัฒนามาตรฐาน CSS โดยทั่วไป นักพัฒนาจะสามารถขยาย CSS ด้วยคุณสมบัติใหม่โดยใช้ JavaScript เชื่อมต่อกับกลไกการแสดงผล CSS และบอกเบราว์เซอร์ถึงวิธีการใช้ CSS ในระหว่างกระบวนการแสดงผล ซึ่งจะส่งผลให้ประสิทธิภาพและความเสถียรดีขึ้นมากเมื่อเทียบกับการใช้โพลีฟิลปกติ
ข้อมูลจำเพาะของ Houdini ประกอบด้วยกลุ่ม API สองกลุ่ม - API ระดับสูง และ API ระดับ ต่ำ
API ระดับสูง มีความเกี่ยวข้องอย่างใกล้ชิดกับกระบวนการแสดงผลของเบราว์เซอร์ (สไตล์ → เลย์เอาต์ → เพ้นท์ → คอมโพสิต) ซึ่งรวมถึง:
- Paint API
จุดต่อขยายสำหรับขั้นตอนการแสดงสีของเบราว์เซอร์ซึ่งกำหนดคุณสมบัติการมองเห็น (สี พื้นหลัง เส้นขอบ ฯลฯ) - เค้าโครงAPI
จุดต่อขยายสำหรับขั้นตอนการแสดงผลเลย์เอาต์ของเบราว์เซอร์ซึ่งกำหนดมิติ ตำแหน่ง และการจัดตำแหน่งองค์ประกอบ - แอนิเมชั่น API
จุดต่อขยายสำหรับขั้นตอนการเรนเดอร์คอมโพสิตของเบราว์เซอร์ โดยที่เลเยอร์ต่างๆ จะถูกดึงไปที่หน้าจอและเคลื่อนไหว
API ระดับต่ำ เป็นรากฐานสำหรับ API ระดับสูง ซึ่งรวมถึง:
- พิมพ์วัตถุโมเดล API
- คุณสมบัติที่กำหนดเอง & ค่า API
- Font Metrics API
- งาน
Houdini API บางตัวพร้อมให้ใช้งานแล้วในเบราว์เซอร์บางตัวกับ API อื่นๆ เพื่อให้พร้อมสำหรับการเปิดตัว
อนาคตของ CSS
ซึ่งแตกต่างจากข้อกำหนดคุณลักษณะ CSS ทั่วไปที่ได้รับการแนะนำมาจนถึงตอนนี้ Houdini โดดเด่นโดยอนุญาตให้นักพัฒนาขยาย CSS ด้วยวิธีดั้งเดิมมากขึ้น นี่หมายความว่าข้อกำหนด CSS จะหยุดพัฒนาและจะไม่มีการนำคุณลักษณะ CSS ไปใช้อย่างเป็นทางการใหม่หรือไม่ นั่นไม่ใช่กรณี เป้าหมายของ Houdini คือการช่วยกระบวนการพัฒนาคุณลักษณะ CSS โดยอนุญาตให้นักพัฒนาสร้างต้นแบบการทำงานที่สามารถกำหนดมาตรฐานได้อย่างง่ายดาย
นอกจากนี้ นักพัฒนาจะสามารถแชร์โอเพ่นซอร์ส CSS Worklets ได้ง่ายขึ้นและไม่จำเป็นต้องแก้ไขจุดบกพร่องเฉพาะเบราว์เซอร์
พิมพ์วัตถุโมเดล API
ก่อนเปิดตัว Houdini วิธีเดียวสำหรับ JavaScript ในการโต้ตอบกับ CSS คือการแยกวิเคราะห์ CSS ที่แสดงเป็นค่าสตริงและแก้ไข การแยกวิเคราะห์และการแทนที่สไตล์ด้วยตนเองอาจทำได้ยากและเกิดข้อผิดพลาดได้ง่าย เนื่องจากต้องเปลี่ยนประเภทค่ากลับไปกลับมา และต้องผนวกหน่วยค่าด้วยตนเองเมื่อกำหนดค่าใหม่
selectedElement.style.fontSize = newFontSize + "px"; // newFontSize = 20 console.log(selectedElement.style.fontSize); // "20px"
Typed Object Model (Typed OM) API เพิ่มความหมายเชิงความหมายให้กับค่า CSS โดยเปิดเผยเป็นวัตถุ JavaScript ที่พิมพ์ ปรับปรุงโค้ดที่เกี่ยวข้องอย่างมาก และทำให้มีประสิทธิภาพ เสถียร และสามารถบำรุงรักษาได้มากขึ้น ค่า CSS จะแสดงโดยอินเทอร์เฟซ CSSUnitValue
ซึ่งประกอบด้วยค่าและคุณสมบัติของหน่วย
{ value: 20, unit: "px" }
อินเทอร์เฟซใหม่นี้สามารถใช้กับคุณสมบัติใหม่ดังต่อไปนี้:
-
computedStyleMap()
: สำหรับการแยกวิเคราะห์รูปแบบที่คำนวณแล้ว (ไม่ใช่แบบอินไลน์) นี่เป็นวิธีการขององค์ประกอบที่เลือกซึ่งจำเป็นต้องเรียกใช้ก่อนแยกวิเคราะห์หรือใช้วิธีอื่น -
attributeStyleMap
: สำหรับการแยกวิเคราะห์และแก้ไขสไตล์อินไลน์ นี่คือคุณสมบัติที่มีอยู่ในองค์ประกอบที่เลือก
// Get computed styles from stylesheet (initial value) selectedElement.computedStyleMap().get("font-size"); // { value: 20, unit: "px"} // Set inline styles selectedElement.attributeStyleMap.set("font-size", CSS.em(2)); // Sets inline style selectedElement.attributeStyleMap.set("color", "blue"); // Sets inline style // Computed style remains the same (initial value) selectedElement.computedStyleMap().get("font-size"); // { value: 20, unit: "px"} // Get new inline style selectedElement.attributeStyleMap.get("font-size"); // { value: 2, unit: "em"}
สังเกตว่ามีการใช้ประเภท CSS เฉพาะอย่างไรเมื่อตั้งค่าตัวเลขใหม่ เมื่อใช้ไวยากรณ์นี้ จะสามารถหลีกเลี่ยงปัญหาที่เกี่ยวข้องกับประเภทที่อาจเกิดขึ้นได้มากมาย และโค้ดที่ได้จะเชื่อถือได้และปราศจากข้อผิดพลาดมากขึ้น
เมธอด get
และ set
เป็นเพียงชุดย่อยเล็กๆ ของเมธอดที่มีอยู่ทั้งหมดที่กำหนดโดย Typed OM API บางส่วน ได้แก่ :
-
clear
: ลบสไตล์อินไลน์ทั้งหมด -
delete
: ลบคุณสมบัติ CSS ที่ระบุและค่าของมันออกจากสไตล์อินไลน์ -
has
: คืนค่าบูลีนหากคุณสมบัติ CSS ที่ระบุถูกตั้งค่า -
append
: เพิ่มค่าเพิ่มเติมให้กับคุณสมบัติที่รองรับหลายค่า - ฯลฯ
การตรวจจับคุณสมบัติ
var selectedElement = document.getElementById("example"); if(selectedElement.attributeStyleMap) { /* ... */ } if(selectedElement.computedStyleMap) { /* ... */ }
สถานะข้อมูลจำเพาะ W3C
- Working Draft: เผยแพร่เพื่อการตรวจสอบโดยชุมชน
รองรับเบราว์เซอร์
Google Chrome | Microsoft Edge | Opera Browser | Firefox | ซาฟารี |
---|---|---|---|---|
ได้รับการสนับสนุน | ได้รับการสนับสนุน | ได้รับการสนับสนุน | ไม่รองรับ | การสนับสนุนบางส่วน (*) |
* รองรับด้วย "คุณลักษณะแพลตฟอร์มเว็บทดลอง" หรือการตั้งค่าสถานะคุณลักษณะอื่น ๆ
แหล่งข้อมูล: ฮูดินี่ พร้อมยัง?
คุณสมบัติที่กำหนดเองและค่า API
คุณสมบัติ CSS และ API ค่า ช่วยให้นักพัฒนาขยายตัวแปร CSS โดยการเพิ่มประเภท ค่าเริ่มต้น และกำหนดมรดก นักพัฒนาสามารถกำหนดคุณสมบัติที่กำหนดเองของ CSS ได้โดยการลงทะเบียนโดยใช้เมธอด registerProperty
ซึ่งจะบอกเบราว์เซอร์ถึงวิธีการเปลี่ยนและจัดการทางเลือกในกรณีที่เกิดข้อผิดพลาด
CSS.registerProperty({ name: "--colorPrimary", syntax: "<color>", inherits: false, initialValue: "blue", });
เมธอดนี้ยอมรับอาร์กิวเมนต์อินพุตที่เป็นอ็อบเจ็กต์ที่มีคุณสมบัติดังต่อไปนี้:
-
name
: ชื่อของคุณสมบัติที่กำหนดเอง -
syntax
: บอกเบราว์เซอร์ถึงวิธีแยกวิเคราะห์คุณสมบัติที่กำหนดเอง ค่าเหล่านี้เป็นค่าที่กำหนดไว้ล่วงหน้า เช่น<color>
,<integer>
,<number>
,<length>
,<percentage>
เป็นต้น -
inherits
: บอกเบราว์เซอร์ว่าคุณสมบัติที่กำหนดเองรับช่วงค่าพาเรนต์หรือไม่ -
initialValue
: บอกค่าเริ่มต้นที่ใช้จนกว่าจะถูกแทนที่ และใช้เป็นทางเลือกสำรองในกรณีที่เกิดข้อผิดพลาด
ในตัวอย่างต่อไปนี้ กำลังตั้งค่าคุณสมบัติที่กำหนดเองประเภท <color>
คุณสมบัติที่กำหนดเองนี้จะถูกนำมาใช้ในการไล่ระดับสี คุณอาจกำลังคิดว่า CSS ปัจจุบันไม่สนับสนุนการเปลี่ยนภาพสำหรับการไล่ระดับสีพื้นหลัง และคุณคิดถูกแล้ว สังเกตว่าคุณสมบัติที่กำหนดเองนั้นถูกใช้อย่างไรใน transition
แทนที่จะเป็นคุณสมบัติ background
ที่จะใช้สำหรับการเปลี่ยน background-color
ปกติ
.gradientBox { background: linear-gradient(45deg, rgba(255,255,255,1) 0%, var(--colorPrimary) 60%); transition: --colorPrimary 0.5s ease; /* ... */ } .gradientBox:hover { --colorPrimary: red /* ... */ }
เบราว์เซอร์ไม่ทราบวิธีจัดการกับการไล่ระดับสี แต่รู้วิธีจัดการกับการเปลี่ยนสีเนื่องจากคุณสมบัติที่กำหนดเองถูกระบุเป็นประเภท <color>
บนเบราว์เซอร์ที่รองรับ Houdini การไล่ระดับสีจะเกิดขึ้นเมื่อวางองค์ประกอบไว้ เปอร์เซ็นต์ตำแหน่งการไล่สียังสามารถแทนที่ด้วยคุณสมบัติ CSS แบบกำหนดเอง (ลงทะเบียนเป็นประเภท <percentage>
) และเพิ่มไปยังการเปลี่ยนแปลงในลักษณะเดียวกับในตัวอย่าง
ถ้า registerProperty
ถูกลบและคุณสมบัติ CSS แบบกำหนดเองปกติถูกลงทะเบียนใน :root
ตัวเลือกการไล่ระดับสีจะไม่ทำงาน จำเป็นต้องใช้ registerProperty
เพื่อให้เบราว์เซอร์รู้ว่าควรถือว่าเป็นสี
ในการใช้งาน API นี้ในอนาคต เป็นไปได้ที่จะลงทะเบียนคุณสมบัติที่กำหนดเองใน CSS โดยตรง
@property --colorPrimary { syntax: "<color>"; inherits: false; initial-value: blue; }
ตัวอย่าง
ตัวอย่างง่ายๆ นี้แสดงการไล่ระดับสีและการเปลี่ยนสีของตำแหน่งบนเหตุการณ์โฮเวอร์โดยใช้คุณสมบัติกำหนดเอง CSS ที่ลงทะเบียนสำหรับสีและตำแหน่งตามลำดับ ซอร์สโค้ดที่สมบูรณ์มีอยู่ในที่เก็บตัวอย่าง
การตรวจจับคุณสมบัติ
if (CSS.registerProperty) { /* ... */ }
สถานะข้อมูลจำเพาะ W3C
- Working Draft: เผยแพร่เพื่อการตรวจสอบโดยชุมชน
รองรับเบราว์เซอร์
Google Chrome | Microsoft Edge | Opera Browser | Firefox | ซาฟารี |
---|---|---|---|---|
ได้รับการสนับสนุน | ได้รับการสนับสนุน | ได้รับการสนับสนุน | ไม่รองรับ | ไม่รองรับ |
แหล่งข้อมูล: ฮูดินี่ พร้อมยัง?
Font Metrics API
Font Metrics API ยังอยู่ในช่วงเริ่มต้นของการพัฒนา ดังนั้นข้อมูลจำเพาะอาจเปลี่ยนแปลงได้ในอนาคต ในฉบับร่างปัจจุบัน Font Metrics API จะจัดเตรียมวิธีการวัดขนาดขององค์ประกอบข้อความที่แสดงผลบนหน้าจอ เพื่อให้นักพัฒนาส่งผลต่อวิธีการแสดงองค์ประกอบข้อความบนหน้าจอ ค่าเหล่านี้อาจวัดได้ยากหรือเป็นไปไม่ได้ด้วยฟีเจอร์ปัจจุบัน ดังนั้น API นี้จะช่วยให้นักพัฒนาสร้างฟีเจอร์ CSS ที่เกี่ยวข้องกับข้อความและฟอนต์ได้ง่ายขึ้น การตัดข้อความไดนามิกแบบหลายบรรทัดเป็นตัวอย่างหนึ่งของคุณลักษณะเหล่านั้น
สถานะข้อมูลจำเพาะ W3C
- การรวบรวมแนวคิด: ยังไม่มีการส่งร่างข้อกำหนดในขณะนี้
รองรับเบราว์เซอร์
Google Chrome | Microsoft Edge | Opera Browser | Firefox | ซาฟารี |
---|---|---|---|---|
ไม่รองรับ | ไม่รองรับ | ไม่รองรับ | ไม่รองรับ | ไม่รองรับ |
แหล่งข้อมูล: ฮูดินี่ พร้อมยัง?
งาน
ก่อนที่จะย้ายไปยัง API อื่นๆ จำเป็นต้องอธิบายแนวคิด Worklets ก่อน เวิร์กเล็ต คือสคริปต์ที่ทำงานระหว่างการเรนเดอร์และไม่ขึ้นกับสภาพแวดล้อม JavaScript หลัก สิ่งเหล่านี้เป็นจุดต่อขยายสำหรับเอ็นจิ้นการเรนเดอร์ ได้รับการออกแบบมาสำหรับการทำงานคู่ขนาน (ที่มี 2 อินสแตนซ์ขึ้นไป) และเธรดที่ไม่เชื่อเรื่องพระเจ้า ลดการเข้าถึงขอบเขตส่วนกลางและถูกเรียกโดยเอ็นจิ้นการเรนเดอร์เมื่อจำเป็น เวิร์กเล็ตสามารถรันได้เฉพาะบน HTTPS (บนสภาพแวดล้อมที่ใช้งานจริง) หรือบนโลคัลโฮสต์ (เพื่อวัตถุประสงค์ในการพัฒนา)
Houdini แนะนำ Worklets ต่อไปนี้เพื่อขยายเอ็นจิ้นการเรนเดอร์เบราว์เซอร์:
- งานเพ้นท์ - Paint API
- Animation Worklet - Animation API
- Layout Worklet - Layout API
Paint API
Paint API ช่วยให้นักพัฒนาสามารถใช้ฟังก์ชัน JavaScript เพื่อวาดลงในพื้นหลัง เส้นขอบ หรือเนื้อหาขององค์ประกอบโดยตรงโดยใช้บริบทการแสดงผล 2 มิติ ซึ่งเป็นชุดย่อยของ HTML5 Canvas API Paint API ใช้ Paint Worklet เพื่อวาดภาพที่ตอบสนองต่อการเปลี่ยนแปลงใน CSS แบบไดนามิก (เช่น การเปลี่ยนแปลงในตัวแปร CSS เป็นต้น) ใครก็ตามที่คุ้นเคยกับ Canvas API จะรู้สึกเหมือนอยู่บ้านด้วย Paint API ของ Houdini
มีหลายขั้นตอนที่จำเป็นในการกำหนด Paint Worklet:
- เขียนและลงทะเบียน Paint Worklet โดยใช้ฟังก์ชัน
registerPaint
- เรียก Worklet ในไฟล์ HTML หรือไฟล์ JavaScript หลักโดยใช้ฟังก์ชัน
CSS.paintWorklet.addModule
- ใช้ฟังก์ชัน
paint()
ใน CSS พร้อมชื่อเวิร์กเล็ตและอาร์กิวเมนต์อินพุตเสริม
มาดูฟังก์ชัน registerPaint
ซึ่งใช้ในการลงทะเบียน Paint Worklet และกำหนดฟังก์ชันการทำงาน
registerPaint("paintWorketExample", class { static get inputProperties() { return ["--myVariable"]; } static get inputArguments() { return ["<color>"]; } static get contextOptions() { return {alpha: true}; } paint(ctx, size, properties, args) { /* ... */ } });
ฟังก์ชัน registerPaint
ประกอบด้วยหลายส่วน:
-
inputProperties
:
อาร์เรย์ของคุณสมบัติกำหนดเอง CSS ที่ Worklet จะติดตาม อาร์เรย์นี้แสดงถึงการขึ้นต่อกันของเวิร์กเล็ตสี -
inputArguments
:
อาร์เรย์ของอาร์กิวเมนต์อินพุตที่สามารถส่งผ่านจากฟังก์ชันpaint
จากภายใน CSS -
contextOptions
: อนุญาตหรือไม่อนุญาตความทึบของสี หากตั้งค่าfalse
สีทั้งหมดจะแสดงด้วยความทึบเต็มที่ -
paint
: ฟังก์ชั่นหลักที่ให้อาร์กิวเมนต์ต่อไปนี้:-
ctx
: บริบทการวาด 2D เกือบจะเหมือนกับบริบทการวาด 2D ของ Canvas API -
size
: วัตถุที่มีความกว้างและความสูงขององค์ประกอบ ค่าจะถูกกำหนดโดยกระบวนการแสดงผลโครงร่าง ขนาดผ้าใบเท่ากับขนาดจริงขององค์ประกอบ -
properties
: ตัวแปรอินพุตที่กำหนดในinputProperties
-
args
: อาร์เรย์ของอาร์กิวเมนต์อินพุตที่ส่งผ่านในฟังก์ชันpaint
ใน CSS
-
หลังจากลงทะเบียน Worklet แล้ว จะต้องเรียกใช้งานในไฟล์ HTML โดยเพียงแค่ระบุพาธไปยังไฟล์
CSS.paintWorklet.addModule("path/to/worklet/file.js");
คุณสามารถเพิ่ม Worklet ได้จาก URL ภายนอก (เช่น จากเครือข่ายการจัดส่งเนื้อหา) ซึ่งทำให้เป็นแบบโมดูลและนำกลับมาใช้ใหม่ได้
CSS.paintWorklet.addModule("https://url/to/worklet/file.js");
หลังจากที่เรียก Worklet แล้ว สามารถใช้ใน CSS ได้โดยใช้ฟังก์ชัน paint
ฟังก์ชันนี้ยอมรับชื่อที่ลงทะเบียนของ Worklet เป็นอาร์กิวเมนต์อินพุตแรก และอาร์กิวเมนต์อินพุตแต่ละตัวที่ตามมาคืออาร์กิวเมนต์แบบกำหนดเองที่สามารถส่งผ่านไปยัง Worklet ได้ (กำหนดไว้ภายใน inputArguments ของ inputArguments
) จากจุดนั้น เบราว์เซอร์จะกำหนดว่าเมื่อใดควรเรียกใช้ Worklet และการดำเนินการของผู้ใช้และการเปลี่ยนแปลงค่าคุณสมบัติที่กำหนดเองของ CSS เพื่อตอบสนองต่อ
.exampleElement { /* paintWorkletExample - name of the worklet blue - argument passed to a Worklet */ background-image: paint(paintWorketExample, blue); }
ตัวอย่าง
ตัวอย่างต่อไปนี้แสดง Paint API และการนำ Worklet กลับมาใช้ใหม่และโมดูลาร์ มันใช้ Ripple Worklet โดยตรงจากที่เก็บ Google Chrome Labs และทำงานบนองค์ประกอบอื่นที่มีสไตล์ต่างกัน ซอร์สโค้ดที่สมบูรณ์มีอยู่ในที่เก็บตัวอย่าง
การตรวจจับคุณสมบัติ
if ("paintWorklet" in CSS) { /* ... */ } @supports(background:paint(paintWorketExample)){ /* ... */ }
สถานะข้อมูลจำเพาะ W3C
- คำแนะนำผู้สมัคร: ร่างการทำงานที่มั่นคงพร้อมสำหรับการใช้งาน
รองรับเบราว์เซอร์
Google Chrome | Microsoft Edge | Opera Browser | Firefox | ซาฟารี |
---|---|---|---|---|
ได้รับการสนับสนุน | ได้รับการสนับสนุน | ได้รับการสนับสนุน | ไม่รองรับ | ไม่รองรับ |
แหล่งข้อมูล: ฮูดินี่ พร้อมยัง?
แอนิเมชั่น API
Animation API ขยายแอนิเมชั่นบนเว็บด้วยตัวเลือกในการฟังเหตุการณ์ต่างๆ (เลื่อน โฮเวอร์ คลิก ฯลฯ) และปรับปรุงประสิทธิภาพโดยการเรียกใช้แอนิเมชั่นบนเธรดเฉพาะของตนเองโดยใช้ Animation Worklet อนุญาตให้ผู้ใช้ดำเนินการเพื่อควบคุมการไหลของแอนิเมชั่นที่ทำงานอย่างมีประสิทธิภาพและไม่ปิดกั้น
เช่นเดียวกับ Worklet ใด ๆ Animation Worklet จะต้องลงทะเบียนก่อน
registerAnimator("animationWorkletExample", class { constructor(options) { /* ... */ } animate(currentTime, effect) { /* ... */ } });
คลาสนี้ประกอบด้วยสองหน้าที่:
-
constructor
: เรียกเมื่อมีการสร้างอินสแตนซ์ใหม่ ใช้สำหรับตั้งค่าทั่วไป -
animate
: ฟังก์ชันหลักที่มีตรรกะของแอนิเมชัน จัดเตรียมอาร์กิวเมนต์อินพุตต่อไปนี้:-
currentTime
: ค่าเวลาปัจจุบันจากไทม์ไลน์ที่กำหนด - เอ
effect
: อาร์เรย์ของเอฟเฟกต์ที่แอนิเมชั่นนี้ใช้
-
หลังจากลงทะเบียน Animation Worklet จะต้องรวมอยู่ใน ไฟล์ JavaScript หลัก ต้องมีการกำหนดแอนิเมชั่น (องค์ประกอบ คีย์เฟรม ตัวเลือก) และแอนิเมชั่นจะสร้างอินสแตนซ์ด้วยไทม์ไลน์ที่เลือก แนวคิดเกี่ยวกับไทม์ไลน์และข้อมูลพื้นฐานเกี่ยวกับแอนิเมชั่นบนเว็บจะอธิบายไว้ในส่วนถัดไป
/* Include Animation Worklet */ await CSS.animationWorklet.addModule("path/to/worklet/file.js");; /* Select element that's going to be animated */ const elementExample = document.getElementById("elementExample"); /* Define animation (effect) */ const effectExample = new KeyframeEffect( elementExample, /* Selected element that's going to be animated */ [ /* ... */ ], /* Animation keyframes */ { /* ... */ }, /* Animation options - duration, delay, iterations, etc. */ ); /* Create new WorkletAnimation instance and run it */ new WorkletAnimation( "animationWorkletExample" /* Worklet name */ effectExample, /* Animation (effect) timeline */ document.timeline, /* Input timeline */ {}, /* Options passed to constructor */ ).play(); /* Play animation */
การทำแผนที่ไทม์ไลน์
แอนิเมชั่นบนเว็บจะขึ้นอยู่กับไทม์ไลน์และ การแมปของเวลาปัจจุบันกับไทม์ไลน์ของเวลาท้องถิ่นของเอฟเฟ กต์ ตัวอย่างเช่น มาดูแอนิเมชั่นเชิงเส้นที่เกิดซ้ำซึ่งมี 3 คีย์เฟรม (เริ่มต้น กลาง และสุดท้าย) ที่ทำงาน 1 วินาทีหลังจากโหลดหน้าเว็บ (ล่าช้า) และด้วยระยะเวลา 4 วินาที
ไทม์ไลน์เอฟเฟกต์จากตัวอย่างจะมีลักษณะดังนี้ (ด้วยระยะเวลา 4 วินาทีโดยไม่มีการหน่วงเวลา):
ไทม์ไลน์เอฟเฟกต์ (ระยะเวลา 4 วินาที) | คีย์เฟรม |
---|---|
0ms | คีย์เฟรมแรก - เริ่มแอนิเมชั่น |
2000ms | คีย์เฟรมกลาง - กำลังดำเนินการแอนิเมชัน |
4000ms | คีย์เฟรมสุดท้าย - ภาพเคลื่อนไหวสิ้นสุดหรือรีเซ็ตเป็นคีย์เฟรมแรก |
เพื่อให้เข้าใจ effect.localTime
ได้ดีขึ้น โดยการตั้งค่าเป็น 3000ms (โดยคำนึงถึงการหน่วงเวลา 1000ms) ภาพเคลื่อนไหวที่ได้จะถูกล็อกไว้ที่คีย์เฟรมตรงกลางในไทม์ไลน์ของเอฟเฟกต์ (1000ms ล่าช้า + 2000ms สำหรับคีย์เฟรมกลาง) เอฟเฟกต์เดียวกันนี้จะเกิดขึ้นโดยการตั้งค่าเป็น 7000ms และ 11000ms เนื่องจากภาพเคลื่อนไหวจะเล่นซ้ำในช่วงเวลา 4000ms (ระยะเวลาของภาพเคลื่อนไหว)
animate(currentTime, effect) { effect.localTime = 3000; // 1000ms delay + 2000ms middle keyframe }
ไม่มีภาพเคลื่อนไหวเกิดขึ้นเมื่อมีค่า effect.localTime
คงที่ เนื่องจากภาพเคลื่อนไหวถูกล็อกในคีย์เฟรมเฉพาะ เพื่อให้องค์ประกอบเคลื่อนไหวได้อย่างถูกต้อง effect.localTime
จะต้องเป็นไดนามิก จำเป็นสำหรับค่าที่จะเป็นฟังก์ชันที่ขึ้นอยู่กับอาร์กิวเมนต์อินพุต currentTime
หรือตัวแปรอื่นๆ
รหัสต่อไปนี้แสดงการแสดงฟังก์ชันของการแมปไทม์ไลน์ 1:1 (ฟังก์ชันเชิงเส้น) เพื่อให้มีผลกับเวลาท้องถิ่น
animate(currentTime, effect) { effect.localTime = currentTime; // y = x linear function }
ไทม์ไลน์ ( document.timeline ) | เอฟเฟกต์แมปตามเวลาท้องถิ่น | คีย์เฟรม |
---|---|---|
startTime + 0ms (เวลาที่ผ่านไป) | startTime + 0ms | อันดับแรก |
startTime + 1000ms (เวลาที่ผ่านไป) | startTime + 1000ms (ดีเลย์) + 0ms | อันดับแรก |
startTime + 3000ms (เวลาที่ผ่านไป) | startTime + 1000ms (ดีเลย์) + 2000ms | กลาง |
startTime + 5000ms (เวลาที่ผ่านไป) | startTime + 1000ms (ดีเลย์) + 4000ms | สุดท้าย / ครั้งแรก |
startTime + 7000ms (เวลาที่ผ่านไป) | startTime + 1000ms (ดีเลย์) + 6000ms | กลาง |
startTime + 9000ms (เวลาที่ผ่านไป) | startTime + 1000ms (ดีเลย์) + 8000ms | สุดท้าย / ครั้งแรก |
ไทม์ไลน์ไม่ได้จำกัดเฉพาะการแมป 1:1 ตามเวลาท้องถิ่นของเอฟเฟกต์ Animation API ช่วยให้นักพัฒนา จัดการการแมปไทม์ไลน์ ใน animate
แอนิเมชั่นโดยใช้ฟังก์ชัน JavaScript มาตรฐานเพื่อสร้างไทม์ไลน์ที่ซับซ้อน แอนิเมชั่นไม่จำเป็นต้องทำงานเหมือนกันในแต่ละการวนซ้ำ (หากแอนิเมชั่นซ้ำ)
แอนิเมชั่นไม่จำเป็นต้องขึ้นอยู่กับไทม์ไลน์ของเอกสารซึ่งจะเริ่มนับมิลลิวินาทีนับจากเวลาที่โหลดเท่านั้น การดำเนินการของผู้ใช้ เช่น เหตุการณ์การเลื่อน สามารถใช้เป็นไทม์ไลน์สำหรับแอนิเมชันได้โดยใช้ออบเจกต์ ScrollTimeline
ตัวอย่างเช่น ภาพเคลื่อนไหวสามารถเริ่มได้เมื่อผู้ใช้เลื่อนไปที่ 200 พิกเซล และสามารถสิ้นสุดได้เมื่อผู้ใช้เลื่อนไปที่ 800 พิกเซลบนหน้าจอ
const scrollTimelineExample = new ScrollTimeline({ scrollSource: scrollElement, /* DOM element whose scrolling action is being tracked */ orientation: "vertical", /* Scroll direction */ startScrollOffset: "200px", /* Beginning of the scroll timeline */ endScrollOffset: "800px", /* Ending of the scroll timeline */ timeRange: 1200, /* Time duration to be mapped to scroll values*/ fill: "forwards" /* Animation fill mode */ }); ...
แอนิเมชั่นจะปรับให้เข้ากับความเร็วในการเลื่อนของผู้ใช้โดยอัตโนมัติและยังคงความราบรื่นและตอบสนองได้ดี เนื่องจาก Animation Worklets ทำงานนอกเธรดหลักและเชื่อมต่อกับเอ็นจินการเรนเดอร์ของเบราว์เซอร์ แอนิเมชั่นที่ต้องอาศัยการเลื่อนของผู้ใช้จึงสามารถทำงานได้อย่างราบรื่นและมีประสิทธิภาพมาก
ตัวอย่าง
ตัวอย่างต่อไปนี้แสดงวิธีการใช้ไทม์ไลน์ที่ไม่เป็นเชิงเส้น ใช้ฟังก์ชัน Gaussian ที่แก้ไขแล้ว และใช้ภาพเคลื่อนไหวการแปลและการหมุนในไทม์ไลน์เดียวกัน ซอร์สโค้ดที่สมบูรณ์มีอยู่ในที่เก็บตัวอย่าง
การตรวจจับคุณสมบัติ
if (CSS.animationWorklet) { /* ... */ }
สถานะข้อมูลจำเพาะ W3C
- ร่างการทำงานสาธารณะฉบับแรก: พร้อมสำหรับการตรวจสอบโดยชุมชนมีแนวโน้มที่จะเปลี่ยนแปลงข้อกำหนด
รองรับเบราว์เซอร์
Google Chrome | Microsoft Edge | Opera Browser | Firefox | ซาฟารี |
---|---|---|---|---|
การสนับสนุนบางส่วน (*) | การสนับสนุนบางส่วน (*) | การสนับสนุนบางส่วน (*) | ไม่รองรับ | ไม่รองรับ |
* รองรับด้วยการเปิดใช้งานแฟล็ก "คุณลักษณะแพลตฟอร์มเว็บทดลอง"
แหล่งข้อมูล: ฮูดินี่ พร้อมยัง?
เค้าโครงAPI
Layout API ช่วยให้นักพัฒนาสามารถขยายกระบวนการแสดงเค้าโครงของเบราว์เซอร์ได้โดยการกำหนดโหมดรูปแบบใหม่ที่สามารถใช้ในคุณสมบัติ CSS ที่ display
ได้ Layout API นำเสนอแนวคิดใหม่ ซับซ้อนมาก และมีตัวเลือกมากมายสำหรับการพัฒนาอัลกอริธึมเค้าโครงแบบกำหนดเอง
เช่นเดียวกับ Worklets อื่นๆ โครงร่าง Worklet จะต้องได้รับการลงทะเบียนและกำหนดไว้ก่อน
registerLayout('exampleLayout', class { static get inputProperties() { return ['--exampleVariable']; } static get childrenInputProperties() { return ['--exampleChildVariable']; } static get layoutOptions() { return { childDisplay: 'normal', sizing: 'block-like' }; } intrinsicSizes(children, edges, styleMap) { /* ... */ } layout(children, edges, constraints, styleMap, breakToken) { /* ... */ } });
Worklet register มีวิธีการดังต่อไปนี้:
-
inputProperties
:
อาร์เรย์ของคุณสมบัติ CSS แบบกำหนดเองที่ Worklet จะติดตามซึ่งเป็นขององค์ประกอบเค้าโครงหลัก นั่นคือองค์ประกอบที่เรียกใช้เค้าโครงนี้ อาร์เรย์นี้แสดงถึงการพึ่งพาของ Layout Worklet -
childrenInputProperties
:
อาร์เรย์ของคุณสมบัติ CSS แบบกำหนดเองที่ Worklet จะติดตามว่าเป็นขององค์ประกอบลูกขององค์ประกอบเค้าโครงหลัก เช่น ลูกขององค์ประกอบที่ตั้งค่าเค้าโครงนี้ -
layoutOptions
: กำหนดคุณสมบัติโครงร่างต่อไปนี้:-
childDisplay
: สามารถมีค่าที่กำหนดไว้ล่วงหน้าของblock
หรือnormal
กำหนดว่ากล่องจะแสดงเป็นบล็อกหรืออินไลน์ -
sizing
: สามารถมีค่าที่กำหนดไว้ล่วงหน้าของblock-like
หรือmanual
โดยจะบอกเบราว์เซอร์ให้คำนวณขนาดล่วงหน้าหรือไม่คำนวณล่วงหน้า (เว้นแต่จะกำหนดขนาดไว้อย่างชัดเจน) ตามลำดับ
-
-
intrinsicSizes
: กำหนดว่ากล่องหรือเนื้อหาของกล่องเหมาะสมกับบริบทของเค้าโครงอย่างไร-
children
: องค์ประกอบลูกขององค์ประกอบเค้าโครงหลัก เช่น ลูกขององค์ประกอบที่เรียกเค้าโครงนี้ -
edges
: เค้าโครง ขอบของกล่อง -
styleMap
: พิมพ์ลักษณะ OM ของกล่อง
-
-
layout
ย์เอาต์ : ฟังก์ชั่นหลักที่ทำเลย์เอาต์-
children
: องค์ประกอบลูกขององค์ประกอบเค้าโครงหลัก เช่น ลูกขององค์ประกอบที่เรียกเค้าโครงนี้ -
edges
: เค้าโครง ขอบของกล่อง -
constraints
: ข้อจำกัดของโครงร่างหลัก -
styleMap
: พิมพ์ลักษณะ OM ของกล่อง -
breakToken
: break token ใช้เพื่อดำเนินโครงร่างต่อในกรณีที่มีการแบ่งหน้าหรือการพิมพ์
-
เช่นเดียวกับในกรณีของ Paint API เอ็นจินการเรนเดอร์ของเบราว์เซอร์จะกำหนดว่าจะมีการเรียก paint Worklet เมื่อใด ต้องเพิ่มลงในไฟล์ HTML หรือ JavaScript หลักเท่านั้น
CSS.layoutWorklet.addModule('path/to/worklet/file.js');
และสุดท้าย จะต้องมีการอ้างอิงในไฟล์ CSS
.exampleElement { display: layout(exampleLayout); }
Layout API ดำเนินการ Layout อย่างไร
ในตัวอย่างก่อนหน้านี้ มีการกำหนด exampleLayout
โดยใช้ Layout API
.exampleElement { display: layout(exampleLayout); }
องค์ประกอบนี้เรียกว่า Parent Layout ที่ล้อมรอบด้วย Layout Edges ซึ่งประกอบด้วย paddings, borders และ scroll bar Parent Layout ประกอบด้วยองค์ประกอบย่อยที่เรียกว่า Current Layouts เลย์เอาต์ปัจจุบันคือองค์ประกอบเป้าหมายจริงที่สามารถปรับแต่งเลย์เอาต์ได้โดยใช้ Layout API ตัวอย่างเช่น เมื่อใช้ display: flex;
ในองค์ประกอบ ลูกๆ จะถูกจัดตำแหน่งใหม่เพื่อสร้างเค้าโครงแบบยืดหยุ่น ซึ่งคล้ายกับสิ่งที่กำลังทำกับ Layout API
เลย์เอาต์ ปัจจุบัน แต่ละอันประกอบด้วยเลย์เอาต์ย่อยซึ่งเป็นอัลกอริธึมเลย์เอาต์สำหรับ LayoutChild (องค์ประกอบ ::before
และ ::after
องค์ประกอบหลอก) และ เลย์เอาต์ชิลด์ คือกล่อง CSS ที่สร้างขึ้นซึ่งมีเฉพาะข้อมูลสไตล์ (ไม่มีข้อมูลเลย์เอาต์) องค์ประกอบ LayoutChild ถูกสร้างขึ้นโดยอัตโนมัติโดยเครื่องมือแสดงผลของเบราว์เซอร์ในขั้นตอนสไตล์ Layout Child สามารถสร้าง Fragment ที่ดำเนินการแสดงเลย์เอาต์ได้จริง
ตัวอย่าง
เช่นเดียวกับตัวอย่าง Paint API ตัวอย่างนี้กำลังนำเข้า Worklet ของเลย์เอาต์ก่ออิฐโดยตรงจากที่เก็บ Google Chrome Labs แต่ในตัวอย่างนี้ ใช้กับเนื้อหารูปภาพแทนที่จะเป็นข้อความ ซอร์สโค้ดที่สมบูรณ์มีอยู่ในที่เก็บตัวอย่าง
การตรวจจับคุณสมบัติ
if (CSS.layoutWorklet) { /* ... */ }
สถานะข้อมูลจำเพาะ W3C
- ร่างการทำงานสาธารณะฉบับแรก: พร้อมสำหรับการตรวจสอบโดยชุมชนมีแนวโน้มที่จะเปลี่ยนแปลงข้อกำหนด
รองรับเบราว์เซอร์
Google Chrome | Microsoft Edge | Opera Browser | Firefox | ซาฟารี |
---|---|---|---|---|
การสนับสนุนบางส่วน (*) | การสนับสนุนบางส่วน (*) | การสนับสนุนบางส่วน (*) | ไม่รองรับ | ไม่รองรับ |
* รองรับด้วยการเปิดใช้งานแฟล็ก "คุณลักษณะแพลตฟอร์มเว็บทดลอง"
แหล่งข้อมูล: ฮูดินี่ พร้อมยัง?
ฮูดินี่และการพัฒนาที่ก้าวหน้า
แม้ว่า CSS Houdini จะยังไม่มีการสนับสนุนเบราว์เซอร์ที่เหมาะสมที่สุด แต่ก็สามารถใช้ได้ในปัจจุบันโดยคำนึงถึงการเพิ่มประสิทธิภาพแบบก้าวหน้า หากคุณไม่คุ้นเคยกับการเพิ่มประสิทธิภาพแบบก้าวหน้า คุณควรอ่านบทความที่มีประโยชน์นี้ซึ่งอธิบายได้ดีมาก หากคุณตัดสินใจที่จะนำ Houdini ไปใช้ในโครงการของคุณในวันนี้ มีบางสิ่งที่ควรคำนึงถึง:
- ใช้การตรวจจับคุณสมบัติเพื่อป้องกันข้อผิดพลาด
Houdini API และ Worklet แต่ละรายการมีวิธีง่ายๆ ในการตรวจสอบว่ามีให้บริการในเบราว์เซอร์หรือไม่ ใช้การตรวจหาคุณสมบัติเพื่อใช้การเพิ่มประสิทธิภาพ Houdini กับเบราว์เซอร์ที่สนับสนุนและหลีกเลี่ยงข้อผิดพลาดเท่านั้น - ใช้สำหรับการนำเสนอและการปรับปรุงภาพเท่านั้น
ผู้ใช้ที่กำลังเรียกดูเว็บไซต์บนเบราว์เซอร์ที่ยังไม่สนับสนุน Houdini ควรมีสิทธิ์เข้าถึงเนื้อหาและฟังก์ชันหลักของเว็บไซต์ ประสบการณ์ของผู้ใช้และการนำเสนอเนื้อหาไม่ควรขึ้นอยู่กับคุณลักษณะของ Houdini และควรมีทางเลือกที่น่าเชื่อถือ - ใช้ประโยชน์จากทางเลือก CSS มาตรฐาน
ตัวอย่างเช่น CSS Custom Properties ปกติสามารถใช้เป็นทางเลือกสำหรับสไตล์ที่กำหนดโดยใช้ Custom Properties & Values API
มุ่งเน้นที่การพัฒนาประสบการณ์ผู้ใช้เว็บไซต์ที่มีประสิทธิภาพและเชื่อถือได้ก่อน จากนั้นจึงใช้ฟีเจอร์ของ Houdini เพื่อการตกแต่งเพื่อเป็นการเพิ่มประสิทธิภาพที่ก้าวหน้า
บทสรุป
ในที่สุด Houdini APIs จะช่วยให้นักพัฒนาสามารถเก็บโค้ด JavaScript ที่ใช้สำหรับการจัดการสไตล์และการตกแต่งให้ใกล้เคียงกับไปป์ไลน์การเรนเดอร์ของเบราว์เซอร์ ส่งผลให้ประสิทธิภาพและความเสถียรดีขึ้น ด้วยการอนุญาตให้นักพัฒนาเชื่อมโยงเข้าสู่กระบวนการแสดงผลของเบราว์เซอร์ พวกเขาจะสามารถพัฒนา CSS polyfills ต่างๆ ที่สามารถแชร์ ใช้งาน และเพิ่มลงในข้อกำหนด CSS ได้ Houdini จะทำให้นักพัฒนาและนักออกแบบถูกจำกัดด้วยข้อจำกัด CSS น้อยลงเมื่อทำงานกับสไตล์ เลย์เอาต์ และแอนิเมชั่น ส่งผลให้ได้รับประสบการณ์เว็บใหม่ๆ ที่น่าพึงพอใจ
คุณสามารถเพิ่มคุณสมบัติ CSS Houdini ให้กับโปรเจ็กต์ได้ในปัจจุบัน แต่ควรคำนึงถึงการปรับปรุงแบบก้าวหน้าอย่างเคร่งครัด สิ่งนี้จะเปิดใช้งานเบราว์เซอร์ที่ไม่รองรับคุณสมบัติ Houdini เพื่อแสดงผลเว็บไซต์โดยไม่มีข้อผิดพลาดและมอบประสบการณ์การใช้งานที่ดีที่สุด
น่าตื่นเต้นที่จะได้ชมสิ่งที่ชุมชนนักพัฒนาซอฟต์แวร์จะเกิดขึ้นเมื่อ Houdini ได้รับแรงฉุดลากและการสนับสนุนเบราว์เซอร์ที่ดีขึ้น นี่คือตัวอย่างที่ยอดเยี่ยมของการทดลอง Houdini API จากชุมชน:
- CSS Houdini Experiments
- บทนำเชิงโต้ตอบเกี่ยวกับ CSS Houdini
- ตัวอย่าง Houdini โดย Google Chrome Labs
อ้างอิง
- ร่างข้อกำหนดของ W3C Houdini
- State of Houdini (Chrome Dev Summit 2018)
- งานอนิเมชั่นของ Houdini - Google Developers
- บทนำเชิงโต้ตอบเกี่ยวกับ CSS Houdini