图片画廊浏览:
- 响应式网格布局,自动适应不同屏幕宽度
- 图片分类筛选功能
- 点击图片弹出模态框查看大图
- 支持键盘导航(左右箭头切换,ESC 关闭)
- 悬停效果增强用户体验
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>风景图片画廊</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
<style>
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
}
.gallery-item {
position: relative;
overflow: hidden;
border-radius: 0.75rem;
cursor: pointer;
aspect-ratio: 4/3;
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.gallery-item:hover img {
transform: scale(1.05);
}
.gallery-item::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 60%);
opacity: 0;
transition: opacity 0.3s ease;
}
.gallery-item:hover::after {
opacity: 1;
}
.gallery-caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 1.5rem;
color: white;
transform: translateY(20px);
transition: transform 0.3s ease;
z-index: 1;
}
.gallery-item:hover .gallery-caption {
transform: translateY(0);
}
.modal {
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.modal.active {
opacity: 1;
visibility: visible;
}
.modal-content {
transform: scale(0.95);
transition: transform 0.3s ease;
}
.modal.active .modal-content {
transform: scale(1);
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<header class="bg-white shadow-sm py-6">
<div class="container mx-auto px-4">
<h1 class="text-3xl font-bold text-gray-800 text-center">风景图片画廊</h1>
<p class="text-gray-600 text-center mt-2">探索世界各地的美丽景色</p>
</div>
</header>
<main class="container mx-auto px-4 py-10">
<!-- 画廊过滤器 -->
<div class="flex flex-wrap justify-center gap-3 mb-10">
<button class="filter-btn active px-5 py-2 rounded-full bg-green-600 text-white" data-category="all">全部</button>
<button class="filter-btn px-5 py-2 rounded-full bg-gray-200 hover:bg-gray-300 transition-colors" data-category="mountains">山脉</button>
<button class="filter-btn px-5 py-2 rounded-full bg-gray-200 hover:bg-gray-300 transition-colors" data-category="oceans">海洋</button>
<button class="filter-btn px-5 py-2 rounded-full bg-gray-200 hover:bg-gray-300 transition-colors" data-category="forests">森林</button>
<button class="filter-btn px-5 py-2 rounded-full bg-gray-200 hover:bg-gray-300 transition-colors" data-category="deserts">沙漠</button>
</div>
<!-- 图片画廊 -->
<div class="gallery-grid">
<!-- 图片项将通过JS动态生成 -->
</div>
</main>
<!-- 模态框预览 -->
<div id="image-modal" class="modal fixed inset-0 bg-black/90 z-50 flex items-center justify-center p-4">
<div class="modal-content relative max-w-5xl w-full">
<button id="close-modal" class="absolute -top-12 right-0 text-white text-2xl hover:text-gray-300 transition-colors">
<i class="fa-solid fa-times"></i>
</button>
<button id="prev-btn" class="absolute left-4 top-1/2 -translate-y-1/2 bg-black/50 hover:bg-black/70 text-white w-10 h-10 rounded-full flex items-center justify-center transition-colors">
<i class="fa-solid fa-chevron-left"></i>
</button>
<img id="modal-image" src="" alt="大图预览" class="w-full h-auto rounded-lg">
<button id="next-btn" class="absolute right-4 top-1/2 -translate-y-1/2 bg-black/50 hover:bg-black/70 text-white w-10 h-10 rounded-full flex items-center justify-center transition-colors">
<i class="fa-solid fa-chevron-right"></i>
</button>
<div class="mt-4 text-white">
<h3 id="modal-title" class="text-xl font-semibold"></h3>
<p id="modal-desc" class="text-gray-300"></p>
</div>
</div>
</div>
<footer class="bg-gray-800 text-white py-8 mt-16">
<div class="container mx-auto px-4 text-center">
<p>© 2025 风景画廊 | 用HTML、CSS和JavaScript构建</p>
</div>
</footer>
<script>
// 画廊图片数据
const galleryItems = [
{
id: 1,
src: 'https://picsum.photos/id/29/800/600',
thumbnail: 'https://picsum.photos/id/29/400/300',
title: '山间云海',
description: '清晨的山脉被云海环绕,宛如仙境',
category: 'mountains'
},
{
id: 2,
src: 'https://picsum.photos/id/65/800/600',
thumbnail: 'https://picsum.photos/id/65/400/300',
title: '宁静海岸',
description: '蓝色的海水拍打岸边,留下白色的浪花',
category: 'oceans'
},
{
id: 3,
src: 'https://picsum.photos/id/137/800/600',
thumbnail: 'https://picsum.photos/id/137/400/300',
title: '茂密森林',
description: '阳光透过树叶洒下,形成斑驳的光影',
category: 'forests'
},
{
id: 4,
src: 'https://picsum.photos/id/10/800/600',
thumbnail: 'https://picsum.photos/id/10/400/300',
title: '沙漠日落',
description: '夕阳下的沙漠呈现出温暖的金黄色',
category: 'deserts'
},
{
id: 5,
src: 'https://picsum.photos/id/28/800/600',
thumbnail: 'https://picsum.photos/id/28/400/300',
title: '雪山之巅',
description: '高耸的雪山直插云霄,壮丽非凡',
category: 'mountains'
},
{
id: 6,
src: 'https://picsum.photos/id/76/800/600',
thumbnail: 'https://picsum.photos/id/76/400/300',
title: '热带海滩',
description: '白色的沙滩和清澈的海水,度假胜地',
category: 'oceans'
},
{
id: 7,
src: 'https://picsum.photos/id/129/800/600',
thumbnail: 'https://picsum.photos/id/129/400/300',
title: '迷雾森林',
description: '清晨的森林被薄雾笼罩,神秘而宁静',
category: 'forests'
},
{
id: 8,
src: 'https://picsum.photos/id/110/800/600',
thumbnail: 'https://picsum.photos/id/110/400/300',
title: '沙漠星空',
description: '夜晚的沙漠,繁星满天,银河清晰可见',
category: 'deserts'
}
];
// DOM元素
const galleryGrid = document.querySelector('.gallery-grid');
const filterBtns = document.querySelectorAll('.filter-btn');
const modal = document.getElementById('image-modal');
const modalImage = document.getElementById('modal-image');
const modalTitle = document.getElementById('modal-title');
const modalDesc = document.getElementById('modal-desc');
const closeModal = document.getElementById('close-modal');
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
let currentImageIndex = 0;
let filteredItems = [...galleryItems];
// 初始化画廊
renderGallery(galleryItems);
// 渲染画廊
function renderGallery(items) {
galleryGrid.innerHTML = '';
items.forEach((item, index) => {
const galleryItem = document.createElement('div');
galleryItem.className = 'gallery-item';
galleryItem.dataset.category = item.category;
galleryItem.dataset.index = index;
galleryItem.innerHTML = `
<img src="${item.thumbnail}" alt="${item.title}">
<div class="gallery-caption">
<h3 class="font-semibold text-lg">${item.title}</h3>
<p class="text-sm text-gray-200">${item.description.substring(0, 30)}...</p>
</div>
`;
galleryItem.addEventListener('click', () => {
openModal(index);
});
galleryGrid.appendChild(galleryItem);
});
}
// 打开模态框
function openModal(index) {
currentImageIndex = index;
const item = filteredItems[index];
modalImage.src = item.src;
modalImage.alt = item.title;
modalTitle.textContent = item.title;
modalDesc.textContent = item.description;
modal.classList.add('active');
document.body.style.overflow = 'hidden';
}
// 关闭模态框
function closeModalFunc() {
modal.classList.remove('active');
document.body.style.overflow = '';
}
// 下一张图片
function nextImage() {
currentImageIndex = (currentImageIndex + 1) % filteredItems.length;
const item = filteredItems[currentImageIndex];
modalImage.src = item.src;
modalTitle.textContent = item.title;
modalDesc.textContent = item.description;
}
// 上一张图片
function prevImage() {
currentImageIndex = (currentImageIndex - 1 + filteredItems.length) % filteredItems.length;
const item = filteredItems[currentImageIndex];
modalImage.src = item.src;
modalTitle.textContent = item.title;
modalDesc.textContent = item.description;
}
// 筛选功能
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
// 更新按钮样式
filterBtns.forEach(b => {
b.classList.remove('active', 'bg-green-600', 'text-white');
b.classList.add('bg-gray-200', 'hover:bg-gray-300');
});
btn.classList.add('active', 'bg-green-600', 'text-white');
btn.classList.remove('bg-gray-200', 'hover:bg-gray-300');
// 筛选图片
const category = btn.dataset.category;
if (category === 'all') {
filteredItems = [...galleryItems];
} else {
filteredItems = galleryItems.filter(item => item.category === category);
}
renderGallery(filteredItems);
});
});
// 事件监听
closeModal.addEventListener('click', closeModalFunc);
nextBtn.addEventListener('click', nextImage);
prevBtn.addEventListener('click', prevImage);
// 点击模态框背景关闭
modal.addEventListener('click', (e) => {
if (e.target === modal) {
closeModalFunc();
}
});
// 键盘导航
document.addEventListener('keydown', (e) => {
if (!modal.classList.contains('active')) return;
if (e.key === 'Escape') closeModalFunc();
if (e.key === 'ArrowRight') nextImage();
if (e.key === 'ArrowLeft') prevImage();
});
</script>
</body>
</html>