视频图片类轮播展示放大镜效果【swiper】+【vue-photo-zoom-pro】+【vue-awesome-swiper】+【image-view】

1,261 阅读3分钟

视频图片类轮播展示放大镜效果【swiper】+【vue-photo-zoom-pro】+【vue-awesome-swiper】+【image-viewer】

版本

1.vue:2.6.10

2.swiper:4.0.7

文档

3.vue-awesome-swiper: 3.1.3

安装vue-awesome-swiper需要指定版本,注意在vue3和vue2中使用区别

github地址:vue-awesome-swiper

Examples(vue2)地址:Examples(vue2)

image-20220824155342403.png

4.vue-photo-zoom-pro: 2.5.0

Demo(Examples)地址

类似效果地址

地址

具体踩坑经历

1.查找相关博客,类似的功能实现,相关的插件

1.1 类轮播图

找了一个踩坑的csdn文章(2021.4)建议安装这两个版本的swipervue-awesome-swiper,但是版本有点老旧,写的时候遇到了一些问题。

最开始的时候是想基于轮播图去做,有一些选择,首先是elementUI里面的CarouseliSliderSwiper。最后决定选Swiper,因为Swiper功能属性和事件封装的更全,iSlider看文档感觉适合移动端。

1.2 视频

使用swiper实现视频和图片的混合轮播,参考了本篇文章里面对视频和图片类型判断在切换时控制视频暂停和播放的逻辑。

1.3 放大镜效果

最开始的时候看文章用原生的写法试了一下,放在swiper后判断距离移动遮罩层的时候出现问题(原生例子:京东商品的放大镜效果实现(html+css+js)),加上从个人需求及页面布局上看,不适合这样的放大,然后找了相关插件。vue-product-zoomervue-photo-zoom-pro。决定使用后者。

2.代码实现

2.1 Swiper轮播图

//main.js
import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper'
Vue.use(VueAwesomeSwiper /* { default options with global component } */)
//detailSwiper.vue
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'
import VuePhotoZoomPro from 'vue-photo-zoom-pro'
import 'vue-photo-zoom-pro/dist/style/vue-photo-zoom-pro.css'

使用和引入一些坑,简化的例子

// detailSwiper.vue
<template>
  <div class="recommendPage">
    <swiper :options="swiperOption" ref="mySwiper">
      <swiper-slide>I'm Slide 1</swiper-slide>
      <swiper-slide>I'm Slide 2</swiper-slide>
      <swiper-slide>I'm Slide 3</swiper-slide>
      <div class="swiper-pagination" slot="pagination"></div>
      <div class="swiper-button-prev" slot="button-prev"></div>
      <div class="swiper-button-next" slot="button-next"></div>
    </swiper>
  </div>
</template><script>
// 引入插件
import { swiper, swiperSlide } from "vue-awesome-swiper";
import 'swiper/dist/css/swiper.css'export default {
  components: {
    swiper,
    swiperSlide
  },
  data() {
    return {
      swiperOption: {
        loop: true,
        autoplay: {
          delay: 3000,
          stopOnLastSlide: false,
          disableOnInteraction: false
        },
        // 显示分页
        pagination: {
          el: ".swiper-pagination",
          clickable: true //允许分页点击跳转
        },
        // 设置点击箭头
        navigation: {
          nextEl: ".swiper-button-next",
          prevEl: ".swiper-button-prev"
        }
      }
    };
  },
  computed: {
    swiper() {
      return this.$refs.mySwiper.swiper;
    }
  },
  mounted() {
    // current swiper instance
    // 然后你就可以使用当前上下文内的swiper对象去做你想做的事了
    console.log("this is current swiper instance object", this.swiper);
    // this.swiper.slideTo(3, 1000, false);
  }
};
</script>
<style scoped >
  .recommendPage .swiper-container{
    position: relative;
    width: 100%;
    height: 200px;
    background: pink;
  }
  .recommendPage .swiper-container .swiper-slide{
    width: 100%;
    line-height: 200px;
    background: yellowgreen;
    color: #000;
    font-size: 16px;
    text-align: center;
  }
</style>

成功展示插件之后,调整swiper成自己想要的样式,我这里要用类似example里面的这个,点击右上角可以查看例子代码。

