提交 b93a1a7e 编写于 作者: guangjun.yang's avatar guangjun.yang

被接管时,监听上报

上级 2fa15ef8
<template>
<div
class="pica-video"
:style="{position: isFullScreen ? 'fixed' : ''}">
<template v-if="isWechat">
<video
:id="vid"
:src="url"
:poster="poster"
preload="metadata"
webkit-playsinline
playsinline
x5-playsinline="true"
x-webkit-airplay="allow"
x5-video-player-type="h5"
x5-video-player-fullscreen="false"
mtt-playsinline="true"
controlsList="nodownload"
@loadedmetadata="loadedMetaData"
@timeupdate="onTimeUpdate"
@ended="onEnded"
@error="onError"
>
</video>
<div class="video-cover" @click="showControl = !showControl"></div>
<!-- 控制栏 -->
<div class="control-box" v-show="showControl">
<div class="control-bar">
<div class="btn-play" :class="{'btn-pause': isPaused}" @click="togglePlay"></div>
<div class="progress-box" @click="setProgress">
<div class="progress-inner">
<div class="progress-bar" :style="progressBar"></div>
</div>
<div
class="progress-ball"
:style="{left: `${progressBall}%`}"
@touchstart.stop="onBallStart"
@touchmove.stop="onBallMove"
@touchend.stop="onBallEnd">
</div>
</div>
<div class="time-box">{{ playTime }}/{{ totalTime }}</div>
<!-- <div class="rate-box" @click="showRate=true">
倍速
<div class="rate-list" v-show="showRate">
<div
class="rate-item"
:class="{'rate-active': rate === curtRate}"
v-for="rate in rates"
:key="rate"
@click.stop="selectRate(rate)">{{ rate }}</div>
</div>
</div> -->
<div class="btn-screen" :class="{'btn-screen-mini': isFullScreen}" @click="onFullscreen"></div>
</div>
</div>
<!-- 播放遮罩 -->
<transition name="fade">
<div v-show="logged && isPaused" class="cover cover-play" @click="togglePlay"></div>
</transition>
<!-- 播放错误 -->
<transition name="fade">
<div v-show="logged && showError" class="cover cover-error">
<p>播放失败,请确认网络正常或重新进入页面</p>
</div>
</transition>
<!-- 接续播放 -->
<transition name="fade">
<div v-show="showResume" class="cover cover-resume">
<p>上次观看至{{ history > 59 ? `${parseInt(history / 60)}分钟` : `${history}秒` }},正在续播</p>
</div>
</transition>
<!-- 弹窗放在外部处理 -->
<slot></slot>
<!-- 试看提示 -->
<div class="proved-box" v-show="showProved">
可试看{{ proved > 59 ? `${parseInt(proved / 60)}分钟` : `${proved}秒` }},观看完整版请来 <span @click="download">云鹊医App</span>
</div>
<!-- 重新播放,只有试看有 -->
<div class="replay-box" v-show="showReplay" @click="onReplay">重新播放</div>
</template>
<img v-else class="default-img" src="~@/images/video/course_img.png" alt="" />
</div>
</template>
<script>
/**
* 不使用参数,调用两个方法;
* switchUrl(opt)和switchUrlAndPlay(opt)
* opt: {
* url, // 视频链接
* poster, // 封面
* proved, // 试看时长,单位秒
* history, // 上次观看时长,单位秒
* enable, // 是否可看
* }
* 暴露一个回调onVideoEnd,参数:
* type => 1 播放结束,2 试看结束
*/
import { formatLeftTimeObj } from '@/utils';
export default {
name: 'pica-video',
props: {
coverType: {
type: Number,
default: 0,
},
download: {
type: Function,
default: () => {}
}
},
data() {
return {
url: '', // 视频链接
poster: '', // 封面
proved: 0, // 试看时长,单位秒
history: 0, // 上次观看时长,单位秒
enable: true, // 是否可看
vid: '', // 唯一id
playTime: '00:00', // 播放时长
totalTime: '00:00', // 总时长
isPaused: true, // 是否暂停
isFullScreen: false, // 是否全屏
showControl: true, // 显示控制栏
progressBar: {}, // 进度条
progressBall: 0, // 进度球
rates: [2, 1.5, 1, 0.5],
curtRate: 1,
showRate: false,
showProved: false, // 试看提示文字
showError: false, // 播放错误
showResume: false, // 继续播放
showReplay: false, // 显示重播
isWechat: true, // 微信环境
}
},
computed: {
logged() {
return this.$store.getters.logged;
},
},
created() {
this.vid = `video_${this._uid}`;
this.playTime = 0;
const ua = navigator.userAgent;
this.isWechat = ua.match(/(MicroMessenger)\/([\d.]+)/);
this.isAndroid = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
},
mounted() {
if (this.isWechat) {
const player = document.getElementById(this.vid);
player.addEventListener("timeupdate", this.onTimeUpdate, false);
this.player = player;
}
},
beforeDestroy() {
this.timer && clearTimeout(this.timer);
if (this.currentTime) {
this.reportLeave();
}
if (!this.isPaused && !this.finish) {
this.reportOnOff(2);
}
},
methods: {
togglePlay() {
if (!this.logged) {
this.$store.dispatch('goLogin');
return;
}
if (!this.url || !this.enable) {
return;
}
let isPaused = this.isPaused;
// 试看,且超过时间
if (this.proved && this.player.currentTime >= this.proved) {
return;
}
this.player.style.display = 'block';
if (isPaused) {
this.player.play();
this.playTime++;
this.showProved = false;
} else {
this.player.pause();
}
this.reportOnOff(isPaused ? 1 : 2);
this.isPaused = !isPaused;
},
// 切换视频
switchUrl(opts = {}) {
if (!this.isWechat) {
return;
}
this.player.style.display = 'block';
const { url = '', poster = '', proved = 0, history = 0, enable = true } = opts;
this.loaded = false;
this.player.src = url;
this.url = url;
this.poster = poster;
this.proved = proved;
this.history = history;
this.enable = enable;
this.provedOver = false;
this.finish = false;
this.opts = opts;
this.currentTime = null;
this.duration = null;
this.showProved = proved > 0;
this.showReplay = false;
if (this.showError) this.showError = false;
},
// 切换并播放
switchUrlAndPlay(opts = {}) {
if (!this.isWechat) {
return;
}
if (!this.isPaused && !this.finish) {
this.reportOnOff(2);
}
this.reportLeave();
if (this.playTime === 0) {
this.player.play();
}
this.switchUrl(opts);
this.player.autoplay = true;
if (!this.enable) {
return;
}
const loop = () => {
if (this.loaded) {
this.player.play();
if (!this.player.paused) {
this.isPaused = false;
this.reportOnOff(1);
this.showProved = false;
}
return null;
} else {
return setTimeout(() => {
loop()
}, 100);
}
}
this.timer = loop();
},
// 重播
onReplay() {
this.player.style.display = 'block';
this.player.currentTime = 0;
this.player.play();
this.isPaused = false;
this.reportOnOff(1);
this.$emit('onReplay');
this.showReplay = false;
this.provedOver = false;
},
// 加载完毕,获取总时长和音量。问题:移动端点击播放才会触发,且不一定会获取时长
loadedMetaData() {
let duration = this.player.duration;
if (duration) {
this.totalTime = this.formatTime(duration);
this.loaded = true;
if (this.history) {
this.player.currentTime = this.history;
this.showResume = true;
setTimeout(() => {
this.showResume = false;
}, 2000);
}
}
},
// 播放中
onTimeUpdate(e) {
const { currentTime, duration } = e.target;
// 试看,且超过时间
if (this.proved && currentTime >= this.proved) {
this.provedEnd();
}
if (this.provedOver) {
return
}
if (currentTime) {
this.playTime = this.formatTime(currentTime);
}
if (currentTime && duration) {
this.setBarPosition(currentTime * 100 / duration, 'progressBar');
this.currentTime = currentTime;
this.duration = duration;
}
if (currentTime < 0.1 && !this.loaded) {
this.loadedMetaData();
}
},
// 设置进度条位置
setBarPosition(percent, target) {
this[target] = {
transform: `translateX(${percent}%)`,
'-webkit-transform': `-webkit-translateX(${percent}%)`,
'-moz-transform': `-moz-translateX(${percent}%)`,
'-ms-transform': `-ms-translateX(${percent}%)`,
}
if (target === 'progressBar' && !this.isMoveingBall) {
this.progressBall = percent
}
},
// 点击设置进度
setProgress(e) {
if (!this.url) {
return;
}
const { offsetX } = e;
const { width } = e.target.getBoundingClientRect();
this.setProgressPosition(offsetX * 100 / width);
},
// 设置进度最终位置
setProgressPosition(percent) {
const duration = this.player.duration;
// 试看,且超过时间
if (this.proved && percent >= this.proved * 100 / duration) {
return;
}
this.setBarPosition(percent, 'progressBar');
this.player.currentTime = percent * duration / 100;
if (this.isPaused) {
this.player.style.display = 'block';
this.player.play();
this.playTime++;
this.isPaused = false;
this.reportOnOff(1);
}
},
// 拖动进度条
onBallStart(e) {
this.isMoveingBall = true;
const { width } = this.$el.querySelector('.progress-inner').getBoundingClientRect();
const { pageX } = e.changedTouches[0];
this.ballPageX = pageX;
this.progressW = width;
this.oldBallPercent = this.progressBall;
},
onBallMove(e) {
const { pageX } = e.changedTouches[0];
this.progressBall = this.getNewPercent(pageX);
},
onBallEnd(e) {
this.isMoveingBall = false;
const { pageX } = e.changedTouches[0];
const newPercent = this.getNewPercent(pageX);
this.setProgressPosition(newPercent);
},
// 获取拖动百分比
getNewPercent(pageX) {
let newPercent = (pageX - this.ballPageX) * 100 / this.progressW + this.oldBallPercent;
if (newPercent <= 0) {
newPercent = 0
} else if (newPercent >= 100) {
newPercent = 100
}
return newPercent;
},
selectRate(rate) {
if (!this.url) {
return;
}
this.player.playbackRate = rate;
this.curtRate = rate;
this.showRate = false;
},
formatTime(t) {
const time = formatLeftTimeObj(t);
const h = time.h === '00' ? '' : `${time.h}:`;
return `${h}${time.f}:${time.s}`;
},
// 试看结束
provedEnd() {
this.player.pause();
this.player.currentTime = this.proved;
if (this.isAndroid) {
this.player.style.display = 'none';
}
if (!this.provedOver) {
this.$emit('onVideoEnd', { type: 2 });
this.reportOnOff(2);
}
this.finish = true;
this.showReplay = true;
this.provedOver = true;
this.isFullScreen = false;
},
// 播放结束
onEnded() {
this.$emit('onVideoEnd', { type: 1 });
this.finish = true;
if (this.isAndroid) {
this.player.style.display = 'none';
}
this.reportOnOff(2);
this.$nextTick(() => {
if (this.coverType > 0 && this.coverType !== 13) {
this.isFullScreen = false;
}
})
},
onError() {
if (this.url) {
this.showError = true
}
},
// 全屏
onFullscreen() {
this.isFullScreen = !this.isFullScreen;
},
// 上报播放、暂停,status:1开始,2暂停
reportOnOff(status = 1) {
const { chapterId, courseId, lectureId } = this.opts;
if (!chapterId || !courseId || !lectureId) {
return;
}
this.POST('/contents/files/resourceRecord', {
fileType: 1,
resourceInfo1: courseId,
resourceInfo2: chapterId,
resourceInfo3: lectureId,
resourceType: 1,
status,
systemType: 3,
timestamp: Date.now(),
})
},
// 上报离开
reportLeave() {
if (!this.currentTime || !this.duration) {
return;
}
const { chapterId, courseId, lectureId } = this.opts;
if (!chapterId || !courseId || !lectureId) {
return;
}
this.POST('/contents/joinCourse/', {
requestList: [{
chapterId,
courseId,
lectureId,
nowTime: this.currentTime,
time: this.duration,
timeStamp: Date.now(),
}],
})
},
},
}
</script>
<style lang="less" scoped>
.pica-video{
position: relative;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: #333;
overflow: hidden;
z-index: 1000;
video{
position: absolute;
left: 50%;
top: 50%;
width: 100%;
max-width: 100%;
max-height: 100%;
transform: translate3d(-50%, -50%, 0);
&::-internal-media-controls-download-button {
display: none;
}
&::-webkit-media-controls-enclosure {
overflow: hidden;
}
&::-webkit-media-controls-panel {
width: calc(100% + 30px);
}
}
.video-cover{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.control-box{
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 44px;
}
.control-bar{
position: absolute;
display: flex;
align-items: center;
justify-content: center;
left: 0;
top: 0;
width: 100%;
height: 44px;
// transform: translate3d(0, 60px, 0);
// transition: transform 0.2s;
background: linear-gradient(180deg,rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%);
}
.btn-play{
width: 44px;
height: 44px;
background-image: url('~@/images/video/play.png');
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: 24px auto;
}
.btn-pause{
background-image: url('~@/images/video/pause.png');
}
.progress-box{
position: relative;
display: flex;
align-items: center;
flex: 1;
width: 50px;
height: 44px;
margin-left: 5px;
}
.progress-inner{
position: relative;
width: 50px;
flex: 1;
height: 2px;
background: rgba(255, 255, 255, 0.4);
overflow: hidden;
}
.progress-bar{
position: absolute;
left: -100%;
top: 0;
width: 100%;
height: 100%;
background: #449284;
transform: translateX(0);
pointer-events: none;
}
.progress-ball{
position: absolute;
left: 0;
top: 50%;
width: 7px;
height: 14px;
transform: translate3d(-3px, -7px, 0);
background-color: #fff;
border-radius: 2px;
}
.time-box{
color: #fff;
font-size: 12px;
line-height: 14px;
width: 100px;
text-align: right;
}
.btn-screen{
width: 44px;
height: 44px;
background-image: url('~@/images/video/screen_full.png');
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: 20px auto;
}
.btn-screen-mini{
background-image: url('~@/images/video/screen_mini.png');
}
.rate-box{
position: relative;
color: #fff;
width: 40px;
height: 44px;
line-height: 44px;
font-size: 12px;
text-align: center;
}
.rate-list{
position: absolute;
left: 0;
bottom: 44px;
width: 100%;
background: rgba(0, 0, 0, .7);
border-radius: 2px;
}
.rate-item{
height: 30px;
line-height: 30px;
}
.rate-active{
color: #00a1d6;
}
.proved-box{
position: absolute;
color: #fff;
font-size: 12px;
left: 10px;
bottom: 44px;
height: 24px;
line-height: 24px;
padding: 0 12px;
border-radius: 12px;
background-color: rgba(0, 0, 0, .6);
span{
color: #FFA34B;
}
}
.cover-play{
background: url('~@/images/video/cover_play.png') no-repeat center center;
background-size: 50px auto;
}
.cover-error, .cover-resume{
p{
position: absolute;
left: 0;
top: 50%;
width: 100%;
color: #fff;
font-size: 15px;
text-align: center;
transform: translate3d(0, -50%, 0);
}
}
.replay-box{
position: absolute;
color: #fff;
font-size: 12px;
right: 10px;
bottom: 44px;
height: 24px;
line-height: 24px;
padding: 0 12px 0 22px;
border-radius: 12px;
background: url('~@/images/video/replay.png') no-repeat left center;
background-size: 23px auto;
background-color: rgba(0, 0, 0, .6);
}
.cover{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
}
.default-img{
position: absolute;
left: 50%;
top: 50%;
min-width: 100%;
min-height: 100%;
max-width: 100%;
max-height: 100%;
transform: translate3d(-50%, -50%, 0);
}
}
</style>
......@@ -152,6 +152,8 @@ export default {
if (this.isWechat) {
const player = document.getElementById(this.vid);
player.addEventListener("timeupdate", this.onTimeUpdate, false);
player.addEventListener("play", this.onPlay, false);
player.addEventListener("pause", this.onPause, false);
this.player = player;
}
},
......@@ -165,6 +167,15 @@ export default {
}
},
methods: {
// 被接管时,监听上报
onPlay() {
this.reportOnOff(1);
this.isPaused = false;
},
onPause() {
this.reportOnOff(2);
this.isPaused = true;
},
togglePlay() {
if (!this.logged) {
this.$store.dispatch('goLogin');
......@@ -186,7 +197,7 @@ export default {
} else {
this.player.pause();
}
this.reportOnOff(isPaused ? 1 : 2);
// this.reportOnOff(isPaused ? 1 : 2); // 使用监听发送
this.isPaused = !isPaused;
},
// 切换视频
......@@ -220,7 +231,9 @@ export default {
if (!this.isPaused && !this.finish) {
this.reportOnOff(2);
}
this.reportLeave();
if (!this.finish) {
this.reportLeave();
}
if (this.playTime === 0) {
this.player.play();
}
......@@ -384,6 +397,7 @@ export default {
if (!this.provedOver) {
this.$emit('onVideoEnd', { type: 2 });
this.reportOnOff(2);
this.reportLeave();
}
this.finish = true;
this.showReplay = true;
......@@ -398,6 +412,7 @@ export default {
this.player.style.display = 'none';
}
this.reportOnOff(2);
this.reportLeave();
this.$nextTick(() => {
if (this.coverType > 0 && this.coverType !== 13) {
this.isFullScreen = false;
......@@ -419,7 +434,7 @@ export default {
if (!chapterId || !courseId || !lectureId) {
return;
}
this.POST('/contents/files/resourceRecord', {
this.POST('contents/files/resourceRecord', {
fileType: 1,
resourceInfo1: courseId,
resourceInfo2: chapterId,
......@@ -439,7 +454,7 @@ export default {
if (!chapterId || !courseId || !lectureId) {
return;
}
this.POST('/contents/joinCourse/', {
this.POST('contents/joinCourse/', {
requestList: [{
chapterId,
courseId,
......
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册