最近写手机端项目的时候,用的是uniapp + uView,然后出现好多bug。
- uni.showToast() 失效,只能用 u-toast 组件来替代。
- uni.previewImage() 失效,于是就导致 u-upload 图片上传组件默认的图片预览功能也失效。
然后急着要用图片预览这个功能,索性自己手写一个最基础的图片预览。
1. 效果图
2. 引用
简单封装的一个组件,直接在 components 文件夹下创建一个相同名字的目录和vue文件,免注册直接引入。
<template>
<g-previewImage :show="preview.show" :url="preview.url" @close="preview.show = false"></g-previewImage>
</template>
<script>
export default {
data() {
return {
preview: {
show: false,
url: ""
}
}
}
}
</script>
g-previewImage.vue
<template>
<view class="g-previewImage" v-if="show">
<view class="image" :style="{width, height}">
<image :src="url" :mode="mode"></image>
</view>
<u-icon class="icon" name="close" color="#eee" @click="close"></u-icon>
</view>
</template>
<script>
export default {
name: "PreviewImage",
props: {
show: {
type: Boolean,
default: false
},
url: {
type: String,
default: ''
}
},
data() {
return {
isshow: false,
width: 0,
height: 0,
mode: 'aspectFit' // 保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。
}
},
watch: {
show: {
handler(n) {
this.isshow = n
},
immediate: true
},
url: {
handler(n) {
if (n) {
const img = new Image()
img.src = n
this.width = img.width > img.height ? '100%' : img.width + 'px'
this.height = img.height > img.width ? '100%' : img.height + 'px'
}
},
immediate: true
}
},
methods: {
open() {
this.$emit('change', true)
},
close() {
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
.g-previewImage {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .5);
z-index: 99999;
.image {
// background-color: pink;
image {
width: 100%;
height: 100%;
}
}
.icon {
position: absolute;
top: 40rpx;
right: 40rpx;
}
}
</style>
3. 改进
组件注册引入的方式是救急方案,略显繁琐了,那可不可以做成 uni.previewImage() API 那种 js 直接调用的形式呢?
当然可以实现了!我们就来实现个翻版的 uni.previewImage() API ,主要是通过 Vue.extend() 实现。
preview.js
import Vue from 'vue'
import preview from "@/components/previewImage"
// 创建一个 预览组件类
const previewImg = Vue.extend(preview)
class Preview {
constructor() {
// 组件实例挂载
this.instance = new previewImg().$mount()
document.body.appendChild(this.instance.$el)
}
// 定义一个 open 函数,调用实例中的 open 函数
open(url) {
this.instance.open(url)
}
}
export default new Preview()
main.js
import Preview from "@/static/js/preview.js"
Vue.prototype.$preview = Preview
previewImage.vue
<template>
<view class="g-previewImage" v-if="show">
<view class="image" :style="{width, height}">
<image :src="link" :mode="mode"></image>
</view>
<u-icon class="icon" name="close" color="#eee" @click="close"></u-icon>
</view>
</template>
<script>
export default {
name: "PreviewImage",
data() {
return {
link: "",
show: false,
width: 0,
height: 0,
mode: 'aspectFit' // 保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。
}
},
methods: {
open(url) {
const img = new Image()
img.src = url
this.width = img.width > img.height ? '100%' : img.width + 'px'
this.height = img.height > img.width ? '100%' : img.height + 'px'
this.link = url
this.show = true
},
close() {
this.show = false
}
}
}
</script>
<style lang="scss" scoped>
.g-previewImage {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .5);
z-index: 99999;
.image {
// background-color: pink;
image {
width: 100%;
height: 100%;
}
}
.icon {
position: absolute;
top: 40rpx;
right: 40rpx;
}
}
</style>
应用:this.$preview.open(url)
</template>
<view class="content">
<image :src="url" mode="aspectFit" @click="preview(url)"></image>
</view>
</template>
<script>
export default {
data() {
return {
url: require('../../static/img/1.jpg'),
}
},
methods: {
preview(url) {
this.$preview.open(url)
}
}
}
</script>