image-20220824163032640.png

<template>
  <div class="thumb-example">
    <!-- swiper1 -->
    <swiper ref="swiperTop" class="swiper gallery-top" :options="swiperOptionTop">
      <swiper-slide class="slide-1"></swiper-slide>
      <swiper-slide class="slide-2"></swiper-slide>
      <swiper-slide class="slide-3"></swiper-slide>
      <swiper-slide class="slide-4"></swiper-slide>
      <swiper-slide class="slide-5"></swiper-slide>
      <div slot="button-next" class="swiper-button-next swiper-button-white"></div>
      <div slot="button-prev" class="swiper-button-prev swiper-button-white"></div>
    </swiper>
    <!-- swiper2 Thumbs -->
    <swiper ref="swiperThumbs" class="swiper gallery-thumbs" :options="swiperOptionThumbs">
      <swiper-slide class="slide-1"></swiper-slide>
      <swiper-slide class="slide-2"></swiper-slide>
      <swiper-slide class="slide-3"></swiper-slide>
      <swiper-slide class="slide-4"></swiper-slide>
      <swiper-slide class="slide-5"></swiper-slide>
    </swiper>
  </div>
</template><script>
// 引入插件
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'export default {
  name: 'SwiperExampleThumbsGallery',
  title: 'Thumbs gallery with Two-way control',
  components: {
    swiper,
    swiperSlide
  },
  data() {
    return {
      swiperOptionTop: {
        loop: true,
        loopedSlides: 5, // looped slides should be the same
        spaceBetween: 10,
        mousewheel: false,
        noSwiping: true,
        navigation: {
          nextEl: '.swiper-button-next',
          prevEl: '.swiper-button-prev'
        }
      },
      swiperOptionThumbs: {
        loop: true,
        loopedSlides: 5, // looped slides should be the same
        spaceBetween: 10,
        centeredSlides: true,
        slidesPerView: 'auto',
        touchRatio: 0.2,
        watchSlidesVisibility: true,
        slideToClickedSlide: true
      },
      // 显示分页
      pagination: {
        el: '.swiper-pagination',
        clickable: true //允许分页点击跳转
      },
      // 设置点击箭头
      navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev'
      }
    }
  },
  computed: {
    swiper() {
      return this.$refs.mySwiper.swiper
    }
  },
  mounted() {
    this.$nextTick(() => {
      const swiperTop = this.$refs.swiperTop.swiper
      const swiperThumbs = this.$refs.swiperThumbs.swiper
      swiperTop.controller.control = swiperThumbs
      swiperThumbs.controller.control = swiperTop
    })
  }
}
</script>
<style lang="scss" scoped>
.thumb-example {
  height: 480px;
  background-color: #fff;
}
.swiper {
  .swiper-slide {
    background-size: cover;
    background-position: center;
    &.slide-1 {
      background: url('./images/6.jpg');
    }
    &.slide-2 {
      background: url('./images/7.png');
    }
    &.slide-3 {
      background: url('./images/8.jpg');
    }
    &.slide-4 {
      background: url('./images/9.jpg');
    }
    &.slide-5 {
      background: url('./images/10.jpg');
    }
  }
​
  &.gallery-top {
    height: 80%;
    width: 100%;
  }
  &.gallery-thumbs {
    height: 20%;
    box-sizing: border-box;
    padding: 10px 0;
  }
  &.gallery-thumbs .swiper-slide {
    width: 15%;
    height: 100%;
    opacity: 0.4;
  }
  &.gallery-thumbs .swiper-slide-active {
    opacity: 1;
  }
}
</style>

循环读取图片

<template>
  <div class="thumb-example">
    <swiper ref="swiperTop" class="swiper gallery-top swiper-no-swiping" :options="swiperOptionTop">
      <swiper-slide class="slide-1"></swiper-slide>
      <swiper-slide v-for="(item, index) in pictureList" :key="index">
        <img class="picture_class" :src="item" />
      </swiper-slide>
      <div slot="button-next" class="swiper-button-next swiper-button-white"></div>
      <div slot="button-prev" class="swiper-button-prev swiper-button-white"></div>
    </swiper>
    <!-- swiper2 Thumbs -->
    <swiper ref="swiperThumbs" class="swiper gallery-thumbs" :options="swiperOptionThumbs">
      <swiper-slide class="slide-1"></swiper-slide>
      <swiper-slide v-for="(item, index) in pictureList" :key="index">
        <img class="thumb_picture" :src="item" />
      </swiper-slide>
    </swiper>
  </div>
