背景:因在移动端文档中的图片较小,不清晰,无法放大查看
该方案无需安装任何额外依赖,通过扩展 VitePress 主题,添加自定义样式和交互逻辑即可实现核心的图片点击放大 / 关闭功能
实现步骤
- 创建独立组件 :
- 在 docs/.vitepress/theme/components/ImageViewer.vue 中创建了新的图片查看器组件
- 封装了所有与图片放大相关的 HTML 结构、JavaScript 逻辑和 CSS 样式
- 组件功能完整 :
- 支持点击文档中任何图片放大查看
- 自动适配屏幕大小,保持图片原始比例
- 提供平滑的淡入和缩放动画效果
- 支持点击遮罩层、关闭按钮或按 ESC 键关闭查看器
- 响应式设计,适配移动端
代码实现
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
// 图片查看器状态管理
const showImageViewer = ref(false)
const currentImageUrl = ref('')
const currentImageAlt = ref('')
// 打开图片查看器
function openImageViewer(event) {
const img = event.target
if (img.tagName === 'IMG' && !img.closest('.image-viewer-container')) {
currentImageUrl.value = img.src
currentImageAlt.value = img.alt || ''
showImageViewer.value = true
document.body.style.overflow = 'hidden'
document.documentElement.style.overflow = 'hidden'
}
}
// 关闭图片查看器
function closeImageViewer() {
showImageViewer.value = false
currentImageUrl.value = ''
currentImageAlt.value = ''
document.body.style.overflow = ''
document.documentElement.style.overflow = ''
}
// 为文档中的所有图片添加点击事件
function addImageClickListeners() {
const images = document.querySelectorAll('.VPDoc img')
images.forEach(img => {
img.style.cursor = 'pointer'
img.addEventListener('click', openImageViewer)
})
}
// 移除所有图片的点击事件
function removeImageClickListeners() {
const images = document.querySelectorAll('.VPDoc img')
images.forEach(img => {
img.style.cursor = ''
img.removeEventListener('click', openImageViewer)
})
}
// 组件挂载后初始化
onMounted(() => {
// 为图片添加点击事件监听
addImageClickListeners()
// 监听路由变化,重新为新页面的图片添加点击事件
const router = window.history
const originalPushState = router.pushState
router.pushState = function() {
const result = originalPushState.apply(this, arguments)
setTimeout(addImageClickListeners, 100)
return result
}
})
// 组件卸载前清理
onUnmounted(() => {
removeImageClickListeners()
})
</script>
<template>
<!-- 图片查看器模态框 -->
<div v-if="showImageViewer" class="image-viewer-overlay" @click="closeImageViewer">
<div class="image-viewer-container" @click.stop>
<img :src="currentImageUrl" :alt="currentImageAlt" class="image-viewer-image" />
<button class="image-viewer-close" @click="closeImageViewer">×</button>
</div>
</div>
</template>
<style scoped>
/* 图片查看器样式 */
.image-viewer-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.9);
z-index: 2000;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
box-sizing: border-box;
animation: fadeIn 0.3s ease;
}
.image-viewer-container {
position: relative;
max-width: 100%;
max-height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.image-viewer-image {
max-width: 100%;
max-height: 90vh;
object-fit: contain;
border-radius: 4px;
animation: zoomIn 0.3s ease;
}
.image-viewer-close {
position: absolute;
top: -40px;
right: 0;
background-color: transparent;
border: none;
color: white;
font-size: 32px;
cursor: pointer;
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
padding: 0;
line-height: 1;
}
.image-viewer-close:hover {
opacity: 0.8;
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes zoomIn {
from {
transform: scale(0.8);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* 移动端适配 */
@media (max-width: 640px) {
.image-viewer-overlay {
padding: 10px;
}
.image-viewer-close {
top: -30px;
font-size: 28px;
}
}
</style>
- 在myLayout.vue 中展示
<script setup>
...
import ImageViewer from './ImageViewer.vue'
...
</script>
<template>
<!-- 图片查看器组件 -->
<ImageViewer />
</template>
- 在自定义主题中引入
// 自定义主题配置
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import './custom.css'
import MyLayout from './components/MyLayout.vue'
const theme: Theme = {
extends: DefaultTheme,
Layout: MyLayout,
enhanceApp({ app, router, siteData }) {
console.log('Custom theme initialized')
}
}
export default theme