前言
uniapp的预览API, uni.previewImage预览长图在H5环境下长图会模糊
如下图:
手指可以撑开放大, 但会模糊
解决方法:
使用自定义全局预览接口
global-preview-image/index.vue
<template>
<view v-if="show" class="preview-mask" @click.stop="hide">
<!-- 图片预览遮罩层 -->
<swiper
class="preview-swiper"
:current="current"
duration="300"
@change="swiperChange"
>
<!-- 遍历图片地址列表,生成轮播项 -->
<swiper-item v-for="(item, index) in urls" :key="index">
<movable-area class="movable-area">
<movable-view class="movable-view" scale scale-min="1">
<scroll-view scroll-y class="preview-item">
<view
class="preview-image-box"
:style="{
alignItems:
imagesHeight[index] >= windowHeight
? 'flex-start'
: 'center',
}"
>
<image
class="preview-image"
:class="[`preview-image-${index}`]"
:src="item"
:lazy-load="true"
mode="widthFix"
@click.stop="hide"
@load="load(index)"
/>
</view>
</scroll-view>
</movable-view>
</movable-area>
</swiper-item>
</swiper>
<!-- 显示当前图片索引和总数 -->
<text class="preview-index">{{ current + 1 }}/{{ urls.length }}</text>
</view>
</template>
<script>
export default {
data() {
return {
show: false, // 控制预览组件是否显示
urls: [], // 图片地址列表
loadSuccess: [], // 已加载成功的图片索引列表
current: 0, // 当前显示的图片索引
windowHeight: 0, // 设备高度
imagesHeight: [], // 图片高度列表
}
},
mounted() {
/**
* 组件挂载时获取系统窗口高度信息
*/
uni.getSystemInfo({
success: (res) => {
this.windowHeight = res.windowHeight
},
})
},
methods: {
/**
* 打开图片预览组件
* @param {Object} options - 配置参数
* @param {Array} options.urls - 图片地址数组
* @param {Number} options.current - 当前显示的图片索引
*/
open({ urls = [], current = 0 }) {
uni.showLoading({
title: "加载中...",
})
// 如果urls不为空且current为字符串,则查找current在urls中的索引位置
if (urls.length > 0 && typeof current === "string") {
current = urls.findIndex((item) => item === current)
current = current >= 0 ? current : 0
}
// 设置组件数据
this.urls = urls || []
this.loadSuccess = []
this.imagesHeight = Array(urls.length).fill(0)
this.current = current || 0
this.show = true
},
/**
* 关闭图片预览组件
*/
hide() {
this.show = false
uni.hideLoading()
setTimeout(() => {
this.urls = []
}, 300)
},
/**
* 图片加载完成回调
* @param {Number} i - 图片索引
*/
load(i) {
if (!this.loadSuccess.includes(i)) this.loadSuccess.push(i)
// 如果当前图片已加载完成且为当前显示图片,则隐藏加载提示
if (this.loadSuccess.includes(i) && this.current == i) {
uni.hideLoading()
}
// 获取元素
const query = uni.createSelectorQuery().in(this)
// 获取元素高度
query
.select(".preview-image-" + i)
.boundingClientRect((res) => {
this.$set(this.imagesHeight, i, res.height)
})
.exec()
},
/**
* 轮播图切换事件处理
* @param {Object} e - 事件对象,包含 detail.current 表示当前切换到的图片索引
*/
swiperChange(e) {
// 根据切换后的图片索引判断是否显示加载提示
if (this.loadSuccess.includes(e.detail.current)) {
uni.hideLoading()
} else {
uni.showLoading({
title: "加载中...",
})
}
this.current = e.detail.current
},
},
}
</script>
<style scoped>
/* 预览遮罩层样式 */
.preview-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.9);
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
}
/* 轮播图容器样式 */
.preview-swiper {
width: 100%;
height: 100%;
}
/* 预览项容器样式 */
.preview-item {
width: 100vw;
height: 100vh;
}
.preview-image-box {
display: flex;
}
/* 图片及可移动视图相关样式 */
.preview-image,
.preview-image-box,
.movable-view,
.movable-area {
width: 100%;
height: 100%;
}
/* 可移动视图样式 */
.movable-view {
}
/* 预览图片样式 */
.preview-image {
display: block;
}
/* 图片索引文本样式 */
.preview-index {
position: absolute;
bottom: 60rpx;
color: #fff;
font-size: 32rpx;
}
</style>
挂载body上
global-preview-image/index.js
import Vue from 'vue'
import PreviewImage from './index.vue'
const PreviewImageConstructor = Vue.extend(PreviewImage)
let instance
/**
* 初始化预览图片实例
* 创建PreviewImage组件实例并将其挂载到DOM中
*/
function initInstance() {
instance = new PreviewImageConstructor({
el: document.createElement('div')
})
document.body.appendChild(instance.$el)
}
/**
* 预览图片函数
* @param {Object|String} options - 预览配置选项,如果是字符串则作为单张图片URL处理
* @param {Array} options.urls - 图片URL数组
* @param {Number} options.current - 当前显示图片的索引
* @param {Boolean} options.loop - 是否可以循环预览
* @param {Function} options.success - 接口调用成功的回调函数
* @param {Function} options.fail - 接口调用失败的回调函数
* @param {Function} options.complete - 接口调用结束的回调函数
*/
function previewImage(options) {
// 如果实例不存在,则初始化实例
if (!instance) {
initInstance()
}
// 如果传入的是字符串,则转换为包含单个URL的对象格式
if (typeof options === 'string') {
options = {
urls: [options]
}
}
// 调用实例的open方法打开图片预览
instance.open(options)
}
/**
* 插件安装函数
* @param {Object} Vue - Vue构造函数
*/
function install(Vue) {
// 将previewImage方法挂载到Vue原型上,使所有Vue实例都可以访问
Vue.prototype.$previewImage = previewImage
// 挂载到uni对象
if (typeof uni !== 'undefined') {
uni.previewImage = previewImage
}
}
export default {
install,
previewImage
}
在main.js
引入
...
// #ifdef H5
import PreviewImage from '@/components/global-preview-image'
// #endif
...
// #ifdef H5
Vue.use(PreviewImage)
// #endif
...