Skip to content

默认的 Vitepress 加载图片后,因为布局的原因,图片显得很小,很多图片里的内容看不清晰,所以想要一个可以点击图片放大的效果

去谷歌搜索了一番,找到一个方案issues#854

安装medium-zoom

sh
pnpm add -D medium-zoom

引入库以及配置相关文件

方式一

新建docs/.vitepress/theme/index.ts,添加如下代码

ts
import DefaultTheme from 'vitepress/theme'
import { onMounted, watch, nextTick } from 'vue'
import { useRoute } from 'vitepress'
import mediumZoom from 'medium-zoom'
import './global.css'

export default {
  extends: DefaultTheme,

  setup() {
    const route = useRoute()
    const initZoom = () => {
      // 为所有图片增加缩放功能
      mediumZoom('.main img', { background: 'var(--vp-c-bg)' })
    }
    onMounted(() => {
      initZoom()
    })
    watch(
      () => route.path,
      () => nextTick(() => initZoom())
    )
  }
}

这时候,点击图片放大的功能已经实现了,但是效果不尽如人意,会被其他层级的元素遮挡图片(例如左侧的导航栏),所以需要修改一下样式

新建docs/.vitepress/theme/global.css,添加如下样式代码,然后在docs/.vitepress/theme/index.ts引入它

css
.medium-zoom-overlay {
  background-color: var(--vp-c-bg) !important;
  z-index: 100;
}

.medium-zoom-overlay ~ img {
  z-index: 101;
}

.medium-zoom--opened .medium-zoom-overlay {
  opacity: 0.9 !important;
}

方式二

新建docs/.vitepress/hooks/useMediumZoom.ts,添加如下代码

ts
import mediumZoom from 'medium-zoom'
import { inject, nextTick, onMounted, watch } from 'vue'
import type { Zoom } from 'medium-zoom'
import type { App, InjectionKey } from 'vue'
import type { Router } from 'vitepress'

declare module 'medium-zoom' {
  interface Zoom {
    refresh: (selector?: string) => void
  }
}

export const mediumZoomSymbol: InjectionKey<Zoom> = Symbol('mediumZoom')

export function useMediumZoom() {
  onMounted(() => inject(mediumZoomSymbol)?.refresh())
}

export function useMediumZoomProvider(app: App, router: Router) {
  if (import.meta.env.SSR) return
  const zoom = mediumZoom()
  zoom.refresh = () => {
    zoom.detach()
    zoom.attach(':not(a) > img:not(.image-src)')
  }
  app.provide(mediumZoomSymbol, zoom)
  watch(
    () => router.route.path,
    () => nextTick(() => zoom.refresh())
  )
}
ts
export * from './useMediumZoom'

⚡ 注意

如果是 TS 项目必须要有docs/.vitepress/.env.d.ts文件,否则报错说import.mata对象上没有env属性

ts
/// <reference types="vite/client" />

接着编辑方式一中提到的theme/index.ts文件

ts
import DefaultTheme from 'vitepress/theme'
import type { EnhanceAppContext } from 'vitepress'
import { useMediumZoomProvider } from '../hooks'
import './global.css'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx: EnhanceAppContext) {
    const { app, router, siteData } = ctx
    useMediumZoomProvider(app, router) 
  },
  // ...
}

自定义MarkDown图片渲染插件

新建docs/.vitepress/plugins/markdown/image.ts,添加如下代码

ts
import type MarkdownIt from 'markdown-it'
export function ImagePlugin(md: MarkdownIt) {
  const imageRender = md.renderer.rules.image! // 尾部的这个感叹号的意思是断言此变量肯定有值
  md.renderer.rules.image = (...args) => {
    const [tokens, idx] = args
    if (tokens[idx + 2] && /^<!--.*-->/.test(tokens[idx + 2].content)) {
      const data = tokens[idx + 2].content
      if (/size=/.test(data)) {
        const size = data.match(/size=(\d+)(x\d+)?/)
        tokens[idx].attrs?.push(
          ['width', size?.[1] || ''],
          ['height', size?.[2]?.substring(1) || size?.[1] || '']
        )
      }

      tokens[idx].attrs?.push(['loading', 'lazy'], ['decoding', 'async'])
      tokens[idx + 2].content = ''
      return imageRender(...args)
    }
    tokens[idx].attrs?.push(['loading', 'lazy'], ['decoding', 'async'])
    return imageRender(...args)
  }
}

编辑docs/.vitepress/config.ts

ts
import { ImagePlugin } from './plugins/markdown/image'
export default defineConfig({
  // ...
  markdown: {
    config: (md) => {
      md.use(ImagePlugin) 
    },
  },
})

🎉搞定

现在markdown中的所有图片都能实现点击放大效果了,并且使用了原生自带的懒加载

MIT License