Before/After スライダー
中央のハンドルをドラッグして2枚の画像を見比べる。加工前後やリフォーム事例の比較に。
プレビュー
操作ドラッグ/スワイプで操作できます(PC・スマホ対応)。
使いどころ
リフォーム施工事例、写真の加工前後、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 も併せて監視しておかないと、ドラッグが『離した後も動き続ける』状態になりうる(この実装は両方拾っている)。ハンドルの中央線がコンテナの高さいっぱいに伸びる設計なので、縦横比が極端に細長いとハンドルが目立ちすぎることがある。
アクセシビリティ
現状はポインター操作専用で、キーボードだけでは境界を動かせない。実装するならハンドルを button または tabindex="0" の要素にして role="slider" aria-valuenow(現在の pct) aria-valuemin="0" aria-valuemax="100" aria-label="比較位置" を付け、左右矢印キーで数%ずつ pct を変更する keydown ハンドラを足す必要がある。スクリーンリーダー利用者にとっては視覚的な比較そのものが伝わりにくいため、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">
<div class="ba-line"></div>
<div class="ba-thumb">↔</div>
</div>
</div>
<p class="ba-hint">← ドラッグ →</p>