256 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Vue
		
	
	
	
		
		
			
		
	
	
			256 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Vue
		
	
	
	
| 
								 | 
							
								<template>
							 | 
						|||
| 
								 | 
							
									<view
							 | 
						|||
| 
								 | 
							
										class="u-swiper"
							 | 
						|||
| 
								 | 
							
										:style="{
							 | 
						|||
| 
								 | 
							
											backgroundColor: bgColor,
							 | 
						|||
| 
								 | 
							
											height: $u.addUnit(height),
							 | 
						|||
| 
								 | 
							
											borderRadius: $u.addUnit(radius)
							 | 
						|||
| 
								 | 
							
										}"
							 | 
						|||
| 
								 | 
							
									>
							 | 
						|||
| 
								 | 
							
										<view
							 | 
						|||
| 
								 | 
							
											class="u-swiper__loading"
							 | 
						|||
| 
								 | 
							
											v-if="loading"
							 | 
						|||
| 
								 | 
							
										>
							 | 
						|||
| 
								 | 
							
											<u-loading-icon mode="circle"></u-loading-icon>
							 | 
						|||
| 
								 | 
							
										</view>
							 | 
						|||
| 
								 | 
							
										<swiper
							 | 
						|||
| 
								 | 
							
											v-else
							 | 
						|||
| 
								 | 
							
											class="u-swiper__wrapper"
							 | 
						|||
| 
								 | 
							
											:style="{
							 | 
						|||
| 
								 | 
							
												height: $u.addUnit(height),
							 | 
						|||
| 
								 | 
							
											}"
							 | 
						|||
| 
								 | 
							
											@change="change"
							 | 
						|||
| 
								 | 
							
											:circular="circular"
							 | 
						|||
| 
								 | 
							
											:interval="interval"
							 | 
						|||
| 
								 | 
							
											:duration="duration"
							 | 
						|||
| 
								 | 
							
											:autoplay="autoplay"
							 | 
						|||
| 
								 | 
							
											:current="current"
							 | 
						|||
| 
								 | 
							
											:currentItemId="currentItemId"
							 | 
						|||
| 
								 | 
							
											:previousMargin="$u.addUnit(previousMargin)"
							 | 
						|||
| 
								 | 
							
											:nextMargin="$u.addUnit(nextMargin)"
							 | 
						|||
| 
								 | 
							
											:acceleration="acceleration"
							 | 
						|||
| 
								 | 
							
											:displayMultipleItems="displayMultipleItems"
							 | 
						|||
| 
								 | 
							
											:easingFunction="easingFunction"
							 | 
						|||
| 
								 | 
							
										>
							 | 
						|||
| 
								 | 
							
											<swiper-item
							 | 
						|||
| 
								 | 
							
												class="u-swiper__wrapper__item"
							 | 
						|||
| 
								 | 
							
												v-for="(item, index) in list"
							 | 
						|||
| 
								 | 
							
												:key="index"
							 | 
						|||
| 
								 | 
							
											>
							 | 
						|||
| 
								 | 
							
												<view
							 | 
						|||
| 
								 | 
							
													class="u-swiper__wrapper__item__wrapper"
							 | 
						|||
| 
								 | 
							
													:style="[itemStyle(index)]"
							 | 
						|||
| 
								 | 
							
												>
							 | 
						|||
| 
								 | 
							
													<!-- 在nvue中,image图片的宽度默认为屏幕宽度,需要通过flex:1撑开,另外必须设置高度才能显示图片 -->
							 | 
						|||
| 
								 | 
							
													<image
							 | 
						|||
| 
								 | 
							
														class="u-swiper__wrapper__item__wrapper__image"
							 | 
						|||
| 
								 | 
							
														v-if="getItemType(item) === 'image'"
							 | 
						|||
| 
								 | 
							
														:src="getSource(item)"
							 | 
						|||
| 
								 | 
							
														:mode="imgMode"
							 | 
						|||
| 
								 | 
							
														@tap="clickHandler(index)"
							 | 
						|||
| 
								 | 
							
														:style="{
							 | 
						|||
| 
								 | 
							
															height: $u.addUnit(height),
							 | 
						|||
| 
								 | 
							
															borderRadius: $u.addUnit(radius)
							 | 
						|||
| 
								 | 
							
														}"
							 | 
						|||
| 
								 | 
							
													></image>
							 | 
						|||
| 
								 | 
							
													<video
							 | 
						|||
| 
								 | 
							
														class="u-swiper__wrapper__item__wrapper__video"
							 | 
						|||
| 
								 | 
							
														v-if="getItemType(item) === 'video'"
							 | 
						|||
| 
								 | 
							
														:id="`video-${index}`"
							 | 
						|||
| 
								 | 
							
														:enable-progress-gesture="false"
							 | 
						|||
