高さをなるべく揃えてくれる React Masonry コンポーネントライブラリを作った

公開:2020/12/02
更新:2020/12/02
1 min読了の目安(約1300字TECH技術記事

Masonry とは

サイズがまちまちなコンテンツをタイル状に並べるときに、縦を詰めるようにするレイアウト。

react-masonry-css では駄目なのか

react-masonry-css では要素の高さを計算しているわけではなく、単純に左上から順に並べている。この手法だと、ある程度の数が並ぶと各列の最後の要素の位置がずれてしまう。

つまり、

1 2 3
1 2
1

なら次の要素は 3 の下に来ないといけないが単純に並べているだけなので

1 2 3
1 2 6
1 5
4

となってしまう。これが積み重なるとスクロールし終えたときに高さがかなり違う。

拙作のライブラリでは

1 2 3
1 2 4
1 5 6

となる。

ただし、高さを計算する分重い。

実現方法

  1. 各要素の高さを ResizeObserver + getBoundingClientRect で取得する
  2. 取得した高さを元に、各列がなるべく同じ高さになるように並べる
  3. 並べ終わって再 render する際にも ResizeObserver に引っかかってしまってまた再計算となるととても重くなるので、再計算は throttle する

検討した CSS たち

display: grid

grid を細かく (1px とか) しまくって CSS だけでなんとかするアプローチ。
grid には行数制限があるため駄目だった。

flexbox (row)

各行の高さが一律になってしまう。

flexbox (column)

高さの計算が少しでも狂うと崩壊する。

flexbox (row + コンテナ複数)

DOM ツリーが変わってしまうため css transition が使えない。

position: absolute

高さの計算をミスると悲惨なことになるが column ほどではない。
最終的にはこれを使った。

番外

grid-template-rows: masonry; さえくれば CSS で全部済むのでこんなライブラリは不要になる。