From 8d800eee623031b6d2f6d3acc4bd46ff5b547ae3 Mon Sep 17 00:00:00 2001 From: whm <973418690@qq.com> Date: Thu, 26 Mar 2026 15:17:30 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9B=B4=E6=92=AD=EF=BC=9A=E9=9F=B3=E9=87=8F?= =?UTF-8?q?=E4=B8=8E=E5=85=A8=E5=B1=8F=E5=8F=A0=E5=9C=A8=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E5=BA=95=E9=83=A8=EF=BC=8C=E5=85=A8=E5=B1=8F=E5=86=85=E5=8F=AF?= =?UTF-8?q?=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- web/src/views/LiveRoom.vue | 171 +++++++++++++++++++++++++++++-------- 1 file changed, 137 insertions(+), 34 deletions(-) diff --git a/web/src/views/LiveRoom.vue b/web/src/views/LiveRoom.vue index 3f12509..af5049c 100644 --- a/web/src/views/LiveRoom.vue +++ b/web/src/views/LiveRoom.vue @@ -26,6 +26,7 @@ class="live-room-video live-room-video--watch live-room-video--contain" playsinline autoplay + @volumechange="syncVolumeUIFromVideo" > - @@ -102,6 +121,9 @@ import { getSiteDmToken, getSiteDmUsername, clearSiteDmSession } from '../utils/ const watchVideoRef = ref(null) const liveStageRef = ref(null) const stageFullscreen = ref(false) +/** 与 video.volume / muted 同步(浏览器自动播放可能先静音) */ +const volumePercent = ref(100) +const displayMuted = ref(false) const rawLiveUrl = ref('') const pageTitle = ref('视频直播') const watchStatus = ref('正在检测本站直播…') @@ -180,11 +202,37 @@ function goLiveRoom() { window.location.href = target } -function unmuteAndPlay() { +function syncVolumeUIFromVideo() { const v = watchVideoRef.value if (!v) return - v.muted = false + displayMuted.value = v.muted + volumePercent.value = Math.round(Math.min(1, Math.max(0, v.volume)) * 100) +} + +function onVolumeRangeInput(e) { + volumePercent.value = Number(e.target.value) + const v = watchVideoRef.value + if (!v) return + v.volume = volumePercent.value / 100 + v.muted = volumePercent.value === 0 v.play().catch(() => {}) + displayMuted.value = v.muted +} + +function toggleMute() { + const v = watchVideoRef.value + if (!v) return + if (v.muted) { + v.muted = false + if (v.volume === 0) { + v.volume = 1 + volumePercent.value = 100 + } + } else { + v.muted = true + } + v.play().catch(() => {}) + syncVolumeUIFromVideo() } function syncStageFullscreenFlag() { @@ -384,6 +432,8 @@ onMounted(async () => { watchStatus.value = s } }) + await nextTick() + syncVolumeUIFromVideo() }) onUnmounted(() => { @@ -529,11 +579,6 @@ onUnmounted(() => { 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; @@ -547,6 +592,86 @@ onUnmounted(() => { .live-stage:-webkit-full-screen .live-dm-hint { max-width: none; } +.live-video-overlay { + position: absolute; + left: 0; + right: 0; + bottom: 0; + z-index: 6; + padding: 20px 10px 10px; + border-radius: 0 0 14px 14px; + background: linear-gradient(to top, rgba(0, 0, 0, 0.82) 0%, rgba(0, 0, 0, 0.45) 55%, transparent 100%); + pointer-events: auto; +} +.live-video-overlay-inner { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + gap: 8px 12px; +} +.live-overlay-btn { + padding: 6px 14px; + border-radius: 8px; + border: 1px solid rgba(0, 212, 255, 0.5); + background: rgba(0, 0, 0, 0.45); + color: #00d4ff; + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: background 0.2s, border-color 0.2s; +} +.live-overlay-btn:hover { + background: rgba(0, 212, 255, 0.18); + border-color: rgba(0, 212, 255, 0.85); +} +.live-vol-wrap { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + flex: 1; + justify-content: flex-end; + max-width: min(280px, 58vw); +} +.live-vol-label { + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + flex-shrink: 0; +} +.live-vol-value { + font-size: 12px; + color: rgba(255, 255, 255, 0.65); + width: 2.5em; + text-align: right; + flex-shrink: 0; +} +.live-vol-range { + flex: 1; + min-width: 72px; + height: 6px; + border-radius: 3px; + appearance: none; + background: rgba(255, 255, 255, 0.2); + accent-color: #00d4ff; +} +.live-vol-range::-webkit-slider-thumb { + appearance: none; + width: 14px; + height: 14px; + border-radius: 50%; + background: #00d4ff; + cursor: pointer; + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.35); +} +.live-vol-range::-moz-range-thumb { + width: 14px; + height: 14px; + border-radius: 50%; + background: #00d4ff; + cursor: pointer; + border: none; +} .live-dm-layer { pointer-events: none; position: absolute; @@ -674,28 +799,6 @@ onUnmounted(() => { color: #ffb86c; text-align: left; } -.live-video-toolbar { - display: flex; - flex-wrap: wrap; - gap: 10px; - justify-content: center; - margin-top: 12px; -} -.live-video-toolbtn { - padding: 8px 18px; - border-radius: 10px; - border: 1px solid rgba(0, 212, 255, 0.45); - background: rgba(0, 212, 255, 0.12); - color: #00d4ff; - font-size: 14px; - font-weight: 600; - cursor: pointer; - transition: background 0.2s, border-color 0.2s; -} -.live-video-toolbtn:hover { - background: rgba(0, 212, 255, 0.22); - border-color: rgba(0, 212, 255, 0.75); -} .live-room-actions { margin-top: 8px; }