viewer.js 安装与配置指南:实现图片预览功能

一、概述

1.1 关于 viewerJs

  在现代 Web 应用中,图片展示功能是提升用户体验的关键环节。无论是电商平台的产品详情页、摄影网站的作品展示,还是企业官网的图片画廊,都需要一个功能完善、交互友好的图片查看器。viewer.js 作为一款轻量级的 JavaScript 库,专门用于实现图片查看和预览功能,支持模态弹窗、页面内联两种显示模式,内置缩放、旋转、翻转、拖拽移动等完整图片操作能力,同时适配桌面端键盘快捷键和移动端多点触控手势,兼容所有主流现代浏览器。

1.2 环境准备与安装

  要开始使用 Viewer.js,首先需要将其集成到项目中。使用 npm 安装 Viewer.js 非常简单,只需运行以下命令:

bash 复制代码
# 安装核心包
npm install viewerjs

# TS项目额外安装类型提示(可选)
npm install @types/viewerjs

1.3 引入与注册

  安装完成后,如果项目中多个页面都需要使用图片裁剪,可以考虑全局注册。在 main.js 中添加以下代码:

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
// 引入核心JS与样式
import Viewer from 'viewerjs'
import 'viewerjs/dist/viewer.css'

const app = createApp(App)
// 挂载全局实例,组件内直接使用
app.config.globalProperties.$Viewer = Viewer
app.mount('#app')

  全局注册后,所有组件都可以直接使用、无需重复引入。但注意,这会导致组件始终被打包,如果只有少数页面使用,推荐使用局部引入,这能更好地减少打包体积。请注意,必须同时引入 CSS 样式文件,否则样式会错乱:

javascript 复制代码
import Viewer from 'viewerjs';
import 'viewerjs/dist/viewer.css';

1.4 基础使用

  1. 引入核心文件:在项目中引入 viewerJs 及其 CSS 文件。

  2. HTML 结构:需要为图片提供一个块级容器,单张或多张图片均可。单张图片需包裹在容器中,通过 id 或类名绑定。而多张图片这使用使用 ul 或 div 包裹多个 img 标签。

  3. 初始化Viewer实例:在Vue组件的 mounted 钩子函数中,创建 Viewer 对象,指定图片容器和配置选项。

    javascript 复制代码
    new Viewer(element[, options])

单图单独预览

  这段代码实现了一个基础的图片查看功能,点击图片后会弹出模态框,提供缩放、旋转、翻转等操作。

html 复制代码
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'
import Viewer from 'viewerjs'
import 'viewerjs/dist/viewer.css'

const singleBox = ref()
const singleImg = ref('https://fengyuanchen.github.io/viewerjs/images/tibet-9.jpg')
let viewerInstance: Viewer;

onMounted(() => {
  viewerInstance = new Viewer(singleBox.value)
})
onBeforeUnmount(() => viewerInstance?.destroy())
</script>

<template>
  <div ref="singleBox">
    <img :src="singleImg" alt="单图" />
  </div>
</template>

基础多图预览

  在网站中,详情页通常需要展示多张图片,可以在图片之间切换查看,下面实现一个多图浏览功能。

html 复制代码
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
// 引入viewer核心与样式
import Viewer from 'viewerjs';
import 'viewerjs/dist/viewer.css';

const viewerBox = ref();
let viewerInstance: Viewer;
// 图片数据源
const imageList = ref([
  'https://picsum.photos/id/10/400/300',
  'https://picsum.photos/id/20/400/300',
  'https://picsum.photos/id/30/400/300',
  'https://picsum.photos/id/40/400/300',
  'https://picsum.photos/id/50/400/300'
]);

// 初始化viewer
const initViewer = () => {
  // 先销毁旧实例,防止重复创建
  if (viewerInstance) viewerInstance.destroy();

  nextTick(() => { // 等待DOM渲染完成
    viewerInstance = new Viewer(viewerBox.value, {
      zIndex: 9999, // 弹窗层级,避免被其他组件遮挡
    });
  });
}

