V-005 Media medium

How to create a beforeafter comparison ビフォーアフタースライダーの作り方

Interactive slider to compare two overlapping images. スライダーを動かして2枚の画像を比較できるUI。

ライブデモ Live Demo

After
Before

概要・用途・特徴Overview, Usage & Features

何ができるかWhat it does

Interactive slider to compare two overlapping images.

スライダーを動かして2枚の画像を比較できるUI。

どこで使うかWhere to use

content gallery, video player, media library, portfolio showcase

画像編集デモ、Before/Afterコンテンツ、デザイン比較、UI変更ショーケース

特徴Key features

Interactive before/after image comparison slider. Drag handle reveals before or after image by adjusting clip-path or overflow. Touch and mouse support. Keyboard accessible. Works with any two images of the same dimensions.

インタラクティブな前/後の画像比較スライダー。ドラッグハンドルがclip-pathまたはoverflowを調整して前/後の画像を明らかにする。タッチとマウスのサポート。キーボードアクセシブル。同じ寸法の任意の2つの画像で動作。

調整可能パラメータ Adjustable Parameters

Parameter Default Description
--handle-bgHandle background color
--divider-colorDivider line color

実装コード Implementation Code

// react/V-005.jsx
import { useState, useRef } from 'react';
import './V-005.css';

export default function BeforeAfter({ before, after }) {
  const [width, setWidth] = useState(50);
  const containerRef = useRef(null);

  const handleMove = (e) => {
    const rect = containerRef.current.getBoundingClientRect();
    const x = (e.nativeEvent.clientX || e.nativeEvent.touches[0].clientX) - rect.left;
    let percent = (x / rect.width) * 100;
    if (percent < 0) percent = 0;
    if (percent > 100) percent = 100;
    setWidth(percent);
  };

  return (
    <div className="compare-container" ref={containerRef}
      onMouseMove={handleMove} onTouchMove={handleMove}>
      <img src={after} className="compare-img" alt="After" />
      <div className="compare-overlay" style={{ width: `${width}%` }}>
        <img src={before} alt="Before"
          style={{ width: containerRef.current ? containerRef.current.offsetWidth : '100%' }} />
      </div>
      <div className="compare-handle" style={{ left: `${width}%` }}>
        <svg width="16" height="16" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path d="M8 12h8m-8 0l4-4m-4 4l4 4" /></svg>
      </div>
    </div>
  );
}
/* react/V-005.css */
/* V-005: React styles */
.compare-container {
  position: relative;
  max-width: 600px;
  margin: 0 auto;
  border-radius: 12px;
  overflow: hidden;
  cursor: col-resize;
  aspect-ratio: 16/9;
}

.compare-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.compare-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 50%;
  height: 100%;
  overflow: hidden;
  border-right: 2px solid #fff;
}

.compare-overlay img {
  width: 600px;
  height: 100%;
  object-fit: cover;
}

.compare-handle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 32px;
  height: 32px;
  background: #fff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
  z-index: 10;
  pointer-events: none;
}
import { useState, useRef } from 'react';
import './V-005.css';

export default function BeforeAfter({ before, after }) {
  const [width, setWidth] = useState(50);
  const containerRef = useRef(null);

  const handleMove = (e) => {
    const rect = containerRef.current.getBoundingClientRect();
    const x = (e.nativeEvent.clientX || e.nativeEvent.touches[0].clientX) - rect.left;
    let percent = (x / rect.width) * 100;
    if (percent < 0) percent = 0;
    if (percent > 100) percent = 100;
    setWidth(percent);
  };

  return (
    <div className="compare-container" ref={containerRef}
      onMouseMove={handleMove} onTouchMove={handleMove}>
      <img src={after} className="compare-img" alt="After" />
      <div className="compare-overlay" style={{ width: `${width}%` }}>
        <img src={before} alt="Before"
          style={{ width: containerRef.current ? containerRef.current.offsetWidth : '100%' }} />
      </div>
      <div className="compare-handle" style={{ left: `${width}%` }}>
        <svg width="16" height="16" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path d="M8 12h8m-8 0l4-4m-4 4l4 4" /></svg>
      </div>
    </div>
  );
}
/* V-005: React styles */
.compare-container {
  position: relative;
  max-width: 600px;
  margin: 0 auto;
  border-radius: 12px;
  overflow: hidden;
  cursor: col-resize;
  aspect-ratio: 16/9;
}