</template><script>
// 引入插件
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'export default {
  name: 'SwiperExampleThumbsGallery',
  title: 'Thumbs gallery with Two-way control',
  components: {
    swiper,
    swiperSlide
  },
  data() {
    return {
      pictureList: [
        'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202105%2F09%2F20210509225323_2c0c6.thumb.1000_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1663407474&t=e07d7dc7099dc392fdd51f02cb5c6bc3',
        'https://img2.baidu.com/it/u=1880320954,1568482765&fm=253&fmt=auto&app=138&f=JPEG?w=542&h=500',
        'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg1.doubanio.com%2Fview%2Fgroup_topic%2Fl%2Fpublic%2Fp476331257.jpg&refer=http%3A%2F%2Fimg1.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1663407518&t=9dfb6212cd09c596574d9a0f9e175a9e'
      ],
      swiperOptionTop: {
        loop: true,
        loopedSlides: 5, // looped slides should be the same
        spaceBetween: 10,
        mousewheel: false,
        navigation: {
          nextEl: '.swiper-button-next',
          prevEl: '.swiper-button-prev'
        }
      },
      swiperOptionThumbs: {
        loop: true,
        loopedSlides: 5, // looped slides should be the same
        spaceBetween: 10,
        centeredSlides: true,
        slidesPerView: 'auto',
        touchRatio: 0.2,
        watchSlidesVisibility: true,
        slideToClickedSlide: true
      },
      // 显示分页
      pagination: {
        el: '.swiper-pagination',
        clickable: true //允许分页点击跳转
      },
      // 设置点击箭头
      navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev'
      }
    }
  },
  computed: {
    swiper() {
      return this.$refs.swiperTop.swiper
    }
  },
​
  mounted() {
    this.$nextTick(() => {
      const swiperTop = this.$refs.swiperTop.swiper
      const swiperThumbs = this.$refs.swiperThumbs.swiper
      swiperTop.controller.control = swiperThumbs
      swiperThumbs.controller.control = swiperTop
    })
  }
}
</script>
<style lang="scss" scoped>.thumb-example {
  height: 480px;
  background-color: #fff;
}
.swiper {
  .swiper-slide {
    background-size: cover;
    background-position: center;
    &.slide-1 {
      background: url('./images/1.gif');
    }
    &.slide-2 {
      background: url('./images/7.png');
    }
  }
​
  &.gallery-top {
    height: 80%;
    width: 100%;
  }
  .picture_class {
    width: 886px;
    height: 384px;
  }
  &.gallery-thumbs {
    height: 20%;
    box-sizing: border-box;
    padding: 10px 0;
  }
  &.gallery-thumbs .swiper-slide {
    width: 15%;
    height: 100%;
    opacity: 0.4;
    .thumb_picture {
      width: 100%;
      height: 100%;
    }
  }
  &.gallery-thumbs .swiper-slide-active {
    opacity: 1;
  }
}
</style>

2.2 加上video视频,detailList从父组件传进来

<template>
  <div class="thumb-example">
    <!-- swiper1 -->
    <!--    swiper-no-swiping禁止鼠标滑动-->
    <swiper ref="swiperTop" class="swiper gallery-top " :options="swiperOptionTop">
      <swiper-slide >
        <video
          class="picture_class"
          preload="auto"
          crossorigin="anonymous"
          autoplay=""
          src="https://a.sinaimg.cn/mintra/pic/2112130543/weibo_login.mp4"
        ></video>
      </swiper-slide>
      <swiper-slide v-for="(item, index) in detailList" :key="index">
        <img :src="item" />
      </swiper-slide>
      <div slot="button-next" class="swiper-button-next swiper-button-white"></div>
      <div slot="button-prev" class="swiper-button-prev swiper-button-white"></div>
    </swiper>
    <!-- swiper2 Thumbs -->
    <swiper ref="swiperThumbs" class="swiper gallery-thumbs" :options="swiperOptionThumbs">
      <swiper-slide >
        <video
          class="picture_class"
          preload="auto"
          crossorigin="anonymous"
          autoplay=""
          src="https://a.sinaimg.cn/mintra/pic/2112130543/weibo_login.mp4"
        ></video>
      </swiper-slide>
      <swiper-slide v-for="(item, index) in detailList" :key="index">
        <img class="thumb_picture" :src="item" />
      </swiper-slide>
    </swiper>
  </div>
