|
|
- <template>
- <view class="banner" :style="{ height: height }">
- <swiper
- :indicator-dots="false"
- :autoplay="true"
- :interval="3000"
- :duration="500"
- circular
- :style="{ width: '100%', height: height }"
- >
- <swiper-item v-for="(item, index) in bannerList" :key="item.id || index">
- <view v-if="item.type == 1" class="video-container">
- <!-- 预览状态:显示封面图 -->
- <image
- v-if="!videoPlayingStates[index]"
- :src="item.image || ''"
- mode="aspectFill"
- style="width: 100%; height: 100%;"
- class="video-poster"
- @click="playVideoFullscreen(item, index)"
- />
- <!-- 播放状态:显示视频 -->
- <video
- v-else
- :id="`${videoIdPrefix}-${index}`"
- :src="item.voUrl"
- :autoplay="true"
- :muted="false"
- :loop="false"
- :controls="true"
- :show-play-btn="true"
- :show-center-play-btn="false"
- :show-fullscreen-btn="true"
- :show-progress="true"
- :show-mute-btn="true"
- :enable-progress-gesture="true"
- :enable-play-gesture="true"
- object-fit="cover"
- style="width: 100%; height: 100%;"
- @fullscreenchange="onFullscreenChange"
- @play="onVideoPlay(index)"
- @pause="onVideoPause(index)"
- @ended="onVideoEnded(index)"
- ></video>
- <!-- 播放按钮覆盖层 -->
- <view v-if="!videoPlayingStates[index]" class="video-overlay" @click="playVideoFullscreen(item, index)">
- <view class="play-button-large">
- <view class="play-triangle"></view>
- </view>
- </view>
- </view>
- <image v-else :src="item.image" mode="aspectFill" style="width: 100%; height: 100%;" @click="onImageClick(item, index)" />
- </swiper-item>
- </swiper>
-
- <!-- 备用静态图片,当没有轮播图数据时显示 -->
- <image v-if="!bannerList || bannerList.length === 0" :src="fallbackImage" mode="aspectFill" style="width: 100%; height: 100%;" />
- </view>
- </template>
-
- <script>
- export default {
- name: 'BannerSwiper',
- props: {
- // 轮播图数据
- bannerList: {
- type: Array,
- default: () => []
- },
- // 轮播图高度
- height: {
- type: String,
- default: '400rpx'
- },
- // 备用图片
- fallbackImage: {
- type: String,
- default: ''
- },
- // 视频ID前缀,用于区分不同页面的视频
- videoIdPrefix: {
- type: String,
- default: 'video'
- }
- },
- data() {
- return {
- videoPlayingStates: {} // 记录每个视频的播放状态
- }
- },
- methods: {
- // 视频全屏播放
- playVideoFullscreen(item, index) {
- if (!this.videoPlayingStates[index]) {
- // 第一次点击:显示视频并开始播放
- this.$set(this.videoPlayingStates, index, true)
- // 等待视频元素渲染后再进行操作
- this.$nextTick(() => {
- setTimeout(() => {
- const videoContext = uni.createVideoContext(`${this.videoIdPrefix}-${index}`, this)
- if (videoContext) {
- // 直接进入全屏播放,强制竖屏
- videoContext.requestFullScreen({
- direction: 0 // 竖屏方向,0代表竖屏,1代表横屏,-1自动
- })
- }
- }, 200)
- })
- }
- },
-
- // 图片点击事件
- onImageClick(item, index) {
- this.$emit('image-click', { item, index })
- },
-
- // 视频播放事件
- onVideoPlay(index) {
- this.$set(this.videoPlayingStates, index, true)
- },
-
- // 视频暂停事件
- onVideoPause(index) {
- this.$set(this.videoPlayingStates, index, false)
- },
-
- // 视频结束事件
- onVideoEnded(index) {
- // 视频播放结束,回到预览状态
- this.$set(this.videoPlayingStates, index, false)
- },
-
- // 全屏状态改变事件
- onFullscreenChange(e) {
- console.log('全屏状态改变:', e.detail)
- const videoIndex = e.target.id.replace(`${this.videoIdPrefix}-`, '')
-
- if (e.detail.fullScreen) {
- // 进入全屏时,不做任何操作
- console.log('进入全屏模式,方向:', e.detail.direction)
- } else {
- // 退出全屏时,回到预览状态
- console.log('退出全屏模式,回到预览状态')
- this.$set(this.videoPlayingStates, videoIndex, false)
- }
- }
- }
- }
- </script>
-
- <style lang="scss" scoped>
- .banner {
- width: 100%;
- position: relative;
- overflow: hidden;
- border-radius: 0 0 30rpx 30rpx;
-
- image {
- width: 100%;
- height: 100%;
- }
-
- .video-container {
- position: relative;
- width: 100%;
- height: 100%;
-
- .video-poster {
- cursor: pointer;
- transition: transform 0.2s ease;
-
- &:active {
- transform: scale(0.98);
- }
- }
-
- .video-overlay {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.4);
- z-index: 10;
- cursor: pointer;
- display: flex;
- justify-content: center;
- align-items: center;
-
- .play-button-large {
- width: 120rpx;
- height: 120rpx;
- background: transparent;
- border-radius: 50%;
- display: flex;
- justify-content: center;
- align-items: center;
- backdrop-filter: blur(10rpx);
- transition: all 0.3s ease;
- box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.3);
-
- &:active {
- transform: scale(0.9);
- background: rgba(255, 255, 255, 0.1);
- }
-
- .play-triangle {
- width: 0;
- height: 0;
- border-left: 24rpx solid #fff;
- border-top: 18rpx solid transparent;
- border-bottom: 18rpx solid transparent;
- margin-left: 8rpx;
- transition: all 0.3s ease;
- filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.3));
- }
- }
-
- &:hover .play-button-large {
- transform: scale(1.1);
- box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.4);
-
- .play-triangle {
- border-left-color: #ff8917;
- }
- }
- }
- }
- }
- </style>
|