Houdini – Làm sáng tỏ CSS

Bạn đã bao giờ nghĩ về khối lượng công việc mà CSS thực hiện chưa? Bạn thay đổi một thuộc tính duy nhất và đột nhiên toàn bộ trang web của bạn xuất hiện theo một bố cục khác. Quả là ma thuật. Cho đến nay, chúng tôi, cộng đồng các nhà phát triển web, chỉ có thể chứng kiến và quan sát điều kỳ diệu đó. Nếu chúng ta muốn nghĩ ra phép màu của riêng mình thì sao? Nếu chúng ta muốn trở thành ảo thuật gia thì sao?

Nhập Houdini!

Nhóm đặc biệt Houdini bao gồm các kỹ sư của Mozilla, Apple, Opera, Microsoft, HP, Intel và Google làm việc cùng nhau để hiển thị một số phần của công cụ CSS cho các nhà phát triển web. Nhóm đặc biệt đang nghiên cứu tập hợp các bản nháp với mục tiêu làm cho các bản nháp đó được W3C chấp nhận trở thành các tiêu chuẩn web thực tế. Họ tự đặt ra một số mục tiêu cấp cao, biến các mục tiêu đó thành bản nháp thông số kỹ thuật, từ đó cho ra một nhóm bản nháp thông số kỹ thuật hỗ trợ ở cấp thấp hơn.

Bộ sưu tập các bản nháp này thường có ý nghĩa khi ai đó nói về "Houdini". Tại thời điểm viết, danh sách bản nháp chưa hoàn chỉnh và một số bản nháp chỉ là phần giữ chỗ.

Thông số kỹ thuật

Worklet (spec)

Bản thân các Workwork sẽ không thực sự hữu ích. Đây là một khái niệm được đưa ra để giúp nhiều bản nháp sau này có thể thực hiện. Nếu bạn nghĩ đến Web Worker khi đọc "worklet" thì bạn đã không nhầm. Hai vấn đề này có rất nhiều điểm trùng lặp về khái niệm. Vậy tại sao lại có một điều mới khi chúng ta đã có worker?

Mục tiêu của Houdini là cung cấp các API mới nhằm cho phép nhà phát triển web kết nối mã của riêng họ vào công cụ CSS và các hệ thống xung quanh. Không có gì phi thực tế khi giả định rằng một số đoạn mã trong số này sẽ phải chạy mọi khung hình. Một vài trong số đó phải tuân thủ định nghĩa. Trích dẫn thông số kỹ thuật Web Worker:

Điều đó có nghĩa là các nhân viên web không có khả năng thực hiện những việc Houdini dự định làm. Do đó, worker đã được phát minh. Worklet sử dụng các lớp ES2015 để xác định một tập hợp các phương thức, chữ ký trong đó được xác định trước theo loại worklet. Chúng nhẹ và tồn tại trong thời gian ngắn.

CSS Paint API (spec)

API Paint được bật theo mặc định trong Chrome 65. Hãy đọc phần giới thiệu chi tiết.

Worklet của bộ kết hợp

API được mô tả ở đây đã lỗi thời. Worklet của trình tổng hợp đã được thiết kế lại và hiện được đề xuất dưới dạng "Animation Worklet". Hãy đọc thêm về vòng lặp hiện tại của API.

Mặc dù thông số kỹ thuật của trình tổng hợp đã được chuyển sang WICG và sẽ được lặp lại, nhưng đây là thông số làm tôi hứng thú nhất. Một số thao tác do công cụ CSS thực hiện trên thẻ đồ hoạ của máy tính, mặc dù thao tác đó phụ thuộc vào cả thẻ đồ hoạ và thiết bị của bạn nói chung.

Trình duyệt thường lấy cây DOM và dựa trên các tiêu chí cụ thể, trình duyệt sẽ quyết định cung cấp một lớp riêng cho một số nhánh và cây con. Các cây con này tự vẽ lên trên đó (có thể sử dụng một worklet màu trong tương lai). Bước cuối cùng, tất cả các lớp riêng lẻ này (hiện đã được vẽ) này được xếp chồng và xếp chồng lên nhau, tuân theo các chỉ số z, chuyển đổi 3D, v.v. để tạo ra hình ảnh cuối cùng hiển thị trên màn hình của bạn. Quá trình này được gọi là kết hợp và do trình tổng hợp thực thi.

Ưu điểm của quá trình kết hợp là bạn không phải khiến tất cả các phần tử phải tự tô màu lại khi trang cuộn một chút. Thay vào đó, bạn có thể sử dụng lại các lớp trong khung trước và chỉ cần chạy lại trình tổng hợp với vị trí cuộn đã cập nhật. Việc này giúp mọi thứ diễn ra nhanh chóng. Điều này giúp chúng tôi đạt được tốc độ 60 khung hình/giây.

Worklet của trình tổng hợp.

Như cái tên cho thấy, worklet của trình tổng hợp cho phép bạn nối vào trình tổng hợp và tác động đến cách định vị và xếp lớp của một lớp thành phần đã được vẽ lên trên các lớp khác.

Để cụ thể hơn một chút, bạn có thể cho trình duyệt biết rằng bạn muốn chuyển vào quy trình kết hợp cho một nút DOM nhất định và có thể yêu cầu quyền truy cập vào một số thuộc tính nhất định như vị trí cuộn, transform hoặc opacity. Thao tác này buộc phần tử này nằm trên lớp riêng và trên mỗi khung, mã của bạn sẽ được gọi. Bạn có thể di chuyển lớp của mình bằng cách điều khiển các lớp biến đổi và thay đổi các thuộc tính của lớp đó (như opacity), nhờ đó bạn có thể thực hiện những tác vụ đẹp mắt với tốc độ lên tới 60 khung hình/giây.