| 
								 | 
							
														:src="getSource(item)"
							 | 
						|||
| 
								 | 
							
														:poster="getPoster(item)"
							 | 
						|||
| 
								 | 
							
														:title="showTitle && $u.test.object(item) && item.title ? item.title : ''"
							 | 
						|||
| 
								 | 
							
														:style="{
							 | 
						|||
| 
								 | 
							
															height: $u.addUnit(height)
							 | 
						|||
| 
								 | 
							
														}"
							 | 
						|||
| 
								 | 
							
														controls
							 | 
						|||
| 
								 | 
							
														@tap="clickHandler(index)"
							 | 
						|||
| 
								 | 
							
													></video>
							 | 
						|||
| 
								 | 
							
													<text
							 | 
						|||
| 
								 | 
							
														v-if="showTitle && $u.test.object(item) && item.title && $u.test.image(getSource(item))"
							 | 
						|||
| 
								 | 
							
														class="u-swiper__wrapper__item__wrapper__title u-line-1"
							 | 
						|||
| 
								 | 
							
													>{{ item.title }}</text>
							 | 
						|||
| 
								 | 
							
												</view>
							 | 
						|||
| 
								 | 
							
											</swiper-item>
							 | 
						|||
| 
								 | 
							
										</swiper>
							 | 
						|||
| 
								 | 
							
										<view class="u-swiper__indicator" :style="[$u.addStyle(indicatorStyle)]">
							 | 
						|||
| 
								 | 
							
											<slot name="indicator">
							 | 
						|||
| 
								 | 
							
												<u-swiper-indicator
							 | 
						|||
| 
								 | 
							
													v-if="!loading && indicator && !showTitle"
							 | 
						|||
| 
								 | 
							
													:indicatorActiveColor="indicatorActiveColor"
							 | 
						|||
| 
								 | 
							
													:indicatorInactiveColor="indicatorInactiveColor"
							 | 
						|||
| 
								 | 
							
													:length="list.length"
							 | 
						|||
| 
								 | 
							
													:current="currentIndex"
							 | 
						|||
| 
								 | 
							
													:indicatorMode="indicatorMode"
							 | 
						|||
| 
								 | 
							
												></u-swiper-indicator>
							 | 
						|||
| 
								 | 
							
											</slot>
							 | 
						|||
| 
								 | 
							
										</view>
							 | 
						|||
| 
								 | 
							
									</view>
							 | 
						|||
| 
								 | 
							
								</template>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<script>
							 | 
						|||
| 
								 | 
							
									import props from './props.js';
							 | 
						|||
