CodePenエディタのような可変グリッドで、拡張可能なグリッドがほしかったので作ってみました。
詳しい話
templateでは可変グリッドのCSS書くのが大変になったのでrenderを使ったりして色々勉強になりました。という話です。
可変グリッド
ロジックは、sparklinlabs/resize-handle: Robust resize handles / splitters for your HTML5 apps and websitesをまるっと参考にしました。
CSSはflexboxを使っていて、リサイズイベントはJavaScriptで以下のような感じで制御してます
- リサイズバーのmousedownイベント発火
- サイズを変える要素を特定、初期値記録
- キャプチャーを制限 or イベントの対象をwindowオブジェクトに
- mousemoveとmouseupイベントを追加
- mousemoveでサイズ変更
- mouseupでmousemoveとmouseupイベント削除
本家のソースコードは上下左右に対応した汎用的なコードなのですが、今回作ったものは決め打ちのハードコードになっています。
CSS(flexbox)
縦横グリッドなので、flex(column)>flex(row)の入れ子になってます。
実際にはグリッド内の要素にも適用しているので、flex(column)>flex(folumn)>flex(row)>flex(row)になっています。
今回やって思ったのは、flexboxはネストして使うと便利ということでした。
普段からCSSをもりもり書いているわけではないのでflexboxはトップレベルに近いところで使うと勝手に思っていたのですが、ネストするとスッキリするケースもあるので複雑になりすぎない程度にはネストさせると良いと思いました。
レイアウト
参考にした可変グリッドがグリッド要素とリサイズバーが同レベルに並んでいる構成になっているのですが、v-forを使うとどうしてもroot要素が必要になるので同じDOM構成にならないという問題がありました。
どういうことかというと、こうしたい
<div class="grid">...<div>
<div class="resizer"><div>
<div class="grid">...<div>
<div class="resizer"><div>
けど、v-forでやるとこうなります。
<div> <!-- v-forで増える要素 -->
<div class="grid">...<div>
<div class="resizer"><div>
</div>
<div> <!-- v-forで増える要素 -->
<div class="grid">...<div>
<div class="resizer"><div>
</div>
DOMの描画をもっと細かく制御したいと思ってたら、Vue.jsにはrender関数という低レイヤでDOM描画を扱える関数があるのでtemplateの代わりにそちらを使いました。
今回のような$dataのリストに対してリストの間に別のDOMを挟み込みたいというような込み入った構造を作りたいときに、このようなレイヤの低めなAPIが提供されているのは良いなぁと思いました。当たり前かもしれませが、$dataと紐付いたDOMは$dataと連動して書き換えられます。
デメリットとしてはコード量がtemplateを使用する時と比べてかなり増えます、10倍くらいになった気がします。
問題点ぽいところ
要素を追加するときは良い感じのサイズで追加されてほしいのでflexboxで追加してるのですが、リサイズするときはflexから外して固定サイズにしてるので、リサイズ前とリサイズ後で挙動が変わってしまいますが今のところ解決策を思いついてないのでそのままです。
Show comments