StreamElements告警框Typewriter打字机音效随文字启停问题
StreamElements打字机告警组件音效同步问题
需求说明
- 使用平台:StreamElements直播覆盖层告警组件
- 基础效果:告警框采用Typewriter打字机动画逐字输出文本
- 音效预期逻辑:
- 仅在文字逐字输出阶段播放键盘敲击音效
- 单段文字输出完成、进入停顿等待阶段时,音效自动暂停/停止
- 全部文字输出完成后,音效直接终止
- 当前问题:现有代码中添加的键盘音效会持续播放到整个告警流程结束,不符合预期。
- 已尝试通过代码结构标注音效播放/暂停的触发节点,相关实现代码如下:
现有代码
JavaScript
const name='{name}'; const amount='{amount}'; const wrapData=(message)=>{ return message.replace(/\b{name}\b/g, '<span class="username">{name}</span>').replace(/\{currency}{amount}\b/g, '<span class="amount">\{currency}{amount}</span>').replace(/\b{amount}\{currency}/g, '<span class="amount">{amount}\{currency}</span>').replace(/\b{amount}\b/g, '<span class="amount">{amount}</span>').replace(/\{currency}/g, '<span class="amount">\{currency}</span>'); } const typewriter = new Typewriter('.text-container', { skipAddStyles:true, delay:50 }); const row1a=wrapData(`{row1}`); const row2a=wrapData(`{row2}`); const row3a=wrapData(`{row3}`); const row4a=wrapData(`{row4}`); var audio = new Audio('keyboard.wav'); audio.play(); typewriter.typeString(`<div class="row1 message" style="display: inline-block;">${row1a}</div>`) .pauseFor(200) .stop(); audio.pause(); typewriter.typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">DONE!</div>`) .pauseFor(50) .stop(); audio.play(); typewriter.typeString(`<div class="row2 message">${row2a}</div>`) .pauseFor(50) .typeString(`<div class="row3 message">${row3a}</div>`) .pauseFor(50) .typeString(`<div class="row4 message">System Message: <span class="username">Thank you!</span></div>`) .pauseFor(50) .typeString(`<div class="row4 message" style="display: inline-block;">${row4a}</div>`) .pauseFor(200) .stop(); audio.pause(); typewriter.typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">DONE!</div>`) .pauseFor(50) .start();
CSS
@import url('https://fonts.googleapis.com/css?family={fontName}'); * { font-family: '{{fontName}}', sans-serif; color:{{fontColor}}; font-weight:{{fontWeight}}; } body{ background-image:url('{image}'); background-size:contain; background-repeat:no-repeat; position:relative; margin:0px; } .text-container{ width:100%; height:100%; position: absolute; top: 10px; padding-left:15px; } .alertbox-message-emote{ height:1em; } .added-divs { display: inline-block; color:{{row1FontColor}}; font-weight:{{row1FontWeight}}; font-size:{{row1FontSize}}px; } .row1{ color:{{row1FontColor}}; font-weight:{{row1FontWeight}}; font-size:{{row1FontSize}}px; animation-delay:1s; } .row2{ color:{{row2FontColor}}; font-weight:{{row2FontWeight}}; font-size:{{row2FontSize}}px; animation-delay:3.5s; } .row3{ color:{{row3FontColor}}; font-weight:{{row3FontWeight}}; font-size:{{row3FontSize}}px; animation-delay:5.5s; } .row4{ color:{{row4FontColor}}; font-weight:{{row4FontWeight}}; font-size:{{row4FontSize}}px; animation-delay:7.5s; } .username{ color:{usernameFontColor}; } .amount{ color:{amountFontColor}; }
HTML
<script src="https://unpkg.com/typewriter-effect@latest/dist/core.js"></script> <div class="text-container"> <div class="row1 message"></div> <div class="row2 message"></div> <div class="row3 message"></div> <div class="row4 message"></div> </div>
问题原因
现有代码里的audio.play()/audio.pause()是同步执行逻辑,不会等待Typewriter的异步打字队列运行,代码加载时会直接顺序跑完所有播放/暂停调用,完全无法和动画节点对齐,最终导致音效播放逻辑混乱。
修复方案
直接调用Typewriter内置的队列方法绑定音效逻辑,不需要手动在链式调用外插入播放暂停代码:
- 利用字符创建回调,在每打出一个字时触发短音效,重置音频进度实现连续敲击效果
- 用队列自带的
callFunction()方法,在每段打字结束、进入停顿、全流程结束的节点插入音效停止逻辑,完全对齐动画时序 - 删除原有写在链式调用外的
audio.play()/audio.pause()代码避免冲突 - 键盘音效建议裁剪为100ms以内的单键敲击短音频,播放效果更真实
修复后可直接使用的JS代码
const name='{name}'; const amount='{amount}'; const wrapData=(message)=>{ return message.replace(/\b{name}\b/g, '<span class="username">{name}</span>').replace(/\{currency}{amount}\b/g, '<span class="amount">\{currency}{amount}</span>').replace(/\b{amount}\{currency}/g, '<span class="amount">{amount}\{currency}</span>').replace(/\b{amount}\b/g, '<span class="amount">{amount}</span>').replace(/\{currency}/g, '<span class="amount">\{currency}</span>'); } // 初始化音效 可自行调整volume值改音量 const audio = new Audio('keyboard.wav'); audio.volume = 0.3; const typewriter = new Typewriter('.text-container', { skipAddStyles:true, delay:50, // 每打出一个字符触发敲击音效 onCreateTextNode: () => { audio.currentTime = 0; audio.play().catch(()=>{}); } }); const row1a=wrapData(`{row1}`); const row2a=wrapData(`{row2}`); const row3a=wrapData(`{row3}`); const row4a=wrapData(`{row4}`); // 按队列时序绑定音效停止逻辑 typewriter .typeString(`<div class="row1 message" style="display: inline-block;">${row1a}</div>`) .callFunction(() => { audio.pause(); audio.currentTime = 0; }) .pauseFor(200) .typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">DONE!</div>`) .callFunction(() => { audio.pause(); audio.currentTime = 0; }) .pauseFor(50) .typeString(`<div class="row2 message">${row2a}</div>`) .pauseFor(50) .typeString(`<div class="row3 message">${row3a}</div>`) .pauseFor(50) .typeString(`<div class="row4 message">System Message: <span class="username">Thank you!</span></div>`) .pauseFor(50) .typeString(`<div class="row4 message" style="display: inline-block;">${row4a}</div>`) .callFunction(() => { audio.pause(); audio.currentTime = 0; }) .pauseFor(200) .typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">.</div>`) .pauseFor(200) .typeString(`<div class="added-divs">DONE!</div>`) .callFunction(() => { // 全流程结束 彻底终止音效 audio.pause(); audio.currentTime = 0; }) .start();
CSS和HTML部分不需要修改,保持原有代码即可正常运行。
内容的提问来源于stack exchange,提问作者Can
相关产品推荐
相关产品推荐