| 
								 | 
							
									/**
							 | 
						|||
| 
								 | 
							
									 * Swiper 轮播图
							 | 
						|||
| 
								 | 
							
									 * @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用,
							 | 
						|||
| 
								 | 
							
									 * @tutorial https://www.uviewui.com/components/swiper.html
							 | 
						|||
| 
								 | 
							
									 * @property {Array}			list					轮播图数据
							 | 
						|||
| 
								 | 
							
									 * @property {Boolean}			indicator				是否显示面板指示器(默认 false )
							 | 
						|||
| 
								 | 
							
									 * @property {String}			indicatorActiveColor	指示器非激活颜色(默认 '#FFFFFF' )
							 | 
						|||
| 
								 | 
							
									 * @property {String}			indicatorInactiveColor	指示器的激活颜色(默认 'rgba(255, 255, 255, 0.35)' )
							 | 
						|||
| 
								 | 
							
									 * @property {String | Object}	indicatorStyle			指示器样式,可通过bottom,left,right进行定位
							 | 
						|||
| 
								 | 
							
									 * @property {String}			indicatorMode			指示器模式(默认 'line' )
							 | 
						|||
| 
								 | 
							
									 * @property {Boolean}			autoplay				是否自动切换(默认 true )
							 | 
						|||
| 
								 | 
							
									 * @property {String | Number}	current					当前所在滑块的 index(默认 0 )
							 | 
						|||
| 
								 | 
							
									 * @property {String}			currentItemId			当前所在滑块的 item-id ,不能与 current 被同时指定
							 | 
						|||
| 
								 | 
							
									 * @property {String | Number}	interval				滑块自动切换时间间隔(ms)(默认 3000 )
							 | 
						|||
| 
								 | 
							
									 * @property {String | Number}	duration				滑块切换过程所需时间(ms)(默认 300 )
							 | 
						|||
| 
								 | 
							
									 * @property {Boolean}			circular				播放到末尾后是否重新回到开头(默认 false )
							 | 
						|||
| 
								 | 
							
									 * @property {String | Number}	previousMargin			前边距,可用于露出前一项的一小部分,nvue和支付宝不支持(默认 0 )
							 | 
						|||
| 
								 | 
							
									 * @property {String | Number}	nextMargin				后边距,可用于露出后一项的一小部分,nvue和支付宝不支持(默认 0 )
							 | 
						|||
| 
								 | 
							
									 * @property {Boolean}			acceleration			当开启时,会根据滑动速度,连续滑动多屏,支付宝不支持(默认 false )
							 | 
						|||
| 
								 | 
							
									 * @property {Number}			displayMultipleItems	同时显示的滑块数量,nvue、支付宝小程序不支持(默认 1 )
							 | 
						|||
| 
								 | 
							
									 * @property {String}			easingFunction			指定swiper切换缓动动画类型, 只对微信小程序有效(默认 'default' )
							 | 
						|||
| 
								 | 
							
									 * @property {String}			keyName					list数组中指定对象的目标属性名(默认 'url' )
							 | 
						|||
| 
								 | 
							
									 * @property {String}			imgMode					图片的裁剪模式(默认 'aspectFill' )
							 | 
						|||
| 
								 | 
							
									 * @property {String | Number}	height					组件高度(默认 130 )
							 | 
						|||
| 
								 | 
							
									 * @property {String}			bgColor					背景颜色(默认 	'#f3f4f6' )
							 | 
						|||
| 
								 | 
							
									 * @property {String | Number}	radius					组件圆角,数值或带单位的字符串(默认 4 )
							 | 
						|||
| 
								 | 
							
									 * @property {Boolean}			loading					是否加载中(默认 false )
							 | 
						|||
| 
								 | 
							
									 * @property {Boolean}			showTitle				是否显示标题,要求数组对象中有title属性(默认 false )
							 | 
						|||
| 
								 | 
							
									 * @event {Function(index)}	click	点击轮播图时触发	index:点击了第几张图片,从0开始
							 | 
						|||
| 
								 | 
							
									 * @event {Function(index)}	change	轮播图切换时触发(自动或者手动切换)	index:切换到了第几张图片,从0开始
							 | 
						|||
| 
								 | 
							
									 * @example	<u-swiper :list="list4" keyName="url" :autoplay="false"></u-swiper>
							 | 
						|||
| 
								 | 
							
									 */
							 | 
						|||
