|
@@ -4,17 +4,44 @@
|
|
|
autoplay
|
|
|
object-fit="contain"
|
|
|
show-mute-btn
|
|
|
- show-background-playback-button
|
|
|
enable-play-gesture
|
|
|
:poster="videoInfo.cover_img_url"
|
|
|
:src="videoInfo.video_url"
|
|
|
:id="videoInfo.community_video_id"
|
|
|
@ended="handleVideoEnd"
|
|
|
+ @play="handleVideoPlay"
|
|
|
@pause="handleVideoPause"
|
|
|
@timeupdate="handleTimeUpdate"
|
|
|
+ @fullscreenchange="handleFullscreenchange"
|
|
|
+ @controlstoggle="handleControlstoggle"
|
|
|
v-if="videoInfo.id==curVideoId"
|
|
|
>
|
|
|
- <view class="video-inner-right-box">
|
|
|
+
|
|
|
+ <!-- 弹幕滚动模块 -->
|
|
|
+ <view class="danmu-scroll-box" v-show="!closeDM">
|
|
|
+ <view
|
|
|
+ :class="[
|
|
|
+ 'danmu-item',
|
|
|
+ play?'animat-run':'animat-pause',
|
|
|
+ item.user_id==selfUserid?'border':''
|
|
|
+ ]"
|
|
|
+ v-for="item in danmuList"
|
|
|
+ :key="item.id"
|
|
|
+ :style="{color:item.color,top:item.top,animationDuration:item.speed+'s'}"
|
|
|
+ >{{item.content}}</view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="video-inner-right-box" v-if="isShowControls">
|
|
|
+ <!-- 切换音频播放按钮 -->
|
|
|
+ <view class="change-music-icon" @click="handleChangeMusic"></view>
|
|
|
+ <!-- 弹幕控制按钮 -->
|
|
|
+ <view class="video-danmu-control-box">
|
|
|
+ <view class="show-btn" v-if="!closeDM" @click.stop="closeDM=true"></view>
|
|
|
+ <view class="close-btn" v-else @click.stop="closeDM=false"></view>
|
|
|
+ <view class="send-btn" v-if="!closeDM" @click.stop="showInput=true">发弹幕</view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 倍速控制按钮 -->
|
|
|
<view class="video-speed-btn" @click.stop="showSpeedOpt=true">倍速</view>
|
|
|
</view>
|
|
|
|
|
@@ -28,7 +55,7 @@
|
|
|
@click.stop="handleVideoSpeedChange(item)"
|
|
|
>{{item}}X</view>
|
|
|
</view>
|
|
|
-
|
|
|
+
|
|
|
</video>
|
|
|
<image @click="handelClickPlay" v-else class="poster" :src="videoInfo.cover_img_url" mode="aspectFill" lazy-load/>
|
|
|
|
|
@@ -38,11 +65,11 @@
|
|
|
<view class="left" @click.stop="showInput=true"></view>
|
|
|
<view class="right" @click.stop="closeDM=true"></view>
|
|
|
</view>
|
|
|
- <view class="small-box" v-else @click.stop="closeDM=false"></view>
|
|
|
+ <view class="small-box" v-else @click.stop="handleShowDM"></view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 弹幕输入弹窗 -->
|
|
|
- <view class="flex danmu-input-box" v-if="showInput">
|
|
|
+ <view class="flex danmu-input-box" :style="{bottom:keyboardheight+'px',paddingLeft:isFullScreen?safeAreaTop+17+'px':'34rpx'}" v-if="showInput">
|
|
|
<view class="flex input-box">
|
|
|
<input
|
|
|
type="text"
|
|
@@ -52,18 +79,21 @@
|
|
|
maxlength="50"
|
|
|
focus
|
|
|
confirm-type="send"
|
|
|
+ :adjust-position="false"
|
|
|
@keyboardheightchange="keyboardheightchange"
|
|
|
+ @confirm="handleSendDanmu"
|
|
|
/>
|
|
|
<text>{{danmuText.length}}/50</text>
|
|
|
</view>
|
|
|
- <view class="btn">发送</view>
|
|
|
+ <view class="btn" @click="handleSendDanmu">发送</view>
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-
|
|
|
+import {apiVideoDanmuSend} from '@/api/video'
|
|
|
+import { debounce } from '@/utils/common.js';
|
|
|
export default {
|
|
|
props:{
|
|
|
showDanmu:{
|
|
@@ -76,21 +106,68 @@ export default {
|
|
|
watch: {
|
|
|
curVideoId(){
|
|
|
this.curSpeed='1.0'
|
|
|
+ if(this.videoInfo.id==this.curVideoId){//显示弹幕
|
|
|
+ this.closeDM=false
|
|
|
+ }else{
|
|
|
+ this.closeDM=true
|
|
|
+ this.curVideoIns=null
|
|
|
+ this.play=false
|
|
|
+ this.danmuList=[]
|
|
|
+ this.temdanmuList.forEach(item=>{
|
|
|
+ item.done=false
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ showInput(n){
|
|
|
+ //写弹幕时暂停视频
|
|
|
+ if(n){
|
|
|
+ this.curVideoIns.pause()
|
|
|
+ }else{
|
|
|
+ this.curVideoIns.play()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ closeDM(){
|
|
|
+ if(this.closeDM){
|
|
|
+ this.danmuList=[]//如果关闭了弹幕显示 则清空一次 弹幕列表 防止 切换回来又会重新播放一次已有的弹幕
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed:{
|
|
|
+ selfUserid(){
|
|
|
+ return this.$store.state.user.userInfo.user_id;
|
|
|
}
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
curVideoIns:null,
|
|
|
+ play:false,//视频播放状态
|
|
|
+ curVideoTime:0,
|
|
|
|
|
|
showInput:false,//显示悬浮输入弹幕弹窗
|
|
|
- closeDM:false,//是否关闭弹幕
|
|
|
+ closeDM:true,//是否关闭弹幕
|
|
|
danmuText:'',
|
|
|
|
|
|
showSpeedOpt:false,
|
|
|
speedOpts:['0.5','0.8','1.0','1.25','1.5','2.0'],
|
|
|
curSpeed:'1.0',
|
|
|
+
|
|
|
+ keyboardheight:0,//键盘高度
|
|
|
+ isFullScreen:false,//是否为全屏
|
|
|
+ isShowControls:false,//是否显示视频的控制栏
|
|
|
+
|
|
|
+ safeAreaTop:0,
|
|
|
+
|
|
|
+ temdanmuList:this.videoInfo.bullet_chat_list||[],
|
|
|
+ danmuList:[],
|
|
|
}
|
|
|
},
|
|
|
+ created(){
|
|
|
+ uni.getSystemInfo({
|
|
|
+ success:(res)=>{
|
|
|
+ this.safeAreaTop=res.safeArea.top
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
methods: {
|
|
|
//点击最外层盒子
|
|
|
handleClickWrap(){
|
|
@@ -104,28 +181,50 @@ export default {
|
|
|
this.curVideoIns=uni.createVideoContext(this.curVideoId.toString(),this)//由于是在自定义组件内 所有this不可少
|
|
|
}, 300);
|
|
|
},
|
|
|
+ handleVideoPlay(){
|
|
|
+ this.play=true
|
|
|
+ },
|
|
|
handleVideoEnd(){
|
|
|
// 此处因为如果不调用退出全屏方法 安卓和ios页面均会表现异常,安卓横屏不恢复竖屏,ios底部tabbar渲染异常
|
|
|
this.curVideoIns.exitFullScreen()
|
|
|
this.curVideoIns=null
|
|
|
+ this.play=false
|
|
|
+ this.danmuList=[]
|
|
|
+ this.temdanmuList.forEach(item=>{
|
|
|
+ item.done=false
|
|
|
+ })
|
|
|
this.$emit('ended')
|
|
|
},
|
|
|
handleVideoPause(){
|
|
|
+ this.play=false
|
|
|
this.$emit('pause')
|
|
|
},
|
|
|
handleTimeUpdate(e){
|
|
|
+ this.addDanmu(e.detail.currentTime)
|
|
|
+ this.curVideoTime=e.detail.currentTime
|
|
|
this.$emit('timeupdate',e)
|
|
|
},
|
|
|
-
|
|
|
+ handleFullscreenchange(e){
|
|
|
+ this.isFullScreen=e.detail.fullScreen
|
|
|
+ },
|
|
|
+ handleControlstoggle(e){
|
|
|
+ this.isShowControls=e.detail.show
|
|
|
+ },
|
|
|
//键盘高度变为0 则收起悬浮弹幕输入框
|
|
|
keyboardheightchange(e){
|
|
|
- if(e.detail.height===0){
|
|
|
- setTimeout(() => {
|
|
|
- this.showInput=false
|
|
|
- }, 60);
|
|
|
- }
|
|
|
+ this.keyboardheight=e.detail.height
|
|
|
+ this.handlekeyboardheight()
|
|
|
},
|
|
|
+ handlekeyboardheight:debounce(function(){
|
|
|
+ if(this.keyboardheight==0&&this.showInput){
|
|
|
+ this.showInput=false
|
|
|
+ }
|
|
|
+ },60),
|
|
|
|
|
|
+ //切换为背景音频播放
|
|
|
+ handleChangeMusic(){
|
|
|
+ this.curVideoIns.requestBackgroundPlayback()
|
|
|
+ },
|
|
|
|
|
|
// 倍速切换
|
|
|
handleVideoSpeedChange(item){
|
|
@@ -133,6 +232,67 @@ export default {
|
|
|
this.curVideoIns.playbackRate(num)
|
|
|
this.curSpeed=item
|
|
|
this.showSpeedOpt=false
|
|
|
+ },
|
|
|
+
|
|
|
+ //点击视频区域外面的 显示弹幕按钮 进行判断如果当前视频没有播放 则不改变状态
|
|
|
+ handleShowDM(){
|
|
|
+ if(this.videoInfo.id==this.curVideoId){
|
|
|
+ this.closeDM=false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ //发送弹幕
|
|
|
+ handleSendDanmu(){
|
|
|
+ if(!this.danmuText) return
|
|
|
+ apiVideoDanmuSend({
|
|
|
+ content:this.danmuText,
|
|
|
+ seconds:parseInt(this.curVideoTime),
|
|
|
+ primary_id:this.videoInfo.id,
|
|
|
+ source:this.videoInfo.source
|
|
|
+ }).then(res=>{
|
|
|
+ this.danmuText=''
|
|
|
+ if(res.code===200){
|
|
|
+ this.temdanmuList.push({...res.data,seconds:Number(res.data.seconds)+3})
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ //添加弹幕到视频上去
|
|
|
+ addDanmu(ctime){
|
|
|
+ this.temdanmuList.forEach(item => {
|
|
|
+ if(item.seconds>ctime-1&&item.seconds<ctime+1){// 前后误差一秒
|
|
|
+ if(!item.done){
|
|
|
+ item.done=true
|
|
|
+ this.danmuList.push({
|
|
|
+ ...item,
|
|
|
+ top:this.getTopPosition(),
|
|
|
+ speed:Math.floor(Math.random()*(16-8+1))+8//4~8 之间的随机数
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ // 如果播放过了 手贱又把进度条拖回去了 则重置done
|
|
|
+ if(ctime-1<item.seconds){
|
|
|
+ item.done=false
|
|
|
+ }
|
|
|
+ if(ctime+1>item.seconds){
|
|
|
+ item.done=true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ //设置弹幕位置
|
|
|
+ getTopPosition(){
|
|
|
+ const length=this.danmuList.length
|
|
|
+ let num=0
|
|
|
+ if(length%3===1){
|
|
|
+ num=10
|
|
|
+ }else if(length%3===2){
|
|
|
+ num=30
|
|
|
+ }else{
|
|
|
+ num=50
|
|
|
+ }
|
|
|
+ return num+'px'
|
|
|
}
|
|
|
|
|
|
},
|
|
@@ -199,8 +359,8 @@ export default {
|
|
|
left: 0;
|
|
|
right: 0;
|
|
|
bottom: 0;
|
|
|
- z-index: 999999;
|
|
|
- background-color: #fff;
|
|
|
+ z-index: 999999999;
|
|
|
+ background-color: #ffffff;
|
|
|
padding: 10rpx 34rpx;
|
|
|
border-top: 1px solid #E5E5E5;
|
|
|
align-content: center;
|
|
@@ -235,6 +395,16 @@ export default {
|
|
|
position: absolute;
|
|
|
bottom: 30%;
|
|
|
right: 5%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ .change-music-icon{
|
|
|
+ width: 40rpx;
|
|
|
+ height: 40rpx;
|
|
|
+ background-image: url('@/static/headphones-icon.png');
|
|
|
+ background-size: cover;
|
|
|
+ margin-bottom: 10rpx;
|
|
|
}
|
|
|
|
|
|
.video-speed-btn{
|
|
@@ -267,5 +437,72 @@ export default {
|
|
|
text-align: center;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .video-danmu-control-box{
|
|
|
+ margin-bottom: 10rpx;
|
|
|
+ view{
|
|
|
+ margin-left: auto;
|
|
|
+ margin-right: auto;
|
|
|
+ }
|
|
|
+ .show-btn{
|
|
|
+ width: 80rpx;
|
|
|
+ height: 50rpx;
|
|
|
+ background-image: url('@/static/danmu-show-btn-2.png');
|
|
|
+ background-size: cover;
|
|
|
+ }
|
|
|
+ .close-btn{
|
|
|
+ width: 80rpx;
|
|
|
+ height: 50rpx;
|
|
|
+ background-image: url('@/static/danmu-close-btn-2.png');
|
|
|
+ background-size: cover;
|
|
|
+ }
|
|
|
+ .send-btn{
|
|
|
+ width: 100rpx;
|
|
|
+ height: 44rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ background: rgba(0, 0, 0, 0.4);
|
|
|
+ border-radius: 22rpx;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 12px;
|
|
|
+ margin-top: 20rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .danmu-scroll-box{
|
|
|
+ .danmu-item{
|
|
|
+ color: #fff;
|
|
|
+ animation: move 6s linear;
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ display: block;
|
|
|
+ left: 150%;
|
|
|
+ position: absolute;
|
|
|
+ font-size: 12px;
|
|
|
+ height: 18px;
|
|
|
+ }
|
|
|
+ .animat-pause{
|
|
|
+ animation-play-state: paused;
|
|
|
+ }
|
|
|
+ .animat-run{
|
|
|
+ animation-play-state: running;
|
|
|
+ }
|
|
|
+ .border{
|
|
|
+ border: 1px solid #fff;
|
|
|
+ border-radius: 10px;
|
|
|
+ padding-top: 8px;
|
|
|
+ padding-left: 2px;
|
|
|
+ padding-right: 2px;
|
|
|
+ }
|
|
|
+ @keyframes move {
|
|
|
+ 0%{
|
|
|
+ left: 150%;
|
|
|
+ }
|
|
|
+ 100%{
|
|
|
+ left: -200%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|