Before/After スライダー
中央のハンドルをドラッグして2枚の画像を見比べる。加工前後やリフォーム事例の比較に。
プレビュー
操作ドラッグ/スワイプで操作できます(PC・スマホ対応)。
仕様書
- 向く場面
- 施工前後・加工前後の写真比較
- UIリニューアルの新旧比較
- 同じ構図で何が変わったかを直感的に見せたいとき
- 避ける場面
- 数値・項目で正確に比べる比較(表にする)
- 構図・倍率が揃っていない2枚
- 印刷・PDF化される文脈
- 構成要素
- Before画像
- After画像
- 境界線
- ハンドル
- 操作ヒント
- 持っている状態
- 通常
- フォーカス
- キーボード
Tab でハンドル(role=slider)に到達。←→ で 2%・Shift+←→ で 10%・Home/End で端へ。ドラッグは枠内どこからでも開始できる
- 支援技術
ハンドルは role=slider・aria-valuenow・aria-valuetext(Before 50%)。視覚的な比較そのものは伝わらないので Before/After の内容は alt や隣接テキストで言語化する
- 動き
なし(ドラッグ位置を clip-path へ即時反映。戻りアニメは無い)
デザインの要点
- ドラッグ開始は細いハンドルではなく枠全体で受け、setPointerCapture で枠外へ出ても離すまで追従させる
- 境界は clip-path: inset の右側幅だけを動かし、2枚は同じ座標に固定する。ずらすと被写体が千切れて見える
- ハンドルは role=slider にして矢印キーで 2% ずつ動かす。ポインタ専用だと比較そのものに到達できない人が出る
- 初期位置は 50% で両方を等しく見せる。片側に寄せた初期値は「どちらが Before か」の誤解を生む
使いどころ
リフォーム施工事例、写真の加工前後、UIのリニューアル比較のように『同じ構図で何が変わったか』を直感的に見せたいときに使う。前提として before/after の2枚は同じアングル・同じ拡大率で撮影/書き出されていないと、境界をドラッグしたときに被写体がズレて見え、比較として成立しない。逆に、数値や項目単位で正確に比較したい場合(価格表・スペック表など)はスライダーではなく通常の表組みにすべきで、視覚的な『雰囲気の変化』を伝える用途に限定するのがよい。印刷やPDF化される文脈では機能しないため、Web上でのインタラクティブな訴求専用と考える。
仕組み
背景いっぱいに敷いた .ba-after の上に、同じ画像を clip-path: inset(0 50% 0 0) で右側を隠した .ba-before を重ねているだけの2レイヤー構成。ハンドルをドラッグして setPos() が呼ばれるたびに、ポインタのX座標をコンテナの getBoundingClientRect() 基準の割合(0〜100%)に変換し、その値をそのまま clip-path の右側の隠し幅(100-pct)%として反映する。ハンドルの見た目の位置も同じ pct を left に使って同期させている。ポインタの捕捉は setPointerCapture で行っており、指やマウスがハンドルの外(コンテナ全体)に出てもドラッグ中として扱われ続けるため、細いハンドルを正確につまみ続けなくても操作できる。
実装の注意
clip-path で隠しているだけなので、2枚の画像のピクセル位置が揃っていないと境界線で被写体が『ズレて千切れた』ように見える。撮影条件を揃えるか object-position で位置合わせをしてから使う。touch-action: none を外すと、スマホでハンドルを横に動かそうとした操作がページの縦スクロールと競合し、ドラッグがカクつく、あるいはページ自体がスクロールしてしまう。pointerdown はコンテナ全体(.ba)に付けているため、コンテナの外側で発生した pointerup を取りこぼさないよう pointercancel も併せて監視しておかないと、ドラッグが『離した後も動き続ける』状態になりうる(この実装は両方拾っている)。ハンドルの中央線がコンテナの高さいっぱいに伸びる設計なので、縦横比が極端に細長いとハンドルが目立ちすぎることがある。
アクセシビリティ
ハンドルは tabindex="0" の要素に role="slider" aria-valuenow(現在の pct) aria-valuemin="0" aria-valuemax="100" aria-label="比較位置" を付け、左右矢印キーで 2%(Shift で 10%)ずつ pct を変更し、Home/End で端へ飛ぶ keydown ハンドラを持たせている。aria-valuetext で「Before 50%」のように読み上げさせ、数値だけでは分からない意味を補う。スクリーンリーダー利用者にとっては視覚的な比較そのものが伝わりにくいため、before/after それぞれの内容を alt テキストや隣接するテキストで言語化しておくことも欠かせない。ドラッグ中の追従は transition を使わず値をそのまま反映しているため prefers-reduced-motion の影響は小さいが、離したときに『元の位置へ戻る』アニメーションを足す場合は reduce 設定で無効化する。
調整して使う
初期位置(現在は50%)、縦方向のスライダー(clip-pathの引数をtop/bottomに変える)、ハンドルの見た目(矢印アイコン、太さ)が主な調整点。境界の左右に『Before』『After』のラベルを常時、またはpctに応じてフェードで出し入れすると、どちらがどちらか一目で分かるようになる(特に初見のユーザーには必須に近い)。自動でハンドルが往復するデモ演出を足すこともできるが、その場合はユーザーが操作を始めた瞬間に自動アニメーションを止めないと、ドラッグ中に値が競合して暴れる。ハンドルの当たり判定(この実装はコンテナ全体)を細い帯だけに絞ると誤操作は減るが、スマホでは指が太くつまみにくくなるため、実用上は現状の『コンテナ全体どこでもドラッグ開始』のままの方が扱いやすい。
コード(コピーして使えます)
<div class="ba">
<!-- .ba-before / .ba-after を <img> に置き換えて使う -->
<div class="ba-after"></div>
<div class="ba-before"></div>
<div class="ba-handle" role="slider" tabindex="0" aria-label="比較位置" aria-valuemin="0" aria-valuemax="100" aria-valuenow="50" aria-valuetext="Before 50%">
<div class="ba-line"></div>
<div class="ba-thumb" aria-hidden="true">↔</div>
</div>
</div>
<p class="ba-hint">← ドラッグ / 矢印キー →</p>