播放器资源销毁逻辑优化

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