chore: 初始开源发布
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import mediumZoom from 'medium-zoom'
|
||||
import { onContentUpdated } from 'vitepress'
|
||||
import { onMounted } from 'vue'
|
||||
import type { Theme } from 'vitepress'
|
||||
|
||||
import './custom.css'
|
||||
|
||||
/**
|
||||
* 参与「点击放大」的图片范围。
|
||||
*
|
||||
* `.vp-doc` 是 VitePress 默认主题给正文容器加的类名,使用手册 9 页与接口文档
|
||||
* 全部页面共用这一套布局,所以在这里定义一次即对全站生效。
|
||||
*
|
||||
* 排除项:
|
||||
* a img 图片本身是链接(如带外链的截图),点击应跳转而不是放大
|
||||
* .no-zoom 需要单独关闭放大的图片,示例:
|
||||
* `<img src="/images/manual/xx.png" class="no-zoom" alt="xx">`
|
||||
*/
|
||||
const ZOOM_SELECTOR = '.vp-doc img:not(a img):not(.no-zoom)'
|
||||
|
||||
/**
|
||||
* 全站只保留一个 medium-zoom 实例,但【必须惰性创建】。
|
||||
*
|
||||
* 两个坑叠在一起,才得到这个写法:
|
||||
*
|
||||
* 1. 不能在模块顶层创建实例。medium-zoom 1.1.0 的工厂函数第一件事就是读
|
||||
* `window.Promise`(dist/medium-zoom.min.js 里即 `var Promise = window.Promise || ...`),
|
||||
* 而主题模块在 `vitepress build` 时会被 Node 端 SSR 打包并执行 —— 没有 window,
|
||||
* 直接 `ReferenceError: window is not defined` 构建失败。
|
||||
* 所以实例只能在浏览器生命周期回调里创建。
|
||||
*
|
||||
* 2. 也不能每次路由切换都 `mediumZoom(...)` 新建。每个实例都会在 document 上注册
|
||||
* click / keyup / scroll、在 window 上注册 resize 监听器,且没有卸载入口
|
||||
* (见 dist/medium-zoom.esm.js:576-581)。翻 N 个页面就会留下 N 组永不释放的
|
||||
* 全局监听器。正确做法是复用实例,路由切换时只做 attach —— attach 内部会跳过
|
||||
* 已挂载的图片(dist/medium-zoom.esm.js:199-204),因此重复调用是幂等的。
|
||||
*/
|
||||
let zoom: ReturnType<typeof mediumZoom> | null = null
|
||||
|
||||
const attachZoom = () => {
|
||||
if (typeof window === 'undefined') return
|
||||
if (!zoom) {
|
||||
zoom = mediumZoom({
|
||||
background: 'var(--vp-c-bg)',
|
||||
margin: 24,
|
||||
})
|
||||
}
|
||||
zoom.attach(ZOOM_SELECTOR)
|
||||
}
|
||||
|
||||
const closeZoomOnClick = (event: MouseEvent) => {
|
||||
const target = event.target as Element | null
|
||||
if (!zoom || !target) return
|
||||
if (target.closest('.medium-zoom-image--opened, .medium-zoom-overlay')) {
|
||||
void zoom.close()
|
||||
}
|
||||
}
|
||||
|
||||
const theme: Theme = {
|
||||
extends: DefaultTheme,
|
||||
setup() {
|
||||
// onContentUpdated 由 VitePress 在「新页面内容挂载完成后」触发,覆盖首次进入
|
||||
// 与 SPA 客户端路由切换。它挂在路由组件的 onVnodeMounted 上
|
||||
// (见 vitepress/dist/client/app/components/Content.js:13-20),
|
||||
// 比 watch(route.path) + nextTick 更可靠 —— 后者可能在异步页面组件挂载前就执行,
|
||||
// 导致某些页面图片没挂上监听、点击放大静默失效。
|
||||
onContentUpdated(attachZoom)
|
||||
|
||||
// 兜底:主题 setup 时内容已就绪(不经过 onVnodeMounted 的极端情况)也能挂上
|
||||
onMounted(attachZoom)
|
||||
if (typeof document !== 'undefined') {
|
||||
document.addEventListener('click', closeZoomOnClick, true)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default theme
|
||||
Reference in New Issue
Block a user