HTML版英语学习系统
这是一个完全免费、无需安装、功能完整的英语学习工具,使用HTML + CSS +JavaScript实现。
功能
- 文本朗读练习 - 输入英文文章,系统朗读帮助练习听力和发音,适合跟读练习,模仿学习;
- 实时词典查询 - 双击任意英文单词即可查看释义、音标和发音,适合新词汇学习和发音;(这一点,使用 Free Dictionary API,要联网)
- 分段学习 - 可以选择朗读全文、选中段落或当前段落;
- 重复练习 - 支持1-3次重复或循环播放,强化记忆;
- 学生自学 - 适合学生课文预习和自学;
个性化设置
- 语音选择 - 可选择不同的英语发音(美音、英音等)
- 语速调节 - 从0.5到2倍速,适应不同学习阶段
- 音调控制 - 调整音调让发音更清晰
- 音量控制 - 根据环境调整合适音量
运行截图
双击单词情况如下:
源码如下:
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>英语学习系统 - 文本转语音与词典查询</title><style>body {font-family: Arial, sans-serif;max-width: 1000px;margin: 0 auto;padding: 20px;color: #333;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);min-height: 100vh;}.container {background: rgba(255, 255, 255, 0.95);border-radius: 15px;padding: 30px;box-shadow: 0 20px 40px rgba(0,0,0,0.1);}h1 {color: #2c3e50;text-align: center;margin-bottom: 10px;font-size: 28px;}.subtitle {text-align: center;color: #666;margin-bottom: 30px;font-style: italic;}.controls {margin: 20px 0;background-color: #fff;padding: 25px;border-radius: 12px;box-shadow: 0 4px 15px rgba(0,0,0,0.08);}textarea {width: 100%;height: 220px;margin: 10px 0;padding: 20px;border: 2px solid #e0e0e0;border-radius: 8px;font-size: 18px;line-height: 1.6;resize: vertical;box-sizing: border-box;transition: border-color 0.3s;}textarea:focus {outline: none;border-color: #667eea;box-shadow: 0 0 10px rgba(102, 126, 234, 0.2);}.parameter {display: flex;align-items: center;margin: 20px 0;justify-content: flex-start;gap: 15px;}.parameter label {min-width: 90px;text-align: right;margin-right: 15px;color: #555;font-weight: 600;}select {padding: 10px;border: 2px solid #e0e0e0;border-radius: 6px;background-color: #fff;font-size: 14px;transition: border-color 0.3s;}select:focus {outline: none;border-color: #667eea;}#voiceSelect {width: 460px;}#playCount {width: 130px;}input[type="range"] {width: 300px;height: 8px;margin: 0 15px;-webkit-appearance: none;background: linear-gradient(to right, #667eea, #764ba2);border-radius: 4px;outline: none;}input[type="range"]::-webkit-slider-thumb {-webkit-appearance: none;width: 20px;height: 20px;background: #fff;border: 3px solid #667eea;border-radius: 50%;cursor: pointer;box-shadow: 0 2px 6px rgba(0,0,0,0.2);}.value-display {min-width: 40px;text-align: center;background: #f8f9fa;padding: 5px 10px;border-radius: 4px;font-weight: 600;color: #667eea;}.status-display {margin: 25px 0;padding: 15px 20px;background: linear-gradient(135deg, #667eea, #764ba2);color: white;border-radius: 8px;font-size: 15px;font-weight: 500;text-align: center;}.buttons-container {margin-top: 25px;display: flex;justify-content: center;gap: 15px;flex-wrap: wrap;}.special-buttons {margin-top: 20px;padding-top: 20px;border-top: 2px solid #f0f0f0;display: flex;justify-content: center;gap: 15px;flex-wrap: wrap;}button {padding: 12px 25px;border: none;border-radius: 8px;font-size: 15px;font-weight: 600;cursor: pointer;transition: all 0.3s;color: white;background: linear-gradient(135deg, #4CAF50, #45a049);box-shadow: 0 4px 15px rgba(76, 175, 80, 0.3);}button:hover {transform: translateY(-2px);box-shadow: 0 6px 20px rgba(76, 175, 80, 0.4);}button:active {transform: translateY(0);}.special-button {background: linear-gradient(135deg, #2196F3, #1976D2);box-shadow: 0 4px 15px rgba(33, 150, 243, 0.3);}.special-button:hover {box-shadow: 0 6px 20px rgba(33, 150, 243, 0.4);}/* 词典查询相关样式 */.word-popup {position: absolute;background: white;border: 2px solid #667eea;border-radius: 12px;padding: 25px;max-width: 450px;box-shadow: 0 10px 30px rgba(0,0,0,0.2);z-index: 1000;font-size: 14px;animation: popupShow 0.3s ease;}@keyframes popupShow {from { opacity: 0; transform: scale(0.9) translateY(-10px); }to { opacity: 1; transform: scale(1) translateY(0); }}.word-popup .word-header {display: flex;align-items: baseline;gap: 15px;flex-wrap: wrap;margin-bottom: 20px;border-bottom: 2px solid #f0f0f0;padding-bottom: 15px;}.word-popup h3 {margin: 0;color: #667eea;font-size: 24px;font-weight: 700;}.word-popup .phonetic {color: #666;font-family: monospace;background: #f8f9fa;padding: 4px 8px;border-radius: 4px;}.word-popup .audio-btn {background: linear-gradient(135deg, #667eea, #764ba2);border: none;padding: 8px 15px;border-radius: 6px;cursor: pointer;color: white;font-size: 13px;margin-bottom: 15px;transition: all 0.3s;}.word-popup .audio-btn:hover {transform: translateY(-1px);box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);}.word-popup .meanings {max-height: 350px;overflow-y: auto;margin-bottom: 20px;}.word-popup .meaning-group {margin-bottom: 20px;background: #fafbfc;padding: 15px;border-radius: 8px;}.word-popup .part-of-speech {color: #667eea;font-weight: 700;margin-bottom: 10px;font-size: 16px;}.word-popup ol {margin: 0;padding-left: 25px;color: #333;}.word-popup ol li {margin: 10px 0;line-height: 1.5;}.word-popup .definition {color: #333;margin-bottom: 6px;}.word-popup .example {color: #666;font-style: italic;background: #e8f4fd;padding: 8px;border-left: 3px solid #667eea;margin-top: 8px;border-radius: 4px;}.word-popup .close-btn {width: 100%;margin-top: 20px;padding: 12px;background: linear-gradient(135deg, #95a5a6, #7f8c8d);border: none;border-radius: 6px;cursor: pointer;color: white;font-weight: 600;}.word-popup .close-btn:hover {background: linear-gradient(135deg, #7f8c8d, #6c7b7d);}.error-message {position: fixed;bottom: 30px;right: 30px;background: linear-gradient(135deg, #e74c3c, #c0392b);color: white;padding: 15px 25px;border-radius: 8px;animation: fadeIn 0.3s ease;box-shadow: 0 6px 20px rgba(231, 76, 60, 0.3);font-weight: 600;}@keyframes fadeIn {from { opacity: 0; transform: translateY(30px); }to { opacity: 1; transform: translateY(0); }}.learning-tip {background: linear-gradient(135deg, #f093fb, #f5576c);color: white;padding: 15px;border-radius: 8px;margin-bottom: 20px;<!-- text-align: center; -->font-weight: 500;}@media (max-width: 768px) {body {padding: 10px;}.container {padding: 20px;}.parameter {flex-direction: column;align-items: flex-start;}.parameter label {text-align: left;margin-bottom: 8px;}#voiceSelect {width: 100%;}input[type="range"] {width: 100%;margin: 10px 0;}.buttons-container,.special-buttons {flex-direction: column;align-items: center;}button {width: 200px;}}</style>
</head>
<body><div class="container"><h1>英语学习系统</h1><p class="subtitle">文本转语音 + 实时词典查询</p><div class="learning-tip">💡 (1)如果“开始朗读”不发音,请考虑选择语音是否不当。(2)可以设置播放次数,调整语速、音量和音调。(2)双击英文单词可查看英文释义、音标(这项功能需要联网,使用 Free Dictionary API实现)。</div><div class="controls"><label>你可以在下面文本区域输入文字,或者往其中粘贴复制的文本:</label><textarea id="textToSpeak" placeholder="请输入英文文本开始学习...">Welcome to the English Learning System!
This is an interactive text-to-speech application with dictionary lookup.
You can practice pronunciation, adjust speech parameters, and learn new vocabulary.
Double-click on any word like "pronunciation" or "vocabulary" to see its definition and hear how it sounds.</textarea><div class="parameter"><label for="voiceSelect">语音:</label><select id="voiceSelect"></select><label for="playCount">次数:</label><select id="playCount"><option value="1">播放1次</option><option value="2">播放2次</option><option value="3">播放3次</option><option value="-1">循环播放</option></select></div><div class="parameter"><label for="rate">语速:</label><input type="range" id="rate" min="0.5" max="2" step="0.1" value="1"><span id="rateValue" class="value-display">1.0</span><label for="pitch">音调:</label><input type="range" id="pitch" min="0.5" max="2" step="0.1" value="1"><span id="pitchValue" class="value-display">1.0</span></div><div class="parameter"> <label for="volume">音量:</label><input type="range" id="volume" min="0" max="1" step="0.1" value="1"><span id="volumeValue" class="value-display">1.0</span></div><div class="status-display" id="statusDisplay">系统就绪 - 开始你的英语学习之旅!</div><div class="buttons-container"><button onclick="speak()">🔊 开始朗读</button><button onclick="pause()">⏸️ 暂停</button><button onclick="resume()">▶️ 继续</button><button onclick="stop()">⏹️ 停止</button><button class="special-button" onclick="speakSelectedText()">📖 读选中文本</button><button class="special-button" onclick="speakCurrentParagraph()">📄 读当前段落</button></div></div></div><script>let speechSynth = window.speechSynthesis;let utterance = null;let currentVoice = null;let voices = [];let playCount = 1;let totalPlayCount = 1;// 加载语音列表function loadVoices() {voices = speechSynth.getVoices();let voiceSelect = document.getElementById('voiceSelect');// 对语音进行排序voices.sort((a, b) => {if (a.lang < b.lang) return -1;if (a.lang > b.lang) return 1;return a.name.localeCompare(b.name);});// 创建语言分组的对象let voicesByLang = {};voices.forEach(voice => {if (!voicesByLang[voice.lang]) {voicesByLang[voice.lang] = [];}voicesByLang[voice.lang].push(voice);});voiceSelect.innerHTML = '';// 遍历排序后的语言分组Object.keys(voicesByLang).sort().forEach(lang => {let groupElement = document.createElement('optgroup');groupElement.label = getLangLabel(lang);voicesByLang[lang].forEach((voice) => {let option = document.createElement('option');option.textContent = `${voice.name}`;option.setAttribute('data-voice-index', voices.indexOf(voice));if (currentVoice && voice.name === currentVoice.name) {option.selected = true;}groupElement.appendChild(option);});voiceSelect.appendChild(groupElement);});// 初始化当前语音if (!currentVoice && voices.length > 0) {currentVoice = voices[0];voiceSelect.selectedIndex = 0;}}// 添加双击事件监听document.getElementById('textToSpeak').addEventListener('dblclick', function(e) {const selectedText = window.getSelection().toString().trim();if (selectedText && /^[a-zA-Z]+$/.test(selectedText)) {lookupWord(selectedText);}});// 查询单词async function lookupWord(word) {updateStatus(`正在查询单词 "${word}"...`);try {const response = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${word}`);const data = await response.json();if (data && data.length > 0) {showWordDefinition(word, data[0]);updateStatus(`单词 "${word}" 查询成功!`);} else {showError('未找到该单词的释义');updateStatus('查询失败 - 未找到释义');}} catch (error) {showError('查询失败,请稍后重试');updateStatus('网络查询失败');}}// 显示单词释义function showWordDefinition(word, data) {let popup = document.createElement('div');popup.className = 'word-popup';// 构建释义内容let content = `<div class="word-header"><h3>${word}</h3>${data.phonetics && data.phonetics[0]?.text ? `<span class="phonetic">${data.phonetics[0].text}</span>` : ''}</div>`;// 添加发音按钮(如果有音频链接)const audioUrl = data.phonetics?.find(p => p.audio)?.audio;if (audioUrl) {content += `<button class="audio-btn" onclick="playAudio('${audioUrl}')">🔊 播放发音</button>`;}// 添加释义if (data.meanings && data.meanings.length > 0) {content += '<div class="meanings">';data.meanings.forEach(meaning => {content += `<div class="meaning-group"><div class="part-of-speech">${meaning.partOfSpeech}</div><ol>${meaning.definitions.map(def => `<li><div class="definition">${def.definition}</div>${def.example ? `<div class="example">Example: ${def.example}</div>` : ''}</li>`).join('')}</ol></div>`;});content += '</div>';}// 添加关闭按钮content += '<button class="close-btn" onclick="this.parentElement.remove()">关闭</button>';popup.innerHTML = content;document.body.appendChild(popup);// 定位弹出框const selection = window.getSelection();const range = selection.getRangeAt(0);const rect = range.getBoundingClientRect();// 调整弹出位置,确保在可视区域内let left = rect.left + window.scrollX;let top = rect.bottom + window.scrollY + 10;// 检查右边界if (left + 450 > window.innerWidth) {left = window.innerWidth - 470;}popup.style.left = `${left}px`;popup.style.top = `${top}px`;}// 播放音频function playAudio(url) {new Audio(url).play();}// 显示错误信息function showError(message) {const errorDiv = document.createElement('div');errorDiv.className = 'error-message';errorDiv.textContent = message;document.body.appendChild(errorDiv);setTimeout(() => errorDiv.remove(), 3000);}// 获取语言显示名称function getLangLabel(langCode) {const langNames = {'zh-CN': '中文 (中国)','zh-TW': '中文 (台湾)','zh-HK': '中文 (香港)','en-US': '英语 (美国)','en-GB': '英语 (英国)','ja-JP': '日语 (日本)','ko-KR': '韩语 (韩国)','fr-FR': '法语 (法国)','de-DE': '德语 (德国)','es-ES': '西班牙语 (西班牙)','it-IT': '意大利语 (意大利)','ru-RU': '俄语 (俄罗斯)',};return langNames[langCode] || langCode;}// 初始化语音if (speechSynth.onvoiceschanged !== undefined) {speechSynth.onvoiceschanged = loadVoices;}setTimeout(loadVoices, 100);// 监听播放次数选择变化document.getElementById('playCount').addEventListener('change', function(e) {totalPlayCount = parseInt(e.target.value);});// 语音选择改变事件document.getElementById('voiceSelect').addEventListener('change', function(e) {let selectedOption = e.target.options[e.target.selectedIndex];let voiceIndex = selectedOption.getAttribute('data-voice-index');currentVoice = voices[voiceIndex];});// 更新滑块值显示document.getElementById('rate').addEventListener('input', function(e) {document.getElementById('rateValue').textContent = parseFloat(e.target.value).toFixed(1);});document.getElementById('pitch').addEventListener('input', function(e) {document.getElementById('pitchValue').textContent = parseFloat(e.target.value).toFixed(1);});document.getElementById('volume').addEventListener('input', function(e) {document.getElementById('volumeValue').textContent = parseFloat(e.target.value).toFixed(1);});// 更新状态显示function updateStatus(message) {document.getElementById('statusDisplay').textContent = message;}// 创建并播放音频function createAndPlayUtterance(text) {if (!text.trim()) {updateStatus('没有文本可以朗读');return;}utterance = new SpeechSynthesisUtterance(text);if (currentVoice) {utterance.voice = currentVoice;}utterance.rate = parseFloat(document.getElementById('rate').value);utterance.pitch = parseFloat(document.getElementById('pitch').value);utterance.volume = parseFloat(document.getElementById('volume').value);utterance.onend = function(event) {console.log(`第 ${playCount} 次播放结束`);if (totalPlayCount === -1 || playCount < totalPlayCount) {playCount++;updateStatus(`正在播放第 ${playCount} 次...`);setTimeout(() => createAndPlayUtterance(text), 500);} else {updateStatus('播放完成 ✓');}};utterance.onstart = function() {updateStatus(`正在播放第 ${playCount} 次... 🔊`);};speechSynth.speak(utterance);}// 开始朗读function speak() {stop();let text = document.getElementById('textToSpeak').value;playCount = 1;totalPlayCount = parseInt(document.getElementById('playCount').value);createAndPlayUtterance(text);}// 获取选中的文本function getSelectedText() {const textarea = document.getElementById('textToSpeak');const start = textarea.selectionStart;const end = textarea.selectionEnd;if (start === end) {return '';}return textarea.value.substring(start, end);}// 获取光标所在段落的文本function getCurrentParagraph() {const textarea = document.getElementById('textToSpeak');const text = textarea.value;const cursorPosition = textarea.selectionStart;// 将文本按换行符分割const paragraphs = text.split('\n');let currentPosition = 0;for (let i = 0; i < paragraphs.length; i++) {const paragraphLength = paragraphs[i].length + 1; // +1 是为了计入换行符const paragraphEnd = currentPosition + paragraphLength;if (cursorPosition <= paragraphEnd) {return paragraphs[i].trim();}currentPosition = paragraphEnd;}return paragraphs[paragraphs.length - 1].trim();}// 朗读选中的文本function speakSelectedText() {stop();const selectedText = getSelectedText();if (!selectedText) {updateStatus('请先选中要朗读的文本');return;}playCount = 1;totalPlayCount = parseInt(document.getElementById('playCount').value);updateStatus('开始朗读选中文本...');createAndPlayUtterance(selectedText);}// 朗读光标所在段落function speakCurrentParagraph() {stop();const currentParagraph = getCurrentParagraph();if (!currentParagraph) {updateStatus('光标所在位置没有找到有效段落');return;}playCount = 1;totalPlayCount = parseInt(document.getElementById('playCount').value);updateStatus('开始朗读当前段落...');createAndPlayUtterance(currentParagraph);}// 暂停朗读function pause() {speechSynth.pause();updateStatus('已暂停 ⏸️');}// 继续朗读function resume() {speechSynth.resume();updateStatus('继续播放... ▶️');}// 停止朗读function stop() {speechSynth.cancel();playCount = totalPlayCount;updateStatus('已停止 ⏹️');}</script>
</body>
</html>
OK!