</template><script>
// 引入插件
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'
import { Component, Vue, Emit, Prop, Watch } from 'vue-property-decorator'export default {
  name: 'SwiperExampleThumbsGallery',
  title: 'Thumbs gallery with Two-way control',
  components: {
    swiper,
    swiperSlide
  },
  // eslint-disable-next-line vue/require-default-prop,vue/require-prop-types
  props: { detailList: {} },
​
  data() {
    return {
      swiperOptionTop: {
        loop: true,
        loopedSlides: 8, // looped slides should be the same
        spaceBetween: 10,
        mousewheel: false,
        touchRatio: 0,
        navigation: {
          nextEl: '.swiper-button-next',
          prevEl: '.swiper-button-prev'
        }
      },
      swiperOptionThumbs: {
        loop: true,
        loopedSlides: 8, // looped slides should be the same
        spaceBetween: 10,
        centeredSlides: true,
        slidesPerView: 'auto',
        touchRatio: 0,
        slideToClickedSlide: true
      },
      // 显示分页
      pagination: {
        el: '.swiper-pagination',
        clickable: true //允许分页点击跳转
      },
      // 设置点击箭头
      navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev'
      }
    }
  },
  computed: {
    swiper() {
      return this.$refs.mySwiper.swiper
    }
  },
  mounted() {
    console.log(this.detailList)
    console.log('this.detailList')
    this.$nextTick(() => {
      const swiperTop = this.$refs.swiperTop.swiper
      const swiperThumbs = this.$refs.swiperThumbs.swiper
      swiperTop.controller.control = swiperThumbs
      swiperThumbs.controller.control = swiperTop
    })
  }
}
</script>
<style lang="scss" scoped>
.swiper-slide {
  text-align: center;
  background: #444 !important;
​
  img {
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 100%;
    transform: translate(-50%, -50%);
    position: absolute;
    left: 50%;
    top: 50%;
  }
}
.thumb-example {
  height: 480px;
  background-color: #fff;
}
.swiper {
  .swiper-slide {
    background-size: cover;
    background-position: center;
    &.slide-1 {
      background: url('./images/1.gif');
    }
    &.slide-2 {
      background: url('./images/7.png');
    }
  }
​
  &.gallery-top {
    height: 80%;
    width: 100%;
  }
  .picture_class {
    width: 886px;
    height: 384px;
  }
  &.gallery-thumbs {
    height: 20%;
    box-sizing: border-box;
    padding: 10px 0;
  }
  &.gallery-thumbs .swiper-slide {
    width: 12%;
    height: 100%;
    opacity: 0.4;
    .thumb_picture {
      width: 100%;
      height: 100%;
    }
  }
  &.gallery-thumbs .swiper-slide-active {
    opacity: 1;
  }
}
</style>

2.3 放大镜效果

然后就是加放大镜效果:代码参考,京东商品的放大镜效果实现

然后发现就是获取不到鼠标状态,怀疑是swiper影响的,然后就直接在box上加了@mouseover@mouseout

// 修改前
  box.addEventListener('mouseover', function() {
    mask.style.display = 'block'
    console.log('222')
    big.style.display = 'block'
  })
​
  box.addEventListener('mouseout', function() {
    mask.style.display = 'none'
    big.style.display = 'none'
  })
// 修改后
<div class="box" @mouseover="overHandler" @mouseout="outHandler()"></div>

还有一个问题通过.style.display是修改不了display,就很神奇,打印.style里面的display是更改了,但是打印.style.display就是没变的。然后没办法只能动态绑定style去修改

      let mask = document.querySelector('.mask')
      let big = document.querySelector('.big')
