mirror of
https://gitee.com/TSpecific/tuniao-ui.git
synced 2026-03-07 08:14:01 +08:00
457 lines
14 KiB
Vue
457 lines
14 KiB
Vue
<template>
|
||
<view class="third-page-short-video tn-safe-area-inset-bottom">
|
||
<!-- 顶部自定义导航 -->
|
||
<tn-nav-bar fixed>短视频</tn-nav-bar>
|
||
|
||
<!-- 页面内容 -->
|
||
<view :style="{paddingTop: vuex_custom_bar_height + 'px'}">
|
||
<view class="container" :style="{height: containerHeight + 'px'}">
|
||
<tn-custom-swiper
|
||
class="swiper"
|
||
:current="current"
|
||
:duration="250"
|
||
:circular="!loadingFirst"
|
||
:vertical="true"
|
||
@change="handleSwiperChange"
|
||
>
|
||
<block v-for="(item, index) in swiperList" :key="index">
|
||
<tn-custom-swiper-item class="swiper__item">
|
||
<view class="item__container">
|
||
|
||
|
||
<video
|
||
v-if="item.loading"
|
||
class="item__video"
|
||
:id="`video-${index}`"
|
||
:src="item.src"
|
||
objectFit="cover"
|
||
:autoplay="false"
|
||
:loop="true"
|
||
:controls="false"
|
||
:show-progress="false"
|
||
:show-fullscreen-btn="false"
|
||
:show-center-play-btn="true"
|
||
:show-loading="false"
|
||
@error="handleVideoError($event, index)"
|
||
@loadedmetadata="handleVideoLoadedmetadata($event, index)"
|
||
></video>
|
||
|
||
<block v-if="current === index">
|
||
<view class="item__content">
|
||
<view class="author-name">@{{item.author.name}}</view>
|
||
<view class="desc">{{item.desc}}</view>
|
||
</view>
|
||
|
||
<view class="item__operation">
|
||
<view class="avatar">
|
||
<image class="image" :src="item.author.avatar"></image>
|
||
</view>
|
||
<view class="options like" @tap="clickLike(index)">
|
||
<view class="icon" :class="[item.like.flag ? 'icon__select tn-icon-like-fill' : 'tn-icon-like']"></view>
|
||
<view class="text">{{item.like.count}}</view>
|
||
</view>
|
||
<view class="options message">
|
||
<view class="icon tn-icon-comment"></view>
|
||
<view class="text">{{item.message}}</view>
|
||
</view>
|
||
<view class="options star" @tap="clickStar(index)">
|
||
<view class="icon" :class="[item.star.flag ? 'icon__select tn-icon-star-fill' : 'tn-icon-star']"></view>
|
||
<view class="text">{{item.star.count}}</view>
|
||
</view>
|
||
<view class="options share">
|
||
<view class="icon tn-icon-share-triangle"></view>
|
||
<view class="text">{{item.share}}</view>
|
||
</view>
|
||
</view>
|
||
</block>
|
||
</view>
|
||
</tn-custom-swiper-item>
|
||
</block>
|
||
</tn-custom-swiper>
|
||
|
||
<!-- <video
|
||
src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
|
||
></video>
|
||
<video
|
||
src="http://vjs.zencdn.net/v/oceans.mp4"
|
||
></video> -->
|
||
</view>
|
||
|
||
</view>
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: 'ThirdPageShortVideo',
|
||
data() {
|
||
return {
|
||
containerHeight: 0,
|
||
current: 0,
|
||
videoSrc: ['http://vjs.zencdn.net/v/oceans.mp4','http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4'],
|
||
h5FirstPlay: true,
|
||
loadingFirst: true,
|
||
swiperList: [
|
||
{
|
||
author: {
|
||
name: '小泡泡',
|
||
avatar: 'https://tnuiimage.tnkjapp.com/avatar/xiaomai2.jpg'
|
||
},
|
||
desc: '基于图鸟UI开发的第三方插件,图鸟加油,可以的。基于图鸟UI开发的第三方插件,图鸟加油,可以的。基于图鸟UI开发的第三方插件,图鸟加油,可以的。',
|
||
like: {
|
||
flag: true,
|
||
count: 100
|
||
},
|
||
message: 800,
|
||
star: {
|
||
flag: false,
|
||
count: 200
|
||
},
|
||
share: 300,
|
||
loading: false,
|
||
src: ''
|
||
},
|
||
{
|
||
author: {
|
||
name: '小泡泡',
|
||
avatar: 'https://tnuiimage.tnkjapp.com/avatar/xiaomai2.jpg'
|
||
},
|
||
desc: '基于图鸟UI开发的第三方插件,图鸟加油,可以的。',
|
||
like: {
|
||
flag: true,
|
||
count: 100
|
||
},
|
||
message: 800,
|
||
star: {
|
||
flag: false,
|
||
count: 200
|
||
},
|
||
share: 300,
|
||
loading: false,
|
||
src: ''
|
||
},
|
||
{
|
||
author: {
|
||
name: '小泡泡',
|
||
avatar: 'https://tnuiimage.tnkjapp.com/avatar/xiaomai2.jpg'
|
||
},
|
||
desc: '基于图鸟UI开发的第三方插件,图鸟加油,可以的。',
|
||
like: {
|
||
flag: true,
|
||
count: 100
|
||
},
|
||
message: 800,
|
||
star: {
|
||
flag: false,
|
||
count: 200
|
||
},
|
||
share: 300,
|
||
loading: false,
|
||
src: ''
|
||
},
|
||
{
|
||
author: {
|
||
name: '小泡泡',
|
||
avatar: 'https://tnuiimage.tnkjapp.com/avatar/xiaomai2.jpg'
|
||
},
|
||
desc: '基于图鸟UI开发的第三方插件,图鸟加油,可以的。',
|
||
like: {
|
||
flag: true,
|
||
count: 100
|
||
},
|
||
message: 800,
|
||
star: {
|
||
flag: false,
|
||
count: 200
|
||
},
|
||
share: 300,
|
||
loading: false,
|
||
src: ''
|
||
},
|
||
{
|
||
author: {
|
||
name: '小泡泡',
|
||
avatar: 'https://tnuiimage.tnkjapp.com/avatar/xiaomai2.jpg'
|
||
},
|
||
desc: '基于图鸟UI开发的第三方插件,图鸟加油,可以的。',
|
||
like: {
|
||
flag: true,
|
||
count: 100
|
||
},
|
||
message: 800,
|
||
star: {
|
||
flag: false,
|
||
count: 200
|
||
},
|
||
share: 300,
|
||
loading: false,
|
||
src: ''
|
||
}
|
||
],
|
||
// 存放当前缓冲视频是否加载成功
|
||
loadingVideoFlag: []
|
||
}
|
||
},
|
||
computed: {
|
||
listLength() {
|
||
return this.swiperList.length
|
||
}
|
||
},
|
||
onLoad() {
|
||
this.getContainerHeight()
|
||
this.initData()
|
||
},
|
||
onReady() {
|
||
this.$nextTick(() => {
|
||
this.loadVideo()
|
||
})
|
||
},
|
||
methods: {
|
||
// 点击喜欢按钮
|
||
clickLike(index) {
|
||
let item = this.swiperList[index]
|
||
item.like.flag = !item.like.flag
|
||
this.$set(this.swiperList, index, item)
|
||
},
|
||
// 点击收藏按钮
|
||
clickStar(index) {
|
||
let item = this.swiperList[index]
|
||
item.star.flag = !item.star.flag
|
||
this.$set(this.swiperList, index, item)
|
||
},
|
||
// 获取可用区域的高度
|
||
getContainerHeight() {
|
||
const systemInfo = uni.getSystemInfoSync()
|
||
this.containerHeight = systemInfo.safeArea.height - this.vuex_custom_bar_height + systemInfo.statusBarHeight
|
||
},
|
||
// 初始化数据
|
||
initData() {
|
||
this.loadingVideoFlag = Array(this.listLength).fill(false)
|
||
},
|
||
// 加载以当前current为基准的的前后各一个视频
|
||
loadVideo() {
|
||
// 获取当前轮播的index
|
||
const index = this.current
|
||
let prevIndex = index - 1 < 0 ? this.listLength - 1 : index - 1
|
||
let nextIndex = index + 1 > this.listLength - 1 ? 0 : index + 1
|
||
let currentItem = this.swiperList[index]
|
||
let prevItem = this.swiperList[prevIndex]
|
||
let nextItem = this.swiperList[nextIndex]
|
||
|
||
if (!currentItem.loading) {
|
||
currentItem.src = this.videoSrc[this.$tn.number.randomInt(0, 1)]
|
||
currentItem.loading = true
|
||
this.$set(this.swiperList, index, currentItem)
|
||
}
|
||
if (!prevItem.loading) {
|
||
prevItem.src = this.videoSrc[this.$tn.number.randomInt(0, 1)]
|
||
prevItem.loading = true
|
||
this.$set(this.swiperList, prevIndex, prevItem)
|
||
}
|
||
if (!nextItem.loading) {
|
||
nextItem.src = this.videoSrc[this.$tn.number.randomInt(0, 1)]
|
||
nextItem.loading = true
|
||
this.$set(this.swiperList, nextIndex, nextItem)
|
||
}
|
||
|
||
// 如果为非当前和前后的item,则进行重置
|
||
for (let i = 0; i < this.listLength; i++) {
|
||
if (![index, prevIndex, nextIndex].includes(i)) {
|
||
let tempItem = this.swiperList[i]
|
||
tempItem.loading = false
|
||
tempItem.src = ''
|
||
this.$set(this.swiperList, i, tempItem)
|
||
// 重置视频加载标记位
|
||
this.loadingVideoFlag[i] = false
|
||
}
|
||
}
|
||
|
||
// 开始播放当前视频
|
||
this.playCurrentVideo()
|
||
},
|
||
// 开始播放当前视频
|
||
playCurrentVideo() {
|
||
// 获取当前轮播的index
|
||
const index = this.current
|
||
// 开始播放当前的视频,并且停止其他视频
|
||
setTimeout(() => {
|
||
if (!this.loadingVideoFlag[index]) {
|
||
this.$tn.message.toast('播放失败,请看其他视频')
|
||
return
|
||
}
|
||
}, 800)
|
||
for (let i = 0; i < this.listLength; i++) {
|
||
const videoContext = uni.createVideoContext(`video-${i}`, this)
|
||
if (i === index) {
|
||
// #ifdef H5
|
||
if (!this.h5FirstPlay) {
|
||
videoContext.play()
|
||
}
|
||
// #endif
|
||
// #ifndef H5
|
||
videoContext.play()
|
||
// #endif
|
||
|
||
} else {
|
||
// #ifdef MP-WEIXIN
|
||
videoContext.stop()
|
||
// #endif
|
||
// #ifndef MP-WEIXIN
|
||
videoContext.pause()
|
||
// #endif
|
||
}
|
||
}
|
||
},
|
||
// 处理轮播的切换
|
||
handleSwiperChange(e) {
|
||
// console.log(e);
|
||
this.current = e.current
|
||
this.loadVideo()
|
||
// #ifdef H5
|
||
if (this.h5FirstPlay) {
|
||
this.h5FirstPlay = false
|
||
}
|
||
// #endif
|
||
|
||
// 当滑动到第3个并且是第一次滑动到该位置
|
||
if (this.loadingFirst && e.current === 2) {
|
||
this.loadingFirst = false
|
||
}
|
||
},
|
||
// 视频出现元数据时触发
|
||
handleVideoLoadedmetadata(e, index) {
|
||
// console.log(index, e);
|
||
if (!this.loadingVideoFlag[index] && this.current === index) {
|
||
console.log('loadmetadata');
|
||
// #ifdef H5
|
||
if (!this.h5FirstPlay) {
|
||
const videoContext = uni.createVideoContext(`video-${index}`, this)
|
||
videoContext.play()
|
||
}
|
||
// #endif
|
||
// #ifndef H5
|
||
const videoContext = uni.createVideoContext(`video-${index}`, this)
|
||
videoContext.play()
|
||
// #endif
|
||
}
|
||
this.loadingVideoFlag[index] = true
|
||
},
|
||
// 视频加载失败时触发
|
||
handleVideoError(e, index) {
|
||
// console.log(index, e);
|
||
this.loadingVideoFlag[index] = false
|
||
if (index === this.current) {
|
||
this.$tn.message.toast('播放失败,请看其他视频')
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
|
||
.swiper {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
|
||
&__item {
|
||
background-color: #000000;
|
||
|
||
.item {
|
||
&__container {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
&__video {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
&__content {
|
||
width: 70%;
|
||
color: #FFFFFF;
|
||
position: absolute;
|
||
bottom: 30rpx;
|
||
left: 30rpx;
|
||
z-index: 1;
|
||
transition-duration: 0s;
|
||
|
||
.author-name {
|
||
font-size: 38rpx;
|
||
font-weight: bold;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
.desc {
|
||
width: 100%;
|
||
line-height: 1.3em;
|
||
white-space: normal !important;
|
||
text-overflow: ellipsis;
|
||
word-wrap: break-word;
|
||
-webkit-line-clamp: 5;
|
||
}
|
||
}
|
||
|
||
&__operation {
|
||
color: #FFFFFF;
|
||
position: absolute;
|
||
right: 30rpx;
|
||
bottom: 120rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
z-index: 1;
|
||
transition-duration: 0s;
|
||
|
||
.avatar {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
border-radius: 50%;
|
||
overflow: hidden;
|
||
border: 6rpx solid #FFFFFF;
|
||
margin-bottom: 20rpx;
|
||
|
||
.image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.icon {
|
||
font-size: 60rpx;
|
||
|
||
&__select {
|
||
color: #FFCA28;
|
||
animation: select-keyframes 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||
}
|
||
}
|
||
|
||
.options {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-bottom: 10rpx;
|
||
|
||
.text {
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@keyframes select-keyframes {
|
||
0% {
|
||
transform: scale(0);
|
||
}
|
||
100% {
|
||
transform: scale(1);
|
||
}
|
||
}
|
||
</style>
|