vue 通过js触发element ui的图片预览组件
element ui的图片预览组件并没有单独提供出来,默认是在image组件里面一起使用,但是有的时候,我们需要点击某一个自定义按钮,然后弹出图片预览,但是,又不需要使用el-image组件,于是乎,有了这篇笔记。
首先思路并不是我的,我也是看了下同事,他网上找的教程,然后我看了下,在掘金找到一篇文章:《Vue中使用element-ui的内置组件实现图片预览全局调用功能》;深受启发,于是也记下自己的笔记。
首先思路是两个:
- element ui的图片预览本身是一个vue组件
- vue.extend构造器将vue组件通过js的方式构造出来,new出来的这个构造器,他的属性为组件的属性以及其他(其他就是我也不明白)
那么就很简单了,我们将图片预览组件引入,通过构造器构造出来,拿到构造后的实例,实例的属性就是组件的配置,我们再把组件的show方法挂载的全局vue的原型上,不就可以在任何地方通过this.xxx
方法触发了吗?
当然,也没有那么简单,还是有点曲折,我们一一解决。
引入图片预览组件
组件的路径大概是:element-ui/packages/image/src/image-viewer
我们查看这个组件,可以看到,他其实是没有v-if指令的,这就导致这个组件一使用,就在html上显示了。
查看一下element的image组件,可以看到,是在外面做了v-if
所以,我们也需要这么做,这样的话就不能直接引入再通过构造器构造了,我们需要一个中间层,用于v-if管理。
所以我们需要创建一个vue组件:Preview.vue
<template>
<transition name="viewer-fade">
<ElImageViewer v-if="show" :urlList="urlList"
:on-close="closeViewer" />
</transition>
</template>
<script>
import ElImageViewer from "element-ui/packages/image/src/image-viewer";
export default {
props: {
urlList: {
type: Array,
default: () => [],
},
},
data() {
return {
show: false,
};
},
methods: {
closeViewer() {
this.show = false;
},
},
components: {
ElImageViewer,
},
};
</script>
组件里面目前用了两个属性一个方法,show用于控制是否显示关闭,closeViewer方法是一个回调,当用户点击了关闭后会触发这个回调方法,我们就需要将show设置为false。
urlList就是图片的数组了,由于图片可以是多个,所以用的数组。
此时中间管理层我们已经做好了,下面开始构造他
vue.enxted构造组件
构造器我们需要使用到Vue实例,而且我们希望的是,这个组件可以像普通组件一样,import引入,然后vue.use()
激活,所以我们要手动先写一个组件的js文件,配合use方法使用。
use方法接收一个参数,这个参数可以是对象,也可以是函数,如果是对象的话,该对象就必须提供一个install的属性,这个属性是一个方法,接收一个参数,就是Vue实例。
因为我们需要import引入刚刚创建的中间管理层组件,所以只能使用对象的形式。
代码如下:
preview.js:
import PreviewItem from "./Preview.vue";
const Preview = {};
Preview.install = function (Vue) {
const PreviewConstructor = Vue.extend(PreviewItem);
const instance = new PreviewConstructor();
instance.$mount(document.createElement("div"));
document.body.appendChild(instance.$el);
Vue.prototype.$showImg = function (urlList=[]) {
instance.urlList = urlList
instance.show = true;
}
}
export default Preview;
大概原理:
引入管理中间件组件,创建一个对象Preview,给对象添加install方法,接收到vue实例对象。
通过vue实例对象的extend构造器,将管理中间件组件作为参数传入,此时会return出一个vue组件的构造函数,我们new出这个构造函数。
此时dom并没有生成,我们需要通过这个new出的构造函数的$mount方法,将它挂载到元素上,但是此时我们并不需要他真实挂载到网页的某个地方,所以我们创建一个div元素,将组件挂载到div中,这样他就不会出现在网页中去。
然后我们再通过instance组件的构造函数实例的$el属性,拿到dom,在将它传入网页的body中去。
此时我们的元素v-if为false,所以并不会显示。
但是,我们还没有办法触发让他显示,但是我们已知了两个条件:
- 有全局的Vue对象了
- instance实例已经可以通过他控制组件的属性
所以我们可以创建一个方法,然后挂载到Vue的原型上,方法接收一个数组,也就是图片数组,并且在方法触发时,控制instance的show属性为true。
然后我们导出这个Preview对象;
main.js 激活
在main.js文件中
import Preview from "./preview.js";
Vue.use(Preview);
这样就行了, 我们可以在项目的vue组件里面,使用:
this.$showImg(["xxx"])
来触发图片预览组件。
谈谈动画
element 原装组件触发的图片预览弹窗,第一次显示的时候会有动画的,关闭的时候就没有动画了,但是我按照图片组件的那种引入方式写,不知道为什么,就是不会有动画出现。
我想了下,可能是因为预览组件transition元素里面并没有使用v-if或者v-show导致的,但是原装里面没有使用,为啥会有动画????
所以很无奈,我只能在管理中间层组件那使用了一个transition元素包裹,这样效果就和官方的保持了一致,但是我也不知道为什么官方的可以有动画,所以,等待以后明白了再说吧,或者有大佬点醒下我。
关于预览组件点击遮罩层关闭的问题
其实官方组件已经考虑到这个问题了,但是并没有使用,可能是体验不好把!
打开图片预览组件,我们找到prop属性,里面有这个一个配置:
maskClosable: {
type: Boolean,
default: true,
},
这个属性字面意思是mask元素的close开关,默认是true,其实表示的是默认点击mask遮罩层的时候,是可以关闭预览弹窗的。
因为我们在去methods里面找handleMaskClick
方法,这个方法是绑定在mask元素上的:
<div class="el-image-viewer__mask" @click.self="handleMaskClick"></div>
方法内容为:
handleMaskClick() {
if (this.maskClosable) {
this.hide();
}
},
hide() {
this.deviceSupportUninstall();
this.onClose();
},
可以看到,handleMaskClick会判断maskClosable是否为true,如果为true,那么就会触发hide方法,hide则会在关闭一些事件监听后,触发onClose,这个是由prop接收的回调。
那么为什么会有人认为element ui的图片预览组件不支持点击遮罩层关闭呢?
我看了下框架的更新日志,这个点击mask关闭功能是在 2.15.0版本更新的,也就是说在那之前,是没有这个功能的,所以,这是个历史遗留问题,好在,现在都有解决方式了。
issues地址:https://github.com/ElemeFE/element/pull/20652
self修饰符表示事件必须是绑定的这个元素本身触发的才会触发函数,不会被冒泡啥的触发。
所以,我们如果想开关这个功能,就需要在中间层绑定这个属性,并prop接收一个对应的设置。
具体后面看完整代码
一些其他配置
图片预览组件还有一些其他配置:
- zIndex 层级
- onSwitch 当图片切换时的回调,参数为图片的index下标
- initialIndex 首次显示时显示的图片index下标
- appendToBody 是否允许传入body
差不多就是这些,所以,如果想完美一点的配置,我们就应该在管理中间层都要对这些参数进行绑定,并且在prop中加上接收对应属性的配置
完整版管理中间件
<template>
<transition name="viewer-fade">
<ElImageViewer v-if="show" :z-index="zIndex" :initial-index="imageIndex" :urlList="urlList"
:on-close="closeViewer" :on-switch="switchViewer" :appendToBody="appendToBody"
:maskClosable="maskClosable" />
</transition>
</template>
<script>
import ElImageViewer from "element-ui/packages/image/src/image-viewer";
export default {
props: {
urlList: {
type: Array,
default: () => [],
},
zIndex: {
type: Number,
default: 2000,
},
onSwitch: {
type: Function,
default: () => {},
},
onClose: {
type: Function,
default: () => {},
},
initialIndex: {
type: Number,
default: 0,
},
appendToBody: {
type: Boolean,
default: true,
},
maskClosable: {
type: Boolean,
default: true,
},
},
data() {
return {
show: false,
};
},
methods: {
closeViewer() {
this.show = false;
this.onClose();
},
switchViewer(index) {
this.onSwitch(index);
},
},
components: {
ElImageViewer,
},
};
</script>
构造器调整
preview.js:
import PreviewItem from "./Preview.vue";
const Preview = {};
Preview.install = function (Vue) {
const PreviewConstructor = Vue.extend(PreviewItem);
const instance = new PreviewConstructor();
instance.$mount(document.createElement("div"));
document.body.appendChild(instance.$el);
Vue.prototype.$showImg = function (urlList=[],initialIndex=0,onSwitch=()=>{}) {
//在这里加上一些配置
instance.urlList = urlList;
instance.initialIndex = initialIndex;
instance.onSwitch = onSwitch;
instance.show = true;
}
}
export default Preview;
具体的一些用法,可能会有一些自定义,所以这里是一个示例,没有经过测试,主要是同事写好了,我再去改的话有点吃饱了,就记个笔记在这。
如果你在使用中发现了问题,欢迎留言反馈,我会第一时间处理的,所以留言一定要留真实邮箱地址,才能接收到我的回复邮件。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据