Houdini - CSS に関する誤解の解消

CSS が行う処理の量について考えたことはありますか?1 つの属性を変更すると、突然ウェブサイト全体が別のレイアウトで表示されます。まるでマジックです。今のところ、私たちウェブ デベロッパーのコミュニティは 魔法を目撃し、観察することしかできませんでした。独自の魔法を考案したい場合はどうすればよいでしょうか。マジシャンになりたいと思ったら?

Houdini の登場です!

Houdini タスクフォースは、Mozilla、Apple、Opera、Microsoft、HP、Intel、Google のエンジニアで構成されており、協力して CSS エンジンの特定部分をウェブ デベロッパーに公開しています。タスクフォースでは、W3C に実際のウェブ標準として受け入れられるために、ドラフトのコレクションに取り組んでいます。そして、いくつかの大まかな目標を設定し、それらを仕様のドラフトに変えて、サポートとなる一連のより低い仕様のドラフトを作成しました。

これらの下書きのコレクションは、通常、誰かが「Houdini」について話すときに意味するものです。このドキュメントを執筆している時点では、下書きのリストは不完全であり、一部の下書きは単なるプレースホルダです。

仕様

ワークレット(spec

ワークレット自体はあまり役に立ちません。これは、以降のドラフトの多くを可能にするために導入されたコンセプトです。「ワークレット」と聞いてウェブ ワーカーを思い浮かべたとしても、間違っているとは言えません。両者にはコンセプトの重複が多くあります。ワーカーがいるのに 新しいことになるのはなぜでしょうか

Houdini の目標は、ウェブ デベロッパーが独自のコードを CSS エンジンと周辺システムにフックできるように、新しい API を公開することです。これらのコード フラグメントの一部が単一のフレームごとに実行する必要があると考えるのは、おそらく非現実的ではありません。定義にそぐわないものもあるでしょう。Web Worker の仕様を引用すると、次のようになります。

つまり、Houdini で予定されていた作業にはウェブワーカーは実行できません。そのため、ワークレットが考案されました。ワークレットは ES2015 クラスを利用して、メソッドのコレクションを定義します。メソッドのコレクションは、ワークレットのタイプに応じて事前定義されています。軽量で短命です。

CSS Paint API(spec

Chrome 65 では、Paint API がデフォルトで有効になります。詳細な概要をご覧ください。

コンポジタ ワークレット

ここで説明する API はサポートが終了しています。コンポジタ ワークレットが再設計され、「アニメーション ワークレット」として提案されました。詳細については、API の現在のイテレーションをご覧ください。

コンポジタ ワークレット仕様は WICG に移行され、今後も繰り返されますが、この仕様について最も興味を引かれた点です。一部の操作は CSS エンジンによってコンピュータのグラフィック カードに外部委託されますが、一般的にはグラフィック カードとデバイスの両方に依存します。

通常、ブラウザは DOM ツリーを取得し、特定の条件に基づいて一部のブランチやサブツリーに独自のレイヤを割り当てます。これらのサブツリーは、自身をペイントします(今後、ペイント ワークレットが使用される可能性があります)。最後のステップとして、このようにペイントされた個々のレイヤがすべてスタックされ、Z インデックスや 3D 変換などに従って重なって配置されます。これにより、画面に表示される最終的な画像が生成されます。このプロセスは合成と呼ばれ、コンポジタによって実行されます。

合成プロセスの利点は、ページがわずかにスクロールしたときに、すべての要素を再描画する必要がないことです。代わりに、前のフレームのレイヤを再利用し、更新されたスクロール位置でコンポジタを再実行できます。これにより処理が速くなります。そうすれば 60 fps に到達できます。

コンポジタ ワークレット。

その名前が示すように、コンポジタ ワークレットを使用すると、コンポジタに接続し、すでにペイントされている要素のレイヤを他のレイヤの上に配置およびレイヤ化する方法に影響を与えることができます。

もう少し具体的に言うと、特定の 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

最初の実際の仕様ドラフトが提案されています。実装はすぐに終わります

この場合も、仕様は実際には空ですが、独自のレイアウトを作成するというコンセプトは興味深いものです。レイアウト ワークレットを使用すると、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 が入力しようとしています。

このドラフトは完成度の高いものの 1 つで、polyfillがすでに作業中です。(免責条項: ポリフィルを使用すると、明らかにさらに計算オーバーヘッドが増加します。重要なのは、API の使いやすさを示すことです)。

文字列ではなく、要素の StylePropertyMap で作業します。各 CSS 属性には、それぞれ独自のキーと、対応する値の型があります。width などの属性の値の型は LengthValue です。LengthValue は、すべての 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"
    });
});

フォントの指標

フォント指標はその名のとおりです。フォント Y でサイズ Z の文字列 X をレンダリングした場合の境界ボックス(または境界ボックス)は何ですか?Ruby アノテーションを使用するとどうなるでしょうか?多くのリクエストが寄せられたので、Houdini はついに願いをかなえようとしています。

他にもあります。

Houdini の下書きのリストにはさらに多くの仕様がありますが、その未来はかなり不確実であり、アイデアのプレースホルダにすぎません。たとえば、カスタムのオーバーフロー動作、CSS 構文拡張 API、ネイティブ スクロール動作の拡張、同様の野心的な機能などがあり、いずれもウェブ プラットフォームでこれまで不可能だった機能を実現しています。

デモ

デモ用のコードはオープンソース化しています(ポリフィルを使用したライブデモ)。