vue3中使用medium-zoom方法实例代码

 更新时间:2026年03月12日 09:31:58   作者:在掘金80110  
这篇文章主要介绍了vue3中使用medium-zoom方法的相关资料,Medium Zoom是一个轻量级的JavaScript库,用于实现类似Medium网站的图片点击放大缩放效果,文中通过代码介绍的非常详细,需要的朋友可以参考下

简介

Medium Zoom 是一个轻量级 JavaScript 库,用于实现类似 Medium 网站的图片点击放大缩放效果。本文档专注于在 Vue3 项目中的集成和使用。

核心特性

  • 📱 响应式设计 - 完美适配移动端和桌面端
  • 🚀 高性能 - 优化至 60fps 流畅动画
  • ⚡️ 高清图支持 - 通过 data-zoom-src 属性加载高清大图
  • 🎨 可定制化 - 支持自定义边距、背景、滚动偏移等
  • 🎂 事件系统 - 完整的生命周期事件监听
  • 🔌 零依赖 - 无任何运行时依赖
  • 💎 框架友好 - 完美支持 Vue3 组合式 API

安装

使用 npm

npm install medium-zoom

使用 yarn

yarn add medium-zoom

使用 pnpm

pnpm add medium-zoom

Vue3 快速开始

方法一:封装组件(推荐)

创建一个可复用的 ImageZoom.vue 组件:

<script setup lang="ts">
import { watch, type ImgHTMLAttributes, type ComponentPublicInstance } from 'vue'
import mediumZoom, { type Zoom, type ZoomOptions } from 'medium-zoom'

interface Props extends ImgHTMLAttributes {
  options?: ZoomOptions
}

const props = defineProps<Props>()

let zoom: Zoom | null = null

function getZoom() {
  if (zoom === null) {
    zoom = mediumZoom(props.options)
  }
  return zoom
}

function attachZoom(ref: Element | ComponentPublicInstance | null) {
  const image = ref as HTMLImageElement | null
  const zoom = getZoom()

  if (image) {
    zoom.attach(image)
  } else {
    zoom.detach()
  }
}

watch(() => props.options, (options) => {
  const zoom = getZoom()
  zoom.update(options || {})
})
</script>

<template>
  <img :ref="attachZoom" v-bind="$attrs" />
</template>

使用封装的组件

<script setup lang="ts">
import ImageZoom from './components/ImageZoom.vue'
</script>

<template>
  <article>
    <h1>我的图片画廊</h1>

    <!-- 基础使用 -->
    <ImageZoom src="/images/photo-1.jpg" alt="照片1" />

    <!-- 自定义背景色 -->
    <ImageZoom 
      src="/images/photo-2.jpg" 
      alt="照片2"
      :options="{ background: '#000000' }" 
    />

    <!-- 自定义边距 -->
    <ImageZoom 
      src="/images/photo-3.jpg" 
      alt="照片3"
      :options="{ margin: 48, background: 'rgba(0,0,0,0.9)' }" 
    />

    <!-- 高清图支持 -->
    <ImageZoom 
      src="/images/thumbnail.jpg" 
      data-zoom-src="/images/hd-image.jpg"
      alt="高清图片"
    />
  </article>
</template>

方法二:在 Composable 中使用

创建 useImageZoom.ts

import { onMounted, onUnmounted, ref, type Ref } from 'vue'
import mediumZoom, { type Zoom, type ZoomOptions } from 'medium-zoom'

export function useImageZoom(
  selector?: string | HTMLElement,
  options?: ZoomOptions
) {
  const zoom: Ref<Zoom | null> = ref(null)

  onMounted(() => {
    zoom.value = mediumZoom(selector, options)
  })

  onUnmounted(() => {
    zoom.value?.detach()
  })

  return {
    zoom,
    open: () => zoom.value?.open(),
    close: () => zoom.value?.close(),
    toggle: () => zoom.value?.toggle(),
  }
}

使用 Composable:

<script setup lang="ts">
import { onMounted } from 'vue'
import { useImageZoom } from './composables/useImageZoom'

// 初始化 zoom
const { zoom, open, close } = useImageZoom('[data-zoomable]', {
  margin: 24,
  background: '#BADA55',
})

// 也可以在挂载后动态附加图片
onMounted(() => {
  zoom.value?.attach('.gallery-image')
})
</script>

