/** * Audiobook Player - App JavaScript * Minimalist immersive reading experience */ // Theme Management function initTheme() { const savedTheme = localStorage.getItem('theme') || 'light'; if (savedTheme === 'dark') { document.body.classList.remove('light-mode'); document.body.classList.add('dark-mode'); updateThemeIcon(true); } } function toggleTheme() { const isDark = document.body.classList.contains('dark-mode'); if (isDark) { document.body.classList.remove('dark-mode'); document.body.classList.add('light-mode'); localStorage.setItem('theme', 'light'); } else { document.body.classList.remove('light-mode'); document.body.classList.add('dark-mode'); localStorage.setItem('theme', 'dark'); } updateThemeIcon(!isDark); } function updateThemeIcon(isDark) { const toggle = document.querySelector('.theme-toggle'); if (toggle) { toggle.textContent = isDark ? '☀️' : '🌙'; } } // Navigation function goBack() { window.history.back(); } function closePlayer() { // Override in player page window.history.back(); } // Save and Restore Reading Position function saveReadingPosition(storyId, scrollPosition) { const positions = JSON.parse(localStorage.getItem('readingPositions') || '{}'); positions[storyId] = { position: scrollPosition, timestamp: Date.now() }; localStorage.setItem('readingPositions', JSON.stringify(positions)); } function getReadingPosition(storyId) { const positions = JSON.parse(localStorage.getItem('readingPositions') || '{}'); return positions[storyId]?.position || 0; } // Smooth Scroll function scrollToPosition(position) { window.scrollTo({ top: position, behavior: 'smooth' }); } // Touch Gestures (for mobile) let touchStartX = 0; let touchEndX = 0; function initTouchGestures() { document.addEventListener('touchstart', (e) => { touchStartX = e.changedTouches[0].screenX; }, { passive: true }); document.addEventListener('touchend', (e) => { touchEndX = e.changedTouches[0].screenX; handleSwipe(); }, { passive: true }); } function handleSwipe() { const swipeThreshold = 100; const diff = touchStartX - touchEndX; // Swipe right (go back) if (diff < -swipeThreshold) { // Only if we're not on the home page if (window.location.pathname !== '/') { goBack(); } } } // Keyboard Shortcuts function initKeyboardShortcuts() { document.addEventListener('keydown', (e) => { // Space: Play/Pause (on player page) if (e.code === 'Space' && document.getElementById('playBtn')) { e.preventDefault(); togglePlay(); } // Escape: Go back if (e.code === 'Escape') { if (window.location.pathname !== '/') { goBack(); } } }); } // Audio Player Enhancements function initAudioPlayer() { const audio = document.getElementById('audioPlayer'); if (!audio) return; // Handle errors audio.addEventListener('error', (e) => { console.error('Audio error:', e); showNotification('播放出错,请重试'); }); // Handle ended audio.addEventListener('ended', () => { isPlaying = false; updatePlayButton(); }); // Handle loading audio.addEventListener('loadstart', () => { showNotification('加载中...', 1000); }); } // Notification System function showNotification(message, duration = 3000) { // Remove existing notification const existing = document.querySelector('.notification'); if (existing) { existing.remove(); } const notification = document.createElement('div'); notification.className = 'notification'; notification.textContent = message; notification.style.cssText = ` position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: var(--bg-card); color: var(--text-primary); padding: 12px 24px; border-radius: 24px; box-shadow: 0 4px 16px var(--shadow); z-index: 9999; font-size: 0.9rem; animation: slideUp 0.3s ease; `; document.body.appendChild(notification); if (duration > 0) { setTimeout(() => { notification.style.animation = 'slideDown 0.3s ease'; setTimeout(() => notification.remove(), 300); }, duration); } return notification; } // Add animation styles const style = document.createElement('style'); style.textContent = ` @keyframes slideUp { from { opacity: 0; transform: translateX(-50%) translateY(20px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } } @keyframes slideDown { from { opacity: 1; transform: translateX(-50%) translateY(0); } to { opacity: 0; transform: translateX(-50%) translateY(20px); } } `; document.head.appendChild(style); // Prefetch next chunk (optimization) function prefetchNextChunk(currentIndex) { // Implementation for preloading next audio chunk // This would be implemented in player.html } // Performance: Debounce scroll events function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Initialize on DOM ready document.addEventListener('DOMContentLoaded', () => { initTheme(); initTouchGestures(); initKeyboardShortcuts(); initAudioPlayer(); }); // Handle page visibility change (pause when switching tabs) document.addEventListener('visibilitychange', () => { const audio = document.getElementById('audioPlayer'); if (document.hidden && audio && !audio.paused) { // Optional: pause when switching tabs // audio.pause(); // isPlaying = false; // updatePlayButton(); } }); // Prevent zoom on double tap (mobile) let lastTouchEnd = 0; document.addEventListener('touchend', (event) => { const now = Date.now(); if (now - lastTouchEnd <= 300) { event.preventDefault(); } lastTouchEnd = now; }, { passive: false });