Files
AI_English/src/js/pages/words-autoplay.js
T
2026-07-19 21:09:58 +08:00

107 lines
3.4 KiB
JavaScript

import { state } from '../core/state.js';
import { logTts, prefetchAudio, speak, stopAllAudio } from '../services/tts.js';
let autoPlayActive = false;
let autoPlayTimer = null;
export function toggleAutoPlay() {
if (autoPlayActive) stopAutoPlay();
else startAutoPlay();
}
export function startAutoPlay() {
const rows = document.querySelectorAll('.word-table tbody tr[data-word-id]');
const wordIds = Array.from(rows).map(row => Number.parseInt(row.dataset.wordId, 10));
if (wordIds.length === 0) return;
autoPlayActive = true;
updateAutoPlayBtn();
prefetchWordListAudio(wordIds);
playWordSequence(wordIds, 0);
}
export function prefetchWordListAudio(wordIds) {
const texts = wordIds
.map(id => state.words.find(word => word.id === id)?.english)
.filter(Boolean);
if (!texts.length) return;
void prefetchAudio(texts, 0.92, { allowCloud: true })
.catch(error => logTts('word list audio prefetch failed', error));
}
export function playWordSequence(wordIds, index) {
if (!autoPlayActive || index >= wordIds.length) {
stopAutoPlay();
return;
}
const word = state.words.find(item => item.id === wordIds[index]);
if (!word) {
playWordSequence(wordIds, index + 1);
return;
}
document.querySelectorAll('.word-table tbody tr').forEach(row => row.classList.remove('is-playing'));
const row = document.querySelector(`tr[data-word-id="${word.id}"]`);
if (row) {
row.classList.add('is-playing');
let shouldScroll = true;
if (state.autoPlayScrollMode === 'visible') {
const rect = row.getBoundingClientRect();
const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
const header = document.getElementById('mobile-header');
const topBound = header ? header.getBoundingClientRect().height : 0;
shouldScroll = rect.top < topBound || rect.bottom > viewportHeight;
}
if (shouldScroll) {
row.scrollIntoView({
behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth',
block: 'center',
inline: 'nearest'
});
}
}
const repeatCount = Math.max(1, state.autoPlayRepeat || 1);
const repeatDelay = state.autoPlayRepeatDelay || 1000;
const wordInterval = state.autoPlayInterval || 2500;
let spoken = 0;
async function playCurrentWord() {
if (!autoPlayActive) return;
await speak(word.english);
if (!autoPlayActive) return;
spoken++;
if (spoken < repeatCount) {
autoPlayTimer = setTimeout(playCurrentWord, repeatDelay);
} else {
autoPlayTimer = setTimeout(() => playWordSequence(wordIds, index + 1), wordInterval);
}
}
void playCurrentWord();
}
export function stopAutoPlay() {
autoPlayActive = false;
if (autoPlayTimer) {
clearTimeout(autoPlayTimer);
autoPlayTimer = null;
}
stopAllAudio();
document.querySelectorAll('.word-table tbody tr').forEach(row => row.classList.remove('is-playing'));
updateAutoPlayBtn();
}
export function updateAutoPlayBtn() {
const button = document.getElementById('auto-play-btn');
if (!button) return;
if (autoPlayActive) {
button.innerHTML = '<i class="fas fa-stop"></i> 停止播放';
button.className = 'btn btn-error';
} else {
button.innerHTML = '<i class="fas fa-play"></i> 自动播放';
button.className = 'btn btn-secondary';
}
}