element-plus中图片预览插件源码改动

最近有一个需求,要求对图片进行预览的同时,可以对图片进行放大、缩小、旋转、查看原图(放大或者缩小之后)等功能点,说白了这就是一个图片编辑器。既然提了这个需求,就要想办法去实现它。
先说一下项目背景:pc端项目,项目构成是vue3.2+vite+pinia+element-plus

既然要实现图片预览,就要从ui组件库element-plus入手,看了一下官方文档中的组件image图片这一部分,发现是有图片预览功能的,而且可以放大、缩小、旋转、查看原图等,完美地符合了需求。所以就准备用这个插件来实现了。

虽然image图片组件满足了需求,但是它有个前提条件就是点击图片本身时才会触发预览功能。

代码如下:

<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这个组件,这个组件就是名副其实的预览组件,非常切合开发需求。

<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方法,

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),滚轮监听事件,还有就是键盘监听事件;

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这样一个方法

扫描二维码关注公众号,回复: 17199936 查看本文章
function unregisterEventListener() {
    
    
   scopeEventListener.stop();
}

通过unregisterEventListener就可以接触对wheel、keydown的监听。

但是unregisterEventListener这个事件我们组件外面拿不到,因为这个组件没有暴露这个unregisterEventListener方法。
所以需要修改源码,把这个事件暴露出来;
修改的代码如下:

expose({
    
    
  setActiveItem,
  unregisterEventListener
});

这样在引用el-image-viewer组件的页面中就可以使用unregisterEventListener这个方法,去掉滚轮和键盘的监听事件

1.2 修改源码解决了这个问题之后,又有一个新问题,就是要实现点击图片实现跳转或者下载的效果。(这里为什么说是跳转或者下载呢,因为如果文件是pdf、excel,预览图展示的是一个固定的图,点击就会跳转到对应的文件预览页面,如果是zip等文件,点击固定的图片就会直接下载)。
因为el-image-viewer组件渲染的图片列表,没有点击事件,所以就又要修改源码,增加图片点击事件;
el-image-viewer组件原来是有close、switch事件,增加一个和它们并列的imageClick就可以了,然后把imageClick事件emit出去就可以了。

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
步骤如下

  1. 下载element-plus源码

  2. 在下载的源码项目中的packages中的component中修改对应的组件源码;

  3. 修改完成之后,npm run build进行打包,打包之后的文件是dist;

  4. 拿着dist下面的element-plus文件夹,替换自己项目中node_modules的element-plus(注意,源码项目中打包的dist下面的element-plus可能缺少global.d.ts、package.json、README.md等这几个文件,所以别忘了先拷贝进去(把原来的element-plus中的这几个文件拷贝到现在的element-plus中),然后再进行替换原来的element-plus);

  5. 做完这些之后,启动项目发现没有变化,这个时候我们要清除一下缓存,怎么清除缓存呢?首先把node_modules中的.vite文件删除掉,然后在package.json中的scripts中的dev上加上–force,如下:
    ”scripts": {
    “dev”: “vite --force”,
    “build”: “vite build”,
    “preview”: “vite preview”
    },

  6. 做完这些之后,重新启动项目,发现修改源码的地方已经生效了。

  7. 注意点:现在element-plus的源码项目已经使用pnpm了,所以clone源码后安装依赖的时候,注意一下;

做完这一切就完成了使用element-plus中的el-image-viewer组件实现图片预览的效果(轮播、放大、缩小、旋转、查看原图等)

下面的缩略图轮播我会再写一篇文章,希望本篇文章对大家有所帮助。

猜你喜欢

转载自blog.csdn.net/xiaolinlife/article/details/134435052