<template>
  <div>
    <img src="/image-1.jpg" data-zoomable alt="图片1" />
    <img src="/image-2.jpg" data-zoomable alt="图片2" />
    <img src="/image-3.jpg" class="gallery-image" alt="图片3" />
    
    <!-- 手动控制按钮 -->
    <button @click="open">放大第一张图片</button>
    <button @click="close">关闭缩放</button>
  </div>
</template>

方法三:直接在组件中使用

<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import mediumZoom from 'medium-zoom'

const zoomRef = ref(null)

onMounted(() => {
  zoomRef.value = mediumZoom('.zoomable', {
    margin: 24,
    background: '#fff',
    scrollOffset: 40,
  })
})

onUnmounted(() => {
  zoomRef.value?.detach()
})
</script>

<template>
  <div>
    <img src="/image.jpg" class="zoomable" alt="可缩放图片" />
  </div>
</template>

配置选项 (ZoomOptions)

属性类型默认值说明
marginnumber0缩放图片外的空白边距(像素)
backgroundstring"#fff"遮罩层背景色,支持任何 CSS 颜色值
scrollOffsetnumber40滚动多少像素后自动关闭缩放
containerstring | HTMLElement | objectnull指定缩放渲染的容器视口
templatestring | HTMLTemplateElementnull自定义模板元素

配置示例

const options: ZoomOptions = {
  margin: 48,                    // 48px 边距
  background: 'rgba(0,0,0,0.9)', // 半透明黑色背景
  scrollOffset: 100,             // 滚动 100px 后关闭
  container: '#zoom-container',  // 在指定容器内缩放
}

API 方法

open(options?: { target?: HTMLElement }): Promise<Zoom>

打开缩放,返回 Promise。

zoom.value?.open()
// 或指定目标图片
zoom.value?.open({ target: imgElement })

close(): Promise<Zoom>

关闭缩放,返回 Promise。

zoom.value?.close()

toggle(options?: { target?: HTMLElement }): Promise<Zoom>

切换缩放状态(打开/关闭)。

zoom.value?.toggle()

attach(...selectors): Zoom

附加图片到缩放实例。

zoom.value?.attach('#image-1', '#image-2')
zoom.value?.attach(document.querySelector('#image-3'))

detach(...selectors): Zoom

从缩放实例中移除图片。

zoom.value?.detach('#image-1')
zoom.value?.detach() // 移除所有图片

update(options: ZoomOptions): Zoom

更新配置选项。

zoom.value?.update({ 
  background: '#000',
  margin: 50 
})

clone(options?: ZoomOptions): Zoom

克隆一个新的缩放实例,合并新配置。

const newZoom = zoom.value?.clone({ margin: 100 })

on(type: string, listener: Function, options?): Zoom

注册事件监听器。

zoom.value?.on('open', (event) => {
  console.log('图片已打开', event.detail.zoom)
})

off(type: string, listener: Function, options?): Zoom

移除事件监听器。

const handler = (event) => { /* ... */ }
zoom.value?.on('open', handler)
zoom.value?.off('open', handler)

getOptions(): ZoomOptions

获取当前配置选项。

const currentOptions = zoom.value?.getOptions()

getImages(): HTMLElement[]

获取所有已附加的图片元素。

const images = zoom.value?.getImages()

getZoomedImage(): HTMLElement | null

获取当前正在缩放的图片元素。

const currentImage = zoom.value?.getZoomedImage()

事件系统

可用事件

事件名触发时机说明
open调用 open() 方法时立即触发缩放动画开始前
opened缩放动画完成后图片已完全放大
close调用 close() 方法时立即触发关闭动画开始前
closed关闭动画完成后图片已完全缩小
detach调用 detach() 方法时图片从实例中移除
update调用 update() 方法时配置选项已更新

Vue3 中使用事件

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import mediumZoom from 'medium-zoom'

const zoom = ref(null)
const zoomCount = ref(0)

onMounted(() => {
  zoom.value = mediumZoom('[data-zoomable]')
  
  // 监听打开事件
  zoom.value.on('open', (event) => {
    console.log('开始缩放:', event.target.alt)
  })
  
  // 监听打开完成事件
  zoom.value.on('opened', (event) => {
    zoomCount.value++
    console.log(`已缩放 ${zoomCount.value} 次`)
  })
  
  // 监听关闭事件
  zoom.value.on('close', () => {
    console.log('开始关闭缩放')
  })
  
  // 监听关闭完成事件
  zoom.value.on('closed', () => {
    console.log('缩放已关闭')
  })
  
  // 单次事件监听
  zoom.value.on('open', (event) => {
    console.log('这个只触发一次')
  }, { once: true })
})
</script>

