You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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内置的队列方法绑定音效逻辑,不需要手动在链式调用外插入播放暂停代码:

  1. 利用字符创建回调,在每打出一个字时触发短音效,重置音频进度实现连续敲击效果
  2. 用队列自带的callFunction()方法,在每段打字结束、进入停顿、全流程结束的节点插入音效停止逻辑,完全对齐动画时序
  3. 删除原有写在链式调用外的audio.play()/audio.pause()代码避免冲突
  4. 键盘音效建议裁剪为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 02:18:04