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

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

Houdini を導入しましょう。

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

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

仕様

ワークレット(仕様

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

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

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

レイアウト ワークレット(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(仕様

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

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

ここでは数値を文字列に変換して単位を追加し、ブラウザでその文字列を解析し、CSS エンジンで使用する数値に変換します。JavaScript で変換を操作すると、さらに複雑になります。CSS に入力を追加します。

このドラフトは、より成熟したドラフトの 1 つで、ポリフィルがすでに開発中です。(免責条項: ポリフィルを使用すると、計算のオーバーヘッドがさらに増加することは明らかです。ポイントは、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}

プロパティと値

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 をレンダリングする場合、境界ボックス(または境界ボックス)は何ですか?Ruby アノテーションを使用する場合はどうなりますか?これは多くのユーザーからリクエストされていた機能で、Houdini でついに実現しました。

他にもあります。

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

デモ

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