最近有一个需求,要求对图片进行预览的同时,可以对图片进行放大、缩小、旋转、查看原图(放大或者缩小之后)等功能点,说白了这就是一个图片编辑器。既然提了这个需求,就要想办法去实现它。
先说一下项目背景:pc端项目,项目构成是vue3.2+vite+pinia+element-plus等
既然要实现图片预览,就要从ui组件库element-plus入手,看了一下官方文档中的组件image图片 这一部分,发现是有图片预览 功能的,而且可以放大、缩小、旋转、查看原图等,完美地符合了需求。所以就准备用这个插件来实现了。
虽然image图片组件满足了需求,但是它有个前提条件就是点击图片本身时才会触发预览功能。
代码如下:
javascript
<template>
<div class="demo-image__preview">
<el-image
style="width: 100px; height: 100px"
:src="url"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
:preview-src-list="srcList"
:initial-index="4"
fit="cover"
/>
</div>
</template>
<script lang="ts" setup>
const url =
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg'
const srcList = [
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg',
]
</script>
<style scoped>
.demo-image__error .image-slot {
font-size: 30px;
}
.demo-image__error .image-slot .el-icon {
font-size: 30px;
}
.demo-image__error .el-image {
width: 100%;
height: 200px;
}
</style>
其中的preview-src-list是开启预览功能的,还有一些配套的参数设置:zoom-rate等,
但是我们项目的需求是点击按钮进入一个预览页面,直接展示预览图片,并且左边有文件名列表,点击列表中的文件名,展示这个文件名下上传的所有图片,然后进行轮播预览,同时在大图预览下面有对应的缩略图轮播,大图预览轮播和缩略图轮播形成联动效果,大图轮播,下面缩略图也跟着轮播。缩略图轮播也是一样的。
所以需要根据需求点来进行对应的改造 ,
1、首先要实现点击按钮来进行图片预览的效果(利用element-plus中的el-image-viewer 这个组件);
2、其次实现缩略图的轮播展示(使用swiper插件 实现);
3、实现图片预览轮播和缩略图轮播的联动效果;
我们一步一步来实现:
1.1 、因为el-image 这个组件只能在点击图片的时候才能展示图片轮播,所以只能改其他的方案,经过探索之后,发现了el-image-viewer这个组件,这个组件就是名副其实的预览组件,非常切合开发需求。
javascript
<el-image-viewer
ref="allImageViewerList"
v-if="isShow"
:url-list="imageList"
@switch="($event) => onSwitch($event, 'imageView')"
:hideOnClickModal="true"
@imageClick="imageClick"
/>
直接传入图片列表url-list ,然后展示对应的轮播预览图效果,这样就实现了点击按钮展示预览轮播图的效果了。
虽然效果展示出来了,但是有些方面还是不能满足我们的需求,比如,预览图在一个固定的区域内,但是因为有鼠标滚轮事件,可以滚动的时候实现预览图的放大缩小,所以导致在左边的列表区域上下滚动的时候,预览图也跟着放大和缩小。所以要解决的问题,就是禁止掉滚轮事件。怎么禁止呢,api文档中没有提到。需要翻看源码。
我先看了一下node_modules中安装的element-plus中的源码,
发现在onMounted中触发了registerEventListener方法,
javascript
vue.onMounted(() => {
var _a, _b;
registerEventListener();
(_b = (_a = wrapper.value) == null ? void 0 : _a.focus) == null ? void 0 : _b.call(_a);
});
registerEventListener方法代码如下,在registerEventListener中发现了core.useEventListener(document, "wheel", mousewheelHandler),滚轮监听事件,还有就是键盘监听事件;
javascript
const scopeEventListener = vue.effectScope();
function registerEventListener() {
const keydownHandler = lodashUnified.throttle((e) => {
switch (e.code) {
case aria.EVENT_CODE.esc:
props.closeOnPressEscape && hide();
break;
case aria.EVENT_CODE.space:
toggleMode();
break;
case aria.EVENT_CODE.left:
prev();
break;
case aria.EVENT_CODE.up:
handleActions("zoomIn");
break;
case aria.EVENT_CODE.right:
next();
break;
case aria.EVENT_CODE.down:
handleActions("zoomOut");
break;
}
});
const mousewheelHandler = lodashUnified.throttle((e) => {
const delta = e.deltaY || e.deltaX;
handleActions(delta < 0 ? "zoomIn" : "zoomOut", {
zoomRate: props.zoomRate,
enableTransition: false
});
});
scopeEventListener.run(() => {
core.useEventListener(document, "keydown", keydownHandler);
core.useEventListener(document, "wheel", mousewheelHandler);
});
}
怎么取消这些监听事件呢?(可以先查一下effectScope的用法)
发现了unregisterEventListener这样一个方法
javascript
function unregisterEventListener() {
scopeEventListener.stop();
}
通过unregisterEventListener就可以接触对wheel、keydown的监听。
但是unregisterEventListener这个事件我们组件外面拿不到,因为这个组件没有暴露这个unregisterEventListener方法。
所以需要修改源码,把这个事件暴露出来;
修改的代码如下:
javascript
expose({
setActiveItem,
unregisterEventListener
});
这样在引用el-image-viewer组件的页面中就可以使用unregisterEventListener这个方法,去掉滚轮和键盘的监听事件。
1.2 修改源码解决了这个问题之后,又有一个新问题 ,就是要实现点击图片实现跳转或者下载的效果。(这里为什么说是跳转或者下载呢,因为如果文件是pdf、excel,预览图展示的是一个固定的图,点击就会跳转到对应的文件预览页面,如果是zip等文件,点击固定的图片就会直接下载)。
因为el-image-viewer组件渲染的图片列表,没有点击事件,所以就又要修改源码,增加图片点击事件;
el-image-viewer组件原来是有close、switch事件,增加一个和它们并列的imageClick就可以了,然后把imageClick事件emit出去就可以了。
javascript
function imgClick(url) {
if (["jpg", "jpeg", "png", "gif", "bmp", "svg", "tif", "pcx", "tga", "exif", "fpx", "psd", "cdr", "pcd", "dxf", "ufo", "eps", "ai", "raw", "wmv", "webp", "avif", "apng", "heic", "heif"].includes(url == null ? void 0 : url.fileName.substring((url == null ? void 0 : url.fileName.lastIndexOf(".")) + 1).toLowerCase())) {
return;
}
emit("imageClick", url);
}
在源码中加入这个参数,首先数据类型为图片格式的,点击没有反应,非图片格式的才有返回值,返回之后会进行下载或者预览(引用el-image-viewer组件的页面中做的处理)。
1.3
好了,改完源码,满足需求了。我说说改源码的流程以及源码打包后怎么放入项目中。
我用的element-plus版本是目前最新的,2.4.1
步骤如下:
-
下载element-plus源码
-
在下载的源码项目中的packages中的component中修改对应的组件源码;
-
修改完成之后,npm run build进行打包,打包之后的文件是dist;
-
拿着dist下面的element-plus文件夹,替换自己项目中node_modules的element-plus(注意,源码项目中打包的dist下面的element-plus可能缺少global.d.ts、package.json、README.md等这几个文件,所以别忘了先拷贝进去(把原来的element-plus中的这几个文件拷贝到现在的element-plus中),然后再进行替换原来的element-plus);
-
做完这些之后,启动项目发现没有变化,这个时候我们要清除一下缓存,怎么清除缓存呢?首先把node_modules中的.vite文件删除掉,然后在package.json中的scripts中的dev上加上--force,如下:
"scripts": {
"dev": "vite --force",
"build": "vite build",
"preview": "vite preview"
},
-
做完这些之后,重新启动项目,发现修改源码的地方已经生效了。
-
注意点:现在element-plus的源码项目已经使用pnpm了,所以clone源码后安装依赖的时候,注意一下;
做完这一切就完成了使用element-plus中的el-image-viewer组件实现图片预览的效果(轮播、放大、缩小、旋转、查看原图等)
下面的缩略图轮播我会再写一篇文章,希望本篇文章对大家有所帮助。