79 lines
2.1 KiB
TypeScript
79 lines
2.1 KiB
TypeScript
// 自定义主题:为文档图片启用「点击全屏查看」
|
|
//
|
|
// 点击正文(.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 = '<img alt="" />'
|
|
// 点击遮罩或图片本身关闭
|
|
viewer.addEventListener('click', closeViewer)
|
|
document.addEventListener('keydown', (event) => {
|
|
if (event.key === 'Escape') closeViewer()
|
|
})
|
|
document.body.appendChild(viewer)
|
|
}
|
|
|
|
/** 为正文图片绑定点击全屏事件(幂等) */
|
|
function bindImages() {
|
|
const images = document.querySelectorAll<HTMLImageElement>('.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())
|
|
},
|
|
)
|
|
},
|
|
}
|