Interactive slider for comparing before/after images
<head>
.<!-- Font Awesome Icons; Make sure to add it in the <head> tag -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<div class="image-comparison">
<div class="comparison-container">
<div class="comparison-before">
<img src="path/to/before-image.jpg" alt="Before image">
<div class="comparison-label">BEFORE</div>
</div>
<div class="comparison-after">
<img src="path/to/after-image.jpg" alt="After image">
<div class="comparison-label">AFTER</div>
</div>
<div class="comparison-handle">
<div class="handle-circle">
<i class="fas fa-chevron-left"></i>
<i class="fas fa-chevron-right"></i>
</div>
</div>
</div>
</div>
/* Image Comparison Slider Base Styles */
.image-comparison {
width: 100%;
max-width: 800px;
margin: 0 auto;
font-family: Arial, sans-serif;
}
.comparison-container {
position: relative;
width: 100%;
height: 0;
padding-bottom: 66.66%; /* 3:2 aspect ratio, adjust as needed */
overflow: hidden;
border-radius: 8px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.comparison-before,
.comparison-after {
position: absolute;
top: 0;
width: 100%;
height: 100%;
}
.comparison-before {
left: 0;
z-index: 1;
}
.comparison-after {
left: 0;
z-index: 2;
width: 50%; /* Initial position - half-way */
overflow: hidden;
}
.comparison-before img,
.comparison-after img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.comparison-after img {
width: 200%; /* Ensure the width is double */
max-width: none;
}
/* Handle design */
.comparison-handle {
position: absolute;
top: 0;
left: 50%; /* Initial position - half-way */
height: 100%;
width: 2px;
background-color: white;
z-index: 3;
transform: translateX(-50%);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
cursor: col-resize;
}
.handle-circle {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 40px;
height: 40px;
background-color: white;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
transition: transform 0.2s ease;
}
.handle-circle i {
font-size: 12px;
color: #555;
}
.comparison-handle:hover .handle-circle {
transform: translate(-50%, -50%) scale(1.1);
}
.comparison-handle:active .handle-circle {
transform: translate(-50%, -50%) scale(0.95);
}
/* Labels */
.comparison-label {
position: absolute;
top: 15px;
padding: 5px 10px;
color: white;
font-size: 12px;
font-weight: bold;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 4px;
letter-spacing: 1px;
}
.comparison-before .comparison-label {
left: 15px;
}
.comparison-after .comparison-label {
right: 15px;
}
/* Responsive styles */
@media (max-width: 768px) {
.handle-circle {
width: 36px;
height: 36px;
}
.handle-circle i {
font-size: 10px;
}
.comparison-label {
font-size: 10px;
padding: 4px 8px;
}
}
@media (max-width: 480px) {
.handle-circle {
width: 32px;
height: 32px;
}
.comparison-label {
top: 10px;
font-size: 9px;
}
.comparison-before .comparison-label {
left: 10px;
}
.comparison-after .comparison-label {
right: 10px;
}
}
document.addEventListener('DOMContentLoaded', function() {
// Get all comparison sliders on the page
const sliders = document.querySelectorAll('.image-comparison');
sliders.forEach(slider => {
const container = slider.querySelector('.comparison-container');
const before = slider.querySelector('.comparison-before');
const after = slider.querySelector('.comparison-after');
const handle = slider.querySelector('.comparison-handle');
let isResizing = false;
// Handle mouse/touch events
handle.addEventListener('mousedown', startResize);
handle.addEventListener('touchstart', startResize);
// Mouse events
document.addEventListener('mousemove', resizeImages);
document.addEventListener('mouseup', stopResize);
// Touch events
document.addEventListener('touchmove', resizeImages);
document.addEventListener('touchend', stopResize);
// Initialize slider position on load (50%)
updateSliderPosition(50);
// Start resizing
function startResize(e) {
e.preventDefault();
isResizing = true;
// Add active class for visual feedback
container.classList.add('active');
}
// Resize images based on mouse/touch position
function resizeImages(e) {
if (!isResizing) return;
// Get container dimensions and position
const containerRect = container.getBoundingClientRect();
// Calculate cursor position as a percentage of the container width
let clientX = e.clientX;
if (e.type === 'touchmove') {
clientX = e.touches[0].clientX;
}
const position = ((clientX - containerRect.left) / containerRect.width) * 100;
// Restrict position between 0% and 100%
const clampedPosition = Math.min(Math.max(position, 0), 100);
// Update the slider position
updateSliderPosition(clampedPosition);
}
// Update slider position
function updateSliderPosition(percentage) {
// Update after image width
after.style.width = `${percentage}%`;
// Update handle position
handle.style.left = `${percentage}%`;
}
// Stop resizing
function stopResize() {
isResizing = false;
container.classList.remove('active');
}
// Keyboard accessibility
handle.setAttribute('tabindex', '0');
handle.setAttribute('role', 'slider');
handle.setAttribute('aria-label', 'Image comparison slider');
handle.setAttribute('aria-valuemin', '0');
handle.setAttribute('aria-valuemax', '100');
handle.setAttribute('aria-valuenow', '50');
handle.addEventListener('keydown', function(e) {
let newPosition = parseInt(handle.getAttribute('aria-valuenow'));
// Move 5% with arrow keys
if (e.key === 'ArrowLeft') {
newPosition = Math.max(0, newPosition - 5);
e.preventDefault();
} else if (e.key === 'ArrowRight') {
newPosition = Math.min(100, newPosition + 5);
e.preventDefault();
}
updateSliderPosition(newPosition);
handle.setAttribute('aria-valuenow', newPosition);
});
});
});