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

0 阅读12分钟

image

一、概述

1.1 关于 viewerJs

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

image

1.2 环境准备与安装

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

# 安装核心包
npm install viewerjs

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

1.3 引入与注册

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

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 样式文件,否则样式会错乱:

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 对象,指定图片容器和配置选项。

    new Viewer(element[, options])
    

单图单独预览

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

<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>

基础多图预览

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

<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 元素的显示与隐藏。

配置项类型默认值说明
inlinebooleanfalse是否开启内嵌预览模式,false 开启弹窗模式
buttonbooleantrue查看图片时是否显示右上角的关闭按钮
backdropbooleantrue是否启用模态背景遮罩。
设置为 static 时,点击背景不会关闭查看器。
navbarboolean、Visibilitytrue是否显示底部的缩略图
toolbarBoolean、Visibility、ToolbarOptionstrue是否显示工具栏(数值控制响应式显示条件)
titleboolean、Visibility、Functiontrue是否显示当前图片的标题(默认读取 alt 属性及图片尺寸)
tooltipbooleantrue在缩放图片时,是否显示带有图片比例(百分比)的提示
loadingbooleantrue加载图片时是否显示加载动画
classNamestring自定义类名,用于自定义样式

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

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

2.2 交互与操作配置

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

配置项类型默认值说明
movableBooleantrue是否允许拖动、移动图片
zoomableBooleantrue是否允许缩放图片
rotatableBooleantrue是否允许旋转图片
scalableBooleantrue是否允许翻转图片(水平/垂直)
transitionBooleantrue是否使用 CSS3 过渡动画
keyboardBooleantrue是否支持键盘快捷键操作(如 Esc 退出、方向键切换)
loopBooleantrue切换图片时是否循环播放
toggleOnDblclickbooleantrue当放大或者缩小图片时,双击还原
fullscreenboolean、FullscreenOptionstrue播放幻灯片时是否全屏
focusbooleantrue是否在图片加载完成后自动聚焦到图片上
slideOnTouchbooleantrue是否在触摸设备上滑动时切换图片

2.3 尺寸、层级与缩放配置

配置项类型默认值说明
containerstring、HTMLElementbody容器元素选择器,只有在 inline为 false的时候才可以使用
zoomRatioNumber0.1鼠标滚轮每次滚动时的缩放比例增量
minZoomRatioNumber0.01允许的最小缩放比例
maxZoomRatioNumber100允许的最大缩放比例
minHeightnumber定义图片查看器的最小高度,单位为像素
minWidthnumber定义图片查看器的最小宽度,单位为像素
zIndexnumber2015设置图片查看器弹窗层级,避免被其他组件遮挡
zIndexInlinenumber0设置图片查看器内联层级
initialCoveragenumber0.9初始缩放比例,必须是介于 0 (0%) 和 1 (100%) 之间的正数
initialViewIndexnumber0定义用于查看的图像的初始索引
zoomOnTouchbooleantrue是否在触摸设备上缩放图片
zoomOnWheelbooleantrue是否在鼠标滚轮上缩放图片

2.4 播放与数据源配置

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

三、事件监听与交互扩展

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

3.1 查看

名称说明
view(event: CustomEvent)当图片开始被展示时触发,此时图片可能还在加载中
viewed(event: CustomEvent)当图片查看完成(图片完全加载并渲染完毕)后触发,所有操作图片的方法都已就绪
hide(event: CustomEvent)查看器开始隐藏时触发(动画开始前)
hidden(event: CustomEvent)查看器隐藏完成时触发(动画结束后)
show(event: CustomEvent)查看器开始显示时触发(动画开始前)
shown(event: CustomEvent)查看器显示完成时触发(动画结束后)
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 支持丰富的图片变换操作,所有方法均支持链式调用:

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 调整按钮样式,甚至添加完全自定义的功能按钮(如下载、分享等)。

配置项类型默认值说明
toolbarboolean、Visibility、ToolbarOptionstrue是否显示工具栏并设置其在工具栏中的显示优先级
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 会将其渲染为一个自定义按钮。

属性类型简要说明默认显示
zoomInboolean放大图片的按钮
zoomOutboolean缩小图片的按钮
oneToOneboolean1:1 原始尺寸
resetboolean重置图片大小的按钮
prevboolean查看上一张图片的按钮
playboolean播放图片的按钮
nextboolean查看下一张图片的按钮
rotateLeftboolean向左旋转图片的按钮
rotateRightboolean向右旋转图片的按钮
flipHorizontalboolean图片左右翻转的按钮
flipVerticalboolean图片上下翻转的按钮
<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 完全自定义工具栏

<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>

image