Vue 单/多图片(可全屏预览)翻页轮播组件

  • 支持单/多张图翻页轮播
  • 可全屏预览
  • 自定义图片宽高
  • 自定义无图提示语
  • 自带图片加载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[]

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


在这里插入图片描述

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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
引用\[1\]中提到了一个场景,即在一个项目中需要实现一个图片组件,支持放大、切换音视频和文件、缩放、旋转、移动等功能。该组件已经封装好,注释详细,可以直接拿来使用。引用\[2\]中提到了一个适用于Vue 3.0的视频放插件vue3-video-play,该插件的UI和功能都很好,可以作为实现视频放功能的参考。引用\[3\]中提到了定义一个transform样式对象,包含缩放、旋转、移动等属性,可以在computed计算属性中返回一个由该对象组成的CSS样式对象,然后在模板中将该样式对象绑定到图片上,当触发特效相关的事件时,改变对象中的某个值,就会重新计算computed属性,从而实时更新图片的样式。 综合以上引用内容,可以根据项目需求使用已封装好的图片组件,并结合vue3-video-play插件实现视频放功能。同时,可以定义一个transform样式对象,通过computed属性实时更新图片的样式,以实现缩放、旋转、移动等效果。 #### 引用[.reference_title] - *1* *2* *3* [Vue3.0实现图片组件(媒体查看器)](https://blog.csdn.net/dabaooooq/article/details/128841487)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张兴华(MarsXH.Chang)

喜欢的可以请作者喝杯咖啡~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值