修改直播结束后的效果

master
于肖磊 2025-11-28 17:20:46 +08:00
parent f0d125e6e0
commit 4944cd41d3
7 changed files with 396 additions and 392 deletions

View File

@ -1,147 +1,137 @@
<!-- eslint-disable -->
<template>
<view class="player-wrapper" :id="videoWrapperId" :randomNum="randomNum"
:change:randomNum="hlsVideoPlayer.randomNumChange" :viewportProps="viewportProps"
:change:viewportProps="hlsVideoPlayer.viewportChange" :videoSrc="videoSrc"
:change:videoSrc="hlsVideoPlayer.initVideoPlayer" :command="eventCommand"
:change:command="hlsVideoPlayer.triggerCommand" :func="renderFunc" :change:func="hlsVideoPlayer.triggerFunc" />
<view class="player-wrapper" :id="videoWrapperId" :randomNum="randomNum" :change:randomNum="hlsVideoPlayer.randomNumChange" :viewportProps="viewportProps" :change:viewportProps="hlsVideoPlayer.viewportChange" :videoSrc="videoSrc" :change:videoSrc="hlsVideoPlayer.initVideoPlayer" :command="eventCommand" :change:command="hlsVideoPlayer.triggerCommand" :func="renderFunc" :change:func="hlsVideoPlayer.triggerFunc" />
</template>
<script>
export default {
props: {
src: {
type: String,
default: ''
},
autoplay: {
type: Boolean,
default: false
},
controls: {
type: Boolean,
default: false
},
objectFit: {
type: String,
default: 'cover'
},
muted: {
type: Boolean,
default: false
},
playbackRate: {
type: Number,
default: 1
},
poster: {
type: String,
default: ''
export default {
props: {
src: {
type: String,
default: ''
},
autoplay: {
type: Boolean,
default: false
},
controls: {
type: Boolean,
default: false
},
objectFit: {
type: String,
default: 'cover'
},
muted: {
type: Boolean,
default: false
},
playbackRate: {
type: Number,
default: 1
},
poster: {
type: String,
default: ''
}
},
data() {
return {
randomNum: Math.floor(Math.random() * 100000000),
videoSrc: '',
// video
eventCommand: null,
// renderjs
renderFunc: {
name: null,
params: null
},
//
currentTime: 0,
duration: 0,
playing: false
}
},
watch: {
//
src: {
handler(val) {
if (!val) return
setTimeout(() => {
this.videoSrc = val
}, 0)
},
immediate: true
}
},
computed: {
videoWrapperId() {
return `video-wrapper-${this.randomNum}`
},
// renderjs
viewportProps() {
return {
autoplay: this.autoplay,
muted: this.muted,
controls: this.controls,
objectFit: this.objectFit,
poster: this.poster,
playbackRate: this.playbackRate
}
}
},
//
methods: {
//
eventEmit({ event, data }) {
this.$emit(event, data)
},
// viewdata
setViewData({ key, value }) {
key && this.$set(this, key, value)
},
//
resetEventCommand() {
this.eventCommand = null
},
//
play() {
this.eventCommand = 'play'
},
//
pause() {
this.eventCommand = 'pause'
},
//
resetFunc() {
this.renderFunc = {
name: null,
params: null
}
},
// -
remove(params) {
this.renderFunc = {
name: 'removeHandler',
params
}
},
// -
fullScreen(params) {
this.renderFunc = {
name: 'fullScreenHandler',
params
}
},
//
toSeek(time) {
this.renderFunc = {
name: 'toSeekHandler',
params: time
}
}
}
}
},
data() {
return {
randomNum: Math.floor(Math.random() * 100000000),
videoSrc: '',
// video
eventCommand: null,
// renderjs
renderFunc: {
name: null,
params: null
},
//
currentTime: 0,
duration: 0,
playing: false
}
},
watch: {
//
src: {
handler(val) {
if (!val) return
setTimeout(() => {
this.videoSrc = val
}, 0)
},
immediate: true
}
},
computed: {
videoWrapperId() {
return `video-wrapper-${this.randomNum}`
},
// renderjs
viewportProps() {
return {
autoplay: this.autoplay,
muted: this.muted,
controls: this.controls,
objectFit: this.objectFit,
poster: this.poster,
playbackRate: this.playbackRate
}
}
},
//
methods: {
//
eventEmit({
event,
data
}) {
this.$emit(event, data)
},
// viewdata
setViewData({
key,
value
}) {
key && this.$set(this, key, value)
},
//
resetEventCommand() {
this.eventCommand = null
},
//
play() {
this.eventCommand = 'play'
},
//
pause() {
this.eventCommand = 'pause'
},
//
resetFunc() {
this.renderFunc = {
name: null,
params: null
}
},
// -
remove(params) {
this.renderFunc = {
name: 'removeHandler',
params
}
},
// -
fullScreen(params) {
this.renderFunc = {
name: 'fullScreenHandler',
params
}
},
//
toSeek(time) {
this.renderFunc = {
name: 'toSeekHandler',
params: time
}
}
}
}
</script>
<script module="hlsVideoPlayer" lang="renderjs">
@ -180,14 +170,7 @@ export default {
this.videoEl = videoEl
//
this.listenVideoEvent()
const {
autoplay,
muted,
controls,
playbackRate,
objectFit,
poster
} = this.renderProps
const { autoplay, muted, controls, playbackRate, objectFit, poster } = this.renderProps
videoEl.autoplay = autoplay
videoEl.controls = controls
videoEl.muted = muted
@ -230,244 +213,236 @@ export default {
},
//
initHlsPlayer(src) {
if (hlsjs.isSupported()) {
this.hlsPlayer = new hlsjs()
this.hlsPlayer.loadSource(src)
this.hlsPlayer.attachMedia(this.videoEl)
this.hlsPlayer.on(hlsjs.Events.MANIFEST_PARSED, () => {
// hls
this.$ownerInstance.callMethod('eventEmit', {
event: 'hlsManifestParsed'
})
if (hlsjs.isSupported()) {
this.hlsPlayer = new hlsjs()
this.hlsPlayer.loadSource(src)
this.hlsPlayer.attachMedia(this.videoEl)
this.hlsPlayer.on(hlsjs.Events.MANIFEST_PARSED, () => {
// hls
this.$ownerInstance.callMethod('eventEmit', {
event: 'hlsManifestParsed'
})
this.hlsPlayer.on(hlsjs.Events.ERROR, (event, data) => {
})
this.hlsPlayer.on(hlsjs.Events.ERROR, (event, data) => {
console.error('HLS Error:', data)
// HLS
if (this.videoEl && data.fatal) {
this.videoEl.src = src
} else {
// hls
console.error('HLS Error:', event, data)
this.$ownerInstance.callMethod('eventEmit', {
event: 'hlsError',
data
})
// HLS
if (this.videoEl && data.fatal) {
this.videoEl.src = src
}
})
} else {
// hls.js使
this.videoEl.src = src
}
},
//
listenVideoEvent() {
//
const playHandler = () => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'play'
})
this.$ownerInstance.callMethod('setViewData', {
key: 'playing',
value: true
})
}
this.videoEl.removeEventListener('play', playHandler)
this.videoEl.addEventListener('play', playHandler)
//
const pauseHandler = () => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'pause'
})
this.$ownerInstance.callMethod('setViewData', {
key: 'playing',
value: false
})
}
this.videoEl.removeEventListener('pause', pauseHandler)
this.videoEl.addEventListener('pause', pauseHandler)
//
const endedHandler = () => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'ended'
})
this.$ownerInstance.callMethod('resetEventCommand')
}
this.videoEl.removeEventListener('ended', endedHandler)
this.videoEl.addEventListener('ended', endedHandler)
//
const canPlayHandler = () => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'canplay'
})
}
this.videoEl.removeEventListener('canplay', canPlayHandler)
this.videoEl.addEventListener('canplay', canPlayHandler)
//
const errorHandler = (e) => {
console.error('Video element error:', e)
this.$ownerInstance.callMethod('eventEmit', {
event: 'error',
data: e
})
}
this.videoEl.removeEventListener('error', errorHandler)
this.videoEl.addEventListener('error', errorHandler)
// loadedmetadata
const loadedMetadataHandler = () => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'loadedmetadata'
})
//
const duration = this.videoEl.duration
this.$ownerInstance.callMethod('eventEmit', {
event: 'durationchange',
data: duration
})
this.$ownerInstance.callMethod('setViewData', {
key: 'duration',
value: duration
})
}
this.videoEl.removeEventListener('loadedmetadata', loadedMetadataHandler)
this.videoEl.addEventListener('loadedmetadata', loadedMetadataHandler)
//
const timeupdateHandler = (e) => {
const currentTime = e.target.currentTime
this.$ownerInstance.callMethod('eventEmit', {
event: 'timeupdate',
data: currentTime
})
this.$ownerInstance.callMethod('setViewData', {
key: 'currentTime',
value: currentTime
})
}
this.videoEl.removeEventListener('timeupdate', timeupdateHandler)
this.videoEl.addEventListener('timeupdate', timeupdateHandler)
//
const ratechangeHandler = (e) => {
const playbackRate = e.target.playbackRate
this.$ownerInstance.callMethod('eventEmit', {
event: 'ratechange',
data: playbackRate
})
}
this.videoEl.removeEventListener('ratechange', ratechangeHandler)
this.videoEl.addEventListener('ratechange', ratechangeHandler)
//
if (this.isApple()) {
const webkitbeginfullscreenHandler = () => {
const presentationMode = this.videoEl.webkitPresentationMode
let isFullScreen = null
if (presentationMode === 'fullscreen') {
isFullScreen = true
} else {
isFullScreen = false
}
this.$ownerInstance.callMethod('eventEmit', {
event: 'fullscreenchange',
data: isFullScreen
})
}
this.videoEl.removeEventListener('webkitpresentationmodechanged', webkitbeginfullscreenHandler)
this.videoEl.addEventListener('webkitpresentationmodechanged', webkitbeginfullscreenHandler)
} else {
const fullscreenchangeHandler = () => {
let isFullScreen = null
if (document.fullscreenElement) {
isFullScreen = true
} else {
isFullScreen = false
}
this.$ownerInstance.callMethod('eventEmit', {
event: 'fullscreenchange',
data: isFullScreen
})
}
document.removeEventListener('fullscreenchange', fullscreenchangeHandler)
document.addEventListener('fullscreenchange', fullscreenchangeHandler)
}
},
//
destroyPlayer() {
if (this.hlsPlayer) {
this.hlsPlayer.destroy()
this.hlsPlayer = null
}
if (this.videoEl) {
this.videoEl.pause()
this.videoEl.src = ''
//
this.videoEl.removeAttribute('src')
this.videoEl.load()
this.videoEl = null
}
},
triggerCommand(eventType) {
if (eventType) {
this.$ownerInstance.callMethod('resetEventCommand')
if (this.videoEl) {
try {
this.videoEl[eventType]()
} catch (e) {
console.error(`Error executing ${eventType}:`, e)
}
}
}
},
triggerFunc(func) {
const {
name,
params
} = func || {}
if (name) {
if (typeof this[name] === 'function') {
this[name](params)
}
this.$ownerInstance.callMethod('resetFunc')
}
},
removeHandler() {
this.destroyPlayer()
this.$ownerInstance.callMethod('setViewData', {
key: 'videoSrc',
value: ''
})
},
fullScreenHandler() {
if (this.videoEl) {
if (this.isApple()) {
if (this.videoEl.webkitEnterFullscreen) {
this.videoEl.webkitEnterFullscreen()
}
} else {
// hls.js使
this.videoEl.src = src
}
},
//
listenVideoEvent() {
//
const playHandler = () => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'play'
})
this.$ownerInstance.callMethod('setViewData', {
key: 'playing',
value: true
})
}
this.videoEl.removeEventListener('play', playHandler)
this.videoEl.addEventListener('play', playHandler)
//
const pauseHandler = () => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'pause'
})
this.$ownerInstance.callMethod('setViewData', {
key: 'playing',
value: false
})
}
this.videoEl.removeEventListener('pause', pauseHandler)
this.videoEl.addEventListener('pause', pauseHandler)
//
const endedHandler = () => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'ended'
})
this.$ownerInstance.callMethod('resetEventCommand')
}
this.videoEl.removeEventListener('ended', endedHandler)
this.videoEl.addEventListener('ended', endedHandler)
//
const canPlayHandler = () => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'canplay'
})
}
this.videoEl.removeEventListener('canplay', canPlayHandler)
this.videoEl.addEventListener('canplay', canPlayHandler)
//
const errorHandler = (e) => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'error',
data: e
})
}
this.videoEl.removeEventListener('error', errorHandler)
this.videoEl.addEventListener('error', errorHandler)
// loadedmetadata
const loadedMetadataHandler = () => {
this.$ownerInstance.callMethod('eventEmit', {
event: 'loadedmetadata'
})
//
const duration = this.videoEl.duration
this.$ownerInstance.callMethod('eventEmit', {
event: 'durationchange',
data: duration
})
this.$ownerInstance.callMethod('setViewData', {
key: 'duration',
value: duration
})
}
this.videoEl.removeEventListener('loadedmetadata', loadedMetadataHandler)
this.videoEl.addEventListener('loadedmetadata', loadedMetadataHandler)
//
const timeupdateHandler = (e) => {
const currentTime = e.target.currentTime
this.$ownerInstance.callMethod('eventEmit', {
event: 'timeupdate',
data: currentTime
})
this.$ownerInstance.callMethod('setViewData', {
key: 'currentTime',
value: currentTime
})
}
this.videoEl.removeEventListener('timeupdate', timeupdateHandler)
this.videoEl.addEventListener('timeupdate', timeupdateHandler)
//
const ratechangeHandler = (e) => {
const playbackRate = e.target.playbackRate
this.$ownerInstance.callMethod('eventEmit', {
event: 'ratechange',
data: playbackRate
})
}
this.videoEl.removeEventListener('ratechange', ratechangeHandler)
this.videoEl.addEventListener('ratechange', ratechangeHandler)
//
if (this.isApple()) {
const webkitbeginfullscreenHandler = () => {
const presentationMode = this.videoEl.webkitPresentationMode
let isFullScreen = null
if (presentationMode === 'fullscreen') {
isFullScreen = true
} else {
if (this.videoEl.requestFullscreen) {
this.videoEl.requestFullscreen()
}
isFullScreen = false
}
this.$ownerInstance.callMethod('eventEmit', {
event: 'fullscreenchange',
data: isFullScreen
})
}
this.videoEl.removeEventListener('webkitpresentationmodechanged', webkitbeginfullscreenHandler)
this.videoEl.addEventListener('webkitpresentationmodechanged', webkitbeginfullscreenHandler)
} else {
const fullscreenchangeHandler = () => {
let isFullScreen = null
if (document.fullscreenElement) {
isFullScreen = true
} else {
isFullScreen = false
}
this.$ownerInstance.callMethod('eventEmit', {
event: 'fullscreenchange',
data: isFullScreen
})
}
document.removeEventListener('fullscreenchange', fullscreenchangeHandler)
document.addEventListener('fullscreenchange', fullscreenchangeHandler)
}
},
//
destroyPlayer() {
if (this.hlsPlayer) {
this.hlsPlayer.destroy()
this.hlsPlayer = null
}
if (this.videoEl) {
this.videoEl.pause()
this.videoEl.src = ''
//
this.videoEl.removeAttribute('src')
this.videoEl.load()
this.videoEl = null
}
},
triggerCommand(eventType) {
if (eventType) {
this.$ownerInstance.callMethod('resetEventCommand')
if (this.videoEl) {
try {
this.videoEl[eventType]()
} catch (e) {
console.error(`Error executing ${eventType}:`, e)
}
}
},
toSeekHandler(time) {
if (this.videoEl) {
this.videoEl.currentTime = time
}
},
viewportChange(props) {
this.renderProps = props
const {
autoplay,
muted,
controls,
playbackRate
} = props
if (this.videoEl) {
this.videoEl.autoplay = autoplay
this.videoEl.controls = controls
this.videoEl.muted = muted
this.videoEl.playbackRate = playbackRate
}
},
randomNumChange(val) {
this.num = val
}
},
triggerFunc(func) {
const { name, params } = func || {}
if (name) {
if (typeof this[name] === 'function') {
this[name](params)
}
this.$ownerInstance.callMethod('resetFunc')
}
},
removeHandler() {
this.destroyPlayer()
this.$ownerInstance.callMethod('setViewData', {
key: 'videoSrc',
value: ''
})
},
fullScreenHandler() {
if (this.videoEl) {
if (this.isApple()) {
if (this.videoEl.webkitEnterFullscreen) {
this.videoEl.webkitEnterFullscreen()
}
} else {
if (this.videoEl.requestFullscreen) {
this.videoEl.requestFullscreen()
}
}
}
},
toSeekHandler(time) {
if (this.videoEl) {
this.videoEl.currentTime = time
}
},
viewportChange(props) {
this.renderProps = props
const { autoplay, muted, controls, playbackRate } = props
if (this.videoEl) {
this.videoEl.autoplay = autoplay
this.videoEl.controls = controls
this.videoEl.muted = muted
this.videoEl.playbackRate = playbackRate
}
},
randomNumChange(val) {
this.num = val
}
},
//
beforeDestroy() {
@ -476,10 +451,10 @@ export default {
}
</script>
<style scoped>
.player-wrapper {
overflow: hidden;
height: 100%;
padding: 0;
}
<style lang="scss" scoped>
.player-wrapper {
overflow: hidden;
height: 100%;
padding: 0;
}
</style>

View File

@ -88,7 +88,7 @@
}
</script>
<style lang="scss">
<style lang="scss" scoped>
/* #ifndef APP-NVUE */
@import url('@/static/icon/iconfont.css');
/* #ifndef MP-WEIXIN */

