|
|
import { ref, onMounted, onUnmounted, nextTick } from 'vue' |
|
|
import { usePlayerStore } from '@/stores/player' |
|
|
import { imageCacheManager } from '@/utils/imageCache' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const useSongCoverLoader = () => { |
|
|
const playerStore = usePlayerStore() |
|
|
const observer = ref(null) |
|
|
const observedImages = new Set() |
|
|
const loadQueue = [] |
|
|
const isProcessingQueue = ref(false) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const processLoadQueue = async () => { |
|
|
if (isProcessingQueue.value || loadQueue.length === 0) return |
|
|
|
|
|
isProcessingQueue.value = true |
|
|
|
|
|
while (loadQueue.length > 0) { |
|
|
const { img, song } = loadQueue.shift() |
|
|
|
|
|
|
|
|
if (img.parentNode) { |
|
|
await loadCoverForElement(img, song) |
|
|
} |
|
|
|
|
|
|
|
|
if (loadQueue.length > 0) { |
|
|
await new Promise(resolve => setTimeout(resolve, 200)) |
|
|
} |
|
|
} |
|
|
|
|
|
isProcessingQueue.value = false |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getDefaultCover = () => { |
|
|
|
|
|
return 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleImageError = (event) => { |
|
|
|
|
|
|
|
|
event.target.dispatchEvent(new CustomEvent('cover-load-failed')) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const initLazyLoading = () => { |
|
|
if (!window.IntersectionObserver) { |
|
|
console.warn('浏览器不支持 IntersectionObserver,使用立即加载') |
|
|
return false |
|
|
} |
|
|
|
|
|
observer.value = new IntersectionObserver( |
|
|
(entries) => { |
|
|
entries.forEach(entry => { |
|
|
if (entry.isIntersecting) { |
|
|
const img = entry.target |
|
|
const songData = img.dataset.song |
|
|
|
|
|
if (songData) { |
|
|
try { |
|
|
const song = JSON.parse(songData) |
|
|
|
|
|
|
|
|
loadQueue.push({ img, song }) |
|
|
|
|
|
|
|
|
processLoadQueue() |
|
|
|
|
|
|
|
|
observer.value.unobserve(img) |
|
|
observedImages.delete(img) |
|
|
} catch (error) { |
|
|
console.error('解析歌曲数据失败:', error) |
|
|
} |
|
|
} |
|
|
} |
|
|
}) |
|
|
}, |
|
|
{ |
|
|
|
|
|
rootMargin: '100px 0px', |
|
|
threshold: 0.1 |
|
|
} |
|
|
) |
|
|
|
|
|
return true |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const loadCoverForElement = async (imgElement, song) => { |
|
|
if (!imgElement || !song) return |
|
|
|
|
|
try { |
|
|
|
|
|
if (!imgElement.parentNode) { |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
const { getCachedMusicPicUrlWithDelay } = await import('@/utils/musicPicCache.js') |
|
|
const coverUrl = await getCachedMusicPicUrlWithDelay( |
|
|
song.source, |
|
|
song.pic_id || song.id, |
|
|
300 |
|
|
) |
|
|
|
|
|
if (coverUrl && imgElement.parentNode) { |
|
|
imgElement.src = coverUrl |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('加载封面失败:', error) |
|
|
if (imgElement.parentNode) { |
|
|
imgElement.src = getDefaultCover() |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const observeImage = (imgElement, song) => { |
|
|
if (!imgElement || !song || observedImages.has(imgElement)) return |
|
|
|
|
|
|
|
|
imgElement.src = getDefaultCover() |
|
|
|
|
|
|
|
|
imgElement.dataset.song = JSON.stringify(song) |
|
|
|
|
|
|
|
|
if (!observer.value && !initLazyLoading()) { |
|
|
|
|
|
loadQueue.push({ imgElement, song }) |
|
|
processLoadQueue() |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
observer.value.observe(imgElement) |
|
|
observedImages.add(imgElement) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const loadCoverImmediately = async (song, size = 300) => { |
|
|
if (!song) return getDefaultCover() |
|
|
|
|
|
try { |
|
|
|
|
|
const { getCachedMusicPicUrl } = await import('@/utils/musicPicCache.js') |
|
|
const coverUrl = await getCachedMusicPicUrl( |
|
|
song.source, |
|
|
song.pic_id || song.id, |
|
|
size |
|
|
) |
|
|
return coverUrl || getDefaultCover() |
|
|
} catch (error) { |
|
|
console.error('加载封面失败:', error) |
|
|
return getDefaultCover() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const setCoverForElement = async (imgElement, song, size = 300) => { |
|
|
if (!imgElement || !song) return |
|
|
|
|
|
try { |
|
|
|
|
|
const { getCachedMusicPicUrlWithDelay } = await import('@/utils/musicPicCache.js') |
|
|
const coverUrl = await getCachedMusicPicUrlWithDelay( |
|
|
song.source, |
|
|
song.pic_id || song.id, |
|
|
size |
|
|
) |
|
|
|
|
|
if (coverUrl) { |
|
|
imgElement.src = coverUrl |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('加载封面失败:', error) |
|
|
imgElement.src = getDefaultCover() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
onUnmounted(() => { |
|
|
if (observer.value) { |
|
|
observer.value.disconnect() |
|
|
observer.value = null |
|
|
} |
|
|
observedImages.clear() |
|
|
}) |
|
|
|
|
|
return { |
|
|
getDefaultCover, |
|
|
handleImageError, |
|
|
observeImage, |
|
|
loadCoverImmediately, |
|
|
setCoverForElement |
|
|
} |
|
|
} |