最近のYouTube君がよく使うこのUIパターン、名前ついてるのか気になってる
— クロパンダ (@le_panda_noir) 2022年8月12日
(フロート型で下スワイプで消せて上部にバーがついてるやつ) pic.twitter.com/EJeLQ8wSoS
↑これを CSS + JS で作りました。フロートカードと呼んでますが、正式名称は分かりません。半モーダルとか bottom drawer とか slidable drawer と呼ぶらしい?
実装のポイント: scroll-snap
JS をほぼ使わずに CSS の scroll-snap を使って実装しました。scroll-snap を使うと次のような実装ができます。
- スワイプが中途半端だったら元の位置に戻す
- 一定量を超えたら画面外に移動させて消す
便利ですね。touchstart や touchmove のイベント量を監視してアレコレするよりよほどシンプルです。
scroll-snap はかなり便利
scroll-snap を使うと、カルーセルのようにスクロールした際に特定の位置でピタッと止まるように吸着させられます。
scroll-snap は応用の幅があります。例えば、「スワイプで LIKE」「スワイプで削除」のような機能もカンタンに作れます。ほかの人が作ったデモ。便利ですね。
今回のフロートカードも scroll-snap 応用編に近いですよね。
苦労したポイント
カードの外の部分をスワイプしたときにカードが移動しないようにするのが難しかった
これは、カード外がタップされたら overflow-y: hidden
をつけるようにして解決しました。 overflow-y: scroll
を消すというやり方だと iPhone の safari でうまく動作しなかったので…
カードを画面外にスワイプさせるのが難しかった
これは、カードの上下にスナップポイントを用意してやることで解消しました。今回初めて scroll-snap 使ったので、思いつくまでに時間がかかりました。
未実装の箇所
実はいくつか未実装の箇所がある。
- 画面の高さが十分でない時(横画面の時など)に上にスワイプすることでカードを上に伸ばす
- リストアイテムが多い時のスクロールとの共存
この辺りは素直に scroll-snap を使うだけだと難しい気がします。どうしたらできるんだろ。
まとめ
初めにも張りましたが、動かせるデモはこちらです。ぜひ触ってみてください。結構わけわからないコードになってるので。
scroll-snap について知らなかったのでだいぶ勉強になりました。仕事でカルーセル実装するときに JS で頑張っていたけど、scroll-snap を使っていればもっと楽できたのでは…?とムチャクチャ思いました。
あと、StackBlitz でゴリゴリ初めて書きましたが、微妙にかゆいところに手が届かない感じですね…例えばこのあたり。
useState
を補完で出すとReact.useState
になる- remove braces from arrow function ができない
- デフォルトの React テンプレートから作ると
import * as React from 'react'
が消せない
今回みたいにパッと動くものを作る用途では便利ですが、1時間以上書く場合は少しストレスですね。
みんなもフロートカードを作ってみよう!意外と苦戦するよ!