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