下面我将为你提供几种从简单到复杂的实现方法,包括纯 CSS、结合少量 JavaScript 的现代方案,以及一个功能更丰富的专业级图片查看器。

(图片来源网络,侵删)
纯 CSS 方案(简单、快速)
这种方法利用 CSS 的 target 伪类,无需任何 JavaScript,当用户点击图片时,它会链接到一个锚点(),然后通过 CSS 将图片放大并居中显示。
实现步骤:
-
HTML 结构
- 每张图片用一个
<a>标签包裹,href指向一个唯一的 ID(#img1)。 - 放大后的图片是一个隐藏的
<div>,它的 ID 与上面<a>标签的href对应。 - 在这个
<div>内部,放置一张<img>,并添加一个关闭按钮。
<style> /* 基础样式 */ .gallery img { width: 200px; height: 150px; object-fit: cover; cursor: pointer; transition: transform 0.3s ease; } .gallery img:hover { transform: scale(1.05); } /* 默认隐藏放大后的图片容器 */ .lightbox { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.9); z-index: 1000; text-align: center; } /* 当链接被点击后(:target),显示放大图片 */ .lightbox:target { display: flex; justify-content: center; align-items: center; } /* 放大后的图片样式 */ .lightbox img { max-width: 90%; max-height: 90%; object-fit: contain; } /* 关闭按钮样式 */ .close { position: absolute; top: 20px; right: 35px; color: #f1f1f1; font-size: 40px; font-weight: bold; cursor: pointer; } .close:hover { color: #bbb; } </style> <div class="gallery"> <a href="#img1"> <img src="https://via.placeholder.com/200x150?text=Image+1" alt="图片1"> </a> <a href="#img2"> <img src="https://via.placeholder.com/200x150?text=Image+2" alt="图片2"> </a> <!-- 更多图片... --> </div> <!-- 放大后的图片容器 --> <div id="img1" class="lightbox"> <span class="close">×</span> <img src="https://via.placeholder.com/800x600?text=Image+1+Large" alt="放大图片1"> </div> <div id="img2" class="lightbox"> <span class="close">×</span> <img src="https://via.placeholder.com/800x600?text=Image+2+Large" alt="放大图片2"> </div> - 每张图片用一个
优缺点:
- 优点:代码简单,无需 JS,性能好。
- 缺点:
- URL 会变得奇怪(页面后缀会加上
#img1)。 - 如果图片很多,HTML 会变得很冗余。
- 功能有限,无法实现平滑的淡入淡出动画(除非配合 JS)。
- URL 会变得奇怪(页面后缀会加上
JavaScript + CSS 方案(灵活、推荐)
这是目前最主流和灵活的方法,通过 JS 控制一个模态框(Modal)的显示和隐藏,可以实现更丰富的交互效果,如平滑动画、键盘关闭等。
实现步骤:
-
HTML 结构
(图片来源网络,侵删)- 图片列表。
- 一个隐藏的模态框容器,里面包含放大的图片和关闭按钮。
<style> /* 图片列表样式 */ .gallery img { width: 200px; height: 150px; object-fit: cover; cursor: pointer; margin: 5px; transition: transform 0.3s ease; } .gallery img:hover { transform: scale(1.05); } /* 模态框基础样式 */ .modal { display: none; /* 默认隐藏 */ position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0, 0, 0, 0.9); opacity: 0; /* 初始透明 */ transition: opacity 0.3s ease; /* 淡入效果 */ } /* 模态框内容 */ .modal-content { display: block; margin: auto; max-width: 80%; max-height: 80%; animation: zoom 0.3s ease; /* 缩放动画 */ } /* 关闭按钮 */ .close { position: absolute; top: 15px; right: 35px; color: #f1f1f1; font-size: 40px; font-weight: bold; cursor: pointer; transition: color 0.3s ease; } .close:hover { color: #bbb; } /* 动画效果 */ @keyframes zoom { from { transform: scale(0.5); } to { transform: scale(1); } } </style> <div class="gallery"> <img src="https://picsum.photos/200/150?random=1" alt="图片1" onclick="openModal(this)"> <img src="https://picsum.photos/200/150?random=2" alt="图片2" onclick="openModal(this)"> <img src="https://picsum.photos/200/150?random=3" alt="图片3" onclick="openModal(this)"> </div> <!-- 模态框 --> <div id="imageModal" class="modal"> <span class="close" onclick="closeModal()">×</span> <img class="modal-content" id="modalImg"> </div> -
JavaScript 代码
openModal()函数:获取被点击图片的src,将其赋值给模态框内的<img>,然后显示模态框。closeModal()函数:隐藏模态框。
function openModal(img) { // 获取模态框和其中的图片元素 const modal = document.getElementById("imageModal"); const modalImg = document.getElementById("modalImg"); // 设置模态框内图片的源为被点击图片的源 modalImg.src = img.src; // 显示模态框 modal.style.display = "block"; // 淡入效果 setTimeout(() => { modal.style.opacity = "1"; }, 10); } function closeModal() { const modal = document.getElementById("imageModal"); // 淡出效果 modal.style.opacity = "0"; // 等待淡出动画完成后,再隐藏元素 setTimeout(() => { modal.style.display = "none"; }, 300); } // 点击模态框的背景部分也可以关闭 window.onclick = function(event) { const modal = document.getElementById("imageModal"); if (event.target == modal) { closeModal(); } } // 按下 ESC 键关闭模态框 document.addEventListener('keydown', function(event) { if (event.key === 'Escape') { closeModal(); } });
优缺点:
- 优点:功能强大,交互流畅,URL 保持干净,代码结构清晰,可扩展性强。
- 缺点:需要编写少量 JavaScript 代码。
使用专业库(功能强大、省心)
如果你不希望自己编写代码,可以直接使用现成的图片查看器库,它们提供了非常丰富的功能,如全屏、缩放、旋转、播放幻灯片等。
推荐库:PhotoSwipe
PhotoSwipe 是一个功能强大、移动端友好的开源图片库。
实现步骤:
-
引入 CSS 和 JS 文件
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/5.4.2/photoswipe.min.css" integrity="sha512-yJ+y8v4ylVjN/JdMl0YkV6gHKd3boLd7PpA/PF8ruFmW3/AlU6Ll+Vj5D6n8yfNUZ6xL7s6+V6yU1v3b5c3Q==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/5.4.2/default-skin/default-skin.min.css" integrity="sha512-A9eOZ5nKk7lB3j8PuzL8iGq7t6zXnZ5oqH4n6l7b5u1g8s3a6wGz5b5w6jz5b5u1g8s3a6wGz5b5w6jz5b5u1g8s3A==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/5.4.2/photoswipe.min.js" integrity="sha512-6sJHn2XTHiZJi6w9Tg3Zo/NfSgejQgOj8tXz5gKzUWVw5k7k+Y4y5L5i5k5y5L5i5k5y5L5i5k5y5L5i5k5y5A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/5.4.2/photoswipe-ui-default.min.js" integrity="sha512-5s3n4r3L4LhJ2qz8X5j3K8eQH7f5J6y4X5k5y5L5i5k5y5L5i5k5y5L5i5k5y5L5i5k5y5L5i5k5y5L5i5k5y5A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
-
准备 HTML 结构
.pswp__bg是背景。.pswp__scroll-wrap是滚动容器。.pswp__container是图片容器。.pswp__item是每张图片的容器。- 在
<a>标签中放置<img>,并添加data-pswp-width和data-pswp-height属性。
<!-- Root element of PhotoSwipe --> <div class="pswp" tabindex="-1" role="dialog" aria-hidden="true"> <div class="pswp__bg"></div> <div class="pswp__scroll-wrap"> <div class="pswp__container"> <div class="pswp__item"></div> <div class="pswp__item"></div> <div class="pswp__item"></div> </div> <div class="pswp__ui pswp__ui--hidden"> <!-- ... UI Controls ... --> </div> </div> </div> <!-- 你的图片列表 --> <div class="my-gallery" itemscope itemtype="http://schema.org/ImageGallery"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject"> <a href="https://picsum.photos/1200/800?random=1" itemprop="contentUrl" data-size="1200x800"> <img src="https://picsum.photos/200/150?random=1" itemprop="thumbnail" alt="图片1"> </a> </figure> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject"> <a href="https://picsum.photos/1200/800?random=2" itemprop="contentUrl" data-size="1200x800"> <img src="https://picsum.photos/200/150?random=2" itemprop="thumbnail" alt="图片2"> </a> </figure> <!-- 更多图片... --> </div> -
初始化 PhotoSwipe
document.addEventListener('DOMContentLoaded', function () { var initPhotoSwipeFromDOM = function(gallerySelector) { var gallery = document.querySelector(gallerySelector); var parseThumbnailElements = function(el) { var thumbElements = el.childNodes, numNodes = thumbElements.length, items = [], figureEl, linkEl, size, item; for(var i = 0; i < numNodes; i++) { figureEl = thumbElements[i]; // <figure> element if(figureEl.nodeType !== 1) { continue; } linkEl = figureEl.children[0]; // <a> element size = linkEl.getAttribute('data-size').split('x'); // create slide object item = { src: linkEl.getAttribute('href'), w: parseInt(size[0], 10), h: parseInt(size[1], 10), author: linkEl.getAttribute('data-author') }; if(figureEl.children.length > 1) { // <figcaption> content item.title = figureEl.children[1].innerHTML; } if(linkEl.children.length > 0) { // <img> thumbnail element, retrieving thumbnail url item.msrc = linkEl.children[0].getAttribute('src'); } item.el = figureEl; // save link to element for getThumbBoundsFn items.push(item); } return items; }; var closest = function closest(el, fn) { return el && ( fn(el) ? el : closest(el.parentNode, fn) ); }; var onThumbnailsClick = function(e) { e = e || window.event; e.preventDefault ? e.preventDefault() : e.returnValue = false; var eTarget = e.target || e.srcElement; var clickedListItem = closest(eTarget, function(el) { return (el.tagName && el.tagName.toUpperCase() === 'FIGURE'); }); if(!clickedListItem) { return; } var clickedGallery = clickedListItem.parentNode, childNodes = clickedListItem.parentNode.childNodes, numChildNodes = childNodes.length, nodeIndex = 0, index; for (var i = 0; i < numChildNodes; i++) { if(childNodes[i].nodeType !== 1) { continue; } if(childNodes[i] === clickedListItem) { index = nodeIndex; break; } nodeIndex++; } if(index >= 0) { openPhotoSwipe( index, clickedGallery ); } return false; }; var photoswipeParseHash = function() { var hash = window.location.hash.substring(1), params = {}; if(hash.length < 5) { // pid=1 return params; } var vars = hash.split('&'); for (var i = 0; i < vars.length; i++) { if(!vars[i]) { continue; } var pair = vars[i].split('='); if(pair.length < 2) { continue; } params[pair[0]] = pair[1]; } if(params.gid) { params.gid = parseInt(params.gid, 10); } return params; }; var openPhotoSwipe = function(index, galleryElement, disableAnimation, fromThumb) { var pswpElement = document.querySelectorAll('.pswp')[0], gallery, options, items; items = parseThumbnailElements(galleryElement); // define options (if needed) options = { galleryUID: galleryElement.getAttribute('data-pswp-uid'), getThumbBoundsFn: function(index) { // See Options -> getThumbBoundsFn section of documentation for more info var thumbnail = items[index].el.getElementsByTagName('img')[0], // find thumbnail pageYScroll = window.pageYOffset || document.documentElement.scrollTop, rect = thumbnail.getBoundingClientRect(); return {x:rect.left, y:rect.top + pageYScroll, w:rect.width}; } }; if(disableAnimation) { options.showAnimationDuration = 0; } if(fromThumb) { options.animateZoom = false; } if(options.galleryUID) { options.history = false; } // Pass data to PhotoSwipe and initialize it gallery = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options); gallery.init(); }; // loop through all gallery elements and bind events var galleryElements = document.querySelectorAll( gallerySelector ); for(var i = 0, l = galleryElements.length; i < l; i++) { galleryElements[i].setAttribute('data-pswp-uid', i+1); galleryElements[i].onclick = onThumbnailsClick; } // Parse URL and open gallery if it contains #&gid=3&pid=45 var hashData = photoswipeParseHash(); if(hashData.pid && hashData.gid) { openPhotoSwipe( hashData.pid , galleryElements[ hashData.gid - 1 ], true, true ); } }; // initialize PhotoSwipe initPhotoSwipeFromDOM('.my-gallery'); });
优缺点:
- 优点:功能极其丰富(全屏、缩放、旋转、幻灯片、键盘/手势支持),跨浏览器兼容性好,移动端体验极佳。
- 缺点:引入了外部库,会增加页面体积,需要遵循其特定的 HTML 结构。
总结与选择建议
| 方法 | 实现难度 | 功能丰富度 | 适用场景 |
|---|---|---|---|
| 纯 CSS | 非常低 | 基础 | 快速实现、少量图片、对 URL 有洁癖、不想用 JS 的场景。 |
| JS + CSS | 低 | 丰富 | 强烈推荐,大多数网页应用的理想选择,平衡了功能、性能和开发成本。 |
| 专业库 | 中 | 极其丰富 | 专业图片画廊、作品集、电商网站等对图片查看体验要求极高的场景。 |
对于大多数开发者来说,方法二(JavaScript + CSS) 是最佳选择,它提供了足够的灵活性,可以轻松定制以适应各种需求,同时保持了代码的简洁和可控性。