// 修改前
      mask.style.display = 'block'
      big.style.display = 'block'
// 修改后,在mask的div上动态绑定style
      :style="maskStyle"
      this.maskStyle = 'display:block'

再往后就是发现这样动态绑定style实在是太复杂麻烦,而且要很多个地方去修改style,所以在这就直接使用了 vue-photo-zoom-pro

ps:中间还尝试不用swiper直接用vue-product-zoomer,不要尝试,会变得不幸。

把swiper1的img替换成vue-photo-zoom-pro

<vue-photo-zoom-pro class="zoom_picture" :url="item" :high-url="item"></vue-photo-zoom-pro>

2.4 修改相关的css样式

让图片和video正常居中,控制了图片的height:100%,video的width:100%

//最后的style
<style lang="scss" scoped>
.zoom_picture {
  width: auto !important;
  height: 100% !important;
  img {
    width: auto !important;
    height: 100% !important;
  }
}
.swiper-slide {
  text-align: center;
  background: #f7f8fa !important;
}
.thumb-example {
  /*height: 500px;最外层*/
  background-color: #fff;
}
.swiper {
  height: auto;
  &.gallery-top {
    /*height: 85%;原demo例子中的样式*/
    width: 100%;
  }
  .video_class {
    width: 100%;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }
  &.gallery-thumbs {
    height: 20%;
    box-sizing: border-box;
    padding: 10px 0;
  }
  &.gallery-thumbs .swiper-slide {
    width: 13%;
    height: 100%;
    /*opacity: 0.4;*/
    .thumb_picture {
      width: 100%;
      height: 100%;
    }
  }
  &.gallery-thumbs .swiper-slide-active {
    opacity: 1;
  }
  /*以上为轮播图css样式*/
}
</style>

2.5 调整video的暂停和播放,通过第二个swiperThumbs控制第一个swiperTop的切换

在第一个swiper上添加事件函数。在当前Slide切换到另一个Slide时执行(activeIndex发生改变),一般是在点击控制组件、释放滑动的时间点。可选Swiper实例作为参数。

参考:使用swiper实现视频和图片混合轮播

    @slideChange="onSlideChange"
    //这里video需要添加controls属性,显示进度条及其他
    onSlideChange(e) {
      this.swiper = this.$refs.swiperTop.swiper
      if (this.detailList[this.swiper.activeIndex].fileType === 'VIDEO') {
        // 如果当前页是视频,停止轮播图自动切换,如果视频是暂停状态,开始播放
        // this.swiper.autoplay.stop()
        let videoele = this.$refs['video' + this.swiper.activeIndex][0]
        if (videoele.paused) {
          console.log('playstart')
          videoele.play()
        }
      } else {
        // 当前页是普通图片,开启视频自动轮播,将其他页面(主要是上一个可能为视频播放的页面)正在播放的视频暂停
        // this.swiper.autoplay.start()
        this.detailList.forEach((item, index) => {
          if (item.fileType === 'VIDEO') {
            let videoele = this.$refs['video' + Number(index)][0]
            console.log('pauseend')
            videoele.pause()
          }
        })
      }
    },

这里的this.swiper.autoplay.start()是控制轮播的,个人需求是不需要的所以去掉了autoplay,如果需要自动轮播可以添加autoplay属性然后控制。

swiperThumbs中slide里面的img上添加了click事件去控制在swiperTop中显示图片,slideTo的第三个参数是回调函数transitionEnd

    clickThumbs(index) {
      const swiperTop = this.$refs.swiperTop.swiper
      const swiperThumbs = this.$refs.swiperThumbs.swiper
      swiperTop.slideTo(index, 1000, true)
      swiperThumbs.slideTo(index, 1000, true)
      swiperThumbs.$wrapperEl.transitionEnd(() => {
        swiperTop.realIndex = index
        swiperThumbs.slides[index].style.opacity = 1
      })
    },