| 
								 | 
							
									export default {
							 | 
						|||
| 
								 | 
							
										name: 'u-swiper',
							 | 
						|||
| 
								 | 
							
										mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
							 | 
						|||
| 
								 | 
							
										data() {
							 | 
						|||
| 
								 | 
							
											return {
							 | 
						|||
| 
								 | 
							
												currentIndex: 0
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
										watch: {
							 | 
						|||
| 
								 | 
							
											current(val, preVal) {
							 | 
						|||
| 
								 | 
							
												if(val === preVal) return;
							 | 
						|||
| 
								 | 
							
												this.currentIndex = val; // 和上游数据关联上
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
										computed: {
							 | 
						|||
| 
								 | 
							
											itemStyle() {
							 | 
						|||
| 
								 | 
							
												return index => {
							 | 
						|||
| 
								 | 
							
													const style = {}
							 | 
						|||
| 
								 | 
							
													// #ifndef APP-NVUE || MP-TOUTIAO
							 | 
						|||
| 
								 | 
							
													// 左右流出空间的写法不支持nvue和头条
							 | 
						|||
| 
								 | 
							
													// 只有配置了此二值,才加上对应的圆角,以及缩放
							 | 
						|||
| 
								 | 
							
													if (this.nextMargin && this.previousMargin) {
							 | 
						|||
| 
								 | 
							
														style.borderRadius = uni.$u.addUnit(this.radius)
							 | 
						|||
| 
								 | 
							
														if (index !== this.currentIndex) style.transform = 'scale(0.92)'
							 | 
						|||
| 
								 | 
							
													}
							 | 
						|||
| 
								 | 
							
													// #endif
							 | 
						|||
| 
								 | 
							
													return style
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
										methods: {
							 | 
						|||
| 
								 | 
							
								      getItemType(item) {
							 | 
						|||
| 
								 | 
							
								        if (typeof item === 'string') return uni.$u.test.video(this.getSource(item)) ? 'video' : 'image'
							 | 
						|||
| 
								 | 
							
								        if (typeof item === 'object' && this.keyName) {
							 | 
						|||
| 
								 | 
							
								          if (!item.type) return uni.$u.test.video(this.getSource(item)) ? 'video' : 'image'
							 | 
						|||
| 
								 | 
							
								          if (item.type === 'image') return 'image'
							 | 
						|||
| 
								 | 
							
								          if (item.type === 'video') return 'video'
							 | 
						|||
| 
								 | 
							
								          return 'image'
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								      },
							 | 
						|||
| 
								 | 
							
											// 获取目标路径,可能数组中为字符串,对象的形式,额外可指定对象的目标属性名keyName
							 | 
						|||
| 
								 | 
							
											getSource(item) {
							 | 
						|||
| 
								 | 
							
												if (typeof item === 'string') return item
							 | 
						|||
| 
								 | 
							
												if (typeof item === 'object' && this.keyName) return item[this.keyName]
							 | 
						|||
| 
								 | 
							
												else uni.$u.error('请按格式传递列表参数')
							 | 
						|||
| 
								 | 
							
												return ''
							 | 
						|||
| 
								 | 
							
											},
							 | 
						|||
| 
								 | 
							
											// 轮播切换事件
							 | 
						|||
| 
								 | 
							
											change(e) {
							 | 
						|||
| 
								 | 
							
												// 当前的激活索引
							 | 
						|||
| 
								 | 
							
												const {
							 | 
						|||
| 
								 | 
							
													current
							 | 
						|||
| 
								 | 
							
												} = e.detail
							 | 
						|||
| 
								 | 
							
												this.pauseVideo(this.currentIndex)
							 | 
						|||
| 
								 | 
							
												this.currentIndex = current
							 | 
						|||
| 
								 | 
							
												this.$emit('change', e.detail)
							 | 
						|||
| 
								 | 
							
											},
							 | 
						|||
| 
								 | 
							
											// 切换轮播时,暂停视频播放
							 | 
						|||
| 
								 | 
							
											pauseVideo(index) {
							 | 
						|||
| 
								 | 
							
												const lastItem = this.getSource(this.list[index])
							 | 
						|||
| 
								 | 
							
												if (uni.$u.test.video(lastItem)) {
							 | 
						|||
| 
								 | 
							
													// 当视频隐藏时,暂停播放
							 | 
						|||
| 
								 | 
							
													const video = uni.createVideoContext(`video-${index}`, this)
							 | 
						|||
| 
								 | 
							
													video.pause()
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											},
							 | 
						|||
| 
								 | 
							
											// 当一个轮播item为视频时,获取它的视频海报
							 | 
						|||
| 
								 | 
							
											getPoster(item) {
							 | 
						|||
| 
								 | 
							
												return typeof item === 'object' && item.poster ? item.poster : ''
							 | 
						|||
| 
								 | 
							
											},
							 | 
						|||
| 
								 | 
							
											// 点击某个item
							 | 
						|||
| 
								 | 
							
											clickHandler(index) {
							 | 
						|||
| 
								 | 
							
												this.$emit('click', index)
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								</script>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<style lang="scss" scoped>
							 | 
						|||
| 
								 | 
							
									@import "../../libs/css/components.scss";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									.u-swiper {
							 | 
						|||
| 
								 | 
							
										@include flex;
							 | 
						|||
| 
								 | 
							
										justify-content: center;
							 | 
						|||
| 
								 | 
							
										align-items: center;
							 | 
						|||
| 
								 | 
							
										position: relative;
							 | 
						|||
| 
								 | 
							
										overflow: hidden;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										&__wrapper {
							 | 
						|||
| 
								 | 
							
											flex: 1;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											&__item {
							 | 
						|||
| 
								 | 
							
												flex: 1;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												&__wrapper {
							 | 
						|||
| 
								 | 
							
													@include flex;
							 | 
						|||
| 
								 | 
							
													position: relative;
							 | 
						|||
| 
								 | 
							
													overflow: hidden;
							 | 
						|||
| 
								 | 
							
													transition: transform 0.3s;
							 | 
						|||
| 
								 | 
							
													flex: 1;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													&__image {
							 | 
						|||
| 
								 | 
							
														flex: 1;
							 | 
						|||
| 
								 | 
							
													}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													&__video {
							 | 
						|||
| 
								 | 
							
														flex: 1;
							 | 
						|||
| 
								 | 
							
													}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													&__title {
							 | 
						|||
| 
								 | 
							
														position: absolute;
							 | 
						|||
| 
								 | 
							
														background-color: rgba(0, 0, 0, 0.3);
							 | 
						|||
| 
								 | 
							
														bottom: 0;
							 | 
						|||
| 
								 | 
							
														left: 0;
							 | 
						|||
| 
								 | 
							
														right: 0;
							 | 
						|||
| 
								 | 
							
														font-size: 28rpx;
							 | 
						|||
| 
								 | 
							
														padding: 12rpx 24rpx;
							 | 
						|||
| 
								 | 
							
														color: #FFFFFF;
							 | 
						|||
| 
								 | 
							
														flex: 1;
							 | 
						|||
| 
								 | 
							
													}
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										&__indicator {
							 | 
						|||
| 
								 | 
							
											position: absolute;
							 | 
						|||
| 
								 | 
							
											bottom: 10px;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								</style>
							 |