zhangmeng
2024-04-19 e3ba120cb766a17e098e58d11c39ffc600a3070c
commit | author | age
e3ba12 1 <template>
Z 2     <view
3         class="u-swiper"
4         :style="{
5             backgroundColor: bgColor,
6             height: $u.addUnit(height),
7             borderRadius: $u.addUnit(radius)
8         }"
9     >
10         <view
11             class="u-swiper__loading"
12             v-if="loading"
13         >
14             <u-loading-icon mode="circle"></u-loading-icon>
15         </view>
16         <swiper
17             v-else
18             class="u-swiper__wrapper"
19             :style="{
20                 height: $u.addUnit(height),
21             }"
22             @change="change"
23             :circular="circular"
24             :interval="interval"
25             :duration="duration"
26             :autoplay="autoplay"
27             :current="current"
28             :currentItemId="currentItemId"
29             :previousMargin="$u.addUnit(previousMargin)"
30             :nextMargin="$u.addUnit(nextMargin)"
31             :acceleration="acceleration"
32             :displayMultipleItems="displayMultipleItems"
33             :easingFunction="easingFunction"
34         >
35             <swiper-item
36                 class="u-swiper__wrapper__item"
37                 v-for="(item, index) in list"
38                 :key="index"
39             >
40                 <view
41                     class="u-swiper__wrapper__item__wrapper"
42                     :style="[itemStyle(index)]"
43                 >
44                     <!-- 在nvue中,image图片的宽度默认为屏幕宽度,需要通过flex:1撑开,另外必须设置高度才能显示图片 -->
45                     <image
46                         class="u-swiper__wrapper__item__wrapper__image"
47                         v-if="getItemType(item) === 'image'"
48                         :src="getSource(item)"
49                         :mode="imgMode"
50                         @tap="clickHandler(index)"
51                         :style="{
52                             height: $u.addUnit(height),
53                             borderRadius: $u.addUnit(radius)
54                         }"
55                     ></image>
56                     <video
57                         class="u-swiper__wrapper__item__wrapper__video"
58                         v-if="getItemType(item) === 'video'"
59                         :id="`video-${index}`"
60                         :enable-progress-gesture="false"
61                         :src="getSource(item)"
62                         :poster="getPoster(item)"
63                         :title="showTitle && $u.test.object(item) && item.title ? item.title : ''"
64                         :style="{
65                             height: $u.addUnit(height)
66                         }"
67                         controls
68                         @tap="clickHandler(index)"
69                     ></video>
70                     <text
71                         v-if="showTitle && $u.test.object(item) && item.title && $u.test.image(getSource(item))"
72                         class="u-swiper__wrapper__item__wrapper__title u-line-1"
73                     >{{ item.title }}</text>
74                 </view>
75             </swiper-item>
76         </swiper>
77         <view class="u-swiper__indicator" :style="[$u.addStyle(indicatorStyle)]">
78             <slot name="indicator">
79                 <u-swiper-indicator
80                     v-if="!loading && indicator && !showTitle"
81                     :indicatorActiveColor="indicatorActiveColor"
82                     :indicatorInactiveColor="indicatorInactiveColor"
83                     :length="list.length"
84                     :current="currentIndex"
85                     :indicatorMode="indicatorMode"
86                 ></u-swiper-indicator>
87             </slot>
88         </view>
89     </view>
90 </template>
91
92 <script>
93     import props from './props.js';
94     /**
95      * Swiper 轮播图
96      * @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用,
97      * @tutorial https://www.uviewui.com/components/swiper.html
98      * @property {Array}            list                    轮播图数据
99      * @property {Boolean}            indicator                是否显示面板指示器(默认 false )
100      * @property {String}            indicatorActiveColor    指示器非激活颜色(默认 '#FFFFFF' )
101      * @property {String}            indicatorInactiveColor    指示器的激活颜色(默认 'rgba(255, 255, 255, 0.35)' )
102      * @property {String | Object}    indicatorStyle            指示器样式,可通过bottom,left,right进行定位
103      * @property {String}            indicatorMode            指示器模式(默认 'line' )
104      * @property {Boolean}            autoplay                是否自动切换(默认 true )
105      * @property {String | Number}    current                    当前所在滑块的 index(默认 0 )
106      * @property {String}            currentItemId            当前所在滑块的 item-id ,不能与 current 被同时指定
107      * @property {String | Number}    interval                滑块自动切换时间间隔(ms)(默认 3000 )
108      * @property {String | Number}    duration                滑块切换过程所需时间(ms)(默认 300 )
109      * @property {Boolean}            circular                播放到末尾后是否重新回到开头(默认 false )
110      * @property {String | Number}    previousMargin            前边距,可用于露出前一项的一小部分,nvue和支付宝不支持(默认 0 )
111      * @property {String | Number}    nextMargin                后边距,可用于露出后一项的一小部分,nvue和支付宝不支持(默认 0 )
112      * @property {Boolean}            acceleration            当开启时,会根据滑动速度,连续滑动多屏,支付宝不支持(默认 false )
113      * @property {Number}            displayMultipleItems    同时显示的滑块数量,nvue、支付宝小程序不支持(默认 1 )
114      * @property {String}            easingFunction            指定swiper切换缓动动画类型, 只对微信小程序有效(默认 'default' )
115      * @property {String}            keyName                    list数组中指定对象的目标属性名(默认 'url' )
116      * @property {String}            imgMode                    图片的裁剪模式(默认 'aspectFill' )
117      * @property {String | Number}    height                    组件高度(默认 130 )
118      * @property {String}            bgColor                    背景颜色(默认     '#f3f4f6' )
119      * @property {String | Number}    radius                    组件圆角,数值或带单位的字符串(默认 4 )
120      * @property {Boolean}            loading                    是否加载中(默认 false )
121      * @property {Boolean}            showTitle                是否显示标题,要求数组对象中有title属性(默认 false )
122      * @event {Function(index)}    click    点击轮播图时触发    index:点击了第几张图片,从0开始
123      * @event {Function(index)}    change    轮播图切换时触发(自动或者手动切换)    index:切换到了第几张图片,从0开始
124      * @example    <u-swiper :list="list4" keyName="url" :autoplay="false"></u-swiper>
125      */
126     export default {
127         name: 'u-swiper',
128         mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
129         data() {
130             return {
131                 currentIndex: 0
132             }
133         },
134         watch: {
135             current(val, preVal) {
136                 if(val === preVal) return;
137                 this.currentIndex = val; // 和上游数据关联上
138             }
139         },
140         computed: {
141             itemStyle() {
142                 return index => {
143                     const style = {}
144                     // #ifndef APP-NVUE || MP-TOUTIAO
145                     // 左右流出空间的写法不支持nvue和头条
146                     // 只有配置了此二值,才加上对应的圆角,以及缩放
147                     if (this.nextMargin && this.previousMargin) {
148                         style.borderRadius = uni.$u.addUnit(this.radius)
149                         if (index !== this.currentIndex) style.transform = 'scale(0.92)'
150                     }
151                     // #endif
152                     return style
153                 }
154             }
155         },
156         methods: {
157       getItemType(item) {
158         if (typeof item === 'string') return uni.$u.test.video(this.getSource(item)) ? 'video' : 'image'
159         if (typeof item === 'object' && this.keyName) {
160           if (!item.type) return uni.$u.test.video(this.getSource(item)) ? 'video' : 'image'
161           if (item.type === 'image') return 'image'
162           if (item.type === 'video') return 'video'
163           return 'image'
164         }
165       },
166             // 获取目标路径,可能数组中为字符串,对象的形式,额外可指定对象的目标属性名keyName
167             getSource(item) {
168                 if (typeof item === 'string') return item
169                 if (typeof item === 'object' && this.keyName) return item[this.keyName]
170                 else uni.$u.error('请按格式传递列表参数')
171                 return ''
172             },
173             // 轮播切换事件
174             change(e) {
175                 // 当前的激活索引
176                 const {
177                     current
178                 } = e.detail
179                 this.pauseVideo(this.currentIndex)
180                 this.currentIndex = current
181                 this.$emit('change', e.detail)
182             },
183             // 切换轮播时,暂停视频播放
184             pauseVideo(index) {
185                 const lastItem = this.getSource(this.list[index])
186                 if (uni.$u.test.video(lastItem)) {
187                     // 当视频隐藏时,暂停播放
188                     const video = uni.createVideoContext(`video-${index}`, this)
189                     video.pause()
190                 }
191             },
192             // 当一个轮播item为视频时,获取它的视频海报
193             getPoster(item) {
194                 return typeof item === 'object' && item.poster ? item.poster : ''
195             },
196             // 点击某个item
197             clickHandler(index) {
198                 this.$emit('click', index)
199             }
200         },
201     }
202 </script>
203
204 <style lang="scss" scoped>
205     @import "../../libs/css/components.scss";
206
207     .u-swiper {
208         @include flex;
209         justify-content: center;
210         align-items: center;
211         position: relative;
212         overflow: hidden;
213
214         &__wrapper {
215             flex: 1;
216
217             &__item {
218                 flex: 1;
219
220                 &__wrapper {
221                     @include flex;
222                     position: relative;
223                     overflow: hidden;
224                     transition: transform 0.3s;
225                     flex: 1;
226
227                     &__image {
228                         flex: 1;
229                     }
230
231                     &__video {
232                         flex: 1;
233                     }
234
235                     &__title {
236                         position: absolute;
237                         background-color: rgba(0, 0, 0, 0.3);
238                         bottom: 0;
239                         left: 0;
240                         right: 0;
241                         font-size: 28rpx;
242                         padding: 12rpx 24rpx;
243                         color: #FFFFFF;
244                         flex: 1;
245                     }
246                 }
247             }
248         }
249
250         &__indicator {
251             position: absolute;
252             bottom: 10px;
253         }
254     }
255 </style>