Houdini - 揭開 CSS 的神秘面紗

您是否曾思考 CSS 供應商的工作量?您變更了單一屬性,但整個網站突然以不同的版面配置顯示。這就是「magic」類型。到目前為止,我們的網頁程式開發人員社群 還是見證了魔法的成真之處如果我們想提出自己的魔法 該怎麼辦?如果我們想成為魔術師,該怎麼做?

進入 Houdini!

Houdini 任務小組是由 Mozilla、Apple、Opera、Microsoft、HP、Intel 和 Google 的工程師攜手合作,為網頁開發人員提供 CSS 引擎的特定部分。任務小組正在處理一組草稿,目標是讓 W3C 接受這些草稿,成為實際的網路標準。他們為自己設定了幾個高階目標,並將其轉換成規格草稿,進而產生一組支援較低層級的規格草稿。

當其他人談論「Houdini」時,這些草稿的集合通常代表著作。在本文撰寫時,草稿清單不完整,部分草稿只是預留位置。

規格

小程式 (spec)

工作程式本身沒那麼實用。這些單元的概念 是為了使後續的草稿能夠派上用場如果您正在閱讀「worklet」時想到網路工作站,那並不算錯。他們的概念很重疊那麼在已有員工名單時 為何要做新事情呢?

Houdini 的目標是公開新的 API,讓網頁開發人員將自己的程式碼掛接到 CSS 引擎和周圍系統。假設部分程式碼片段必須執行 Every.Single. frame 是非常實用的特性,其中有些東西必須定義在定義上引用 Web Worker 規格

這表示網路工作人員無法因應 Houdini 的計畫,因此發明工作程式。工作程式使用 ES2015 類別來定義方法的集合,也就是由工作程式類型預先定義的方法簽名。它是輕量、短時間。

CSS Paint API (spec)

根據預設,Chrome 65 會啟用 Paint API。請參閱詳細簡介

合成器小程式

此處描述的 API 已過時。合成工作程式經過重新設計,現在建議使用「Animation Worklet」。進一步瞭解 API 目前的疊代作業

即使合成程式規格已移至 WICG 並經過疊代作業,這也是最有吸引力的規格。某些作業會由 CSS 引擎外包給電腦的顯示卡,不過一般來說,這取決於顯示卡和裝置。

瀏覽器通常會採用 DOM 樹狀結構,並根據特定條件決定為某些分支和子樹狀結構提供自己的圖層。這些子樹狀結構會自行繪製在上上 (未來或許使用顏料工作)。最後,所有這些人員 (現在的繪製作業) 會堆疊並相互堆疊,同時遵循 Z 索引、3D 轉換等動作,產生畫面上顯示的最終圖片。這個程序稱為「撰寫」,由合成器執行。

合成程序的好處是,當頁面捲動畫面微小時,您不必讓所有元素自行重新繪製。您可以改為重複使用前一個影格中的圖層,然後使用更新後的捲動位置重新執行合成器。這可以加快處理速度。這個做法會達到 60fps。

合成器小程式。

顧名思義,合成工作空間可讓您連結至合成器,並影響元素圖層的繪製方式、位置和分層的方式。

如需具體說明,您可以指示瀏覽器您要連結至特定 DOM 節點的合成程序,並要求存取特定屬性,例如捲動位置、transformopacity。這會強制此元素在其專屬的圖層上,且「每個影格」都會呼叫程式碼。如要移動圖層,您可以操控圖層轉換並變更其屬性 (例如 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)

已提議第一個真實規格草稿。實作並不容易。

再次強調,這個做法的規格幾乎是空的,但這個概念很有趣,那就是:編寫自己的版面配置!版面配置 Worklet 應可讓您執行 display: layout('myLayout') 並執行 JavaScript,在節點方塊中安排節點的子項。

當然,針對 CSS 的 flex-box 版面配置執行完整的 JavaScript 實作作業,比執行對等的原生實作速度慢,不過您可以想到,如果切入某個邊角就能提升效能。假設某個網站僅包含無資訊方塊,例如 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 物件模型或階層式樣式試算表物件模型) 則解決了我們所有人剛剛遇到的問題,以及所學到的技巧。讓我們說明一行 JavaScript:

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

我們正在進行計算,並將數字轉換為字串來附加一個單位,指示瀏覽器剖析該字串並將該字串轉換回 CSS 引擎的數字。使用 JavaScript 處理轉換時,這種做法會更難處理。不要再來了!CSS 即將輸入文字

這個草稿是較成熟的範本之一,而 polyfill 也已在開發中。(免責事項:使用 polyfill 顯然會增加「更多」運算負擔。重點是展示該 API 有多方便。)

您不必使用字串,而是處理元素的 StylePropertyMap,其中每個 CSS 屬性都有專屬的鍵和對應值類型。width 等屬性的值類型為 LengthValueLengthValue 是所有 CSS 單位 (例如 emrempxpercent 等) 的字典。設定 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"
    });
});

字型指標

字型指標就是其聽起來的內容。當我轉譯 Z 大小為 Z 格式的字串 X 時,定界框 (或定界框) 為何?如果我使用 ruby 註解,該怎麼辦?許多人都向我們提出這個要求 最後,Houdini 終於能實現這些願望

等一下,好處可不只這些!

在 Houdini 的草稿清單還有更多規格,但這些規格的未來並沒有定論,且其概念不再只是提案的預留位置。例如自訂溢位行為、CSS 語法擴充功能 API、原生捲動行為的延伸和類似宏大的夢想,這些都能實現網路平台先前無法辦到的事情。

試聽帶

我使用開放原始碼的示範程式碼 (使用 polyfill 的即時示範)。