这里如果添加swiperThumbs.controller.control = swiperTop的话会使点击跳转出现问题,我这里是点击第六个图片detailList里面的第6个,会出现直接跳转最后一个图片,缓慢拉或者打印输出可以看出是先跳转了最后的图然后再跳转第六个再跳转最后一个,最后通过调试和代码觉得是因为取消了centeredSlides之后所有active的图会居左显示,但是后面的图片因为无法滑动到最左出现问题,这个也是后面opacity高亮显示出现问题的原因。所以取消了这个控制。(也有可能是其他原因(;´д`)ゞ

  mounted() {
    console.log(this.detailList)
    console.log('this.detailList')
    this.$nextTick(() => {
      const swiperTop = this.$refs.swiperTop.swiper
      const swiperThumbs = this.$refs.swiperThumbs.swiper
      swiperTop.controller.control = swiperThumbs
      //swiperThumbs.controller.control = swiperTop
    })
  }

2.6 点击放大(类似预览的效果

elementUI当中img里通过previewSrcList控制大图预览的效果

image-20220825165121034.png

直接将element里的引入会出现问题,弹出层会跳到第一个swiper-slide里面,而不是整个页面body

然后尝试将饿了么的image放到这个detailSwiper组件的外面,在父组件里添加之后,将url和type从子组件传给父组件

这样尝试之后发现问题,会在swiper的下面弹出一个图片,点击这个图片之后才会弹出预览框。然后看element的image代码,里面是有两个image,提供的功能里的第一层img我这里是不需要的,只需要里面的image-viewer组件,就是这个弹出预览层。但是这个组件没有暴露出来,所以直接移植了一下(所有相关的都要,封装了一个image-viewer组件

image-20220825165410308.png

            <detail-swiper
              :detail-list="pictureList"
              @imageclick="imageclick"
              @prevOverflow="getPrevOverflow"
            ></detail-swiper>
            <image-viewer
              v-if="showViewer"
              :z-index="zIndex"
              :initial-index="imageIndex"
              :on-close="closeViewer"
              :url-list="previewSrcList"
            ></image-viewer>
<template>
  <div class="thumb-example ">
    <!--    swiper-no-swiping禁止鼠标滑动-->
    <swiper
      ref="swiperTop"
      class="swiper gallery-top  "
      :options="swiperOptionTop"
      @slideChange="onSlideChange"
      @click="clickImage"
    >
      <swiper-slide v-for="(item, index) in detailList" :key="index" style="  width: 886px;height: 498px;">
        <video
          v-if="item.fileType === 'VIDEO'"
          :ref="'video' + item.seq"
          class="video_class"
          style="object-fit: fill"
          :src="item.fileUrl"
          controls
        ></video>
        <vue-photo-zoom-pro
          v-if="item.fileType === 'IMG'"
          class="zoom_picture"
          :data-id="item.fileUrl"
          :url="item.fileUrl"
          :high-url="item.fileUrl"
          @click="clickImage(item.fileUrl, item.fileType)"
        ></vue-photo-zoom-pro>
      </swiper-slide>
    </swiper>
    <!-- swiper2 Thumbs -->
    <swiper ref="swiperThumbs" class="swiper gallery-thumbs" :options="swiperOptionThumbs">
      <swiper-slide v-for="(item, index) in detailList" :key="index" style="height: 98px;">
        <img class="thumb_picture" :src="item.fileUrl" @click="clickThumbs(index)" />
      </swiper-slide>
    </swiper>
  </div>
</template><script>
// 引入插件
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'
import VuePhotoZoomPro from 'vue-photo-zoom-pro'
import 'vue-photo-zoom-pro/dist/style/vue-photo-zoom-pro.css'
import { Component, Vue, Emit, Prop, Watch } from 'vue-property-decorator'
export default {
  name: 'SwiperExampleThumbsGallery',
  title: 'Thumbs gallery with Two-way control',
  components: {
    swiper,
    swiperSlide,
    VuePhotoZoomPro
  },
  // eslint-disable-next-line vue/require-default-prop,vue/require-prop-types
  props: { detailList: {} },
​
  data() {
    return {
      curindex: 0,
      carouselList: [],
      swiperOptionTop: {
        loop: false,
        lazy: true,
        spaceBetween: 10,
        mousewheel: false,
        touchRatio: 0.2
      },
      swiperOptionThumbs: {
        loop: false,
        lazy: true,
        spaceBetween: 10,
        slidesPerView: 'auto',
        centeredSlides: false,
        slideToClickedSlide: true,
        touchRatio: 0.4
      },
      // 显示分页
      pagination: {
        el: '.swiper-pagination',
        clickable: true //允许分页点击跳转
      }
      // 设置点击箭头
      // navigation: {
      //   nextEl: '.swiper-button-next',
      //   prevEl: '.swiper-button-prev'
      // }
    }
  },
  computed: {
    swiper: {
      get() {
        return this.$refs.swiperTop.swiper
      },
      set(val) {}
    }
  },
  mounted() {
    //页面预加载
    setTimeout(() => {
      this.init()
    }, 5000)
    console.log(this.detailList)
    console.log('this.detailList')
    this.$nextTick(() => {
      setTimeout(() => {
        const swiperTop = this.$refs.swiperTop.swiper
        const swiperThumbs = this.$refs.swiperThumbs.swiper
        swiperTop.controller.control = swiperThumbs
      }, 3000)
    })
  },
  methods: {
    init() {},
    onSlideChange(e) {
      this.swiper = this.$refs.swiperTop.swiper
      // 由于轮播图前后无缝隙滚动的实现机制,此处索引会出现大于实际素材长度的个数,第0张实际也是最后一张,最后一张后面的一张实际是第一张
      if (this.detailList[this.swiper.activeIndex].fileType === 'VIDEO') {
        // 如果当前页是视频,停止轮播图自动切换,如果视频是暂停状态,开始播放
        let videoele = this.$refs['video' + this.swiper.activeIndex][0]
        if (videoele.paused) {
          console.log('playstart')
          videoele.play()
        }
      } else {
        // 当前页是普通图片,开启视频自动轮播,将其他页面(主要是上一个可能为视频播放的页面)正在播放的视频暂停
        this.detailList.forEach((item, index) => {
          if (item.fileType === 'VIDEO') {
            let videoele = this.$refs['video' + Number(index)][0]
            console.log('pauseend')
            videoele.pause()
          }
        })
      }
    },
    clickThumbs(index) {
      const swiperTop = this.$refs.swiperTop.swiper
      const swiperThumbs = this.$refs.swiperThumbs.swiper
      swiperTop.slideTo(index, 1000, false)
      swiperThumbs.slideTo(index, 1000, true)
      swiperThumbs.$wrapperEl.transitionEnd(() => {
        swiperTop.realIndex = index
      })
    },
    clickImage() {
      let url = ''
      let type = ''
      let prevOverflow = ''
      type = this.detailList[this.swiper.activeIndex].fileType
      url = this.detailList[this.swiper.activeIndex].fileUrl
      prevOverflow = document.body.style.overflow
      this.$emit('imageclick', url, type)
      this.$emit('prevOverflow', prevOverflow)
    }
  }
}
</script>

最后效果

1.鼠标左右滑动上方图片,下方会随着滑动(居左,图片数量不够显示时不动)

2.点击下方图片上方大图会滑动跳转

3.可以进行多个视频展示,切换到图片会暂停视频播放

4.图片有滑动放大镜,点击上方图后弹出大图预览框(可左右切换所有图片)

image-20220825165617784.png

image-20220825165809922.png

image-20220825170152288.png

其他

如果只用上下swiper滑动控制可以直接使用swiper里面的Controller相关双向控制

image-20220825170841255.png

只是下方固定点击控制的话直接使用slideTo

image-20220825170926760.png

想想好像如果不需要上方swiperTop去控制下方的swiperThumbs的话,上方可以不用swiper去实现,直接放图片然后切换下方swiper时去控制替换上方就行了,也没有什么双向影响时会出现的问题了。类似最开始给的爱采购的效果地址,上方不需要控制下方。

如果控制高亮显示在上述基础之前的话需要类似获取节点循环取消高亮,再赋值active的亮度

D4E55B331CA2B49B72543DCA6818E5C3-image.jpeg

总结

很多地方还可以简化+优化,而且有些地方遇到问题直接跨过了,没有更仔细的去解决而是曲线救国了,感觉还可以看看源码多研究研究。

\