<template> 
 | 
  <view class="tn-swiper__wrap-class tn-swiper__wrap" :style="{borderRadius: `${radius}rpx`}"> 
 | 
    <!-- 轮播图 --> 
 | 
    <swiper 
 | 
      class="tn-swiper" 
 | 
      :class="[backgroundColorClass]" 
 | 
      :style="[swiperStyle]" 
 | 
      :current="current" 
 | 
      :interval="interval" 
 | 
      :circular="circular" 
 | 
      :autoplay="autoplay" 
 | 
      :duration="duration" 
 | 
      :previous-margin="effect3d ? effect3dPreviousSpacing + 'rpx' : '0'" 
 | 
      :next-margin="effect3d ? effect3dPreviousSpacing + 'rpx' : '0'" 
 | 
      @change="change" 
 | 
    > 
 | 
      <swiper-item 
 | 
        v-for="(item, index) in list" 
 | 
        :key="index" 
 | 
        class="tn-swiper__item" 
 | 
      > 
 | 
        <view 
 | 
          class="tn-swiper__item__image__wrap" 
 | 
          :class="[swiperIndex !== index ? 'tn-swiper__item__image--scale' : '']" 
 | 
          :style="{ 
 | 
            borderRadius: `${radius}rpx`, 
 | 
            transform: effect3d && swiperIndex !== index ? 'scaleY(0.9)' : 'scaleY(1)', 
 | 
            margin: effect3d && swiperIndex !== index ? '0 20rpx' : 0 
 | 
          }" 
 | 
          @click="click(index)" 
 | 
        > 
 | 
          <image class="tn-swiper__item__image" :src="item[name] || item" :mode="imageMode"></image> 
 | 
          <view 
 | 
            v-if="title && item[titleName]" 
 | 
            class="tn-swiper__item__title tn-text-ellipsis" 
 | 
            :style="[titleStyle]"> 
 | 
            {{ item[titleName] }} 
 | 
          </view> 
 | 
        </view> 
 | 
      </swiper-item> 
 | 
    </swiper> 
 | 
     
 | 
    <!-- 指示点 --> 
 | 
    <view class="tn-swiper__indicator" :style="[indicatorStyle]"> 
 | 
      <block v-if="mode === 'rect'"> 
 | 
        <view 
 | 
          v-for="(item, index) in list" 
 | 
          :key="index" 
 | 
          class="tn-swiper__indicator__rect" 
 | 
          :class="{'tn-swiper__indicator__rect--active': swiperIndex === index}" 
 | 
        ></view> 
 | 
      </block> 
 | 
      <block v-if="mode === 'dot'"> 
 | 
        <view 
 | 
          v-for="(item, index) in list" 
 | 
          :key="index" 
 | 
          class="tn-swiper__indicator__dot" 
 | 
          :class="{'tn-swiper__indicator__dot--active': swiperIndex === index}" 
 | 
        ></view> 
 | 
      </block> 
 | 
      <block v-if="mode === 'round'"> 
 | 
        <view 
 | 
          v-for="(item, index) in list" 
 | 
          :key="index" 
 | 
          class="tn-swiper__indicator__round" 
 | 
          :class="{'tn-swiper__indicator__round--active': swiperIndex === index}" 
 | 
        ></view> 
 | 
      </block> 
 | 
      <block v-if="mode === 'number'"> 
 | 
        <view class="tn-swiper__indicator__number">{{ swiperIndex + 1 }}/{{ list.length }}</view> 
 | 
      </block> 
 | 
    </view> 
 | 
  </view> 
 | 
</template> 
 | 
  
 | 
