Initial commit

This commit is contained in:
2025-07-18 13:07:28 +08:00
commit e3832acfa8
91 changed files with 10929 additions and 0 deletions

250
lib/background-music.ts Normal file
View File

@@ -0,0 +1,250 @@
// 背景音樂管理系統 - 修復重新播放問題
class BackgroundMusicManager {
private audio: HTMLAudioElement | null = null
private isPlaying = false
private enabled = false
private volume = 0.3
private fadeInterval: NodeJS.Timeout | null = null
constructor() {
this.initAudio()
this.loadState()
}
private loadState() {
try {
const savedState = localStorage.getItem("backgroundMusicState")
if (savedState) {
const state = JSON.parse(savedState)
this.volume = state.volume || 0.3
this.enabled = state.enabled || false
// 不要自動恢復播放狀態,讓用戶手動控制
this.isPlaying = false
}
} catch (error) {
// 忽略載入錯誤
}
}
private saveState() {
try {
const state = {
volume: this.volume,
enabled: this.enabled,
isPlaying: this.isPlaying,
}
localStorage.setItem("backgroundMusicState", JSON.stringify(state))
} catch (error) {
// 忽略保存錯誤
}
}
private initAudio() {
try {
this.audio = new Audio("https://hebbkx1anhila5yf.public.blob.vercel-storage.com/just-relax-11157-iAgp15dV2YGybAezUJFtKmKZPbteXd.mp3")
this.audio.loop = true
this.audio.volume = this.volume
this.audio.preload = "metadata"
// 明確禁用自動播放相關屬性
this.audio.autoplay = false
this.audio.muted = false
// 靜默處理載入完成
this.audio.addEventListener("canplaythrough", () => {
// 音樂載入完成
})
// 靜默錯誤處理
this.audio.addEventListener("error", (e) => {
// 重新初始化音頻對象
this.reinitAudio()
})
// 播放結束處理
this.audio.addEventListener("ended", () => {
if (this.enabled && this.isPlaying) {
// 重新播放
this.audio?.play().catch(() => {
// 如果播放失敗,重新初始化
this.reinitAudio()
})
}
})
} catch (error) {
// 靜默處理初始化錯誤
}
}
// 重新初始化音頻對象
private reinitAudio() {
try {
if (this.audio) {
this.audio.pause()
this.audio.src = ""
this.audio = null
}
// 重新創建音頻對象
setTimeout(() => {
this.initAudio()
}, 100)
} catch (error) {
// 靜默處理錯誤
}
}
// 淡入效果
private fadeIn(duration = 2000) {
if (!this.audio) return
this.audio.volume = 0
const targetVolume = this.volume
const steps = 50
const stepTime = duration / steps
const volumeStep = targetVolume / steps
let currentStep = 0
this.fadeInterval = setInterval(() => {
if (currentStep >= steps || !this.audio) {
if (this.fadeInterval) {
clearInterval(this.fadeInterval)
this.fadeInterval = null
}
if (this.audio) {
this.audio.volume = targetVolume
}
return
}
this.audio.volume = Math.min(volumeStep * currentStep, targetVolume)
currentStep++
}, stepTime)
}
// 淡出效果
private fadeOut(duration = 1000) {
if (!this.audio) return
const startVolume = this.audio.volume
const steps = 50
const stepTime = duration / steps
const volumeStep = startVolume / steps
let currentStep = 0
this.fadeInterval = setInterval(() => {
if (currentStep >= steps || !this.audio) {
if (this.fadeInterval) {
clearInterval(this.fadeInterval)
this.fadeInterval = null
}
if (this.audio) {
this.audio.pause()
this.audio.currentTime = 0 // 重置播放位置
this.audio.volume = this.volume // 恢復原始音量
}
return
}
this.audio.volume = Math.max(startVolume - volumeStep * currentStep, 0)
currentStep++
}, stepTime)
}
async start() {
// 清除任何進行中的淡出效果
if (this.fadeInterval) {
clearInterval(this.fadeInterval)
this.fadeInterval = null
}
// 如果音頻對象不存在,重新初始化
if (!this.audio) {
this.initAudio()
// 等待一下讓音頻對象初始化完成
await new Promise((resolve) => setTimeout(resolve, 100))
}
if (!this.audio) return
try {
this.enabled = true
this.isPlaying = true
this.saveState()
// 確保音頻對象處於正確狀態
this.audio.currentTime = 0
this.audio.volume = 0 // 從0開始準備淡入
// 開始播放並淡入
await this.audio.play()
this.fadeIn(2000)
} catch (error) {
// 播放失敗,重新初始化並重試
this.reinitAudio()
this.isPlaying = false
this.enabled = false
this.saveState()
}
}
stop() {
if (!this.audio) return
this.enabled = false
this.isPlaying = false
this.saveState()
// 清除淡入效果
if (this.fadeInterval) {
clearInterval(this.fadeInterval)
this.fadeInterval = null
}
// 淡出並停止
this.fadeOut(1000)
}
setVolume(volume: number) {
this.volume = Math.max(0, Math.min(1, volume))
if (this.audio && this.isPlaying) {
this.audio.volume = this.volume
}
this.saveState()
}
getVolume() {
return this.volume
}
isEnabled() {
return this.enabled
}
getIsPlaying() {
return this.isPlaying && this.audio && !this.audio.paused
}
// 獲取當前狀態
getState() {
return {
isPlaying: this.getIsPlaying(),
enabled: this.enabled,
volume: this.volume,
}
}
// 獲取音樂資訊
getMusicInfo() {
if (!this.audio) return null
return {
duration: this.audio.duration || 0,
currentTime: this.audio.currentTime || 0,
loaded: this.audio.readyState >= 3,
}
}
}
// 全局背景音樂管理器
export const backgroundMusicManager = new BackgroundMusicManager()