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

多音频进度与进度条同步最优算法(支持网络延迟处理)

多音轨进度条同步最优实现方案

问题根源

原实现中每个音频的timeupdate事件都会独立更新进度条,多音轨播放时事件触发频率不一致导致进度条跳变;同时未处理网络延迟引发的音轨加载完成时间差,造成播放起始点不同步。

核心同步算法

1. 主音轨基准策略

  • 选定单个音轨作为主同步源,进度条仅依据主音轨的播放进度更新
  • 其余音轨(从音轨)实时对齐主音轨的currentTime,消除播放差异

2. 网络延迟适配

  • 等待所有音轨加载完成后统一启动播放,避免因加载速度不同导致的起始延迟
  • 记录音轨加载时间差,播放时补偿延迟确保同步启动

3. 双向同步机制

  • 拖动进度条时,同步设置所有音轨的currentTime
  • 主音轨播放时,定期校准从音轨进度,抵消播放漂移

优化后完整代码

HTML

<div class="range-container">
  <input type="range" name="range" step="0.1" id="range" min="0" max="100" />
  <label for="range">0</label>
</div>
<button id="playbtn" style="position: fixed;top: 45px;">Play</button>
<button id="pausebtn" style="position: fixed;top: 80px;">Pause</button>

CSS

@import url("https://fonts.googleapis.com/css2?family=Lato&display=swap");

* {
  box-sizing: border-box;
}

body {
  background-image: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
  font-family: "Lato", sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  overflow: hidden;
  margin: 0;
}

.range-container {
  position: relative;
}

input[type="range"] {
  width: 300px;
  margin: 18px 0;
  -webkit-appearance: none;
}

input[type="range"]:focus,input[type="range"]:active {
  outline: none;
  cursor: grabbing !important;
}

input[type="range"] + label {
  background-color: #fff;
  position: absolute;
  top: -25px;
  left: 110px;
  width: 80px;
  padding: 5px 0;
  text-align: center;
  border-radius: 4px;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}

/* Chrome & Safari */
input[type="range"]::-webkit-slider-runnable-track {
  background: #ddd;
  border-radius: 4px;
  width: 100%;
  height: 10px;
  cursor: pointer;
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  height: 20px;
  width: 20px;
  background: #2196F3;
  border-radius: 50%;
  cursor: grab;
  margin-top: -5px;
}

JavaScript

const range = document.getElementById("range");
const playBtn = document.getElementById("playbtn");
const pauseBtn = document.getElementById("pausebtn");

// 音轨列表(可按需保留需要的语言轨)
const soundTracks = [
  {id:1,sound:"https://actions.google.com/sounds/v1/alarms/dosimeter_alarm.ogg"},
  {id:2,sound:"https://actions.google.com/sounds/v1/alarms/phone_alerts_and_rings.ogg"}
];

let sounds = [];
let mainTrack = null; // 主音轨
let syncInterval = null; // 同步定时器

// 等待所有音轨加载完成的Promise
function waitForAllTracksLoaded() {
  const loadPromises = soundTracks.map(track => {
    return new Promise((resolve) => {
      const audio = new Audio(track.sound);
      audio.id = track.id;
      audio.autoplay = false;
      audio.preload = 'auto';
      audio.addEventListener("canplaythrough", () => resolve(audio));
      sounds.push(audio);
    });
  });
  return Promise.all(loadPromises);
}

// 更新进度条
function updateProgressBar(currentTime, duration) {
  if (!duration) return;
  const percentComplete = (currentTime / duration) * 100;
  range.value = percentComplete;
  range.nextElementSibling.textContent = percentComplete.toFixed(1);
}

// 同步所有从音轨到主音轨进度
function syncSlaveTracks() {
  if (!mainTrack || mainTrack.paused) return;
  const masterTime = mainTrack.currentTime;
  sounds.forEach(track => {
    if (track !== mainTrack && !track.paused) {
      // 允许微小误差(±0.1s),避免频繁调整导致卡顿
      if (Math.abs(track.currentTime - masterTime) > 0.1) {
        track.currentTime = masterTime;
      }
    }
  });
  updateProgressBar(masterTime, mainTrack.duration);
}

// 播放控制
playBtn.onclick = async () => {
  // 等待所有音轨加载完成
  await waitForAllTracksLoaded();
  // 选第一个音轨作为主音轨
  mainTrack = sounds[0];
  // 同步启动所有音轨
  sounds.forEach(track => track.play());
  // 启动定时同步(每100ms校准一次)
  syncInterval = setInterval(syncSlaveTracks, 100);
};

// 暂停控制
pauseBtn.onclick = () => {
  sounds.forEach(track => track.pause());
  clearInterval(syncInterval);
};

// 进度条拖动同步所有音轨
range.addEventListener("input", (e) => {
  const percent = +e.target.value;
  if (!mainTrack || !mainTrack.duration) return;
  const targetTime = (percent / 100) * mainTrack.duration;
  sounds.forEach(track => {
    track.currentTime = targetTime;
  });
  updateProgressBar(targetTime, mainTrack.duration);
});

关键优化点说明

  • 主音轨单一基准:进度条仅由主音轨驱动,避免多源冲突
  • 加载等待机制:用Promise.all确保所有音轨加载完成后再播放,解决网络延迟导致的起始不同步
  • 增量同步校准:每100ms检查一次从音轨与主音轨的进度差,仅当误差超过0.1s时调整,平衡同步精度与性能
  • 双向控制支持:拖动进度条时同步所有音轨的播放位置,保证交互一致性

内容的提问来源于stack exchange,提问作者EaBengaluru

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 14:10:53