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

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

ป้อน Houdini!

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

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

ข้อกำหนดเฉพาะ

Worklet (spec)

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

เป้าหมายของ Houdini คือการเปิดตัว API ใหม่ เพื่อให้นักพัฒนาเว็บสามารถติดตั้งโค้ดของตนเองลงในเครื่องมือ CSS และระบบที่ครอบคลุม ไม่สมเหตุสมผลนักที่จะสันนิษฐานว่าส่วนย่อยของโค้ดเหล่านี้บางส่วนจะต้องเรียกใช้ทุกเฟรม แต่บางกรณีก็ต้อง ให้คำจำกัดความไว้ การถามข้อกำหนดของ Web Worker มีดังนี้

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

CSS Paint API (spec)

เปิดใช้ Paint API โดยค่าเริ่มต้นใน Chrome 65 อ่านคำแนะนำโดยละเอียด

Worklet คอมโพสิต

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

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

เบราว์เซอร์มักจะใช้แผนผัง DOM และตัดสินว่าจะให้ Branch และซับทรีเป็นเลเยอร์ของตนเองหรือไม่ โดยอิงตามเกณฑ์ที่เจาะจง ต้นไม้ย่อยเหล่านี้จะทาสีตัวเองทับบนต้นไม้ (อาจใช้โมเดลระบายสีในอนาคต) ในขั้นตอนสุดท้าย รูปภาพเหล่านี้ทั้งหมดจะถูกวาดขึ้นเลเยอร์ซ้อนกัน และวางตำแหน่งซ้อนกัน โดยปฏิบัติตามดัชนี z, การแปลงแบบ 3 มิติ และเพื่อให้รูปภาพสุดท้ายที่ปรากฏบนหน้าจอของคุณ กระบวนการนี้เรียกว่าการประกอบและดำเนินการโดยเครื่องมือประกอบ

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

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

เวิร์กเล็ตของคอมโพสิต (Compositor Worklet) จะช่วยให้คุณสามารถดึงดูดเข้าไปในเครื่องมือทำคอมโพสิต และส่งผลต่อวิธีที่เลเยอร์ขององค์ประกอบซึ่งได้ระบายสีไว้แล้ว จะกำหนดตำแหน่งและวางซ้อนบนเลเยอร์อื่นๆ

เพื่อให้เฉพาะเจาะจงมากขึ้นเล็กน้อย คุณระบุเบราว์เซอร์ได้ว่าต้องการเข้าสู่กระบวนการประกอบสำหรับโหนด DOM บางรายการและขอสิทธิ์เข้าถึงแอตทริบิวต์บางอย่าง เช่น ตำแหน่งการเลื่อน transform หรือ opacity ได้ ซึ่งจะบังคับให้องค์ประกอบนี้อยู่ในเลเยอร์ของตัวเองและในแต่ละเฟรมจะมีการเรียกใช้โค้ด คุณสามารถย้ายเลเยอร์ ด้วยการควบคุมเลเยอร์ที่เปลี่ยนรูปแบบและเปลี่ยนแอตทริบิวต์ของเลเยอร์ (เช่น opacity) ทำให้คุณสามารถทำสิ่งต่างๆ แฟนซีได้อย่างดั่งใจด้วยอัตรา 60 fps

นี่คือการใช้งานการเลื่อนพารัลแลกซ์อย่างเต็มรูปแบบโดยใช้เวิร์กเล็ต 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 ได้เขียน 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)

CSSOM (CSS Object Model หรือ Cascading Style Sheets Object Model) ที่พิมพ์เพื่อแก้ไขปัญหาที่เราอาจทุกคนต้องพบและเพิ่งเรียนรู้วิธีจัดการกับปัญหา ผมขออธิบายด้วยบรรทัด JavaScript:

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

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

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

เดโม

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