| <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> |