播放器资源销毁逻辑优化

This commit is contained in:
灌糖包子 2026-05-16 23:18:28 +08:00
parent 6d2e522818
commit 4533a754c9
Signed by: sookie
GPG Key ID: 0599BECB75C1E68D

View File

@ -11,7 +11,7 @@
<div class="vinyl-grooves"></div> <div class="vinyl-grooves"></div>
<div class="disc-cover-wrapper"> <div class="disc-cover-wrapper">
<img v-if="currentMusic?.pic" ref="coverImg" :src="currentMusic.pic" class="disc-cover" crossorigin="anonymous" @load="onCoverLoad" /> <img v-if="currentMusic?.pic" ref="coverImg" :src="currentMusic.pic" class="disc-cover" crossorigin="anonymous" @load="onCoverLoad" />
<div v-else class="disc-cover"></div> <div v-else class="disc-cover disc-cover-empty"></div>
</div> </div>
<div class="disc-center-dot"></div> <div class="disc-center-dot"></div>
</div> </div>
@ -48,7 +48,7 @@
v-for="(line, index) in lrcLines" v-for="(line, index) in lrcLines"
:key="index" :key="index"
:class="{ 'current-line': index === currentLineIndex }" :class="{ 'current-line': index === currentLineIndex }"
>{{ line[1] || '&nbsp;' }}</p> >{{ line[1] || '' }}</p>
</div> </div>
<div v-if="!lrcLines.length" class="no-lyrics">暂无歌词</div> <div v-if="!lrcLines.length" class="no-lyrics">暂无歌词</div>
</div> </div>
@ -123,7 +123,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch, onMounted, onBeforeUnmount, reactive, nextTick } from 'vue' import { ref, computed, watch, onMounted, onBeforeUnmount, reactive, nextTick } from 'vue'
import { MusicPlayerItem, StatType } from '@/model/api/music' import { MusicPlayerItem, StatType } from '@/model/api/music'
import { parseLrc, getElementViewLeft, adaptingThemeColor } from './utils' import { parseLrc, adaptingThemeColor } from './utils'
import Hls from 'hls.js' import Hls from 'hls.js'
const props = defineProps<{ const props = defineProps<{
@ -142,17 +142,10 @@ const playlistVisible = ref(false)
const themeColor = ref<{ r: number; g: number; b: number } | null>(null) const themeColor = ref<{ r: number; g: number; b: number } | null>(null)
const panelBgStyle = computed(() => { const panelBgStyle = computed(() => {
if (!themeColor.value) { if (!themeColor.value) return null
return { background: 'linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)' }
}
const { r, g, b } = themeColor.value const { r, g, b } = themeColor.value
// const dr = Math.floor(r * 0.25), dg = Math.floor(g * 0.25), db = Math.floor(b * 0.25)
const darken = (v: number) => Math.floor(v * 0.3) return { backgroundColor: `rgb(${dr}, ${dg}, ${db})` }
const dr = darken(r), dg = darken(g), db = darken(b)
const dr2 = Math.floor(r * 0.15), dg2 = Math.floor(g * 0.15), db2 = Math.floor(b * 0.15)
return {
background: `linear-gradient(135deg, rgb(${dr2}, ${dg2}, ${db2}) 0%, rgb(${dr}, ${dg}, ${db}) 50%, rgb(${dr2}, ${dg2}, ${db2}) 100%)`
}
}) })
function onCoverLoad() { function onCoverLoad() {
@ -295,6 +288,27 @@ function stopVisualizer() {
} }
} }
function destroyVisualizer() {
stopVisualizer()
source?.disconnect()
analyser?.disconnect()
source = null
analyser = null
dataArray = null
visualizerConnected = false
if (audioCtx && audioCtx.state !== 'closed') {
void audioCtx.close()
}
audioCtx = null
}
function destroyHls() {
hls.value?.detachMedia()
hls.value?.destroy()
hls.value = null
}
// Audio controls // Audio controls
function play() { function play() {
const media = audioEl.value const media = audioEl.value
@ -317,18 +331,22 @@ function switchTrack(index: number) {
nextTick(() => play()) nextTick(() => play())
} }
function resolveProgressRatio(clientX: number) {
if (!progressBar.value) return 0
const rect = progressBar.value.getBoundingClientRect()
if (rect.width === 0) return 0
return Math.max(0, Math.min(1, (clientX - rect.left) / rect.width))
}
function onProgressMouseDown(e: MouseEvent) { function onProgressMouseDown(e: MouseEvent) {
if (!progressBar.value || !audioEl.value) return if (!progressBar.value || !audioEl.value) return
const barWidth = progressBar.value.clientWidth const pct = resolveProgressRatio(e.clientX)
let pct = (e.clientX - getElementViewLeft(progressBar.value)) / barWidth
pct = Math.max(0, Math.min(1, pct))
if (!isNaN(audioEl.value.duration)) { if (!isNaN(audioEl.value.duration)) {
audioEl.value.currentTime = audioEl.value.duration * pct audioEl.value.currentTime = audioEl.value.duration * pct
} }
const onMove = (ev: MouseEvent) => { const onMove = (ev: MouseEvent) => {
let p = (ev.clientX - getElementViewLeft(progressBar.value!)) / barWidth const p = resolveProgressRatio(ev.clientX)
p = Math.max(0, Math.min(1, p))
if (!isNaN(audioEl.value!.duration)) { if (!isNaN(audioEl.value!.duration)) {
audioEl.value!.currentTime = audioEl.value!.duration * p audioEl.value!.currentTime = audioEl.value!.duration * p
} }
@ -410,15 +428,20 @@ function loadSource(music: MusicPlayerItem) {
if (/\.m3u8(?=[#?]|$)/.test(src)) { if (/\.m3u8(?=[#?]|$)/.test(src)) {
if (media.canPlayType('application/x-mpegURL') || media.canPlayType('application/vnd.apple.mpegURL')) { if (media.canPlayType('application/x-mpegURL') || media.canPlayType('application/vnd.apple.mpegURL')) {
destroyHls()
media.src = src media.src = src
} else if (Hls.isSupported()) { } else if (Hls.isSupported()) {
if (!hls.value) hls.value = new Hls() destroyHls()
hls.value.loadSource(src) const hlsInstance = new Hls()
hls.value.attachMedia(media) hlsInstance.loadSource(src)
hlsInstance.attachMedia(media)
hls.value = hlsInstance
} else { } else {
destroyHls()
media.src = src media.src = src
} }
} else { } else {
destroyHls()
media.src = src media.src = src
} }
} }
@ -494,9 +517,9 @@ onMounted(() => {
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
stopVisualizer() destroyVisualizer()
resizeObserver?.disconnect() resizeObserver?.disconnect()
hls.value?.destroy() destroyHls()
}) })
defineExpose({ defineExpose({
@ -524,7 +547,6 @@ defineExpose({
@color-text-secondary: #aaa; @color-text-secondary: #aaa;
@color-text-muted: #888; @color-text-muted: #888;
@color-text-dim: #555; @color-text-dim: #555;
@color-bg-default: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
@color-vinyl-dark: #111; @color-vinyl-dark: #111;
@color-vinyl-groove: #222; @color-vinyl-groove: #222;
@color-vinyl-groove-alt: #1a1a1a; @color-vinyl-groove-alt: #1a1a1a;
@ -575,11 +597,9 @@ defineExpose({
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: @color-bg-default;
color: @color-text; color: @color-text;
padding: 24px 30px; padding: 24px 30px;
overflow: hidden; overflow: hidden;
transition: background 1.5s ease;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5); text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
} }
@ -696,7 +716,7 @@ defineExpose({
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
&-empty { &.disc-cover-empty {
background: radial-gradient(circle, #2a2a2a 30%, @color-vinyl-dark 70%); background: radial-gradient(circle, #2a2a2a 30%, @color-vinyl-dark 70%);
} }
} }