• 支持单/多张图翻页轮播
  • 可全屏预览
  • 自定义图片宽高
  • 自定义无图提示语
  • 自带图片加载loading
  • 可将源码根据实际需求改造。

没安装 Element-UI 的可以将图片标签换为原生

效果图

全屏预览
全屏预览

单图,多图翻页轮播

使用

<image-preview :imgList="['url1', 'url2']"></image-preview>

源码

<template>
  <div :class="['image-preview-wrapper', imgList.length ? 'has-border' : '']">
    <!-- 有图 -->
    <div v-if="imgList.length" class="banner-wrapper" :style="{ width: width + 'px', height: height + 'px' }" @click="isFullPreview = true">
      <div class="banner-content" :style="{ left: left + 'px' }">
        <el-image v-for="(item, index) in imgList" :key="index" :src="item" :style="{ width: width + 'px', height: height + 'px' }">
          <div slot="placeholder" class="loading-placeholder">
            正在加载中
          </div>
        </el-image>
      </div>
    </div>
    <!-- 无图 -->
    <div v-else class="no-img-box" :style="{ width: width - 2 + 'px', height: height - 2 + 'px' }">
      <el-image :src="noImgUrl" class="no-img"></el-image>
      <div v-show="noImgTip" class="no-img-tip">{{ noImgTip }}</div>
    </div>
    <!-- 多图切换 -->
    <div v-if="imgList.length > 1" class="banner-btn-box">
      <div :class="['banner-btn', currentIndex === 0 ? 'disabled-btn' : '']" @click="changeImg"><i class="el-icon-arrow-left"></i></div>
      <div :class="['banner-btn', currentIndex === imgList.length - 1 ? 'disabled-btn' : '']" @click="changeImg('next')">
        <i class="el-icon-arrow-right"></i>
      </div>
      <div class="view-img-index-text">{{ currentIndex + 1 }}/{{ imgList.length }}</div>
    </div>
    <!-- 大图全屏预览 -->
    <div v-if="isFullPreview" class="full-preview-wrapper">
      <div class="full-preview-content">
        <div :class="['full-preview-btn', fullPreviewCurrentIndex === 0 ? 'disabled-btn' : '']" @click="changeImg"><i class="el-icon-arrow-left"></i></div>
        <div class="banner-wrapper" :style="{ width: previewImgWidth + 'px', height: previewImgHeight + 'px' }" @click="isFullPreview = true">
          <div class="banner-content" :style="{ left: fullPreviewLeft + 'px' }">
            <el-image v-for="(item, index) in imgList" :key="index" :src="item" :style="{ width: previewImgWidth + 'px', height: previewImgHeight + 'px' }">
              <div slot="placeholder" class="loading-placeholder">
                正在加载中
              </div>
            </el-image>
          </div>
        </div>
        <div :class="['full-preview-btn', fullPreviewCurrentIndex === imgList.length - 1 ? 'disabled-btn' : '']" @click="changeImg('next')">
          <i class="el-icon-arrow-right"></i>
        </div>
      </div>
      <div class="view-img-index-text">{{ fullPreviewCurrentIndex + 1 }}/{{ imgList.length }}</div>
      <div class="full-preview-btn" @click="closeFullPreview">
        <i class="el-icon-close"></i>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';

@Component({ name: 'imagePreview' })
export default class ImagePreview extends Vue {
  @Prop({ type: String, default: '200' }) private width!: string; // 自定义宽
  @Prop({ type: String, default: '200' }) private height!: string; // 自定义高
  @Prop({ type: String, default: '暂无图片' }) private noImgTip!: string; // 无图提示语
  @Prop({ type: Array, default: () => [] }) private imgList!: string[]; // 图片列表

  private noImgUrl: any = require('@/assets/404_images/404_img.png');
  // 轮播图
  private currentIndex = 0;
  private left = 0;
  // 全屏预览相关
  private isFullPreview = false;
  private fullPreviewCurrentIndex = 0;
  private fullPreviewLeft = 0;

  get previewImgWidth() {
    return document.documentElement.clientHeight * 0.66;
  }

