diff --git a/themes/Ying/assets/js/shortcodes.js b/themes/Ying/assets/js/shortcodes.js index de1da604..31bd26b8 100644 --- a/themes/Ying/assets/js/shortcodes.js +++ b/themes/Ying/assets/js/shortcodes.js @@ -12,6 +12,278 @@ var AudioManager = { currentPlaying: null, currentType: null, savedStates: {}, + // 全局音乐播放器 - PJAX 持久化 + globalAudio: null, + globalMusicSrc: null, + globalMusicCard: null, + // Session storage key + STORAGE_KEY: 'amigo_music_state', + + // 获取当前页面的存储 key(基于 URL path) + _getPageKey: function() { + return window.location.pathname; + }, + + init: function() { + // 创建全局音频元素 + if (!this.globalAudio) { + this.globalAudio = new Audio(); + this.globalAudio.preload = 'metadata'; + + // 监听全局音频事件 + this.globalAudio.addEventListener('timeupdate', this._onTimeUpdate.bind(this)); + this.globalAudio.addEventListener('ended', this._onEnded.bind(this)); + this.globalAudio.addEventListener('loadedmetadata', this._onLoadedMetadata.bind(this)); + } + }, + + // 保存当前播放状态到 sessionStorage(按页面保存) + saveToSession: function() { + if (!this.globalAudio || !this.globalMusicSrc) return; + + var pageKey = this._getPageKey(); + var allStates = this._loadAllStates(); + + allStates[pageKey] = { + src: this.globalMusicSrc, + currentTime: this.globalAudio.currentTime, + isPlaying: !this.globalAudio.paused, + name: this.globalMusicCard ? this.globalMusicCard.dataset.name : '', + artist: this.globalMusicCard ? this.globalMusicCard.dataset.artist : '' + }; + + try { + sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(allStates)); + } catch (e) { + console.warn('Failed to save music state:', e); + } + }, + + // 加载所有页面的状态 + _loadAllStates: function() { + try { + var stored = sessionStorage.getItem(this.STORAGE_KEY); + if (!stored) return {}; + return JSON.parse(stored); + } catch (e) { + return {}; + } + }, + + // 从 sessionStorage 恢复当前页面的播放状态 + restoreFromSession: function() { + var pageKey = this._getPageKey(); + var allStates = this._loadAllStates(); + + var state = allStates[pageKey]; + if (!state || !state.src) return null; + + return state; + }, + + // 清除当前页面的播放状态 + clearSession: function() { + var pageKey = this._getPageKey(); + var allStates = this._loadAllStates(); + + delete allStates[pageKey]; + + try { + sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(allStates)); + } catch (e) {} + }, + + // 恢复播放状态 - 在 PJAX 切换后调用 + restorePlayingState: function() { + var state = this.restoreFromSession(); + if (!state || !state.src) { + // 当前页面没有保存的状态,暂停播放 + if (this.globalAudio && !this.globalAudio.paused) { + this.globalAudio.pause(); + } + return; + } + + var self = this; + + // 找到对应的音乐卡片 + var cards = document.querySelectorAll('.amigo-music-card'); + var targetCard = null; + + for (var i = 0; i < cards.length; i++) { + var card = cards[i]; + if (card.dataset.src === state.src) { + targetCard = card; + break; + } + } + + if (!targetCard) { + // 当前页面没有这个音乐卡片,暂停播放 + if (this.globalAudio && !this.globalAudio.paused) { + this.globalAudio.pause(); + } + return; + } + + // 设置全局音频 + this.globalMusicSrc = state.src; + this.globalMusicCard = targetCard; + this.globalAudio.src = state.src; + + // 更新 UI + this._updateUI(targetCard, state.isPlaying); + + // 等待元数据加载完成 + this.globalAudio.addEventListener('loadedmetadata', function onLoaded() { + self.globalAudio.removeEventListener('loadedmetadata', onLoaded); + + if (state.currentTime > 0 && state.currentTime < self.globalAudio.duration) { + self.globalAudio.currentTime = state.currentTime; + } + + self._onTimeUpdate(); + + if (state.isPlaying) { + self.globalAudio.play().catch(function() {}); + } + }); + + if (this.globalAudio.readyState >= 1) { + if (state.currentTime > 0 && state.currentTime < this.globalAudio.duration) { + this.globalAudio.currentTime = state.currentTime; + } + this._onTimeUpdate(); + if (state.isPlaying) { + this.globalAudio.play().catch(function() {}); + } + } + }, + + // 播放指定音乐卡片 + playMusic: function(card) { + if (!card) return; + + var audioSrc = card.dataset.src; + if (!audioSrc) return; + + // 如果是同一首歌,切换播放/暂停 + if (this.globalMusicSrc === audioSrc && this.globalAudio) { + if (this.globalAudio.paused) { + this.globalAudio.play().catch(function() {}); + this.globalMusicCard = card; + this._updateUI(card, true); + } else { + this.globalAudio.pause(); + this._updateUI(card, false); + } + return; + } + + // 暂停其他所有音频 + this.pauseAll(); + + // 设置新的音频源 + this.globalMusicSrc = audioSrc; + this.globalMusicCard = card; + this.globalAudio.src = audioSrc; + + // 更新 UI + this._updateUI(card, true); + + // 尝试播放 + var self = this; + this.globalAudio.play().then(function() { + // 保存状态 + self.saveToSession(); + }).catch(function(err) { + console.warn('Play failed:', err); + }); + }, + + // 跳转到指定时间 + seekTo: function(time) { + if (this.globalAudio) { + this.globalAudio.currentTime = time; + this.saveToSession(); + } + }, + + // 更新时间回调 + _onTimeUpdate: function() { + if (!this.globalAudio || !this.globalMusicCard) return; + + // 如果正在拖动进度条,跳过 UI 更新 + if (this.globalAudio.__seeking) return; + + var currentTime = this.globalAudio.currentTime; + var duration = this.globalAudio.duration; + + if (isNaN(duration) || duration === 0) return; + + // 更新进度条 + var progress = (currentTime / duration) * 100; + this.globalMusicCard.style.setProperty('--music-progress', progress + '%'); + + // 更新时间显示 + var currentTimeEl = this.globalMusicCard.querySelector('.amigo-music-card__time--current'); + if (currentTimeEl) { + currentTimeEl.textContent = formatTime(currentTime); + } + + // 更新总时长(如果还没更新过) + var durationTimeEl = this.globalMusicCard.querySelector('.amigo-music-card__time--duration'); + if (durationTimeEl && durationTimeEl.textContent === '--:--') { + durationTimeEl.textContent = formatTime(duration); + } + + // 定期保存状态(每5秒) + if (Math.floor(currentTime) % 5 === 0) { + this.saveToSession(); + } + }, + + // 加载完成回调 + _onLoadedMetadata: function() { + if (!this.globalAudio || !this.globalMusicCard) return; + + var duration = this.globalAudio.duration; + + // 更新总时长显示 + var durationTimeEl = this.globalMusicCard.querySelector('.amigo-music-card__time--duration'); + if (durationTimeEl) { + durationTimeEl.textContent = formatTime(duration); + } + }, + + // 播放结束回调 + _onEnded: function() { + if (this.globalMusicCard) { + this._updateUI(this.globalMusicCard, false); + } + this.globalMusicCard = null; + this.clearSession(); + }, + + // 更新 UI 状态 + _updateUI: function(card, isPlaying) { + if (!card) return; + + card.setAttribute('data-playing', isPlaying ? 'true' : 'false'); + + var playBtn = card.querySelector('.amigo-music-card__play'); + if (playBtn) { + playBtn.setAttribute('aria-pressed', isPlaying ? 'true' : 'false'); + } + + if (isPlaying) { + this.currentPlaying = card; + this.currentType = 'music'; + } else if (this.globalMusicCard === card) { + this.currentPlaying = null; + this.currentType = null; + } + }, pauseAll: function() { // Pause all voice bubbles @@ -25,15 +297,13 @@ var AudioManager = { bubble.classList.remove('is-playing'); }); - // Pause all music cards - document.querySelectorAll('.amigo-music-card[data-playing="true"]').forEach(function(card) { - if (card.__audio) { - try { card.__audio.pause(); } catch (e) {} + // Pause global music + if (this.globalAudio && !this.globalAudio.paused) { + this.globalAudio.pause(); + if (this.globalMusicCard) { + this._updateUI(this.globalMusicCard, false); } - card.setAttribute('data-playing', 'false'); - var playBtn = card.querySelector('.amigo-music-card__play'); - if (playBtn) playBtn.setAttribute('aria-pressed', 'false'); - }); + } // Pause all live photos document.querySelectorAll('.amigo-livephoto.is-playing').forEach(function(livePhoto) { @@ -355,7 +625,8 @@ function initVoiceBubbles() { } // ============================================================ -// Music Card +// Music Card - PJAX 持久化版本 +// 使用全局 AudioManager 管理音乐播放 // ============================================================ function initMusicCards() { @@ -368,80 +639,49 @@ function initMusicCards() { var seekInput = card.querySelector('.amigo-music-card__seek'); var currentTime = card.querySelector('.amigo-music-card__time--current'); var durationTime = card.querySelector('.amigo-music-card__time--duration'); - var audio = null; - var isPlaying = false; if (!audioSrc) return; - audio = new Audio(audioSrc); - card.__audio = audio; + // 不再创建单独的 Audio 对象,使用全局 AudioManager + // card.__audio = null; - // Load metadata - audio.addEventListener('loadedmetadata', function() { - var duration = formatTime(audio.duration); - if (durationTime) durationTime.textContent = duration; - }); + // 检查是否应该显示播放状态 + // (由 AudioManager 在 restorePlayingState 中处理) - // Update progress - audio.addEventListener('timeupdate', function() { - if (currentTime) currentTime.textContent = formatTime(audio.currentTime); - if (seekInput && audio.duration) { - var progress = (audio.currentTime / audio.duration) * 100; - seekInput.value = (audio.currentTime / audio.duration * 1000).toString(); - card.style.setProperty('--music-progress', progress + '%'); - } - }); - - // Play/Pause - function togglePlay() { - if (isPlaying) { - audio.pause(); - card.setAttribute('data-playing', 'false'); - if (playBtn) playBtn.setAttribute('aria-pressed', 'false'); - isPlaying = false; - if (AudioManager.currentPlaying === card) { - AudioManager.currentPlaying = null; - AudioManager.currentType = null; - } - } else { - AudioManager.registerPlay(card, 'music'); - audio.play(); - card.setAttribute('data-playing', 'true'); - if (playBtn) playBtn.setAttribute('aria-pressed', 'true'); - isPlaying = true; - } + // Play/Pause - 使用 AudioManager + function togglePlay(e) { + if (e) e.stopPropagation(); + AudioManager.playMusic(card); } if (playBtn) { - playBtn.addEventListener('click', function(e) { - e.stopPropagation(); - togglePlay(); - }); + playBtn.addEventListener('click', togglePlay); } card.addEventListener('click', togglePlay); - // Seek + // Seek - 使用 AudioManager if (seekInput) { seekInput.addEventListener('input', function() { - if (audio.duration) { - var progress = (parseFloat(seekInput.value) / 1000) * 100; - audio.currentTime = (parseFloat(seekInput.value) / 1000) * audio.duration; - card.style.setProperty('--music-progress', progress + '%'); + if (AudioManager.globalAudio && AudioManager.globalAudio.duration) { + var time = (parseFloat(seekInput.value) / 1000) * AudioManager.globalAudio.duration; + AudioManager.seekTo(time); + } + }); + + // 拖动时暂停更新 + seekInput.addEventListener('mousedown', function() { + if (AudioManager.globalAudio) { + AudioManager.globalAudio.__seeking = true; + } + }); + + seekInput.addEventListener('mouseup', function() { + if (AudioManager.globalAudio) { + AudioManager.globalAudio.__seeking = false; } }); } - - // Ended - audio.addEventListener('ended', function() { - card.setAttribute('data-playing', 'false'); - if (playBtn) playBtn.setAttribute('aria-pressed', 'false'); - isPlaying = false; - if (AudioManager.currentPlaying === card) { - AudioManager.currentPlaying = null; - AudioManager.currentType = null; - } - }); }); } @@ -495,15 +735,15 @@ function cleanupLivePhotos() { } function cleanupAllShortcodes() { - // Save playback state before cleanup - AudioManager.saveState(); - + // 保存当前播放状态到 sessionStorage(音乐不中断) + AudioManager.saveToSession(); + + // 只清理语音气泡和实况照片(音乐由全局 AudioManager 管理) cleanupVoiceBubbles(); - cleanupMusicCards(); cleanupLivePhotos(); - - AudioManager.currentPlaying = null; - AudioManager.currentType = null; + + // 不再清理音乐卡片,因为音乐由全局 AudioManager 管理 + // cleanupMusicCards(); // 已移除,音乐在 PJAX 切换时继续播放 } // ============================================================ @@ -511,14 +751,17 @@ function cleanupAllShortcodes() { // ============================================================ function initAllShortcodes() { + // 初始化 AudioManager(创建全局音频元素,恢复播放状态) + AudioManager.init(); + initLivePhotoShortcodes(); initVoiceBubbles(); initMusicCards(); - - // Restore playback state after initialization + + // 恢复 PJAX 切换前的播放状态 setTimeout(function() { - AudioManager.restoreState(); - }, 50); + AudioManager.restorePlayingState(); + }, 100); } // DOMContentLoaded