jiuyiUniapp/jiuyi2/components/index/indexVideo.vue

726 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
/**
* 首页视频组件
* @property {Object} item 视频数据对象
* @property {Number} index 当前视频列表索引
* @property {Number} current 当前预览索引
*/
import {
onMounted,
ref,
reactive,
getCurrentInstance,
watch,
defineEmits,
computed,
nextTick
} from 'vue';
const {
proxy
} = getCurrentInstance()
// 顶部状态栏
import statusBar from '@/components/header/statusBar.vue'
// 工具库
import util from '@/common/js/util';
//
import api from '@/api/index.js';
// 视频进度条
import videoProgress from '@/components/index/videoProgress';
const props = defineProps({
// 当前视频对象
item: {
type: Object,
},
// 列表中的视频下标
index: {
type: Number,
},
// 当前列表的index
current: {
type: Number,
},
// 当前tab的index
tabIndex: {
type: Number,
},
// 模式 list列表 detail详情
mode: {
type: String,
default: 'list'
},
// 是否我自己 0不是 1是
isMine: {
type: [String, Number],
default: 0,
},
width: {
type: Number,
default: 0,
},
height: {
type: Number,
default: 0,
},
})
//
const emit = defineEmits(['showTime', 'showComment', 'showCollect', 'showFastCollect', 'showShareFirend', 'onPlay',
'onPause', 'like', 'detailMenu', 'onEnd', 'longtap'
])
// 视频上下文对象
const videoCtx = ref(null)
// 是否播放
const playState = ref(true)
// 是否显示快速收藏列表
const collectFirst = ref(false)
// 视频时间
const videoTime = ref({
// 总长
duration: 0,
// 当前时间
currentTime: 0,
})
// 记录点击的数组
const tapList = reactive([])
// 点击时间戳
const tapTimer = ref(null)
// 是否在计数
const isTap = ref(false)
// 长按
const isLong = ref(false)
// 收藏按钮判定
const collectBtnActive = ref(false)
// 设置时间
const alarmTime = computed(() => {
let result = uni.$store.state.alarmTime
return result
})
// 填充
const fit = computed(() => {
const ratio1 = parseInt(props.width) / parseInt(props.height)
const ratio2 = props.item.breadth / props.item.height
let result = 'contain'
if (Math.abs(formatNumber(ratio2) - formatNumber(ratio1)) < 1) result = 'cover'
return result
})
watch(() => props.current, (nV) => {
if (nV == props.index) play()
else pause()
})
// 挂载后调用
onMounted(() => {
// 视频上下文对象
videoCtx.value = uni.createVideoContext(`video${props.tabIndex}${props.index}`)
})
// 格式化
function formatNumber(result) {
result = parseFloat(result) * 10
return result
}
// 手指触摸视频容器
function onTouchStart() {
// 是否计数
if (isTap.value) {
return
tapList.length = 0
changeVideoPlay()
} else {
isTap.value = true
// 时间
let time = new Date().getTime()
tapList.push(time)
}
clearTimeout(tapTimer.value)
}
// 手指离开视频容器
function onTouchEnd() {
// 如果不是重复点击的状态
if (isTap.value) {
isTap.value = false
let time = new Date().getTime()
let diff = time - tapList[tapList.length - 1]
// 判断长按
if (diff > 350) {
// 取消视频倍速播放的状态
if (isLong.value) {
isLong.value = false
videoCtx.value.playbackRate(1)
}
// 清空计数
tapList.length = 0
return
}
//
tapTimer.value = setTimeout(() => {
// 是否点赞
const isLike = props.item.isLike
let a = tapList.length
tapList.length = 0
switch (a) {
case 1:
changeVideoPlay()
break;
case 2:
console.log('公开赞')
return
emit('like', {
index: props.index,
isLike: isLike == 0 ? 0 : 1
})
break;
case 3:
console.log('隐私赞')
return
emit('like', {
index: props.index,
isLike: isLike == 0 ? 3 : 1
})
break;
}
}, 200)
}
}
// 手指中断视频容器
function onTouchCancel() {
isTap.value = false
tapList.length = 0
clearTimeout(tapTimer.value)
}
// 切换视频播放
function changeVideoPlay() {
// 根据播放状态切换播放暂停
if (playState.value) pause()
else play()
}
// 视频播放
function play() {
let pages = getCurrentPages();
let page = pages[pages.length - 1];
if (props.index != props.current || !['pages/index/index', 'pages/index/videoDetail'].includes(page.route)) return
videoCtx.value.play()
}
// 视频暂停
function pause() {
videoCtx.value.pause()
}
// 视频播放回调
function onVideoPlay() {
playState.value = true
emit('onPlay')
}
// 视频暂停回调
function onVideoPause() {
playState.value = false
emit('onPause')
}
// 打开评论
function handleComment() {
emit('showComment', props.item)
}
// 打开计时闹钟
function handleTime() {
emit('showTime')
}
// 打开收藏弹窗
function showCollect() {
util.isLogin().then(rs => {
emit('showCollect', props.item)
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
// 切换快速收藏
function handleCollectFirst(ev) {
// 获取当前元素的宽高
const changedTouches = ev.changedTouches[0]
let x = getNumber(props.width) - getNumber(changedTouches.screenX) + getNumber(changedTouches.pageX)
let y = getNumber(changedTouches.screenY) - getNumber(changedTouches.pageY)
emit('showFastCollect', {
item: props.item,
position: {
x,
y,
}
})
return
}
/**
* 手指触摸收藏
* @param {Object} ev
*/
function handleCollectStar(ev) {
collectBtnActive.value = true
util.isLogin().then(rs => {
//
setTimeout(() => {
// 判断是否抬起
if (!collectBtnActive.value) {
// 收藏状态
if (!props.item.isCollect) {
// 单击出菜单
handleCollectFirst(ev)
return
} else cancelCollect()
} else {
setTimeout(() => {
// 长按出弹窗
showCollect()
}, 350)
}
}, 350)
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
/**
* 手指触摸结束
* @param {Object} str
*/
function handleCollectEnd() {
collectBtnActive.value = false
}
// 获取数字
function getNumber(str) {
let result = Math.floor(Number(str))
return result
}
// 取消收藏
function cancelCollect() {
const detail = {
...props.item
}
// 取消收藏
api.video.cancelCollect({
query: {
// 视频id
videoId: detail.videoId,
},
}).then(rs => {
if (rs.code == 200) {
detail.isCollect = false
detail.collect--
uni.$emit('updateVideo', detail)
return
}
//
util.alert({
content: rs.msg,
showCancel: false,
})
})
}
// 分享到好友
function handleShareFirend() {
util.isLogin().then(rs => {
emit('showShareFirend', props.item)
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
/**
* 点赞
* @param {Number} index 操作的视频下标
* @param {Number|String} isLike 点赞操作
*/
function handleLike(index, isLike) {
util.isLogin().then(rs => {
emit('like', {
index,
isLike,
})
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
/**
* 私密赞
* @param {Object} index
*/
function handlePrivateLike(index) {
util.isLogin().then(rs => {
util.alert({
title: '提示',
content: '请确认,是否为隐私赞(隐私赞仅自己和作者可见)?',
confirmText: '隐私赞',
cancelText: '公开赞',
}).then(rs => {
if (rs.confirm) emit('like', {
index,
isLike: 3,
})
else emit('like', {
index,
isLike: 0
})
})
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
/**
* 详情菜单
* @param {Object} item
*/
function handleDetailMenu(item) {
emit('detailMenu')
}
// 播放变化
function handleTimeupdate(ev) {
videoTime.value = ev.detail
// console.log('videoTime.value', videoTime.value)
}
// 进度条拖拽结束
function onProgressEnd(ev) {
videoCtx.value.seek(parseInt(ev.time))
}
/**
* 查看用户主页
* @param {Object} item
*/
function handleUser(item) {
uni.navigateTo({
url: util.setUrl('/pages/index/videoHome', {
userId: item.userId
})
})
}
// 视频出现缓冲
function handleWaiting(ev) {
if (props.index == props.current) play()
else pause()
}
// 长按
function longtap(ev) {
if (isLong.value) return
play()
isLong.value = true
videoCtx.value.playbackRate(2)
}
//
defineExpose({
play,
pause,
videoTime,
item: props.item,
playState,
videoCtx: () => videoCtx.value,
})
</script>
<template>
<view class="container f1 pr" ref="videoBoxRef">
<!-- 视频层 -->
<view class="main f1">
<view class="videoBox f1" @touchmove.stop="" @touchstart="onTouchStart" @touchend="onTouchEnd"
@touchcancel="onTouchCancel" @longpress="longtap">
<statusBar />
<!-- 视频 增加判断防止重复加载 -->
<template v-if="item.videoUrl">
<video class="video f1" :id="'video' + tabIndex + index" :src="item.videoUrl"
:poster="item.coverUrl" :http-cache="true" :show-fullscreen-btn="false"
:enable-progress-gesture="false" :controls="false" @play="onVideoPlay" @pause="onVideoPause"
:show-center-play-btn="false" @timeupdate="handleTimeupdate" @waiting="handleWaiting"
:play-strategy="2" :loop="true" :object-fit="fit" />
</template>
</view>
<!-- 视频进度条 -->
<view class="videoProgress" @touchmove.stop="" @touchstart.stop="" @touchend.stop="">
<videoProgress :time="videoTime" @change="onProgressEnd" :viewWidth="width" />
</view>
</view>
<!-- 倍速播放提示 -->
<view class="speedBox" v-if="isLong">
<view class="speed ptb5 plr10">
<text class="f22 cfff">2倍速播放中...</text>
</view>
</view>
<!-- 暂停蒙版 -->
<view class="pausePanel pfull fmid" v-if="!playState">
<!-- 暂停按钮 -->
<image class="pauseImg" src="@/static/pause.png" mode="aspectFit" />
</view>
<!-- 右侧操作区 -->
<view class="panelRight pa t0 b0 r0">
<statusBar />
<view class="head"></view>
<view class="f1 jcr pl5 pt40 pr20">
<!-- 操作台 -->
<view class="operate f1">
<!-- 用户头像 -->
<navigator :url="util.setUrl('/pages/index/videoHome',{userId:item.userId})" class="item pr mb10">
<view class="col">
<image class="wh80 cir" :src="item.avatar" mode="aspectFill" />
<view class="focus pa" v-if="!item.isAttention">
<image class="wh40" src="@/static/indexAtt.png" mode="aspectFit" />
</view>
</view>
</navigator>
<!-- 点赞 -->
<view class="item">
<view class="col">
<view class="pr">
<image class="wh50" src="/static/indexLike.png" mode="aspectFit" v-if="item.isLike == 0"
@click="handleLike(index, 0)" @longpress="handlePrivateLike(index)" />
<image class="wh50" src="/static/indexLike1.png" mode="aspectFit"
v-else-if="item.isLike == 1" @click="handleLike(index, 1)" />
<!-- 私密赞的图标 -->
<image class="wh50" src="/static/privateLike.png" mode="aspectFit"
v-else-if="item.isLike == 3" @click="handleLike(index, 1)" />
</view>
<view class="txt mt10">
<text class="text">{{ item.publicLikeCount }}</text>
</view>
</view>
</view>
<!-- 留言 -->
<view class="item" @click="handleComment">
<view class="col">
<image class="wh50" src="@/static/indexMsg.png" mode="aspectFit" />
<view class="txt mt10">
<text class="text">{{ item.commentCount }}</text>
</view>
</view>
</view>
<!-- 收藏 -->
<view class="item df fdr">
<view class="col" @touchstart="handleCollectStar" @touchend="handleCollectEnd" ref="collectBtn">
<image class="wh50" src="@/static/indexCollect1.png" mode="aspectFit"
v-if="item.isCollect" />
<image class="wh50" src="@/static/indexCollect.png" mode="aspectFit" v-else />
<view class="txt mt10">
<text class="text">{{ item.favoriteCount }}</text>
</view>
</view>
</view>
<!-- 分享 -->
<view class="item" @click="handleShareFirend">
<view class="col">
<image class="wh50" src="@/static/indexShare.png" mode="aspectFit" />
<view class="txt mt10">
<text class="text">分享</text>
</view>
</view>
</view>
<!-- 闹钟 -->
<view class="item money" @click="handleTime" v-if="mode == 'list'">
<view class="col">
<image class="wh80" src="/static/indexMoney1.png" mode="aspectFit" v-if="alarmTime" />
<image class="wh80" src="/static/indexMoney.png" mode="aspectFit" v-else />
</view>
</view>
<!-- 详情菜单 -->
<view class="item money" @click="handleTime" v-if="mode == 'detail' && isMine == 1">
<view class="col wh90 fmid tac" @click="handleDetailMenu">
<uni-icons type="more-filled" color="#d8d8d8" size="70rpx" />
</view>
</view>
</view>
</view>
</view>
<!-- 底部用户信息 -->
<view class="panelBottom pa l0 r0 b0 pl40 pb30">
<!-- 商品信息 -->
<view class="goods df fdr mb20 br10" v-if="0">
<!-- 商品图片 -->
<image class="image wh100 mr15 br10" src="/static/openPage.png" mode="aspectFill" />
<view class="df fdc jcsb f1">
<view class="">
<text class="cfff f28">果农大王霹雳美味榴莲果子</text>
</view>
<view class="info df fdr aic mr10">
<text class="price mr10 cfff f28 b">超低价¥6.66</text>
<text class="cfff f1 f20">已售666单</text>
<uni-icons type="close" color="#fff" />
</view>
</view>
</view>
<!-- 用户 -->
<view class="user" @click="handleUser(item)">
<text class="cfff f36">@{{ item.userNickname }}</text>
</view>
<!-- 简介 -->
<view class="desc mt5">
<text class="t2hd cfff f28">{{ item.title }}</text>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
// 视频盒子
.container {
background-color: #000;
}
// 视频时长
.duration {
margin-top: 120rpx;
width: 750rpx;
background-color: rgba(255, 255, 255, .3);
// 时间时长
.line {
width: 0;
height: 2rpx;
background-color: rgba(255, 255, 255, .8);
transition-duration: .25s;
}
}
// 倍速播放
.speedBox {
position: absolute;
top: 200rpx;
left: 0;
right: 0;
align-items: center;
// 倍速
.speed {
background-color: rgba(0, 0, 0, .8);
border-radius: 5rpx;
opacity: .6;
}
}
// 暂停蒙层
.pausePanel {
background-color: rgba(0, 0, 0, .5);
//
.pauseImg {
width: 140rpx;
height: 140rpx;
}
}
// 右侧屏幕
.panelRight {
align-items: flex-end;
.text {
text-align: center;
color: #fff;
font-size: 24rpx;
}
// 操作台
.operate {
flex: 1;
justify-content: flex-end;
align-items: flex-end;
text-align: center;
padding-bottom: 130rpx;
.item {
margin: 5rpx 0;
padding: 10rpx 5rpx;
&.money {
padding: 0;
}
//
.col {
align-items: center;
justify-content: center;
width: 80rpx;
}
}
}
// 关注的小加号
.focus {
align-items: center;
left: 0;
right: 0;
bottom: 0;
}
}
// 底部
.panelBottom {
padding-right: 150rpx;
//
.goods {
padding: 10rpx;
width: 480rpx;
margin-bottom: 20rpx;
background-color: rgba(0, 0, 0, .3);
}
}
//
.videoProgress {
position: absolute;
left: 0;
right: 0;
bottom: 0;
}
</style>