<script> 
 | 
  export default { 
 | 
    name: 'tn-swiper', 
 | 
    props: { 
 | 
      // 轮播图列表数据 
 | 
      // [{image: xxx.jpg, title: 'xxxx'}] 
 | 
      list: { 
 | 
        type: Array, 
 | 
        default() { 
 | 
          return [] 
 | 
        } 
 | 
      }, 
 | 
      // 初始化时,默认显示第几项 
 | 
      current: { 
 | 
        type: Number, 
 | 
        default: 0 
 | 
      }, 
 | 
      // 高度 
 | 
      height: { 
 | 
        type: Number, 
 | 
        default: 250 
 | 
      }, 
 | 
      // 背景颜色 
 | 
      backgroundColor: { 
 | 
        type: String, 
 | 
        default: 'transparent' 
 | 
      }, 
 | 
      // 图片的属性名 
 | 
      name: { 
 | 
        type: String, 
 | 
        default: 'image' 
 | 
      }, 
 | 
      // 是否显示标题 
 | 
      title: { 
 | 
        type: Boolean, 
 | 
        default: false 
 | 
      }, 
 | 
      // 标题的属性名 
 | 
      titleName: { 
 | 
        type: String, 
 | 
        default: 'title' 
 | 
      }, 
 | 
      // 用户自定义标题样式 
 | 
      titleStyle: { 
 | 
        type: Object, 
 | 
        default() { 
 | 
          return {} 
 | 
        } 
 | 
      }, 
 | 
      // 圆角的值 
 | 
      radius: { 
 | 
        type: Number, 
 | 
        default: 8 
 | 
      }, 
 | 
      // 指示器模式 
 | 
      // rect -> 方形 round -> 圆角方形 dot -> 点 number -> 轮播图下标 
 | 
      mode: { 
 | 
        type: String, 
 | 
        default: 'round' 
 | 
      }, 
 | 
      // 指示器位置 
 | 
      // topLeft \ topCenter \ topRight \ bottomLeft \ bottomCenter \ bottomRight 
 | 
      indicatorPosition: { 
 | 
        type: String, 
 | 
        default: 'bottomCenter' 
 | 
      }, 
 | 
      // 开启3D缩放效果 
 | 
      effect3d: { 
 | 
        type: Boolean, 
 | 
        default: false 
 | 
      }, 
 | 
      // 在3D缩放模式下,item之间的间隔 
 | 
      effect3dPreviousSpacing: { 
 | 
        type: Number, 
 | 
        default: 50 
 | 
      }, 
 | 
      // 自定播放 
 | 
      autoplay: { 
 | 
        type: Boolean, 
 | 
        default: true 
 | 
      }, 
 | 
      // 图片之间播放间隔多久 
 | 
      interval: { 
 | 
        type: Number, 
 | 
        default: 3000 
 | 
      }, 
 | 
      // 轮播间隔时间 
 | 
      duration: { 
 | 
        type: Number, 
 | 
        default: 500 
 | 
      }, 
 | 
      // 是否衔接滑动 
 | 
      circular: { 
 | 
        type: Boolean, 
 | 
        default: true 
 | 
      }, 
 | 
      // 图片裁剪模式 
 | 
      imageMode: { 
 | 
        type: String, 
 | 
        default: 'aspectFill' 
 | 
      } 
 | 
    }, 
 | 
    computed: { 
 | 
      backgroundColorStyle() { 
 | 
        return this.$t.color.getBackgroundColorStyle(this.backgroundColor) 
 | 
      }, 
 | 
      backgroundColorClass() { 
 | 
        return this.$t.color.getBackgroundColorInternalClass(this.backgroundColor) 
 | 
      }, 
 | 
      swiperStyle() { 
 | 
        let style = {} 
 | 
        if (this.backgroundColorStyle) { 
 | 
          style.backgroundColor = this.backgroundColorStyle 
 | 
        } 
 | 
        if (this.height) { 
 | 
          style.height = this.height + 'rpx' 
 | 
        } 
 | 
        return style 
 | 
      }, 
 | 
      indicatorStyle() { 
 | 
        let style = {} 
 | 
        if (this.indicatorPosition === 'topLeft' || this.indicatorPosition === 'bottomLeft') style.justifyContent = 'flex-start' 
 | 
        if (this.indicatorPosition === 'topCenter' || this.indicatorPosition === 'bottomCenter') style.justifyContent =  'center' 
 | 
        if (this.indicatorPosition === 'topRight' || this.indicatorPosition === 'bottomRight') style.justifyContent =  'flex-end' 
 | 
        if (['topLeft','topCenter','topRight'].indexOf(this.indicatorPosition) >= 0) { 
 | 
          style.top = '12rpx' 
 | 
          style.bottom = 'auto' 
 | 
        } else { 
 | 
          style.top = 'auto' 
 | 
          style.bottom = '12rpx' 
 | 
        } 
 | 
        style.padding = `0 ${this.effect3d ? '74rpx' : '24rpx'}` 
 | 
         
 | 
        return style 
 | 
      }, 
 | 
      swiperTitleStyle() { 
 | 
        let style = {} 
 | 
        if (this.mode === 'none' || this.mode === '') style.paddingBottom = '12rpx' 
 | 
        if (['bottomLeft','bottomCenter','bottomRight'].indexOf(this.indicatorPosition) >= 0 && this.mode === 'number') { 
 | 
          style.paddingBottom = '60rpx' 
 | 
        } else if (['bottomLeft','bottomCenter','bottomRight'].indexOf(this.indicatorPosition) >= 0 && this.mode !== 'number') { 
 | 
          style.paddingBottom = '40rpx' 
 | 
        } else { 
 | 
          style.paddingBottom = '12rpx' 
 | 
        } 
 | 
         
 | 
        style = Object.assign(style, this.titleStyle) 
 | 
        return style 
 | 
      } 
 | 
    }, 
 | 
    data() { 
 | 
      return { 
 | 
        // 当前显示的item的index 
 | 
        swiperIndex: this.current 
 | 
      } 
 | 
    }, 
 | 
    watch: { 
 | 
      list(newVal, oldVal) { 
 | 
        // 如果修改了list的数据,重置current的值 
 | 
        if (newVal.length !== oldVal.length) this.swiperIndex = 0 
 | 
      }, 
 | 
      current(value) { 
 | 
        // 监听外部current的变化,实时修改内部依赖于此测swiperIndex值,如果更新了current,而不是更新swiperIndex,就会错乱,因为指示器是依赖于swiperIndex的 
 | 
        this.swiperIndex = value 
 | 
      } 
 | 
    }, 
 | 
    methods: { 
 | 
      click(index) { 
 | 
        this.$emit('click', index) 
 | 
      }, 
 | 
      // 图片自动切换时触发 
 | 
      change(event) { 
 | 
        const current = event.detail.current 
 | 
        this.swiperIndex = current 
 | 
        this.$emit('change', current) 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
</script> 
 | 
  
 | 
<style lang="scss" scoped> 
 | 
   
 | 
  .tn-swiper { 
 | 
     
 | 
    &__wrap { 
 | 
      position: relative; 
 | 
      overflow: hidden; 
 | 
      transform: translateY(0); 
 | 
    } 
 | 
     
 | 
    &__item { 
 | 
      display: flex; 
 | 
      flex-direction: row; 
 | 
      align-items: center; 
 | 
      overflow: hidden; 
 | 
       
 | 
      &__image { 
 | 
        width: 100%; 
 | 
        height: 100%; 
 | 
        will-change: transform; 
 | 
        display: block; 
 | 
        /* #ifdef H5 */ 
 | 
        pointer-events: none; 
 | 
        /* #endif */ 
 | 
         
 | 
        &__wrap { 
 | 
          width: 100%; 
 | 
          height: 100%; 
 | 
          flex: 1; 
 | 
          transition: all 0.5s; 
 | 
          overflow: hidden; 
 | 
          box-sizing: content-box; 
 | 
          position: relative; 
 | 
        } 
 | 
         
 | 
        &--scale { 
 | 
          transform-origin: center center; 
 | 
        } 
 | 
      } 
 | 
       
 | 
      &__title { 
 | 
        width: 100%; 
 | 
        position: absolute; 
 | 
        background-color: rgba(0, 0, 0, 0.3); 
 | 
        bottom: 0; 
 | 
        left: 0; 
 | 
        font-size: 28rpx; 
 | 
        padding: 12rpx 24rpx; 
 | 
        color: rgba(255, 255, 255, 0.8); 
 | 
      } 
 | 
    } 
 | 
     
 | 
    &__indicator { 
 | 
      padding: 0 24rpx; 
 | 
      position: absolute; 
 | 
      display: flex; 
 | 
      flex-direction: row; 
 | 
      width: 100%; 
 | 
      z-index: 1; 
 | 
       
 | 
      &__rect { 
 | 
        width: 26rpx; 
 | 
        height: 8rpx; 
 | 
        background-color: rgba(0, 0, 0, 0.3); 
 | 
        transition: all 0.5s; 
 | 
         
 | 
        &--active { 
 | 
          background-color: rgba(255, 255, 255, 0.8); 
 | 
        } 
 | 
      } 
 | 
       
 | 
      &__dot { 
 | 
        width: 14rpx; 
 | 
        height: 14rpx; 
 | 
        margin: 0 6rpx; 
 | 
        border-radius: 20rpx; 
 | 
        background-color: rgba(0, 0, 0, 0.3); 
 | 
        transition: all 0.5s; 
 | 
         
 | 
        &--active { 
 | 
          background-color: rgba(255, 255, 255, 0.8); 
 | 
        } 
 | 
      } 
 | 
       
 | 
      &__round { 
 | 
        width: 14rpx; 
 | 
        height: 14rpx; 
 | 
        margin: 0 6rpx; 
 | 
        border-radius: 20rpx; 
 | 
        background-color: rgba(0, 0, 0, 0.3); 
 | 
        transition: all 0.5s; 
 | 
         
 | 
        &--active { 
 | 
          width: 34rpx; 
 | 
          background-color: rgba(255, 255, 255, 0.8); 
 | 
        } 
 | 
      } 
 | 
       
 | 
      &__number { 
 | 
        padding: 6rpx 16rpx; 
 | 
        line-height: 1; 
 | 
        background-color: rgba(0, 0, 0, 0.3); 
 | 
        color: rgba(255, 255, 255, 0.8); 
 | 
        border-radius: 100rpx; 
 | 
        font-size: 26rpx; 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
</style> 
 |