.compare-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.compare-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 50%;
  height: 100%;
  overflow: hidden;
  border-right: 2px solid #fff;
}

.compare-overlay img {
  width: 600px;
  height: 100%;
  object-fit: cover;
}

.compare-handle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 32px;
  height: 32px;
  background: #fff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
  z-index: 10;
  pointer-events: none;
}

仕組みとカスタマイズHow It Works & Customization

仕組みHow it works

Two images are stacked. The "after" image uses clip-path: inset(0 50% 0 0) initially, revealing only the left half. Dragging the handle updates the clip-path percentage via JavaScript (mousemove/touchmove events), progressively revealing or hiding the after image. The handle position is clamped to the image bounds.

2つの画像が積み重ねられます。"after"画像は最初にclip-path:inset(0 50% 0 0)を使用して左半分のみを表示。ハンドルのドラッグがJavaScriptでclip-pathのパーセンテージを更新し(mousemove/touchmoveイベント)、after画像を段階的に表示または非表示にします。ハンドル位置は画像の境界内にクランプされます。

カスタマイズ方法Customization

Switch from clip-path to a two-column layout where the divider is a draggable border for wider browser support. Add labels ("Before" / "After") on each side of the divider. Start the divider at a position other than 50% for emphasis.

より広いブラウザサポートのためにclip-pathからディバイダーがドラッグ可能なボーダーの2列レイアウトに切り替え。ディバイダーの両側にラベル("Before" / "After")を追加。強調のためにディバイダーを50%以外の位置から開始。

注意点Caveats

Provide keyboard control — left/right arrow keys should move the slider. Add an aria-label on the drag handle ("Drag to compare") and announce the current percentage via aria-valuenow for screen readers.

キーボードコントロールを提供してください — 左/右矢印キーでスライダーを移動できるようにしてください。ドラッグハンドルにaria-label("ドラッグして比較")を追加し、スクリーンリーダー向けにaria-valuenowで現在のパーセンテージをアナウンスしてください。

よくある質問 Frequently Asked Questions

How to customize the before/after comparison? Before/After Comparisonをカスタマイズするには?

Modify the CSS custom properties and class styles defined in the code section. Key adjustable values include colors, sizes, durations, and spacing. See the Adjustable Parameters section for specific variables.

コードセクションで定義されているCSSカスタムプロパティとクラススタイルを変更してください。色、サイズ、時間、間隔が主な調整可能値です。具体的な変数は調整可能パラメータセクションを参照してください。

How to use the before/after comparison in React? ReactでBefore/After Comparisonを使うには?

Import the provided React component and its CSS file. The component accepts props for customization. Check the React code section for the full implementation and available props.

提供されているReactコンポーネントとCSSファイルをインポートしてください。コンポーネントのpropsでカスタマイズできます。完全な実装と利用可能なpropsはReactコードセクションを参照してください。

What are the performance implications of before/after comparison? Before/After Comparisonのパフォーマンスへの影響は?

This implementation uses CSS transforms and opacity for animations, which are GPU-accelerated. It's lightweight and doesn't cause layout thrashing. Consider using prefers-reduced-motion for accessibility.

この実装はCSSのtransformとopacityを使用しており、GPUアクセラレーションされます。軽量でレイアウトスラッシングを引き起こしません。アクセシビリティのためにprefers-reduced-motionの使用を検討してください。

AIへの指示テンプレート AI Prompt Template

以下をAIに貼り付けると実装を依頼できます。 Paste the following into your AI assistant to request implementation.