2枚の画像を左右に表示し、境界線をドラッグ(スワイプ)することで比較する「ビフォーアフター比較」を作る方法です。プラグイン無しでコピペで簡単に実装できます!
2枚の画像を用意
ビフォー
アフター
ビフォーとアフターの2枚の画像を用意します。サイズは違っても構いませんが、アスペクト比を合わせておく必要があります。
ビフォーアフター画像比較のサンプル
初期状態はスライドできる画像だよ~」ということをアピールします。
領域内をドラッグしている最中は帯が消えます。カーソルを離すと静止状態の が再出現し、境界線のハンドルとして機能します。
カスタマイズ例
境界線と帯の色、画像の表示サイズは簡単にカスタマイズできます。
また、ビフォーアフター領域はレスポンシブ対応となっており、指定した画像サイズよりも画面が小さい場合、画面幅に合わせて自動的にリサイズします。様々なデバイスで最適な表示が可能です。
ソースコード
JavaScriptコード
<script> (function() { function initBeforeAfter() { const containers = document.querySelectorAll('.before-after-container'); containers.forEach(container => { const beforeImage = container.querySelector('.before-image'); const afterImage = container.querySelector('.after-image'); const sliderHandle = container.querySelector('.slider-handle'); let isDragging = false; let containerWidth; function updateSliderPosition(percentage) { const clampedPercentage = Math.max(0, Math.min(100, percentage)); sliderHandle.style.left = `${clampedPercentage}%`; afterImage.style.clipPath = `polygon(${clampedPercentage}% 0, 100% 0, 100% 100%, ${clampedPercentage}% 100%)`; } function handleDrag(clientX) { if (!isDragging) return; const containerRect = container.getBoundingClientRect(); const position = clientX - containerRect.left; const percentage = (position / containerRect.width) * 100; updateSliderPosition(percentage); } function startDragging(e) { isDragging = true; sliderHandle.classList.add('active'); sliderHandle.classList.add('used'); e.preventDefault(); handleDrag(e.type.includes('mouse') ? e.clientX : e.touches[0].clientX); } function stopDragging() { isDragging = false; sliderHandle.classList.remove('active'); } sliderHandle.addEventListener('mousedown', startDragging); sliderHandle.addEventListener('touchstart', startDragging); document.addEventListener('mousemove', (e) => handleDrag(e.clientX)); document.addEventListener('touchmove', (e) => handleDrag(e.touches[0].clientX)); document.addEventListener('mouseup', stopDragging); document.addEventListener('touchend', stopDragging); function setContainerSize() { const maxWidth = parseInt(container.getAttribute('data-max-width') || '100%'); const viewportWidth = Math.min(document.documentElement.clientWidth, window.innerWidth); containerWidth = Math.min(maxWidth, viewportWidth); container.style.width = `${containerWidth}px`; } function initializeContainer() { setContainerSize(); updateSliderPosition(50); } if (beforeImage.complete && afterImage.complete) { initializeContainer(); } else { beforeImage.onload = afterImage.onload = initializeContainer; } }); } // DOMContentLoaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initBeforeAfter); } else { initBeforeAfter(); } })(); </script>
JavaScriptコードは</body>の前に入れます。
HTMLコード
<div class="before-after-container" data-max-width="300"> <img src="before.jpg" alt="Before" class="before-image"> <img src="after.jpg" alt="After" class="after-image"> <div class="slider-handle"></div> </div>
ビフォーアフターの横幅はdata-max-width="300"の数字をイジると調整できます。 画像のアスペクト比に応じて、縦幅は自動的に決まります。
スマホなどの画面が小さいデバイスにおいて、data-max-widthに設定した値がデカい場合は、画面の横幅に合わせて自動的にリサイズされます。
CSSコード
.before-after-container { --border-color: #ffffff; /* 境界線の色 */ --slide-bg-color: #ffff00; /* スライドテキストの背景色 */ --slide-text-color: #000000; /* スライドテキストの文字色 */ --slide-text: "←Slide→"; /* スライドのテキスト */ --border-width: 4px; /* 境界線の幅 */ position: relative; display: inline-block; overflow: hidden; width: 100%; max-width: 100%; } .before-image, .after-image { display: block; width: 100%; height: auto; } .after-image { position: absolute; top: 0; left: 0; clip-path: polygon(50% 0, 100% 0, 100% 100%, 50% 100%); } .slider-handle { position: absolute; top: 0; bottom: 0; left: 50%; width: var(--border-width); background: var(--border-color); cursor: ew-resize; z-index: 10; } .slider-handle::before { content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 30px; /* 当たり判定を広げる幅 */ transform: translateX(-50%); background: transparent; pointer-events: auto; } .slider-handle::after { content: var(--slide-text); position: absolute; top: 50%; left: 50%; background: var(--slide-bg-color); color: var(--slide-text-color); transform: translate(-50%, -50%); padding: 5px; border-radius: 5px; font-size: 14px; white-space: nowrap; opacity: 0.8; transition: opacity 0.3s ease; font-weight: bold; pointer-events: auto; animation: slideText 0.5s ease-in-out infinite; } .slider-handle.active::after { opacity: 0; } .slider-handle.used::after { animation: none; } @keyframes slideText { 0%, 100% { transform: translate(-50%, -50%) translateX(-2px); } 50% { transform: translate(-50%, -50%) translateX(2px); } }
境界線や
の帯のカスタマイズは2行目から6行目で行います。44行目のwidth: 30px;は「当たり判定」を設定しています。
わずか4pxの細い境界線の上をドラッグするのが意外とムズい!ってことで、境界線の上に「透明の当たり判定領域」を設けました。30pxの値を変更することで、当たり判定の範囲を調整できます。
CSSで帯の演出変更
CSSを少しイジって、スライダーの見た目を変更しました。
初期表示時は の帯を表示し、一度操作した後は丸い●マークに切り替わります。
CSSの.slider-handle.used::after、つまり73~75行目を下記のものに置き換えることで実現できます。
CSSコード変更
.slider-handle.used::after { animation: none; content: ""; width: 15px; height: 15px; opacity: 1; border-radius: 50%; background: var(--border-color); }