Houdini - 揭開 CSS 的神秘面紗

您是否曾經思考過 CSS 的工作量?您變更了單一屬性,網站突然以不同版面配置呈現。屬於魔術。到目前為止,我們 (網路開發人員) 社群 都目睹了並觀察也可以自行發想出自己的魔法嗎?如何成為魔術師

進入《Houdini》!

Houdini 工作小組包含來自 Mozilla、Apple、Opera、Microsoft、HP、Intel 和 Google 的工程師,致力向網頁程式開發人員公開 CSS 引擎的特定部分。工作團隊正在研究一系列草稿,目標是讓 W3C 接受這些草稿成為實際網路標準。他們為自己設定了幾個概略目標,並將其轉換為規格草稿,進而提供一組支援的較低層級規格草稿。

有人談論「Houdini」時,通常就是這些草稿的集合。在撰寫時,草稿清單不完整,有些草稿只是預留位置。

規格

工作流程 (spec)

單獨的運動程式並不實用,這些概念可以引進許多後續草稿如果您在閱讀「worklet」時想到網路工作人員,就沒有錯。因為創作者在概念上有許多重疊的概念既然我們已有工作站資料 為什麼要新增工作呢?

Houdini 的目標是提供新的 API,讓網頁程式開發人員能夠將自己的程式碼連結到 CSS 引擎和周圍系統。不太可能假設部分程式碼片段必須執行每個單一影格。有些則需自行定義引用網路工作站規格

這意味著網路工作人員無法執行 Houdini 的計畫。因此,我們發明瞭工作團隊。Worklet 會使用 ES2015 類別來定義方法集合,也就是由 Worklet 類型預先定義的簽名。這兩款裝置不僅輕巧,還具有時效性。

CSS Paint API (spec)

Chrome 65 預設啟用 Paint API。請參閱詳細介紹

合成器工作流程

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

雖然合成工作 let 規格已移至 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,讓您可以嘗試執行,顯然對效能的影響更大。

版面配置 Worklet (spec)

已提議第一個實際規格草稿。雖然離開後會逐漸實行,這是很好的選擇。

再次強調,這個規格的規格幾乎是空的,但這個概念很有趣,那就是自行編寫版面配置!版面配置工作流程應該要可讓您執行 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 物件模型或 Cascading StyleSheet Object Model) 可以解決我們可能遇到的問題,剛好可以解決這個情況。讓我來說明一行 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 尺寸使用 Y 字型顯示字串 X 時,定界框 (或定界框) 為何?如果我使用Rby 註解,會怎麼樣?這項要求一直出現大量要求 Houdini 終於能實現這些願望

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

Houdini 草稿清單也有更多規格,但這些規格的未來仍不甚明確,而且只是想法的預留位置。例如自訂溢位行為、CSS 語法擴充功能 API、原生捲動行為的擴充功能,以及建立前所未有的野心,讓網路平台的一切得以實現。

試聽帶

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