// 监听图片列表变化(接口动态加载图片必备)
watch(imageList,initViewer, { deep: true });
// 初始化
onMounted(initViewer);
// 组件销毁释放实例,内存泄漏/路由残留弹窗
onBeforeUnmount(() => viewerInstance?.destroy());
</script>

<template>
  <!-- 所有图片包裹在同一个父容器,绑定ref -->
  <div class="img-container" ref="viewerBox">
    <div v-for="(item, index) in imageList" :key="index" class="img-item">
      <img :src="item" alt="预览图" />
    </div>
  </div>
</template>

<style scoped>
.img-container {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}
.img-item img {
  width: 160px;
  height: 120px;
  object-fit: cover;
  cursor: zoom-in;
}
</style>

二、配置项

2.1 基础显示配置

  用于控制查看器中各个 UI 元素的显示与隐藏。

配置项 类型 默认值 说明
inline boolean false 是否开启内嵌预览模式,false 开启弹窗模式
button boolean true 查看图片时是否显示右上角的关闭按钮
backdrop boolean true 是否启用模态背景遮罩。 设置为 static 时,点击背景不会关闭查看器。
navbar boolean、Visibility true 是否显示底部的缩略图
toolbar Boolean、Visibility、ToolbarOptions true 是否显示工具栏(数值控制响应式显示条件)
title boolean、Visibility、Function true 是否显示当前图片的标题(默认读取 alt 属性及图片尺寸)
tooltip boolean true 在缩放图片时,是否显示带有图片比例(百分比)的提示
loading boolean true 加载图片时是否显示加载动画
className string 自定义类名,用于自定义样式

这里提一下有关 Visibility 的取值规则,如下表所示:

属性值 简要说明
0 隐藏工具栏
1 显示工具栏
2 当屏幕宽度大于 768 像素时显示工具栏
3 当屏幕宽度大于 992 像素时显示工具栏
4 当屏幕宽度大于 1200 像素时显示工具栏

2.2 交互与操作配置

  用于定义用户与图片的交互方式。

配置项 类型 默认值 说明
movable Boolean true 是否允许拖动、移动图片
zoomable Boolean true 是否允许缩放图片
rotatable Boolean true 是否允许旋转图片
scalable Boolean true 是否允许翻转图片(水平/垂直)
transition Boolean true 是否使用 CSS3 过渡动画
keyboard Boolean true 是否支持键盘快捷键操作(如 Esc 退出、方向键切换)
loop Boolean true 切换图片时是否循环播放
toggleOnDblclick boolean true 当放大或者缩小图片时,双击还原
fullscreen boolean、FullscreenOptions true 播放幻灯片时是否全屏
focus boolean true 是否在图片加载完成后自动聚焦到图片上
slideOnTouch boolean true 是否在触摸设备上滑动时切换图片

2.3 尺寸、层级与缩放配置

配置项 类型 默认值 说明
container string、HTMLElement body 容器元素选择器,只有在 inline为 false的时候才可以使用
zoomRatio Number 0.1 鼠标滚轮每次滚动时的缩放比例增量
minZoomRatio Number 0.01 允许的最小缩放比例
maxZoomRatio Number 100 允许的最大缩放比例
minHeight number 定义图片查看器的最小高度,单位为像素
minWidth number 定义图片查看器的最小宽度,单位为像素
zIndex number 2015 设置图片查看器弹窗层级,避免被其他组件遮挡
zIndexInline number 0 设置图片查看器内联层级
initialCoverage number 0.9 初始缩放比例,必须是介于 0 (0%) 和 1 (100%) 之间的正数
initialViewIndex number 0 定义用于查看的图像的初始索引
zoomOnTouch boolean true 是否在触摸设备上缩放图片
zoomOnWheel boolean true 是否在鼠标滚轮上缩放图片

2.4 播放与数据源配置

配置项 类型 默认值 说明
url String、Function src 获取原始图片 URL 的位置。 如果是字符串,应该是每个图片元素的属性之一。 如果是函数,应返回有效的图片 URL
filter Function 筛选用于查看的图片。如果图片可查看返回 true,否则返回 false
interval Number 5000 播放时自动切换图片的延迟时间,单位为毫秒

