ฮูดินี่ - ไขข้อข้องใจเกี่ยวกับ CSS

คุณเคยคิดถึงจำนวนงาน CSS ที่ทำไหม คุณเปลี่ยนแอตทริบิวต์เดียว แล้วจู่ๆ ทั้งเว็บไซต์ก็ปรากฏในเลย์เอาต์อื่น เป็นเวทมนตร์ ที่ผ่านมาเรา ซึ่งก็คือชุมชนนักพัฒนาเว็บ ทำได้เพียงพบเห็นและสังเกตความมหัศจรรย์ ถ้าเราต้องการสร้าง พลังพิเศษของเราล่ะ ถ้าเราอยากเป็นนักมายากลล่ะ

เข้าสู่ฮูดินี่!

คณะทำงานของ Houdini ประกอบด้วยวิศวกรจาก Mozilla, Apple, Opera,Microsoft, HP, Intel และ Google ที่ทำงานร่วมกันเพื่อเปิดตัวเครื่องมือ CSS บางส่วนให้แก่นักพัฒนาเว็บ ทีมงานดังกล่าวกำลังทำคอลเล็กชันฉบับร่าง โดยมีเป้าหมายให้ W3C ยอมรับและกลายเป็นมาตรฐานเว็บจริง โดยได้ตั้งเป้าหมายระดับสูง 2-3 ข้อด้วยตนเอง แล้วเปลี่ยนเป้าหมายเหล่านั้นเป็นฉบับร่างที่เจาะจง ซึ่งก็เกิดเป็นชุดข้อกำหนดฉบับร่างที่รองรับในระดับต่ำกว่า

คอลเล็กชันฉบับร่างเหล่านี้มักมีไว้เพื่อพูดถึง "ฮูดินี่" ในขณะที่เขียน รายการแบบร่างไม่สมบูรณ์ และแบบร่างบางส่วนเป็นเพียงตัวยึดตำแหน่ง

ข้อกำหนด

Worklet (spec)

Worklet เพียงอย่างเดียวไม่มีประโยชน์มากนัก สิ่งเหล่านี้เป็นแนวคิดที่นำมาใช้เพื่อทำให้ ร่างคำตอบแบบถัดๆ ไปได้หลายๆ แบบ ถ้าคุณนึกถึง Web Workers เมื่ออ่าน "เวิร์กเลต" คุณก็ไม่ผิด แต่มีแนวคิดที่คาบเกี่ยวกันอยู่มาก แล้วทำไมต้องเป็น สิ่งใหม่ที่เรามีคนทำงานอยู่แล้ว

เป้าหมายของ Houdini คือการเปิดตัว API ใหม่ๆ เพื่อให้นักพัฒนาเว็บสามารถเชื่อมโยงโค้ดของตนเองเข้ากับเครื่องมือ CSS และระบบการทำงานโดยรอบ คุณอาจคิดว่า Fragment ของโค้ดเหล่านี้บางส่วนจะต้องเรียกใช้ Every.Single.frame บางรูปแบบก็มีข้อจำกัด อ้างอิงจากข้อกำหนดของ Web Worker ดังนี้

นั่นหมายความว่าเว็บผู้ปฏิบัติงานไม่มีความสามารถในการทำสิ่งต่างๆ ที่ Houdini วางแผนจะทำ ดังนั้น จึงมีงานประดิษฐ์ขึ้น Worklet ใช้คลาส ES2015 เพื่อกำหนดคอลเล็กชันเมธอด ซึ่งเป็นลายเซ็นที่มีการกำหนดไว้ล่วงหน้าตามประเภทของเวิร์กเลต มีน้ำหนักเบาและอายุสั้น

CSS Paint API (spec)

Paint API จะเปิดใช้โดยค่าเริ่มต้นใน Chrome 65 อ่านข้อมูลเบื้องต้นโดยละเอียด

เวิร์กเล็ตแบบผสม

API ที่อธิบายไว้ที่นี่ล้าสมัยแล้ว เวิร์กเลตแบบผสมมีการออกแบบใหม่และมีการเสนอเป็น "งาน Animation Worklet" อ่านเพิ่มเติมเกี่ยวกับการปรับปรุง API ในปัจจุบัน

แม้ว่าสเปคเครื่องทำคอมโพสิเตอร์จะเปลี่ยนไปใช้ WICG แล้ว และจะทำซ้ำ แต่เป็นข้อมูลจำเพาะที่ทำให้ฉันตื่นเต้นมากที่สุด การดำเนินการบางอย่างจะเป็นเครื่องมือ CSS สำหรับการ์ดแสดงผลของคอมพิวเตอร์ของคุณ แม้ว่าจะขึ้นอยู่กับทั้งการ์ดกราฟิกและอุปกรณ์โดยทั่วไปก็ตาม

เบราว์เซอร์มักจะใช้ต้นไม้ DOM และตัดสินใจว่าจะให้สาขาและซับทรีชั้นของตนเองตามเกณฑ์เฉพาะ โครงสร้างย่อยเหล่านี้จะระบายสีตัวเองลงบนต้นไม้ (อาจจะใช้เวิร์กเล็ตสีในอนาคต) ในขั้นตอนสุดท้าย องค์ประกอบทั้งหมดที่ลงสีแล้วในปัจจุบันจะเป็นเลเยอร์วางซ้อนกัน และวางตำแหน่งทับกัน โดยพิจารณาจากดัชนี Z, การเปลี่ยนรูปแบบ 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 สำหรับเวิร์กเลตเครื่องมือประกอบ เพื่อให้คุณสามารถลองใช้ ซึ่งเห็นได้ชัดว่ามีประสิทธิภาพสูงกว่ามาก

เวิร์กเล็ตของเลย์เอาต์ (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 ที่พิมพ์ (spec)

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

พร็อพเพอร์ตี้และค่า

(spec)

คุณรู้จักพร็อพเพอร์ตี้ที่กำหนดเองของ 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 มีรายละเอียดมากยิ่งขึ้น แต่อนาคตของทั้ง 2 แบบนั้น ไม่แน่นอนและเป็นมากกว่าตัวยึดตำแหน่งสำหรับไอเดีย ตัวอย่างเช่น พฤติกรรมรายการเพิ่มเติมที่กำหนดเอง, API ส่วนขยายไวยากรณ์ CSS, ส่วนขยายของลักษณะการเลื่อนแบบเนทีฟ และสิ่งที่ท้าทายในทำนองเดียวกัน ทั้งหมดนี้ทำให้สิ่งต่างๆ บนแพลตฟอร์มเว็บทำงานอย่างที่ไม่เคยทำได้มาก่อน

เดโม

ฉันได้เปิดให้โค้ดการสาธิตเป็นแบบโอเพนซอร์ส (การสาธิตแบบสดโดยใช้ Polyfill)