Houdini – Làm sáng tỏ CSS

Bạn đã bao giờ nghĩ đến lượng công việc mà CSS cần làm 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 ở một bố cục khác. Đó là một loại kỳ diệu. Cho đến nay, chúng tôi – cộng đồng các nhà phát triển web – mới có thể chứng kiến và quan sát sự kỳ diệu này. Nếu chúng ta muốn nảy ra điều kỳ diệu của chính mình thì sao? Nếu chúng ta muốn trở thành nhà ảo thuật thì sao?

Tiến vào Houdini!

Lực lượng chuyên trách của Houdini bao gồm các kỹ sư của Mozilla, Apple, Opera, Microsoft, HP, Intel và Google. Họ phối hợp với nhau để cung cấp một số phần của công cụ CSS cho các nhà phát triển web. Lực lượng chuyên trách đang nỗ lực để thu thập các bản nháp với mục tiêu là khiến W3C chấp nhận các bản nháp đó để trở thành tiêu chuẩn web thực sự. Họ đặt ra cho mình 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ừ đó tạo ra một tập hợp các bản nháp thông số kỹ thuật hỗ trợ ở cấp thấp hơn.

Tập hợp các bản nháp này thường được dùng khi ai đó nói về "Houdini". Tại thời điểm viết, danh sách bản nháp chưa đầy đủ và một số bản thảo chỉ là phần giữ chỗ.

Thông số kỹ thuật

Worklet (spec)

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

Mục tiêu của Houdini là đưa ra các API mới để cho phép các 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. Có thể không phi thực tế khi cho rằng một số mảnh mã trong số này sẽ phải chạy mọi khung hình. Một số biến phải theo định nghĩa. Trích dẫn thông số kỹ thuật của Trình chạy web:

Điều đó nghĩa là nhân viên web không thể thực hiện những việc mà Houdini dự định thực hiện. Do đó, worklet thật sự đã được phát minh. Worklet sử dụng các lớp ES2015 để xác định một tập hợp phương thức, chữ ký của các phương thức này đượ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)

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

Worklet trình tổng hợp

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

Mặc dù thông số worklet của trình tổng hợp đã được chuyển sang WICG và sẽ được lặp lại, nhưng đó là thông số khiến tôi thích nhất. Một số thao tác được công cụ CSS giao cho thẻ đồ hoạ của máy tính, mặc dù việc đó phụ thuộc vào cả thẻ đồ hoạ và thiết bị của bạn nói chung.

Một trình duyệt thường lấy cây DOM và dựa trên các tiêu chí cụ thể, quyết định cung cấp cho một số nhánh và cây con lớp riêng của chúng. Các cây con này tự vẽ lên đó (có thể sử dụng một worklet sơn 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ẽ) được xếp chồng và đặt lên nhau, tôn trọng các chỉ số z, biế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à tổng hợp và được 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ử tự vẽ 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 từ 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. Điều này giúp mọi thứ trở nên nhanh chóng. Điều này giúp chúng tôi đạt được tốc độ 60 khung hình/giây.

Worklet trình tổng hợp.

Đúng như tên gọi, 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 lớp của một phần tử (đã được vẽ) được đặt và xếp lớp 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 kết nối với quá 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 vào một lớp riêng và trên mỗi khung mà mã của bạn sẽ được gọi. Bạn có thể di chuyển lớp bằng cách thao tác với các lớp chuyển đổi và thay đổi các thuộc tính của lớp đó (như opacity) cho phép bạn thực hiện những điều thú vị hơn ở tốc độ khổng lồ 60 khung hình/giây.

Dưới đây là cách triển khai đầy đủ cho việc cuộn thị sai, bằng cách sử dụng worklet của bộ 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 worklet trình tổng hợp để bạn có thể dùng thử – rõ ràng là sẽ có 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. Còn khá lâu nữa để triển khai.

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

Tất nhiên, việc chạy triển khai JavaScript đầy đủ cho bố cục flex-box của CSS sẽ chậm hơn so với chạy triển khai gốc tương đương, nhưng 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 tốt hơn. Hãy tưởng tượng một trang web chỉ có các thẻ thông tin, như Windows 10 hoặc bố cục kiểu khối xây. Không 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 bao giờ chồng chéo hay có bất kỳ loại đường viền hoặc tràn lề nào. Việc có thể bỏ qua tất cả các bước kiểm tra này khi bố cục lại có thể mang lại mức tăng hiệu suất.

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 được nhập (Mô hình đối tượng CSS hoặc Mô hình đối tượng bảng định kiểu xếp chồng) giải quyết một vấn đề mà có thể tất cả chúng ta đều đã gặp phải và vừa tìm hiểu để 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 đang làm toán, chuyển đổi một số thành một chuỗi để nối một đơn vị chỉ để 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í còn xấu hơn khi bạn thao tác biến đổi bằng JavaScript. Không còn nữa! CSS sắp bắt đầu nhập.

Bản nháp này là một trong những bản nháp ổn định hơn và chúng tôi đang xử lý polyfill. (Tuyên bố từ chối trách nhiệm: Việc sử dụng polyfill rõ ràng sẽ làm tăng thêm chi phí tính toán thậm chí còn nhiều hơn nữa. Mục đích là để cho thấy API thuận tiện như thế nào.)

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à kiểu giá trị tương ứng riêng. Các thuộc tính như width có loại giá trị là LengthValue. LengthValue là từ điển của 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 nên 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 tuỳ chỉnh CSS (hoặc bí danh không chính thức của chúng là "Biến CSS") không? Đó là chúng nhưng có loại! 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 sẽ cho phép bạn không chỉ chỉ định một loại cho các biến, mà còn xác định giá trị mặc định và ảnh hưởng đến hành vi kế thừa bằng cách sử dụng API JavaScript. Về mặt kỹ thuật, điều 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, điều này 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ố phông chữ

Chỉ số phông chữ chính xác như tên gọi. 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 ở kích thước Z? Điều gì sẽ xảy ra nếu tôi sử dụng chú thích hồng ngọc? Mọi người đã yêu cầu rất nhiều và cuối cùng Houdini cũng đã thực hiện được mong muốn này.

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

Danh sách các 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 nháp đó thực sự không chắc chắn và không nhiều hơn là phần giữ chỗ cho các ý tưởng. Ví dụ: hành vi tràn tuỳ chỉnh, API tiện ích cú pháp CSS, phần mở rộng của hành vi cuộn gốc và những mục tiêu lớn tương tự, tất cả đều hỗ trợ những tính năng trên nền tảng web mà trước đây không thể làm được.

Bản thu thử

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