<template>
  <div>
    <img src="/image.jpg" data-zoomable alt="图片" />
    <p>已缩放次数: {{ zoomCount }}</p>
  </div>
</template>

高级用法

1. 高清图加载

使用 data-zoom-src 属性指定高清大图:

<template>
  <ImageZoom 
    src="/thumbnails/small.jpg" 
    data-zoom-src="/images/large-hd.jpg"
    alt="高清图片"
  />
</template>

2. 在 Markdown 渲染中使用

适用于博客、文档等场景:

<script setup lang="ts">
import { onMounted, ref, nextTick } from 'vue'
import mediumZoom from 'medium-zoom'
import MarkdownIt from 'markdown-it'

const md = new MarkdownIt()
const content = ref('')
const zoom = ref(null)

const markdown = `
# 我的文章
![图片1](/image-1.jpg)
![图片2](/image-2.jpg)
`

onMounted(async () => {
  content.value = md.render(markdown)
  
  await nextTick()
  
  // 为所有 markdown 渲染的图片添加缩放
  zoom.value = mediumZoom('.markdown-body img', {
    background: 'rgba(255,255,255,0.95)',
    margin: 24
  })
})
</script>

<template>
  <div class="markdown-body" v-html="content"></div>
</template>

3. 自定义容器

在指定容器内缩放:

<script setup lang="ts">
import { onMounted } from 'vue'
import mediumZoom from 'medium-zoom'

onMounted(() => {
  mediumZoom('[data-zoomable]', {
    container: '#custom-container',
    background: 'transparent'
  })
})
</script>

<template>
  <div id="custom-container" style="position: relative; height: 500px;">
    <img src="/image.jpg" data-zoomable alt="图片" />
  </div>
</template>

4. 动态添加/移除图片

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import mediumZoom from 'medium-zoom'

const images = ref(['/img1.jpg', '/img2.jpg'])
const zoom = ref(null)

onMounted(() => {
  zoom.value = mediumZoom('.gallery img')
})

function addImage() {
  const newImg = `/img${images.value.length + 1}.jpg`
  images.value.push(newImg)
  
  // 下一帧后附加新图片
  nextTick(() => {
    const newElement = document.querySelector(`.img-${images.value.length - 1}`)
    zoom.value?.attach(newElement)
  })
}

function removeImage(index: number) {
  const imgElement = document.querySelector(`.img-${index}`)
  zoom.value?.detach(imgElement)
  images.value.splice(index, 1)
}
</script>

<template>
  <div class="gallery">
    <img 
      v-for="(src, index) in images" 
      :key="index"
      :src="src" 
      :class="`img-${index}`"
      alt="图片" 
    />
    <button @click="addImage">添加图片</button>
  </div>
</template>

5. 从外部按钮触发缩放

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import mediumZoom from 'medium-zoom'

const zoom = ref(null)
const imageRef = ref(null)

onMounted(() => {
  zoom.value = mediumZoom(imageRef.value)
})

function handleZoom() {
  zoom.value?.toggle({ target: imageRef.value })
}
</script>

<template>
  <div>
    <img ref="imageRef" src="/image.jpg" alt="图片" />
    <button @click="handleZoom">点击缩放图片</button>
  </div>
</template>

6. 图片画廊示例

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import mediumZoom from 'medium-zoom'

const zoom = ref(null)

const gallery = [
  { id: 1, src: '/gallery/img1.jpg', thumb: '/gallery/thumb1.jpg', alt: '风景1' },
  { id: 2, src: '/gallery/img2.jpg', thumb: '/gallery/thumb2.jpg', alt: '风景2' },
  { id: 3, src: '/gallery/img3.jpg', thumb: '/gallery/thumb3.jpg', alt: '风景3' },
]

onMounted(() => {
  zoom.value = mediumZoom('.gallery-image', {
    margin: 48,
    background: '#000',
  })
  
  zoom.value.on('opened', (event) => {
    console.log(`正在查看: ${event.target.alt}`)
  })
})
</script>

<template>
  <div class="gallery-grid">
    <div v-for="item in gallery" :key="item.id" class="gallery-item">
      <img 
        :src="item.thumb" 
        :data-zoom-src="item.src"
        :alt="item.alt"
        class="gallery-image"
      />
      <p>{{ item.alt }}</p>
    </div>
  </div>
</template>

<style scoped>
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
}

.gallery-image {
  width: 100%;
  cursor: zoom-in;
  transition: opacity 0.3s;
}

.gallery-image:hover {
  opacity: 0.8;
}
</style>

