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

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

Houdini を導入しましょう。

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

これらのドラフトのコレクションは、通常「Houdini」と表現されます。執筆時点では、下書きのリストは不完全であり、一部の下書きは単なるプレースホルダです。

仕様

ワークレット(仕様

ワークレット自体はそれほど有用ではありません。これらは、後続のドラフトの多くを可能にするために導入されたコンセプトです。「ワークレット」という言葉を読んでウェブ ワーカーを思い浮かべたのなら、それは間違いではありません。コンセプト的には多くの重複があります。すでにワーカーがいるのに、なぜ新しいものを導入するのでしょうか?

Houdini の目標は、新しい API を公開して、ウェブ デベロッパーが独自のコードを CSS エンジンと周辺システムに接続できるようにすることです。これらのコード フラグメントの一部はフレームごとに実行する必要があると考えるのは非現実的ではありません。定義上、一部は必須です。ウェブワーカーの仕様からの引用:

つまり、Houdini が予定している処理にはウェブワーカーは適していません。そのため、ワークレットが考案されました。ワークレットは ES2015 クラスを使用してメソッドのコレクションを定義します。メソッドのシグネチャは、ワークレットのタイプによって事前定義されます。軽量で存続期間が短い。

CSS Paint API(仕様

Paint API は Chrome 65 でデフォルトで有効になっています。詳細な概要を読む。

コンポジタ ワークレット

ここで説明する 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 がコンポーザ ワークレットのポリフィルを作成したので、試すことができます。ただし、パフォーマンスへの影響は大きくなります。

レイアウト ワークレット(仕様

最初の実際の仕様案が提案されました。実装はしばらく先です。

この仕様も実質的に空ですが、独自のレイアウトを記述するというコンセプトは興味深いものです。レイアウト ワークレットを使用すると、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(仕様

型付き CSSOM(CSS オブジェクト モデルまたは CSS カスケーディング スタイルシート オブジェクト モデル)は、誰もが遭遇し、我慢することを学んだ問題に対処します。次の JavaScript の行で説明します。

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

数学演算を行い、数値を文字列に変換して単位を追加し、ブラウザがその文字列を解析して CSS エンジン用に数値に変換できるようにしています。JavaScript で変換を操作すると、さらに複雑になります。CSS に入力を追加します。

このドラフトは比較的成熟しており、ポリフィルの開発もすでに進められています。(免責条項: ポリフィルを使用すると、計算のオーバーヘッドがさらに増加することは明らかです。ポイントは、API がいかに便利であるかを示すことである)。

文字列ではなく、要素の StylePropertyMap を操作します。各 CSS 属性には独自のキーと対応する値の型があります。width などの属性の値の型は LengthValue です。LengthValue は、emrempxpercent などのすべての CSS 単位の辞書です。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}

プロパティと値

仕様

CSS カスタム プロパティ(または非公式な別名「CSS 変数」)をご存じですか? これが、型付きのものです。これまで、変数には文字列値のみを設定でき、単純な検索と置換のアプローチを使用していました。このドラフトでは、変数の型を指定できるだけでなく、デフォルト値を定義し、JavaScript API を使用して継承動作に影響を与えることができます。技術的には、標準の CSS 遷移とアニメーションでカスタム プロパティをアニメーション化することもできます。これも現在検討中です。

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

フォント指標

フォント メトリックは、その名のとおり、文字列 X をフォント Y でサイズ Z でレンダリングした場合の境界ボックス(または境界ボックス)は何ですか?Ruby アノテーションを使用する場合はどうなりますか?これは多くのユーザーからリクエストされていた機能で、Houdini でついに実現しました。

他にもあります。

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

デモ

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