CSS Houdini をつかってキミだけの最強レイアウトを組み立てよう!
うぇ〜い
昨日は年を忘れるための飲み会、通称「忘年会」に参加していました。 慌てて記事を書いています。*1
この記事は Misoca+弥生+ALTOA Advent Calendar 2018 - Qiita 8日目の記事です。 昨日はt_haraさんの「Kotlinの.isInitialized呼び出しになぜCallableReferenceの指定が必要なのか」でした。
明日は めろたんさん が「なんかがんばります」だそうです。 これは期待できますよ・・・!
去年と同じパターンだ。
CSS Houdini
皆さんがCSSを書くと、なかなかうまくスタイルを当てることができなかったり、そもそもブラウザによっては対応していないことが多くあると思います。
また少し変えただけのはずが、様々な場所がズレたりしてしまいとてもつらいと感じたことが多々あるでしょう。 まるで"魔法"だな。 と思うことも多々あると思います。
みなさんも魔法使いたいですよね?
そこで Houdini というものがあります!
Houdiniで何ができるかって言うと、ざっくり雑に言うとJSでCSSを拡張できます。 すごいですね。
多くの仕様がまだDraftであったりするので、実戦投入は厳しいですが、面白い技術ではありますし、夢があるので紹介しようかな。と思います。
なにができる?
いろいろあるのですが、
などなどができます。
型をつけるとかはクソ便利なのでさっさと実戦で使えるようになるといいなと思っています。
で今回はそれの説明をしても、あまり映えないのでレイアウトを定義するのをやってみましょう。
キミだけの最強のレイアウト作ろう
でこのレイアウトを定義する仕様が 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 }; } });
動かすとこんなかんじになります。
スタイルには対して斜めに配置するようなものは書いていませんが、HoudiniのLayout APIを使うとこういう事ができます。
なにやってるのか簡単に説明
CSS.layoutWorklet.addModule
というので指定したファイルを Layout Worklet
のスクリプトとして追加されます。
Worklet
というのがメインのJS実行環境とは別に実行されるJSで Web Worker と似たものです。(雑)
ですが、スレッドには依存しなかったり等ちょっと違います。(雑)
詳しくは Worklets Level 1 で。
registerLayout
で レイアウト名と実際にどうレイアウトするのかを定義したクラスを渡します。
ここで設定したレイアウトを使うには CSS で layout: 設定したレイアウト名;
とすると使えます。
intrinsicSizes
と layout
がジェネレータ関数として定義されている必要があります。
将来的には Promiseになるかも?みたいな感じっぽいですが、今はジェネレータです。
intrinsicSizes
でそのレイアウトのサイズを返します。ですが、今回は特に使わないので無視!
layout
で小要素をどうレイアウトしていくかを書いていきます。
今回は引数のうち children
, constraints
をつかってます。
children
が小要素ですね。 constrains
はそのレイアウトの制約、利用可能なスペースとかが取得できます。
layoutNextFragment
で CSSフラグメント を取得します。
CSSフラグメントには inlineOffset
, blockOffset
等があります。
inlineOffset
で横の位置、 blockOffset
で縦の位置を設定できます。
constrains
の fixedInlineSize
でそのレイアウトが適用される要素の横幅、 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 に 雑に 書いて出しました。
150部刷って30部くらいしかでなかったのでめちゃくちゃレアですよ! 欲しい人がいたらリプとかDMください…。
こちらまで