diff --git a/web/src/views/LiveRoom.vue b/web/src/views/LiveRoom.vue
index 872d36e..3f12509 100644
--- a/web/src/views/LiveRoom.vue
+++ b/web/src/views/LiveRoom.vue
@@ -18,53 +18,61 @@
{{ watchStatus }}
-
-
-
-
-
{{ d.from }}:{{ d.text }}
+
+
-
-
-
+
-
-
- 已登录:{{ dmDisplayName }}
-
-
-
- 登录发弹幕
- ·
- 注册
-
-
-
-
-
-
-
{{ dmHint }}
@@ -92,6 +100,8 @@ import { startViewing, liveDanmakuWsURL } from '../utils/liveWebRTC'
import { getSiteDmToken, getSiteDmUsername, clearSiteDmSession } from '../utils/siteUserAuth'
const watchVideoRef = ref(null)
+const liveStageRef = ref(null)
+const stageFullscreen = ref(false)
const rawLiveUrl = ref('')
const pageTitle = ref('视频直播')
const watchStatus = ref('正在检测本站直播…')
@@ -177,20 +187,55 @@ function unmuteAndPlay() {
v.play().catch(() => {})
}
-function toggleVideoFullscreen() {
+function syncStageFullscreenFlag() {
+ const d = document
+ const el = liveStageRef.value
+ if (!el) {
+ stageFullscreen.value = false
+ return
+ }
+ stageFullscreen.value =
+ d.fullscreenElement === el || d.webkitFullscreenElement === el
+}
+
+/** 整页舞台全屏:含画面 + 底部登录与发弹幕(非仅 video,避免全屏后看不到输入框) */
+function toggleStageFullscreen() {
+ const stage = liveStageRef.value
const v = watchVideoRef.value
- if (!v) return
+ if (!stage) return
const doc = document
- if (doc.fullscreenElement || doc.webkitFullscreenElement) {
+ const fsEl = doc.fullscreenElement || doc.webkitFullscreenElement
+ if (fsEl) {
doc.exitFullscreen?.() || doc.webkitExitFullscreen?.()
return
}
- if (typeof v.webkitEnterFullscreen === 'function') {
- v.webkitEnterFullscreen()
+ if (typeof stage.requestFullscreen === 'function') {
+ Promise.resolve(stage.requestFullscreen())
+ .then(() => syncStageFullscreenFlag())
+ .catch(() => {
+ if (typeof stage.webkitRequestFullscreen === 'function') {
+ try {
+ stage.webkitRequestFullscreen()
+ } catch (_) {
+ if (v && typeof v.webkitEnterFullscreen === 'function') v.webkitEnterFullscreen()
+ }
+ } else if (v && typeof v.webkitEnterFullscreen === 'function') {
+ v.webkitEnterFullscreen()
+ }
+ })
return
}
- const el = v
- el.requestFullscreen?.() || el.webkitRequestFullscreen?.()
+ if (typeof stage.webkitRequestFullscreen === 'function') {
+ try {
+ stage.webkitRequestFullscreen()
+ } catch (_) {
+ if (v && typeof v.webkitEnterFullscreen === 'function') v.webkitEnterFullscreen()
+ }
+ return
+ }
+ if (v && typeof v.webkitEnterFullscreen === 'function') {
+ v.webkitEnterFullscreen()
+ }
}
function pushDmLine(text, fromRaw) {
@@ -327,6 +372,8 @@ function sendDm() {
onMounted(async () => {
dmIntentionalClose = false
+ document.addEventListener('fullscreenchange', syncStageFullscreenFlag)
+ document.addEventListener('webkitfullscreenchange', syncStageFullscreenFlag)
loadCaptureQualityPref()
loadHomepage()
await nextTick()
@@ -340,6 +387,8 @@ onMounted(async () => {
})
onUnmounted(() => {
+ document.removeEventListener('fullscreenchange', syncStageFullscreenFlag)
+ document.removeEventListener('webkitfullscreenchange', syncStageFullscreenFlag)
dmIntentionalClose = true
dmSendQueue.length = 0
if (dmReconnectTimer) {
@@ -432,11 +481,72 @@ onUnmounted(() => {
margin: 0 0 14px;
min-height: 1.4em;
}
+.live-stage {
+ max-width: 480px;
+ margin: 0 auto;
+}
.live-video-wrap {
position: relative;
max-width: 480px;
margin: 0 auto;
}
+.live-stage:fullscreen,
+.live-stage:-webkit-full-screen {
+ max-width: none;
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 10px 14px calc(12px + env(safe-area-inset-bottom, 0px));
+ box-sizing: border-box;
+ background: #0a0a12;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+.live-stage:fullscreen .live-stage-media,
+.live-stage:-webkit-full-screen .live-stage-media {
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+}
+.live-stage:fullscreen .live-video-wrap,
+.live-stage:-webkit-full-screen .live-video-wrap {
+ flex: 1;
+ min-height: 0;
+ max-width: none;
+ width: 100%;
+ margin: 0;
+ display: flex;
+ flex-direction: column;
+}
+.live-stage:fullscreen .live-room-video.live-room-video--contain,
+.live-stage:-webkit-full-screen .live-room-video.live-room-video--contain {
+ flex: 1;
+ min-height: 0;
+ width: 100%;
+ max-height: none;
+ aspect-ratio: unset;
+ object-fit: contain;
+}
+.live-stage:fullscreen .live-video-toolbar,
+.live-stage:-webkit-full-screen .live-video-toolbar {
+ flex-shrink: 0;
+ margin-top: 8px;
+}
+.live-stage:fullscreen .live-stage-footer,
+.live-stage:-webkit-full-screen .live-stage-footer {
+ flex-shrink: 0;
+ padding-top: 6px;
+}
+.live-stage:fullscreen .live-dm-auth-row,
+.live-stage:fullscreen .live-dm-bar,
+.live-stage:fullscreen .live-dm-hint,
+.live-stage:-webkit-full-screen .live-dm-auth-row,
+.live-stage:-webkit-full-screen .live-dm-bar,
+.live-stage:-webkit-full-screen .live-dm-hint {
+ max-width: none;
+}
.live-dm-layer {
pointer-events: none;
position: absolute;