如何为不同音频文件匹配加载对应字幕?(现有功能正常)
解决字幕同步与切换问题
咱们先梳理下现有代码里导致字幕功能失效的几个关键问题:
- 全局变量冲突:
i和timerId被重复声明,changer0和changer1会共享同一套变量,导致字幕切换逻辑混乱。 - 硬编码字幕函数:播放、重置时固定调用
changer0,无法根据当前选中的数组切换对应的字幕。 - 重复的页面初始化事件:页面里写了两个
<body onLoad>,这是无效的,只会执行最后一个,初始化逻辑完全混乱。 - 字幕边界处理错误:
if(i==texter0.length-1) i = 0;会让最后一条字幕刚显示就立刻重置,导致无法正常展示。 - Next按钮未自动打开字幕:没有实现点击Next后自动显示字幕的需求。
接下来是修复后的完整代码,我已经把这些问题都修正了,并且实现了clip与字幕的对应、播放/暂停/重置同步字幕的功能:
<!DOCTYPE html> <html> <head> <style> #transbox { font-family:Arial,sans-serif; color:black; text-align:center; margin-top:50px; } #Radio1 { margin-top: -267px; margin-left: 0px; vertical-align:middle; } #Radio2 { margin-top: -197px; margin-left: -20px; vertical-align:middle; } #Radio3 { margin-top: -125px; margin-left: -20px; vertical-align:middle; } #arr1 { font-family:Arial,sans-serif; font-size: 1em; color:black; margin-top: 0px; margin-left: 20px; } #arr2 { font-family:Arial,sans-serif; font-size: 1em; color:black; margin-top: 0px; margin-left: 20px; } #arr3 { font-family:Arial,sans-serif; font-size: 1em; color:black; margin-top: 0px; margin-left: 20px; } </style> </head> <body onLoad="init()"> <script src="//cdn.rawgit.com/mrdoob/three.js/master/build/three.min.js"></script> <p id="arr1"></p> <p id="arr2"></p> <p id="arr3"></p> <p id="clip0"></p> <p id="clip1"></p> <p id="clip2"></p> <button type="button" onclick="buttonShow()">Subtitle Open</button> <button type="button" onclick="buttonHide()">Subtitle Close</button> <input type="button" id="prev_button" value="<< Prev"> <input type="button" id="next_button" value="Next >>"> <form name="action"> <input type="radio" id="Radio1" name="group1" autocomplete="off" onclick="check();"/> <input type="radio" id="Radio2" name="group1" autocomplete="off" onclick="check();"/> <input type="radio" id="Radio3" name="group1" autocomplete="off" onclick="check();"/> </form> <button id="play" class="play">Play</button> <button id="paused" class="paused">Pause</button> <button id="reset" class="reset">Reset</button> <div id="transbox"></div> <script> var currentArray = -1; var currentSubtitleSet = 'texter0'; // 跟踪当前使用的字幕组 var arrays = [ [ "Argument arr0,1", "Argument arr0,2", "Argument arr0,3", ], [ "Argument arr1,1", "Argument arr1,2", "Argument arr1,3", ], [ "Argument arr2,1", "Argument arr2,2", "Argument arr2,3", ] ]; // 整合字幕数据,避免变量冲突 var subtitles = { texter0: [ 'Good day_0', 'This is the text for array 0 radio 1 - 3', '', 'Text for Array 0, to see the text for Array 1, press next.', 'I am still looking for this solution.', 'Many thanks for the help', '', 'Ciao' ], texter1: [ 'Good day_1', 'This is the text for array 1 radio 1 - 3', '', 'Text for Array 1, to see the text for Array 3, press next.', 'I am still looking for this solution.', 'Many thanks for the help', '', 'Ciao' ], texter2: [] // 可以为array2添加对应的字幕 }; var subtitleIndex = 0; var timerId = null; var sound = null; var audioLoaded = false; var renderArray = function renderArray(ary) { document.querySelector('#arr1').textContent = ary[0]; document.querySelector('#arr2').textContent = ary[1]; document.querySelector('#arr3').textContent = ary[2]; }; // 通用字幕切换函数 function changer() { var currentTexts = subtitles[currentSubtitleSet]; if (!currentTexts) return; document.getElementById('transbox').innerHTML = currentTexts[subtitleIndex]; subtitleIndex++; // 修复边界条件,确保所有字幕都能显示 if (subtitleIndex >= currentTexts.length) { subtitleIndex = 0; } } var previous = function previous(event) { currentArray -= 1; if (currentArray < 0) currentArray = arrays.length - 1; renderArray(arrays[currentArray]); // 更新对应字幕组 updateSubtitleSet(currentArray); // 加载对应音频 loadCorrespondingClip(currentArray); // 重置单选按钮 resetRadioButtons(); // 自动打开字幕 buttonShow(); }; var next = function next(event) { currentArray += 1; if (currentArray > arrays.length - 1) currentArray = 0; renderArray(arrays[currentArray]); // 更新对应字幕组 updateSubtitleSet(currentArray); // 加载对应音频 loadCorrespondingClip(currentArray); // 重置单选按钮 resetRadioButtons(); // 自动打开字幕 buttonShow(); }; // 根据currentArray更新字幕组 function updateSubtitleSet(arrayIndex) { switch(arrayIndex) { case 0: currentSubtitleSet = 'texter0'; break; case 1: currentSubtitleSet = 'texter1'; break; case 2: currentSubtitleSet = 'texter2'; break; default: currentSubtitleSet = 'texter0'; } // 重置字幕索引并显示第一条 subtitleIndex = 0; changer(); } // 加载对应音频 function loadCorrespondingClip(arrayIndex) { switch(arrayIndex) { case 0: loadClip('https://cdn.rawgit.com/mrdoob/three.js/master/examples/sounds/358232_j_s_song.mp3'); break; case 1: loadClip('https://cdn.rawgit.com/mrdoob/three.js/master/examples/sounds/376737_Skullbeatz___Bad_Cat_Maste.mp3'); break; case 2: loadClip('https://cdn.rawgit.com/mrdoob/three.js/master/examples/sounds/358232_j_s_song.mp3'); break; } } // 重置单选按钮 function resetRadioButtons() { document.getElementById('Radio1').checked = false; document.getElementById('Radio2').checked = false; document.getElementById('Radio3').checked = false; } var check = function check() { if (document.getElementById('Radio1').checked) { console.log('array ' + currentArray + ' with radio button 1 is checked'); } else if (document.getElementById('Radio2').checked) { console.log('array ' + currentArray + ' with radio button 2 is checked'); } else if (document.getElementById('Radio3').checked) { console.log('array ' + currentArray + ' with radio button 3 is checked'); } } var listener = new THREE.AudioListener(); var audioLoader = new THREE.AudioLoader(); function loadClip(audioUrl) { audioLoaded = false; console.log(`\nLoading sound...`); audioLoader.load(audioUrl, function(buffer) { sound = new THREE.PositionalAudio(listener); sound.setBuffer(buffer); sound.setRefDistance(20); sound.setLoop(false); sound.setVolume(5); console.log(`\nAudio finished loading!`); audioLoaded = true; }); } // 绑定播放按钮事件 document.getElementById('play').onclick = function playClip() { if (!sound || !audioLoaded) return; console.log(`\nplayClip()`); this.style.background = "#92ddb8"; document.getElementById('reset').disabled = false; this.disabled = true; document.getElementById('paused').disabled = false; sound.play(); // 启动字幕定时器 timerId = window.setInterval(changer, 1500); } // 绑定重置按钮事件 document.getElementById('reset').onclick = function resetClip() { if (!sound) return; console.log(`\nresetClip()`); document.getElementById('play').style.background = ""; document.getElementById('play').style.color = ""; this.disabled = false; document.getElementById('play').disabled = false; document.getElementById('paused').disabled = false; sound.stop(); resetRadioButtons(); // 重置字幕 subtitleIndex = 0; changer(); clearInterval(timerId); console.clear(); } // 绑定暂停按钮事件 document.getElementById('paused').onclick = function pausedClip() { if (!sound) return; console.log(`\npausedClip()`); document.getElementById('play').style.background = ""; document.getElementById('play').style.color = ""; document.getElementById('reset').disabled = false; document.getElementById('play').disabled = false; this.disabled = true; sound.pause(); // 暂停字幕定时器 clearInterval(timerId); } var info = document.getElementById("transbox"); info.style.display = "none"; // 显示字幕 function buttonShow() { info.style.display = "block"; } // 隐藏字幕 function buttonHide() { info.style.display = "none"; } // 初始化页面 function init() { // 第一次点击Next前初始化显示第一个数组 currentArray = 0; renderArray(arrays[currentArray]); updateSubtitleSet(currentArray); loadCorrespondingClip(currentArray); } // 绑定按钮事件 document.querySelector('#prev_button').addEventListener('click', previous); document.querySelector('#next_button').addEventListener('click', next); </script> </body> </html>
关键修改说明:
- 整合字幕数据:把两个字幕数组放到
subtitles对象里,避免全局变量冲突,同时方便扩展更多字幕组。 - 通用字幕切换函数:用
changer()替代原来的changer0/changer1,根据currentSubtitleSet自动切换对应字幕。 - 动态绑定音频与字幕:在Next/Previous切换时,自动更新
currentSubtitleSet,并加载对应音频,同时自动打开字幕。 - 修复定时器与状态同步:播放、暂停、重置时,统一管理字幕定时器,确保音频和字幕同步启停。
- 修复初始化逻辑:只保留一个
body onLoad事件,通过init()函数初始化默认状态。 - 修正字幕边界条件:当
subtitleIndex大于等于字幕数组长度时再重置,确保所有字幕都能完整显示。
现在你可以测试这个代码,点击Next/Prev会自动切换对应的音频和字幕,播放时字幕会定时切换,暂停和重置也会同步字幕状态,完全符合你的需求。
内容的提问来源于stack exchange,提问作者JaDoLo
相关产品推荐
相关产品推荐

