播放器资源销毁逻辑优化
This commit is contained in:
parent
6d2e522818
commit
4533a754c9
@ -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] || ' ' }}</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%);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user