  get previewImgHeight() {
    return this.previewImgWidth / (Number(this.width) / Number(this.height));
  }

  changeImg(direction = 'previous') {
    let indexKey = 'currentIndex';
    let leftKey = 'left';
    let imgWidth = Number(this.width);
    const isNext = direction === 'next';
    if (this.isFullPreview) {
      indexKey = 'fullPreviewCurrentIndex';
      leftKey = 'fullPreviewLeft';
      imgWidth = this.previewImgWidth;
    }
    if ((isNext && this[indexKey] !== this.imgList.length - 1) || (!isNext && this[indexKey] !== 0)) {
      isNext ? this[indexKey]++ : this[indexKey]--;
      this[leftKey] = this[indexKey] * imgWidth * -1;
    }
  }

  closeFullPreview() {
    this.isFullPreview = false;
    this.fullPreviewCurrentIndex = 0;
    this.fullPreviewLeft = 0;
  }

  @Watch('imgList', { deep: true, immediate: true })
  watchImgListChange(val: string[]) {
    this.currentIndex = 0;
    this.left = 0;
    this.fullPreviewCurrentIndex = 0;
    this.fullPreviewLeft = 0;
  }
}
</script>

<style lang="scss" scoped>
.image-preview-wrapper {
  position: relative;
  overflow: hidden;
  border: 1px solid transparent;
  cursor: default;
  &.has-border:hover {
    border: 1px solid #00b54b;
    cursor: zoom-in;
  }
  // 轮播
  .banner-wrapper {
    overflow: hidden;
    position: relative;
    .banner-content {
      position: absolute;
      transition: left 0.5s;
      display: flex;
      .loading-placeholder {
        width: 100%;
        height: 100%;
        display: flex;
        background: #ededed;
        justify-content: center;
        align-items: center;
        color: #b3b3b3;
      }
    }
  }
  // 无图
  .no-img-box {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    border: 1px solid #ededed;
    .no-img {
      width: 50%;
    }
    .no-img-tip {
      font-size: 14px;
      color: #b3b3b3;
      padding-top: 12px;
    }
  }
  // 按钮
  .banner-btn-box {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 8px;
    display: flex;
    justify-content: center;
    align-items: center;
    .banner-btn {
      width: 20px;
      height: 20px;
      line-height: 20px;
      font-size: 12px;
      text-align: center;
      background: #000000;
      opacity: 0.3;
      color: #ffffff;
      margin: 0 10px;
      cursor: pointer;
      border-radius: 5px;
      &:hover {
        opacity: 0.5;
      }
    }
    .disabled-btn {
      cursor: not-allowed;
      background: #777777;
      &:hover {
        opacity: 0.5;
      }
    }
    .view-img-index-text {
      position: absolute;
      right: 5px;
      font-size: 12px;
      opacity: 0.5;
    }
  }
  // 全屏预览
  .full-preview-wrapper {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1001;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: default;
    .full-preview-btn {
      width: 48px;
      height: 48px;
      line-height: 48px;
      background: #000000;
      opacity: 0.6;
      color: #ffffff;
      font-size: 20px;
      text-align: center;
      // margin: 0 50px;
      cursor: pointer;
      border-radius: 50%;
      &:hover {
        opacity: 0.4;
      }
      &.disabled-btn {
        cursor: not-allowed;
        background: #777777;
        &:hover {
          opacity: 0.5;
        }
      }
    }
    .full-preview-content {
      width: 90%;
      display: flex;
      align-items: center;
      justify-content: space-around;
      margin-bottom: 10px;
    }
    .view-img-index-text {
      font-size: 16px;
      color: #ffffff;
      text-align: center;
      margin-bottom: 10px;
    }
  }
}
</style>

API

参数说明类型可选值默认值
width自定义宽String‘200’
height自定义高String‘200’
noImgTip无图提示语String‘暂无图片’
imgList图片Url列表Array[]

码字不易,觉得有帮助的小伙伴点个赞支持下~


在这里插入图片描述

扫描上方二维码关注我的订阅号~

Logo

前往低代码交流专区

更多推荐