めろたんのアレ

色々書いてくやつ

CSS Houdini をつかってキミだけの最強レイアウトを組み立てよう!

うぇ〜い

昨日は年を忘れるための飲み会、通称「忘年会」に参加していました。 慌てて記事を書いています。*1

この記事は Misoca+弥生+ALTOA Advent Calendar 2018 - Qiita 8日目の記事です。 昨日はt_haraさんの「Kotlinの.isInitialized呼び出しになぜCallableReferenceの指定が必要なのか」でした。

blog.thara.jp

明日は めろたんさん が「なんかがんばります」だそうです。 これは期待できますよ・・・!

去年と同じパターンだ。

CSS Houdini

皆さんがCSSを書くと、なかなかうまくスタイルを当てることができなかったり、そもそもブラウザによっては対応していないことが多くあると思います。

また少し変えただけのはずが、様々な場所がズレたりしてしまいとてもつらいと感じたことが多々あるでしょう。 まるで"魔法"だな。 と思うことも多々あると思います。

みなさんも魔法使いたいですよね?

そこで Houdini というものがあります!

Houdiniで何ができるかって言うと、ざっくり雑に言うとJSでCSSを拡張できます。 すごいですね。

多くの仕様がまだDraftであったりするので、実戦投入は厳しいですが、面白い技術ではありますし、夢があるので紹介しようかな。と思います。

なにができる?

いろいろあるのですが、

  • CSS の値に型を付ける
  • カスタムプロパティを登録する
    • 型の指定や、デフォルト値の設定等ができる
  • CSS を定義する
  • CSSの レイアウト を定義する
  • アニメーションを制御する

などなどができます。

型をつけるとかはクソ便利なのでさっさと実戦で使えるようになるといいなと思っています。

で今回はそれの説明をしても、あまり映えないのでレイアウトを定義するのをやってみましょう。

キミだけの最強のレイアウト作ろう

でこのレイアウトを定義する仕様が CSS Layout API Level 1 になります。

これはほとんどの?ブラウザで動かないので、Chrome Canary で確認します。

Chrome Canaryを起動後 chrome://flags/, chrome://flags/ にアクセスして Experimental Web Platform features を有効にします。

これで使えるようになるはずです。

ではざっとコードをはります。

index.html

<style>
  .container {
    width: 50px;
    height: 50px;
    display: layout(sample-layout);
    padding: 100px;
    border: solid 2px;
  }
</style>
<div class="container">
  <div>左上</div>
  <div>真ん中</div>
  <div>右下</div>
</div>
<script type="text/javascript">
  CSS.layoutWorklet.addModule('layout.js');
</script>

layout.js

registerLayout('sample-layout', class {
  *intrinsicSizes(children, edges, styleMap) {}

  *layout(children, edges, constraints, styleMap, breakToken) {
    const childFragments = yield children.map((child) => {
      return child.layoutNextFragment(constraints);
    });

    childFragments.forEach((fragment, i) => {
      fragment.inlineOffset = constraints.fixedInlineSize * i / childFragments.length;
      fragment.blockOffset = constraints.fixedBlockSize * i / childFragments.length;
    });

    return {
      childFragments
    };
  }
});

CSS Layout API の使用例
バーン

動かすとこんなかんじになります。

スタイルには対して斜めに配置するようなものは書いていませんが、HoudiniのLayout APIを使うとこういう事ができます。

なにやってるのか簡単に説明

CSS.layoutWorklet.addModule というので指定したファイルを Layout Workletスクリプトとして追加されます。
Worklet というのがメインのJS実行環境とは別に実行されるJSで Web Worker と似たものです。(雑)
ですが、スレッドには依存しなかったり等ちょっと違います。(雑)
詳しくは Worklets Level 1 で。

registerLayout で レイアウト名と実際にどうレイアウトするのかを定義したクラスを渡します。 ここで設定したレイアウトを使うには CSSlayout: 設定したレイアウト名; とすると使えます。

intrinsicSizeslayout がジェネレータ関数として定義されている必要があります。 将来的には Promiseになるかも?みたいな感じっぽいですが、今はジェネレータです。

intrinsicSizes でそのレイアウトのサイズを返します。ですが、今回は特に使わないので無視!

layout で小要素をどうレイアウトしていくかを書いていきます。 今回は引数のうち children, constraints をつかってます。

children が小要素ですね。 constrains はそのレイアウトの制約、利用可能なスペースとかが取得できます。

layoutNextFragmentCSSフラグメント を取得します。 CSSフラグメントには inlineOffset, blockOffset 等があります。 inlineOffset で横の位置、 blockOffset で縦の位置を設定できます。

constrainsfixedInlineSize でそのレイアウトが適用される要素の横幅、 fixedBlockSize でそのレイアウトが適用される要素の縦幅が取得できます。

コードを見ると、

childFragments.forEach((fragment, i) => {
  fragment.inlineOffset = constraints.fixedInlineSize * i / childFragments.length;
  fragment.blockOffset = constraints.fixedBlockSize * i / childFragments.length;
});

とあるので、小要素を順に少しずつずらして言ってることがわかりますね。

これでさっきあったようになるわけですね〜〜

わけわかんない?
僕もよくわかない。

気づいた人もいるかもしれませんがこのレイアウトを当ててる要素にはpaddingがあったのですが、どうみても無視されてますね。
このLayout API を使うとそれらも無視することができます。 強いですね。

まとめ

だいぶたのしいですね。あとCSS Houdini ヤバイ(意味深)という感じがしますね!

とりあえず、こんな感じで自作のレイアウトを組み立てられるので、みなさんも自分の手で

「キミだけの最強レイアウトを組み立てよう!!!」

次回予告

明日は、和食@yoshoku さんが 「Numo::NArrayでなにか実装します」とのことです!
きっとすごいなにかが実装されますよ!!!

余談または蛇足

技術書典5雑に 書いて出しました。

techbookfest.org

150部刷って30部くらいしかでなかったのでめちゃくちゃレアですよ! 欲しい人がいたらリプとかDMください…。

twitter.com

こちらまで

*1:去年と同じことしてる…。学ばないやつだ。