- Demo : ketsathalan
<!DOCTYPE html><html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Product Gallery Zoom & Fancybox</title>
<!-- Swiper CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
<!-- Fancybox CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.css" />
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Arial, sans-serif;
padding: 40px;
background-color: #f5f5f5;
}
.product-gallery-wrapper {
display: flex;
gap: 20px;
max-width: 1000px;
margin: 0 auto;
position: relative;
}
/* Container bên trái chứa Ảnh lớn + Thumbnails */
.gallery-left {
width: 450px;
position: relative;
}
/* Khung ảnh chính */
.main-image-container {
width: 100%;
height: 450px;
border: 1px solid #ddd;
position: relative;
overflow: hidden;
cursor: pointer;
background: #fff;
}
.main-image-container img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* Khung Lens di chuyển trên ảnh chính */
.zoom-lens {
position: absolute;
width: 150px;
height: 150px;
border: 1px solid #999;
background-color: rgba(255, 255, 255, 0.4);
display: none;
pointer-events: none; /* Cho phép click xuyên qua lens */
}
/* Khung hiển thị Zoom kết quả (Bên phải) */
.zoom-result {
position: absolute;
top: 0;
left: 470px; /* Nằm ngay cạnh gallery-left */
width: 450px;
height: 450px;
border: 1px solid #ccc;
background-repeat: no-repeat;
background-color: #fff;
display: none;
z-index: 99;
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}
/* Slider ảnh thu nhỏ (Thumbnails) */
.thumb-slider-container {
margin-top: 15px;
position: relative;
padding: 0 30px; /* Dành khoảng trống cho nút Prev/Next */
}
.swiper-slide {
border: 2px solid transparent;
border-radius: 4px;
overflow: hidden;
cursor: pointer;
height: 70px;
opacity: 0.7;
transition: all 0.2s ease;
}
.swiper-slide-thumb-active,
.swiper-slide:hover {
border-color: #d71921;
opacity: 1;
}
.swiper-slide img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Nút di chuyển Prev / Next */
.swiper-button-next,
.swiper-button-prev {
width: 24px;
height: 24px;
color: #333;
}
.swiper-button-next::after,
.swiper-button-prev::after {
font-size: 14px;
font-weight: bold;
}
.swiper-button-prev { left: 0; }
.swiper-button-next { right: 0; }
</style>
</head>
<body>
<div class="product-gallery-wrapper">
<!-- Cột bên trái: Ảnh chính & Slider thu nhỏ -->
<div class="gallery-left">
<!-- Ảnh chính -->
<div class="main-image-container" id="mainImgContainer">
<div class="zoom-lens" id="zoomLens"></div>
<img id="mainImage" src="https://picsum.photos/id/1018/1000/1000" alt="Sản phẩm chính">
</div>
<!-- Thumbnails Carousel (Swiper) -->
<div class="thumb-slider-container">
<div class="swiper thumbSwiper">
<div class="swiper-wrapper">
<div class="swiper-slide">
<img src="https://picsum.photos/id/1018/1000/1000" alt="Ảnh 1">
</div>
<div class="swiper-slide">
<img src="https://picsum.photos/id/1015/1000/1000" alt="Ảnh 2">
</div>
<div class="swiper-slide">
<img src="https://picsum.photos/id/1019/1000/1000" alt="Ảnh 3">
</div>
<div class="swiper-slide">
<img src="https://picsum.photos/id/1025/1000/1000" alt="Ảnh 4">
</div>
<div class="swiper-slide">
<img src="https://picsum.photos/id/1039/1000/1000" alt="Ảnh 5">
</div>
<!-- Ảnh thứ 6 và 7 để kích hoạt chuyển slider -->
<div class="swiper-slide">
<img src="https://picsum.photos/id/1043/1000/1000" alt="Ảnh 6">
</div>
<div class="swiper-slide">
<img src="https://picsum.photos/id/1050/1000/1000" alt="Ảnh 7">
</div>
</div>
</div>
<!-- Nút điều hướng -->
<div class="swiper-button-next"></div>
<div class="swiper-button-prev"></div>
</div>
</div>
<!-- Khung hiển thị Zoom bên phải -->
<div class="zoom-result" id="zoomResult"></div>
</div>
<!-- Swiper JS -->
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<!-- Fancybox JS -->
<script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.umd.js"></script>
<script>
// 1. Cấu hình Swiper Slider cho thumbnails (Hiển thị 5 ảnh, vượt quá 5 sẽ scroll/next)
const swiper = new Swiper('.thumbSwiper', {
slidesPerView: 5,
spaceBetween: 8,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
});
const mainImgContainer = document.getElementById('mainImgContainer');
const mainImage = document.getElementById('mainImage');
const zoomLens = document.getElementById('zoomLens');
const zoomResult = document.getElementById('zoomResult');
const slides = document.querySelectorAll('.thumbSwiper .swiper-slide');
// Mặc định active slide đầu tiên
slides[0].classList.add('swiper-slide-thumb-active');
// Danh sách URL ảnh dùng cho Fancybox
let galleryItems = Array.from(slides).map(slide => {
return { src: slide.querySelector('img').src, type: "image" };
});
let currentActiveIndex = 0;
// 2. Chuyển đổi ảnh chính khi click vào thumbnail
slides.forEach((slide, index) => {
slide.addEventListener('click', () => {
document.querySelector('.swiper-slide-thumb-active')?.classList.remove('swiper-slide-thumb-active');
slide.classList.add('swiper-slide-thumb-active');
currentActiveIndex = index;
mainImage.src = slide.querySelector('img').src;
updateZoomBackground();
});
});
// 3. Xử lý hiệu ứng Zoom khi Hover chuột
function updateZoomBackground() {
zoomResult.style.backgroundImage = `url('${mainImage.src}')`;
}
updateZoomBackground();
mainImgContainer.addEventListener('mouseenter', () => {
zoomLens.style.display = 'block';
zoomResult.style.display = 'block';
// Tính toán tỷ lệ zoom dựa trên kích thước thực tế của ảnh
const cx = zoomResult.offsetWidth / zoomLens.offsetWidth;
const cy = zoomResult.offsetHeight / zoomLens.offsetHeight;
zoomResult.style.backgroundSize = `${mainImage.width * cx}px ${mainImage.height * cy}px`;
});
mainImgContainer.addEventListener('mouseleave', () => {
zoomLens.style.display = 'none';
zoomResult.style.display = 'none';
});
mainImgContainer.addEventListener('mousemove', moveLens);
function moveLens(e) {
e.preventDefault();
const rect = mainImgContainer.getBoundingClientRect();
// Lấy tọa độ chuột tương đối trong khung ảnh
let x = e.clientX - rect.left - (zoomLens.offsetWidth / 2);
let y = e.clientY - rect.top - (zoomLens.offsetHeight / 2);
// Giới hạn không cho lens ra ngoài khung ảnh
if (x > mainImgContainer.offsetWidth - zoomLens.offsetWidth) {
x = mainImgContainer.offsetWidth - zoomLens.offsetWidth;
}
if (x < 0) { x = 0; }
if (y > mainImgContainer.offsetHeight - zoomLens.offsetHeight) {
y = mainImgContainer.offsetHeight - zoomLens.offsetHeight;
}
if (y < 0) { y = 0; }
zoomLens.style.left = x + 'px';
zoomLens.style.top = y + 'px';
// Tính tỷ lệ và di chuyển background của ô zoom kết quả
const cx = zoomResult.offsetWidth / zoomLens.offsetWidth;
const cy = zoomResult.offsetHeight / zoomLens.offsetHeight;
zoomResult.style.backgroundPosition = `-${x * cx}px -${y * cy}px`;
}
// 4. Xử lý Click vào ảnh chính để mở Fancybox Lightbox
mainImgContainer.addEventListener('click', () => {
Fancybox.show(galleryItems, {
startIndex: currentActiveIndex
});
});
</script>
</body>
</html>
