เคยคิดไหมว่า CSS ทำงานหนักเพียงใด คุณเปลี่ยนแอตทริบิวต์เพียงตัวเดียว แล้วเว็บไซต์ทั้งเว็บไซต์ก็ปรากฏขึ้นในเลย์เอาต์อื่นทันที ช่างเป็นเวทมนตร์ ที่ผ่านมาเราซึ่งเป็นชุมชนของนักพัฒนาเว็บ ได้ได้เห็นและเฝ้าสังเกตความมหัศจรรย์เพียงอย่างเดียวเท่านั้น จะเป็นอย่างไรถ้าเราต้องการ คิดเวทมนตร์ของเราเอง ในกรณีที่เราต้องเป็นผู้วิเศษ
พบกับ Houdini
ทีมปฏิบัติการ Houdini ประกอบด้วยวิศวกรจาก Mozilla, Apple, Opera, Microsoft, HP, Intel และ Google ที่ทำงานร่วมกันเพื่อแสดงข้อมูลบางส่วนของเครื่องมือ CSS ต่อนักพัฒนาเว็บ คณะทำงานกำลังดำเนินการกับชุดร่างโดยมีเป้าหมายเพื่อให้ W3C ยอมรับร่างเหล่านี้เพื่อใช้เป็นมาตรฐานเว็บจริง พวกเขาตั้งเป้าหมายระดับสูงไว้ 2-3 รายการ แล้วเปลี่ยนเป็นร่างข้อกำหนด ซึ่งทำให้เกิดชุดร่างข้อกำหนดระดับล่างที่สนับสนุน
โดยทั่วไปแล้ว รายงานฉบับร่างเหล่านี้คือสิ่งที่คนพูดถึงเมื่อพูดถึง "Houdini" ขณะเขียนบทความนี้ รายการฉบับร่างยังไม่สมบูรณ์และฉบับร่างบางรายการเป็นเพียงตัวยึดตำแหน่ง
ข้อกำหนดเฉพาะ
Worklet (spec)
ชิ้นงานเพียงอย่างเดียวนั้นไม่ค่อยมีประโยชน์ แนวคิดเหล่านี้เป็นแนวคิดที่นำมาใช้ในภายหลังเพื่อทำให้ร่างคำขอจำนวนมากเป็นไปได้ ถ้าคุณนึกถึง Web Workers เมื่อคุณอ่าน "worklet" คุณไม่ผิด ซึ่งมีแนวคิดที่ทับซ้อนกันอยู่มากมาย แล้วทำไมเราถึงต้องสร้างสิ่งใหม่เมื่อมีแรงงานอยู่แล้ว
เป้าหมายของ Houdini คือการแสดง API ใหม่เพื่อช่วยให้นักพัฒนาเว็บสามารถเชื่อมต่อโค้ดของตนเองเข้ากับเครื่องมือ CSS และระบบรอบข้างได้ ไม่สมเหตุสมผลนักที่จะสันนิษฐานว่าส่วนย่อยของโค้ดเหล่านี้บางส่วนจะต้องเรียกใช้ทุกเฟรม บางรายการต้องระบุตามคำจำกัดความ ข้อความอ้างอิงจากข้อกําหนดของ Web Worker
ซึ่งหมายความว่า Web Worker ไม่เหมาะสําหรับสิ่งที่ Houdini วางแผนจะทำ เราจึงคิดค้น Worklet ขึ้นมา เวิร์กเลตใช้คลาส ES2015 เพื่อกำหนดชุดเมธอด ซึ่งลายเซ็นจะกำหนดไว้ล่วงหน้าโดยประเภทของเวิร์กเลต มีน้ำหนักเบาและมีอายุสั้น
CSS Paint API (ข้อกำหนด)
Paint API จะเปิดใช้โดยค่าเริ่มต้นใน Chrome 65 อ่านข้อมูลเบื้องต้นโดยละเอียด
Worklet คอมโพสเซอร์
API ที่อธิบายที่นี่ล้าสมัยแล้ว เราได้ออกแบบเวิร์กเลตคอมโพสิตใหม่และเสนอชื่อเป็น "เวิร์กเลตสำหรับแอนิเมชัน" อ่านเพิ่มเติมเกี่ยวกับการปรับปรุง API ในปัจจุบัน
แม้ว่าข้อกำหนดของเวิร์กเลตคอมโพสิตจะย้ายไปยัง WICG และจะมีการปรับปรุง แต่ข้อกำหนดนี้ก็เป็นข้อกำหนดที่เราตื่นเต้นมากที่สุด เครื่องมือ CSS จะส่งการดำเนินการบางอย่างไปยังการ์ดกราฟิกของคอมพิวเตอร์ แต่ทั้งนี้ขึ้นอยู่กับการ์ดกราฟิกและอุปกรณ์ของคุณโดยทั่วไป
เบราว์เซอร์มักจะใช้แผนผัง DOM และตัดสินว่าจะให้ Branch และซับทรีเป็นเลเยอร์ของตนเองหรือไม่ โดยอิงตามเกณฑ์ที่เจาะจง แผนภูมิย่อยเหล่านี้จะวาดตัวเองลงบนแผนภูมิ (อาจใช้เวิร์กเลตการวาดในอนาคต) ขั้นตอนสุดท้ายคือระบบจะวางซ้อนเลเยอร์ทั้งหมดที่วาดแล้วเหล่านี้และวางซ้อนกันโดยคำนึงถึง z-index, การเปลี่ยนรูปแบบ 3 มิติ และอื่นๆ เพื่อให้ได้รูปภาพสุดท้ายที่ปรากฏบนหน้าจอ กระบวนการนี้เรียกว่าการจัดวางและดำเนินการโดยคอมโพสิตอร์
ข้อดีของกระบวนการคอมโพสคือคุณไม่จําเป็นต้องทําให้องค์ประกอบทั้งหมดวาดภาพใหม่เมื่อหน้าเว็บเลื่อนไปเพียงเล็กน้อย แต่คุณใช้เลเยอร์จากเฟรมก่อนหน้าซ้ำได้ แล้วเรียกใช้คอมโพสิตอีกครั้งด้วยตําแหน่งการเลื่อนที่อัปเดต การดำเนินการนี้ช่วยให้สิ่งต่างๆ เป็นไปอย่างรวดเร็ว อัตรานี้ช่วยให้เราบรรลุถึง 60 fps
อย่างที่ชื่อบอกไว้ เวิร์กเลตคอมโพสิตอร์ให้คุณเชื่อมต่อกับคอมโพสิตอร์และส่งผลต่อวิธีวางตำแหน่งและวางเลเยอร์ขององค์ประกอบที่วาดไว้แล้วบนเลเยอร์อื่นๆ
หากต้องการเจาะจงมากขึ้น คุณสามารถบอกเบราว์เซอร์ว่าคุณต้องการเชื่อมต่อกับกระบวนการคอมโพสิตสำหรับโหนด DOM บางรายการ และสามารถขอสิทธิ์เข้าถึงแอตทริบิวต์บางอย่าง เช่น ตำแหน่งการเลื่อน, transform
หรือ opacity
ซึ่งจะบังคับให้องค์ประกอบนี้อยู่ในเลเยอร์ของตัวเองและในแต่ละเฟรมจะมีการเรียกใช้โค้ด คุณสามารถย้ายเลเยอร์ได้ด้วยการปรับเปลี่ยนการเปลี่ยนรูปแบบเลเยอร์และเปลี่ยนแอตทริบิวต์ (เช่น opacity
) ซึ่งจะช่วยให้คุณทำสิ่งต่างๆ ที่น่าสนใจได้แบบ 60 fps
ต่อไปนี้เป็นการใช้งานแบบเต็มสำหรับการเลื่อนแบบพารัลแลกซ์โดยใช้คอมโพสิเตอร์ เวิร์กเลต
// 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 ได้เขียน polyfill สำหรับเวิร์กเลตคอมโพสิตไว้ให้ลองใช้แล้ว ซึ่งแน่นอนว่าจะส่งผลต่อประสิทธิภาพได้ดีกว่ามาก
Worklet เลย์เอาต์ (spec)
มีการเสนอฉบับร่างข้อมูลจำเพาะจริงฉบับแรกแล้ว การนำไปใช้งานอาจได้ผลดี
อีกครั้ง ข้อมูลจำเพาะสำหรับข้อกำหนดนี้แทบจะว่างเปล่า แต่แนวคิดน่าสนใจมาก นั่นคือเขียนเลย์เอาต์ของคุณเอง เวิร์กเลตเลย์เอาต์ควรช่วยให้คุณทำ display: layout('myLayout')
และเรียกใช้ JavaScript เพื่อจัดเรียงรายการย่อยของโหนดในช่องของโหนดได้
แน่นอนว่า การใช้ JavaScript แบบเต็มรูปแบบในการจัดวาง flex-box
ของ CSS จะช้ากว่าการใช้เนทีฟที่เทียบเท่า แต่เราจินตนาการได้ง่ายว่าสถานการณ์ที่ตัดมุมอาจให้ประสิทธิภาพที่ดีขึ้น ลองจินตนาการถึงเว็บไซต์ที่มีแต่การ์ด เช่น Windows 10 หรือเลย์เอาต์สไตล์การก่อสร้าง ระบบจะไม่ใช้ตำแหน่งแบบสัมบูรณ์และคงที่ รวมถึง z-index
ด้วยเช่นกัน และองค์ประกอบไม่ซ้อนทับกันหรือมีเส้นขอบหรือล้นเกินประเภทใดๆ การข้ามการตรวจสอบทั้งหมดเหล่านี้เมื่อจัดเรียงใหม่อาจช่วยเพิ่มประสิทธิภาพได้
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
};
}
});
CSSOM ที่มีประเภท (ข้อกำหนด)
CSSOM แบบมีประเภท (CSS Object Model หรือ Cascading Style Sheets Object Model) จะช่วยแก้ปัญหาที่เราทุกคนอาจพบเจอและเรียนรู้ที่จะทนรับ เราขออธิบายด้วยบรรทัด JavaScript ต่อไปนี้
$('#someDiv').style.height = getRandomInt() + 'px';
เรากําลังทําคณิตศาสตร์ แปลงตัวเลขให้เป็นสตริงเพื่อต่อท้ายหน่วย เพื่อให้เบราว์เซอร์แยกวิเคราะห์สตริงนั้นและแปลงกลับเป็นตัวเลขสําหรับเครื่องมือ CSS ปัญหานี้จะยิ่งแย่ลงเมื่อคุณจัดการการเปลี่ยนรูปแบบด้วย JavaScript ไม่ต้องกังวลอีกต่อไป CSS กำลังจะพิมพ์
ฉบับร่างนี้เป็นหนึ่งในฉบับร่างที่สมบูรณ์มากขึ้นและเรากำลังพัฒนา polyfill อยู่ (ข้อจำกัดความรับผิด: การใช้ Polyfill จะเพิ่มค่าใช้จ่ายด้านการคำนวณมากยิ่งขึ้นอย่างเห็นได้ชัด จุดประสงค์คือเพื่อแสดงให้เห็นว่า API สะดวกเพียงใด)
คุณจะทํางานกับ StylePropertyMap
ขององค์ประกอบแทนสตริง โดยที่แอตทริบิวต์ CSS แต่ละรายการจะมีคีย์และประเภทค่าที่สอดคล้องกัน แอตทริบิวต์ เช่น width
มี LengthValue
เป็นประเภทค่า LengthValue
คือพจนานุกรมของหน่วย CSS ทั้งหมด เช่น em
, rem
, px
, percent
และอื่นๆ การตั้งค่า
height: calc(5px + 5%)
จะแสดงผลเป็น LengthValue{px: 5, percent: 5}
พร็อพเพอร์ตี้บางอย่าง เช่น box-sizing
จะยอมรับเฉพาะคีย์เวิร์ดบางรายการเท่านั้น จึงมีประเภทค่าเป็นKeywordValue
จากนั้นระบบจะตรวจสอบความถูกต้องของแอตทริบิวต์เหล่านั้นได้เมื่อรันไทม์
<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}
พร็อพเพอร์ตี้และค่า
(ข้อกำหนด)
คุณรู้จักพร็อพเพอร์ตี้ที่กำหนดเองของ CSS (หรือชื่อแทนที่ไม่เป็นทางการ "ตัวแปร CSS") ไหม และนี่ก็คือคำตอบประเภทต่างๆ! ที่ผ่านมา ตัวแปรมีได้เฉพาะค่าสตริงและใช้วิธีการค้นหาและแทนที่แบบง่าย ฉบับร่างนี้จะช่วยให้คุณระบุประเภทของตัวแปรได้ และยังกำหนดค่าเริ่มต้นและส่งผลต่อลักษณะการสืบทอดโดยใช้ JavaScript API ได้ด้วย ในทางเทคนิคแล้ว การดำเนินการนี้จะช่วยให้คุณสมบัติที่กำหนดเองสร้างภาพเคลื่อนไหวด้วยการเปลี่ยนและภาพเคลื่อนไหว CSS แบบมาตรฐาน ซึ่งก็กำลังได้รับการพิจารณาเช่นกัน
["--scale-x", "--scale-y"].forEach(function(name) {
document.registerProperty({
name: name,
syntax: "<number>",
inherits: false,
initialValue: "1"
});
});
เมตริกแบบอักษร
เมตริกแบบอักษรมีหน้าตาแบบนี้ กล่องขอบเขต (หรือกล่องขอบเขต) คืออะไรเมื่อฉันแสดงผลสตริง X ด้วยแบบอักษร Y ที่ขนาด Z แล้วถ้าฉันใช้คำอธิบายประกอบ Ruby ล่ะ ฟีเจอร์นี้ได้รับคำขอเข้ามาเป็นจำนวนมาก และ Houdini จะช่วยให้ความปรารถนาเหล่านี้เป็นจริงได้ในที่สุด
แต่ยังไม่หมดเท่านี้!
ยังมีข้อมูลจำเพาะอื่นๆ อีกมากมายในรายการฉบับร่างของ Houdini แต่อนาคตของข้อมูลจำเพาะเหล่านั้นค่อนข้างไม่แน่นอนและเป็นเพียงตัวยึดตําแหน่งสำหรับแนวคิดเท่านั้น ตัวอย่าง ได้แก่ ลักษณะการรองรับการแสดงผลที่มากเกินไปที่กำหนดเอง, API ส่วนขยายไวยากรณ์ CSS, ส่วนขยายลักษณะการเลื่อนแบบเนทีฟ และอื่นๆ อีกมากมาย ซึ่งทั้งหมดนี้ช่วยให้แพลตฟอร์มเว็บทำสิ่งต่างๆ ที่ไม่เคยทำได้มาก่อน
เดโม
เราได้เปิดแหล่งที่มาของโค้ดสําหรับเดโม (เดโมเวอร์ชันที่ใช้จริงโดยใช้ polyfill)