Sau đây là cách triển khai đầy đủ cho thao tác cuộn thị sai bằng cách sử dụng công việc của trình tổng hợp.

// 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 đã viết một polyfill cho công việc của trình tổng hợp để bạn có thể dùng thử – rõ ràng là với tác động hiệu suất cao hơn nhiều.

Worklet bố cục (spec)

Bản nháp thông số kỹ thuật thực đầu tiên đã được đề xuất. Bạn nên triển khai trong khi chờ đợi.

Xin nhắc lại rằng thông số kỹ thuật của phần này gần như trống, nhưng khái niệm khá thú vị: hãy viết bố cục của riêng bạn! Worklet bố cục sẽ cho phép bạn thực hiện display: layout('myLayout') và chạy JavaScript để sắp xếp các phần tử con của nút trong hộp của nút.

Tất nhiên, việc chạy một bản triển khai JavaScript đầy đủ cho bố cục flex-box của CSS sẽ chậm hơn so với việc chạy một phương thức triển khai gốc tương đương. Tuy nhiên, bạn có thể dễ dàng hình dung tình huống trong đó việc cắt góc có thể mang lại hiệu suất. Hãy tưởng tượng một trang web chỉ có các ô như Windows 10 hoặc một bố cục kiểu khối xây. Bạn không được sử dụng vị trí tuyệt đối và cố định, cũng như z-index và các phần tử cũng không được chồng chéo hay có bất kỳ loại đường viền hoặc phần tràn nào. Việc có thể bỏ qua tất cả những bước kiểm tra này khi bố cục lại có thể mang lại hiệu suất cao.

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 đã nhập (spec)

CSSOM đã nhập (Mô hình đối tượng CSS hoặc Mô hình đối tượng biểu định kiểu phân tầng) giải quyết vấn đề mà có thể tất cả chúng ta đều gặp phải và mới học được cách giải quyết. Hãy để tôi minh hoạ bằng một dòng JavaScript:

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

Chúng ta sẽ làm toán, chuyển đổi một số thành một chuỗi để nối một đơn vị chỉ nhằm yêu cầu trình duyệt phân tích cú pháp chuỗi đó và chuyển đổi lại thành một số cho công cụ CSS. Điều này thậm chí sẽ trở nên xấu hơn khi bạn thao tác với các phép biến đổi bằng JavaScript. Không còn nữa! CSS sắp bắt đầu soạn tin nhắn.

Bản nháp này là một trong những bản nháp đã hoàn thiện hơn và polyfill đang trong quá trình hoàn thiện. (Tuyên bố từ chối trách nhiệm: Việc sử dụng polyfill hiển nhiên sẽ làm tăng thêm thậm chí chi phí tính toán hơn nữa). Vấn đề là phải cho thấy mức độ thuận tiện của API này.)

Thay vì chuỗi, bạn sẽ làm việc trên StylePropertyMap của một phần tử, trong đó mỗi thuộc tính CSS đều có khoá và loại giá trị tương ứng. Các thuộc tính như width có loại giá trị là LengthValue. LengthValue là từ điển cho tất cả các đơn vị CSS như em, rem, px, percent, v.v. Việc đặt height: calc(5px + 5%) sẽ tạo ra LengthValue{px: 5, percent: 5}. Một số thuộc tính như box-sizing chỉ chấp nhận một số từ khoá nhất định và do đó có loại giá trị KeywordValue. Sau đó, tính hợp lệ của các thuộc tính đó có thể được kiểm tra trong thời gian chạy.

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

Thuộc tính và giá trị

(spec)

Bạn có biết Thuộc tính tùy chỉnh CSS (hoặc bí danh không chính thức của chúng là "Biến CSS") không? Đây là nhưng với các kiểu! Cho đến nay, các biến chỉ có thể có giá trị chuỗi và sử dụng phương pháp tìm kiếm và thay thế đơn giản. Bản nháp này cho phép bạn không chỉ chỉ định loại cho các biến của mình, mà còn xác định giá trị mặc định và tác động đến hành vi kế thừa bằng cách sử dụng API JavaScript. Về mặt kỹ thuật, việc này cũng cho phép các thuộc tính tuỳ chỉnh tạo ảnh động bằng các hiệu ứng chuyển đổi và ảnh động CSS chuẩn cũng đang được xem xét.

["--scale-x", "--scale-y"].forEach(function(name) {
document.registerProperty({
    name: name,
    syntax: "<number>",
    inherits: false,
    initialValue: "1"
    });
});

Chỉ số về phông chữ

Chỉ số phông chữ chính xác như tên gọi của nó. Hộp giới hạn (hoặc hộp giới hạn) là gì khi tôi kết xuất chuỗi X có phông chữ Y ở cỡ chữ Z? Điều gì sẽ xảy ra nếu tôi sử dụng chú thích hồng ngọc? Chúng tôi đã nhận được rất nhiều yêu cầu này và cuối cùng Houdini cũng đã biến mong muốn đó thành hiện thực.

Tuy nhiên, hãy đợi vì còn nhiều thứ khác nữa!

Danh sách bản nháp của Houdini thậm chí còn có nhiều thông số kỹ thuật hơn, nhưng tương lai của những bản thảo đó khá là không chắc chắn và chúng không chỉ là phần giữ chỗ cho ý tưởng. Một số ví dụ bao gồm hành vi tràn tuỳ chỉnh, API tiện ích cú pháp CSS, tiện ích của hành vi cuộn gốc và những tính năng tương tự. Tất cả đều triển khai những tính năng mà trước đây không thể trên nền tảng web.

Bản thu thử

Tôi đã cấp nguồn mở cho đoạn mã cho bản minh hoạ (bản minh hoạ trực tiếp bằng polyfill).