View File

@ -212,7 +212,7 @@
}
</script>
<style lang="scss">
<style lang="scss" scoped>
.a-img {
position: fixed;
}

View File

@ -616,7 +616,7 @@
},
};
</script>
<style lang="scss">
<style lang="scss" scoped>
.uni-popup {
position: fixed;
/* #ifndef APP-NVUE */

View File

@ -1,17 +1,18 @@
<template>
<!-- #ifdef H5 -->
<h5-hls-video :src="src" autoplay class="video-size" :muted="true"></h5-hls-video>
<h5-hls-video :src="src" autoplay class="video-size" :muted="true" @hlsError="error" @ended="ended"></h5-hls-video>
<!-- #endif -->
<!-- #ifdef MP -->
<live-player :src="src" autoplay :muted="true" class="video-size" @statechange="statechange" @error="error" />
<!-- #endif -->
<!-- #ifdef APP -->
<video :src="src" autoplay :is-video="true" :controls="false" muted object-fit="fill" :style="{width: windowWidth + 'px', height: windowHeight + 'px'}" @error="error" @ended="ended"></video>
<video :src="src" autoplay :is-video="true" :controls="false" muted object-fit="contain" :style="{width: windowWidth + 'px', height: windowHeight + 'px'}" @error="error" @ended="ended"></video>
<!-- #endif -->
</template>
<script>
import H5HlsVideo from '@/pages/plugins/live/pull/components/h5-hls-video/h5-hls-video.vue';
import { isEmpty } from '@/common/js/common/common.js';
export default {
components: {
H5HlsVideo
@ -41,17 +42,24 @@
console.log(e.detail.code);
},
error(e) {
console.log(e.detail.errMsg, 'error');
// #ifdef H5
// ,
if (e.type != 'otherError' || e.details != 'internalException') {
this.$emit('ended');
}
// #endif
console.log(e, 'error');
},
// video app使
ended() {
console.log('ended');
this.$emit('ended');
}
},
}
</script>
<style scoped>
<style lang="scss" scoped>
.video-size {
width: 100vw;
height: 100vh;

View File

@ -1,10 +1,12 @@
<template>
<view :class="theme_view + ' bg-0 flex-row'" :style="'width:' + windowWidth + 'px;height:' + windowHeight + 'px;'">
<view class="flex-1">
<live-video src="http://live-pull-all.shopxo.vip/68f764013572f9240ca7ce6c/shopxo122.m3u8"></live-video>
<live-video src="http://live-pull-all.shopxo.vip/68f764013572f9240ca7ce6c/shopxo122.m3u8" @ended="ended"></live-video>
</view>
<view class="live-content" :style="'width:' + windowWidth + 'px;height:' + windowHeight + 'px;'">
<live-content></live-content>
<view class="live-ended flex-row align-c jc-c" :style="'width:' + windowWidth + 'px;height:' + windowHeight + 'px;'">
<text class="live-ended-text">直播已结束</text>
</view>
</view>
</view>
</template>
@ -27,7 +29,7 @@
}
}
</script>
<style scoped>
<style lang="scss" scoped>
.live-content {
position: absolute;
top: 0;
@ -36,4 +38,15 @@
width: 100%;
height: 100%;
}
.live-ended {
width: 100vw;
height: 100vh;
// 添加渐变背景色
background-image: linear-gradient(to bottom, rgba(18, 12, 39, 0.85), rgba(52, 27, 43, 0.7), rgba(92, 39, 41, 0.6), rgba(132, 51, 39, 0.7), rgba(18, 12, 39, 0.85));
.live-ended-text {
color: #fff;
font-size: 16px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
}
</style>

View File

@ -8,8 +8,8 @@
<live-content></live-content>
</template>
<template v-else>
<view v-if="is_live_ended" class="live-ended flex-row align-c jc-c" :style="'width:' + windowWidth + 'px;height:' + windowHeight + 'px;'">
<text>直播已结束</text>
<view class="live-ended flex-row align-c jc-c">
<text class="live-ended-text">直播已结束</text>
</view>
</template>
</view>
@ -34,7 +34,7 @@
},
}
</script>
<style scoped>
<style lang="scss" scoped>
.live-content {
position: absolute;
top: 0;
@ -44,6 +44,14 @@
height: 100%;
}
.live-ended {
width: 100vw;
height: 100vh;
//
background-image: linear-gradient(to bottom, rgba(18, 12, 39, 0.85), rgba(52, 27, 43, 0.7), rgba(92, 39, 41, 0.6), rgba(132, 51, 39, 0.7), rgba(18, 12, 39, 0.85));
.live-ended-text {
color: rgba(255, 255, 255, 0.95);
font-size: 16px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
}
</style>