三、事件监听与交互扩展

  viewer.js 提供了一套完善的事件回调机制,允许开发者在图片查看器的不同生命周期阶段介入自定义逻辑。通过事件回调,可以实现自定义 Loading 效果、埋点统计、UI 联动等高级功能。

3.1 查看

名称 说明
view(event: CustomEvent) 当图片开始被展示时触发,此时图片可能还在加载中
viewed(event: CustomEvent) 当图片查看完成(图片完全加载并渲染完毕)后触发,所有操作图片的方法都已就绪
hide(event: CustomEvent) 查看器开始隐藏时触发(动画开始前)
hidden(event: CustomEvent) 查看器隐藏完成时触发(动画结束后)
show(event: CustomEvent) 查看器开始显示时触发(动画开始前)
shown(event: CustomEvent) 查看器显示完成时触发(动画结束后)
javascript 复制代码
const viewer = new Viewer(imageElement, {
  shown() {
    console.log('查看器已显示');
  },
  viewed() {
    console.log('图片已加载完成');
  },
  hidden() {
    console.log('查看器已隐藏');
  }
});

3.2 图片操作交互

名称 说明
zoom(event: ZoomEvent) 图片缩放过程中持续触发,实时监听缩放,用于自定义 UI 上的缩放百分比显示
zoomed(event: ZoomedEvent) 图片缩放完成后触发,缩放结束后进行某些计算或状态保存
rotate(event: RotateEvent) 图片旋转过程中触发,实时监听旋转角度
rotated(event: RotatedEvent) 图片旋转完成后触发,旋转结束后更新 UI 状态
move(event: MoveEvent) 图片移动/拖拽过程中持续触发,实时追踪拖拽位置
moved(event: MovedEvent) 图片移动/拖拽完成后触发,拖拽结束后记录最终位置
scale(event: ScaleEvent) 图片翻转时触发的回调函数
scaled(event: ScaledEvent) 图片翻转完成后触发的回调函数

3.3 多图切换与播放

名称 说明
play(event: CustomEvent) 点击播放按钮,开始幻灯片播放时触发
stop(event: CustomEvent) 点击停止按钮或手动切换图片,停止幻灯片播放时触发

3.4 其他

名称 说明
ready(event: CustomEvent) 初始化完成后触发,只会触发一次

四、常用 API 方法

  viewer.js 提供了完整的图片操作API,覆盖从基础显示到高级变换的全部功能。

4.1 显示与导航

方法名 参数 简要说明 示例
show(immediate?: boolean) Immediate:是否立即显示 手动触发预览器显示 viewer.show(true)
hide(immediate?: boolean) Immediate:是否立即隐藏 手动隐藏预览器 viewer.hide()
view(index) 查看指定索引的图片,若不传参数则触发当前图片的查看 viewer.view(2)
prev(loop) 查看下一张图片 viewer.prev(true)
next(loop) 查看下一张图片 viewer.next(true)

4.2 图片变换

方法名 参数 简要说明
zoom(ratio: number, hasTooltip?: boolean) 按相对比例进行缩放。showTooltip 是否显示提示信息
zoomTo(ratio: number, hasTooltip?: boolean) 将图片缩放到指定的比例
rotate(degree: number) 按相对角度进行旋转,正数右转、负数左转
rotateTo(degree: number) 将图片旋转至指定的绝对角度
scale(scaleX: number, scaleY?: number) 对图片进行水平或垂直翻转(镜像)
scaleX(scaleX: number) 水平方向翻转图片
scaleY(scaleY: number) 垂直方向翻转图片
move(offsetX: number, offsetY?: number) 将图片移动到指定的坐标位置。 move(1)右移、move(-1,0)左移、move(0,-1)上移、move(0,1)下移
moveTo(x: number, y?: number) 将图片移动到指定的绝对坐标位置

  viewer.js 支持丰富的图片变换操作,所有方法均支持链式调用:

javascript 复制代码
viewer.rotate(90)    // 顺时针旋转90度
      .scale(1.5)    // 放大1.5倍
      .move(100, 50) // 向右移动100px,向下移动50px
      .zoomTo(2);    // 缩放到原始尺寸的2倍

4.3 播放与全屏控制

