// 自定义主题:为文档图片启用「点击全屏查看」 // // 点击正文(.vp-doc)内的图片后,在全屏遮罩中铺满展示原图; // 再次点击或按 Esc 关闭。路由切换时重新绑定,避免遗漏动态渲染的图片。 import { onMounted, watch, nextTick } from 'vue' import { useRoute } from 'vitepress' import DefaultTheme from 'vitepress/theme' import './custom.css' // 全屏查看器遮罩元素(全站复用一个) let viewer: HTMLDivElement | null = null /** 关闭全屏查看器 */ function closeViewer() { viewer?.classList.remove('active') } /** 打开全屏查看器并展示指定图片 * :param src: 图片地址 * :param alt: 图片替代文本 */ function openViewer(src: string, alt: string) { if (!viewer) return const img = viewer.querySelector('img') if (!img) return img.src = src img.alt = alt viewer.classList.add('active') } /** 创建全屏查看器并注册关闭事件 */ function ensureViewer() { if (viewer) return viewer = document.createElement('div') viewer.className = 'doc-image-viewer' viewer.innerHTML = '' // 点击遮罩或图片本身关闭 viewer.addEventListener('click', closeViewer) document.addEventListener('keydown', (event) => { if (event.key === 'Escape') closeViewer() }) document.body.appendChild(viewer) } /** 为正文图片绑定点击全屏事件(幂等) */ function bindImages() { const images = document.querySelectorAll('.vp-doc img') images.forEach((img) => { if (img.dataset.zoomBound === '1') return img.dataset.zoomBound = '1' img.classList.add('doc-zoomable') img.addEventListener('click', () => { openViewer(img.currentSrc || img.src, img.alt) }) }) } export default { extends: DefaultTheme, setup() { const route = useRoute() onMounted(() => { ensureViewer() bindImages() }) // 路由切换后重新绑定(文章内容异步渲染) watch( () => route.path, () => { closeViewer() nextTick(() => bindImages()) }, ) }, }