样式定制

如果需要自定义 z-index(避免与其他 UI 框架冲突):

/* 在全局样式中添加 */
.medium-zoom-overlay,
.medium-zoom-image--opened {
  z-index: 9999;
}

自定义动画时长:

.medium-zoom-image--opened {
  transition: transform 300ms cubic-bezier(0.2, 0, 0.2, 1);
}

TypeScript 支持

完整的 TypeScript 类型定义:

import mediumZoom, { 
  type Zoom, 
  type ZoomOptions, 
  type ZoomSelector,
  type ZoomOpenOptions 
} from 'medium-zoom'

// Zoom 实例类型
const zoom: Zoom = mediumZoom()

// 配置选项类型
const options: ZoomOptions = {
  margin: 24,
  background: '#fff',
  scrollOffset: 40,
}

// 选择器类型
const selector: ZoomSelector = '.image' // string | HTMLElement | HTMLElement[] | NodeList

常见问题

Q: 缩放的图片看不见?

A: 某些 UI 框架可能设置了较高的 z-index,需要手动设置:

.medium-zoom-overlay,
.medium-zoom-image--opened {
  z-index: 9999;
}

Q: 在 v-for 中使用无效?

A: 确保在 DOM 更新后再附加图片:

<script setup>
import { nextTick } from 'vue'

async function addImages() {
  images.value.push(newImage)
  await nextTick()
  zoom.value?.attach('.new-image')
}
</script>

Q: 如何在路由切换时清理?

A: 使用 onUnmounted 钩子:

<script setup>
import { onUnmounted } from 'vue'

onUnmounted(() => {
  zoom.value?.detach()
})
</script>

浏览器支持

  • Chrome 36+
  • Firefox 34+
  • Safari 9+
  • Edge 12+
  • IE 10+ (需要 template polyfill)

参考资源

提示: 本文档基于 medium-zoom 库分析生成,适用于 Vue 3.x 版本项目。

到此这篇关于vue3中使用medium-zoom方法的文章就介绍到这了,更多相关vue3使用medium-zoom内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈Vue DIFF

    浅谈Vue DIFF

    本文主要介绍了浅谈Vue DIFF,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • vue自定义过滤器创建和使用方法详解

    vue自定义过滤器创建和使用方法详解

    这篇文章主要为大家详细介绍了vue自定义过滤器创建和使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • antd-DatePicker组件获取时间值,及相关设置方式

    antd-DatePicker组件获取时间值,及相关设置方式

    这篇文章主要介绍了antd-DatePicker组件获取时间值,及相关设置方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • vue3 与 vue2 优点对比汇总

    vue3 与 vue2 优点对比汇总

    随着用vue3 的开发者越来越多,其必定是又她一定的有带你,接下来这篇文章小编就为大家介绍vue3 对比 vue2 有什么优点?感兴趣的小伙伴请跟小编一起阅读下文吧
    2021-09-09
  • 基于vue-simplemde实现图片拖拽、粘贴功能

    基于vue-simplemde实现图片拖拽、粘贴功能

    这篇文章主要介绍了基于vue-simplemde实现图片拖拽、粘贴功能,需要的朋友可以参考下
    2018-04-04
  • vue项目中canvas实现截图功能

    vue项目中canvas实现截图功能

    这篇文章主要为大家详细介绍了vue项目中canvas实现截图功能,截取图片的一部分,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • Vue.js中数组变动的检测详解

    Vue.js中数组变动的检测详解

    这篇文章给大家主要介绍了Vue.js中数组变动的检测,文中给出了详细的示例代码和过程介绍,相信会对大家的理解和学习很有帮助,有需要的朋友们下面来一起看看吧。
    2016-10-10
  • react router零基础使用教程

    react router零基础使用教程

    React-Router 路由库,是官方维护的路由库,事实上也是唯一可选的路由库。它通过管理 URL,实现组件的切换和状态的变化,开发复杂的应用几乎肯定会用到
    2022-09-09
  • 微信小程序如何像vue一样在动态绑定类名

    微信小程序如何像vue一样在动态绑定类名

    这篇文章主要介绍了微信小程序如何像vue一样在动态绑定类名,文中给大家提到了vue与微信小程序的区别,需要的朋友可以参考下
    2018-04-04
  • 关于vue-socket.io使用及版本原因消息无法监听bug

    关于vue-socket.io使用及版本原因消息无法监听bug

    这篇文章主要介绍了关于vue-socket.io使用及版本原因消息无法监听bug,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10

最新评论