方法名 参数 简要说明
play(fullscreen?: boolean) 开始全屏幻灯片自动播放
stop() 停止幻灯片自动播放
full() 进入全屏模式
exit() 退出全屏模式
reset() 将图片重置为其初始状态

4.4 实例管理

方法名 参数 简要说明 示例
update() 更新查看器,通常用于容器内图片列表发生动态变化时
destroy() 销毁查看器实例,释放所有绑定事件

五、进阶技巧:打造个性化图片浏览体验

五、进阶技巧:打造个性化图片浏览体验

5.1 自定义工具栏

  Viewer.js 提供了非常灵活的工具栏自定义功能,允许通过配置 toolbar 选项灵活实现内置按钮的显示/隐藏、添加自定义按钮并绑定事件,或者通过 CSS 调整按钮样式,甚至添加完全自定义的功能按钮(如下载、分享等)。

配置项 类型 默认值 说明
toolbar boolean、Visibility、ToolbarOptions true 是否显示工具栏并设置其在工具栏中的显示优先级
graph TD A[ToolbarOptions] A-->B[ToolbarOption] B-->C1[boolean] B-->C2[Visibility]-->C21[&#34;显示优先级<br/>候选值:0、1、2、3、4&#34;] B-->C3[ToolbarButtonSize]-->C31[&#34;按钮大小<br/>候选值:small、medium、large&#34;] B-->C4[Function] B-->C5[ToolbarButtonOptions] C5-->C51[click] C5-->C52[show] C5-->C53[size] style A fill:#D9D919 style B fill:#5F9F9F style C5 fill:#70DB93

  Viewer.js 允许在 toolbar 配置对象中添加新的键值对,如果该键不是内置按钮名称,且对应的值是一个函数,Viewer.js 会将其渲染为一个自定义按钮。

属性 类型 简要说明 默认显示
zoomIn boolean 放大图片的按钮
zoomOut boolean 缩小图片的按钮
oneToOne boolean 1:1 原始尺寸
reset boolean 重置图片大小的按钮
prev boolean 查看上一张图片的按钮
play boolean 播放图片的按钮
next boolean 查看下一张图片的按钮
rotateLeft boolean 向左旋转图片的按钮
rotateRight boolean 向右旋转图片的按钮
flipHorizontal boolean 图片左右翻转的按钮
flipVertical boolean 图片上下翻转的按钮
html 复制代码
<script setup>
import { ref, onMounted } from 'vue'
import Viewer from 'viewerjs'
import 'viewerjs/dist/viewer.css'

const viewerWrapRef = ref();
let viewer;
const imgList = ref([
  'https://picsum.photos/id/237/800/800',
  'https://picsum.photos/id/10/800/800'
])

const openViewer = (index) => {
  if (viewer) {
    viewer.view(index)
    return
  }

  viewer = new Viewer(viewerWrapRef.value, {
    initialViewIndex: index,
    // 工具栏开关配置:true显示 false隐藏
    toolbar: {
      zoomIn: true,
      zoomOut: true,
      oneToOne: false, // 原图尺寸按钮隐藏
      reset: true,
      prev: true,
      play: false, // 自动播放隐藏
      next: true,
      rotateLeft: true,
      rotateRight: true,
      flipHorizontal: false,
      flipVertical: false
    },
    title: true,
    movable: true,
    zoomable: true,
    rotatable: true
  })
  viewer.view(index)
}

onMounted(() => {})
</script>

<template>
  <div ref="viewerWrapRef">
    <img v-for="(src, idx) in imgList" :key="idx" :src="src" class="preview-img" @click="openViewer(idx)" alt="图片" />
  </div>
</template>

<style scoped>
.preview-img {
  width: 150px;
  margin: 8px;
  cursor: pointer;
}
</style>

5.2 完全自定义工具栏

html 复制代码
<script setup>
import { ref, onUnmounted } from 'vue'
import Viewer from 'viewerjs'
import 'viewerjs/dist/viewer.css'

let viewer = null
const showTool = ref(false)
const imgList = ref([
  'https://picsum.photos/id/237/800/800',
  'https://picsum.photos/id/10/800/800',
  'https://picsum.photos/id/100/800/800'
])
const viewerWrapRef = ref();

// 初始化预览,关闭原生工具栏、关闭右上角关闭按钮
const createViewer = (startIdx) => {
  viewer = new Viewer(viewerWrapRef.value, {
    initialViewIndex: startIdx,
    toolbar: false, // 关闭默认工具栏
    button: false, // 关闭右上角关闭按钮
    viewed: () => {
      // 图片打开后显示自定义工具栏
      showTool.value = true
    },
    hidden: () => {
      // 关闭预览隐藏工具栏
      showTool.value = false
    }
  })
}

// 打开预览,指定索引图片开始显示
const openViewer = (index) => {
  if (!viewer) createViewer(index)
  viewer.view(index)
}

// 自定义按钮事件
const handleZoomIn = () => viewer.zoom(0.1)
const handleZoomOut = () => viewer.zoom(-0.1)
const handleRotateL = () => viewer.rotate(-90)
const handleRotateR = () => viewer.rotate(90)
const handleReset = () => viewer.reset()
const handlePrev = () => viewer.prev()
const handleNext = () => viewer.next()
const handleClose = () => viewer.hide()

// 自定义拓展功能:下载当前图片
const downloadImg = () => {
  const currSrc = viewer.image.src
  const a = document.createElement('a')
  a.href = currSrc
  a.download = 'preview-img'
  a.click()
}

onUnmounted(() => {
  viewer?.destroy()
})
</script>

<template>
  <div class="img-wrap" ref="viewerWrapRef">
    <!-- 缩略图 -->
    <img v-for="(src, idx) in imgList" :key="idx" :src="src" class="thumb" @click="openViewer(idx)" alt="图片" />

    <!-- 自定义工具栏弹窗(viewer遮罩上层) -->
    <div v-if="showTool" class="custom-toolbar">
      <button @click="handleZoomIn">放大</button>
      <button @click="handleZoomOut">缩小</button>
      <button @click="handleRotateL">左旋转</button>
      <button @click="handleRotateR">右旋转</button>
      <button @click="handleReset">重置</button>
      <button @click="handlePrev">上一张</button>
      <button @click="handleNext">下一张</button>
      <button @click="downloadImg">下载图片</button>
      <button @click="handleClose">关闭</button>
    </div>
  </div>
</template>

<style scoped>
.thumb {
  width: 120px;
  margin: 6px;
  cursor: pointer;
}
/* 自定义工具栏固定在底部居中,层级高于viewer遮罩 */
.custom-toolbar {
  position: fixed;
  bottom: 40px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 99999;
  background: rgba(0,0,0,0.6);
  padding: 12px 20px;
  border-radius: 8px;
  display: flex;
  gap: 10px;
}
.custom-toolbar button {
  color: #fff;
  background: transparent;
  border: 1px solid #fff;
  padding: 6px 12px;
  border-radius: 4px;
  cursor: pointer;
}
</style>
相关推荐
Fluxart.ai3 小时前
电商商品图审核怎么自动化?规则引擎、人工复核与发布门禁
java·前端·自动化
why技术3 小时前
AI 写的文章,可能都带着手敲一遍都去不掉的“隐形水印”。
前端·人工智能·后端
愚公搬代码4 小时前
【愚公系列】《Android应用案例开发大全》016-LBS类应用掌上杭州(辅助工具类的开发)
android·前端
CodeSheep4 小时前
又一个华为天才少年,离职了!
前端·后端·程序员
kyriewen4 小时前
面试官说"打开你的AI工具"——我才发现,他考的根本不是写代码
前端·人工智能·面试
IT_陈寒5 小时前
Vue的双向绑定把我坑惨了,原来这个场景不能用
前端·人工智能·后端
hunterandroid5 小时前
[Android 从零到一] Compose LazyColumn 性能优化:key、稳定性与重组治理
android·前端
lichenyang4536 小时前
从一次团队邀请开始:用 React、NestJS 与 Socket.IO 做可靠的实时通知
前端
vtian6 小时前
一篇文章吃透 Monorepo:pnpm + Turborepo + Changesets 全流程实战(含 8 个踩坑)
前端