vue3 开发一个图片预览插件
vue3实现图片预览插件开发
·
vue3 的插件开发和vue2思路类似但是写法却迥异。主要变化在vue3没有了extend构造器。
这次我们通过一个图片预览插件,贯通整个vue3插件开发的过程。
1 在src下新建lplugins文件夹,在此文件夹下新建两个文件prevImg.vue和index.js
<template>
<div class="viewer" v-if="isShow">
<div
class="mask"
@click="close"
>
</div>
<div class="actions">
<i
class="el-icon-zoom-in fs24"
@click="operate('zoomIn')"
>-</i>
<i @click="operate('zoomOut')" class="fs24">+</i>
<i class="el-icon-c-scale-to-original fs14"
@click="toggleMode">[]</i>
<i class="el-icon-refresh-left fs24"
@click="operate('rotateDY')"
>☽</i>
<i
class="el-icon-refresh-right fs14"
@click="operate('rotateY')"
></i>
<i
class="el-icon-circle-close fs14"
@click="operate('close')"
></i>
</div>
<div class="img-wrap">
<img
ref="img"
:style="imgStyle"
class="viewer__img"
:src="imgUrl"
/>
</div>
</div>
</template>
<script setup>
import { computed, ref ,nextTick, getCurrentInstance, watch, reactive, onMounted,toRefs} from 'vue'
const Mode = {
CONTAIN: {
name: 'contain',
icon: 'el-icon-full-screen'
},
ORIGINAL: {
name: 'original',
icon: 'el-icon-c-scale-to-original'
}
};
//state
const loading=ref(false)
const isShow=ref(false)
const transform=reactive({
scale: 1,
deg: 0,
offsetX: 0,
offsetY: 0,
enableTransition: false
})
const mode=ref(Mode.CONTAIN)
const instance=getCurrentInstance()
const imgUrl=ref("")
const emits=defineEmits(["close","open"])
const imgStyle=computed(()=> {
const { scale, deg, offsetX, offsetY, enableTransition } = transform;
const style = {
transform: `scale(${scale}) rotate(${deg}deg)`,
transition: enableTransition ? 'transform .3s' : '',
'margin-left': `${offsetX}px`,
'margin-top': `${offsetY}px`,
};
if (mode.value === Mode.CONTAIN) {
style.maxWidth = style.maxHeight = '100%';
}
return style;
})
const handleImgLoad=(e)=>{
loading.value = false;
}
const handleImgError=(e)=>{
loading.value = false;
console.log("e.target",e.target)
e.target.alt = '加载失败';
}
const open=(data)=>{
console.log("组件内---data",data)
debugger
isShow.value=true;
imgUrl.value=data.url
}
const close=()=>{
console.log("close")
isShow.value=false
emits("close")
}
const operate=(action,options={})=>{
if (loading.value) return;
const { zoomRate, rotateDeg, enableTransition } = {
zoomRate: 0.2,
rotateDeg: 90,
enableTransition: true,
...options
};
transform.enableTransition=true;
switch (action) {
case 'zoomOut':
if (transform.scale > 0.2) {
transform.scale = parseFloat((transform.scale - zoomRate).toFixed(3));
}
break;
case 'zoomIn':
transform.scale = parseFloat((transform.scale + zoomRate).toFixed(3));
break;
case 'rotateY':
transform.deg += rotateDeg;
break;
case 'rotateDY':
transform.deg -= rotateDeg;
break;
case "close":
$emit("close");
break;
}
transform.enableTransition = enableTransition;
}
defineExpose({open,close,operate})
</script>
<style scoped>
.mask {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0.5;
background: #000;
}
.viewer {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.img-wrap {
width: 100%;
height: 100%;
/* position:absolute;
top:0;
left:0;
z-index: 100; */
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.loading {
color: #fff;
}
.actions {
position: fixed;
top: 16px;
right: 30px;
width: 220px;
z-index: 9999;
color: #fff;
display: flex;
justify-content: flex-end;
}
.actions i {
display: inline-block;
margin-left: 10px;
}
.viewer__img{
max-height: 100%;
}
.fs24{
font-size: 24px;
}
</style>
在这个文件中我们定义了几个比较重要的函数
open、close、operate分别用于打开图层。关闭图层和对图层进行放大。缩小。旋转等操作。这些函数我们通过defineExpose()对外暴露出去。这样在父组件我们就可以直接调用这些方法。达到穿透的效果。
2 接下里是lib下的index.js文件,它是整个插件的核心
import { createVNode, render, } from 'vue';
import preview from './preview.vue'
export default {
install(app,options) {
console.log("options;",options={})
//createVNode vue提供的底层方法 可以给我们组件创建一个虚拟DOM 也就是Vnode
const vnode = createVNode(preview)
//render 把我们的Vnode 生成真实DOM 并且挂载到指定节点
render(vnode, document.body)
// Vue 提供的全局配置 可以自定义
app.config.globalProperties.$preview = {
open: (opts) => vnode.component?.exposed?.open(opts),
close: () => vnode.component?.exposed?.close()
}
app.component("preview", preview)
}
}
注意这里,由于vue3中没有了原型的概念。vue3给了我们一个新的api, app.config.globalProperties,我们把方法或者属性挂载到globalProperties上,就相当于在vue2中我们挂载到了vue的原型对象上。而open方法里我们传入的参数就是我们在点击预览时要传递的参数。我们这个例子把图片的url作为核心参数传递过去
3 看看在页面中如何调用
<script setup>
import {getCurrentInstance, ref} from "vue"
import Test from './components/test.vue'
const {proxy}=getCurrentInstance()
const imgSrc=ref("https://baj-dabanjiz-conf.oss-cn-hangzhou.aliyuncs.com/intelligent-design/image/20210730/middle/9bbeb6570f7b416b1bcbcc59a1b38635.jpg") //横图
const testPlugin=()=>{
console.log("proxy",proxy)
let obj={url:imgSrc.value}
proxy.$preview.open(obj)
}
</script>
<template>
<el-button @click="testPlugin"> 测试插件</el-button>
</template>
<style scoped>
</style>
4 这里要注意的就是我们在页面中如何获取到vue的实例。当我们点击按钮的时候,
const testPlugin=()=>{
console.log("proxy",proxy)
let obj={url:imgSrc.value}
proxy.$preview.open(obj)
}
实际上这里我们依然可以通过这样的写法来获取我们 的open方法.如下
const instance=getCurrentInstance()
const imgSrc=ref("https://baj-dabanjiz-conf.oss-cn-hangzhou.aliyuncs.com/intelligent-design/image/20210730/middle/9bbeb6570f7b416b1bcbcc59a1b38635.jpg") //横图
const testPlugin=()=>{
console.log("proxy",proxy)
let obj={url:imgSrc.value}
instance.config.globalProperties.$preview.open(obj)
}
但是每次这样会又长又臭。还好vue3在实例里有个prxoy对象。它就相当于当前组件实例的副本。在它上面我们就能获取到我们注册的$preview.
5 main.js注册插件
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
// import Loading from "./libs/testLoading/loading"
import preview from "./libs/preview/preview"
createApp(App).use(ElementPlus).use(preview).mount('#app')
到这里我们就基本上实现了图片预览的基本操作
更多推荐
已为社区贡献14条内容
所有评论(0)