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

滚动时<section>关联音频淡入淡出功能的实现方向咨询

滚动触发区段音频淡入淡出实现方案

1. 基础结构配置

  • 给每个绑定音频的<section>添加自定义属性,示例:<section data-audio-src="xxx.mp3" data-max-volume="0.7">,data-audio-src存音频路径,data-max-volume定义该音频的最大播放音量,默认可设为0.8
  • 页面初始化阶段预创建所有音频实例,初始音量设为0,状态设为暂停,避免滚动时重复创建实例导致性能损耗

2. 可见性监听逻辑

优先使用IntersectionObserver API实现,比直接绑定scroll事件性能高10倍以上,不会造成滚动卡顿:

  • 配置观察者的阈值参数为Array.from({length: 11}, (_, i) => i * 0.1),也就是每变化10%的可见比例就触发回调,方便精准匹配音量和可见度的对应关系
  • 回调逻辑:根据当前section在视口的可见占比,计算目标音量 = 可见比例 * 该section配置的最大音量

3. 平滑音频过渡实现

  • 不要直接修改audio.volume值,用requestAnimationFrame实现帧级渐变,每次调整步长设为0.02,避免音量跳变
  • 加状态锁:只有当目标音量从0升至大于0的时候,才调用audio.play(),播放中只调整音量即可;当目标音量降到0之后,主动调用audio.pause()重置播放位置,节省资源

4. 兼容&边界处理

  • 所有浏览器都限制音频自动播放,必须在用户首次交互(点击/触摸页面)之后再初始化音频实例,否则会触发播放拦截报错,可全局绑定一次click事件触发初始化
  • 多个section同时进入视口时,多音频会按各自可见比例叠加播放,正好匹配你要的街道漫步多声源效果;如果觉得混音太杂,可加逻辑限制最多同时播放音量最高的2-3个音频
  • 移动端适配时需监听resize事件修正视口高度,避免地址栏收起/展开导致的可见性判断误差

核心代码示例

// 全局交互触发初始化
let audioInited = false
document.addEventListener('click', initAudios, { once: true })

const audioMap = new Map()
function initAudios() {
  if (audioInited) return
  audioInited = true
  // 遍历所有带音频的section
  document.querySelectorAll('section[data-audio-src]').forEach(sec => {
    const audio = new Audio(sec.dataset.audioSrc)
    audio.volume = 0
    audio.loop = true // 循环播放可保留,不需要直接删除该行
    audioMap.set(sec, {
      audio,
      maxVolume: parseFloat(sec.dataset.maxVolume || 0.8),
      currentVolume: 0,
      targetVolume: 0
    })
  })
  // 初始化观察者
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      const audioData = audioMap.get(entry.target)
      if (!audioData) return
      // 计算目标音量
      audioData.targetVolume = entry.intersectionRatio * audioData.maxVolume
    })
    // 批量更新音量
    updateVolume()
  }, { threshold: Array.from({length: 11}, (_,i) => i*0.1) })
  // 监听所有section
  audioMap.forEach((_, sec) => observer.observe(sec))
}

function updateVolume() {
  let needUpdate = false
  audioMap.forEach(data => {
    if (Math.abs(data.currentVolume - data.targetVolume) < 0.001) {
      data.currentVolume = data.targetVolume
      data.audio.volume = data.currentVolume
      if (data.currentVolume === 0 && !data.audio.paused) {
        data.audio.pause()
      }
      return
    }
    needUpdate = true
    // 渐变调整音量
    const step = data.targetVolume > data.currentVolume ? 0.02 : -0.02
    data.currentVolume = Math.max(0, Math.min(data.maxVolume, data.currentVolume + step))
    data.audio.volume = data.currentVolume
    if (data.currentVolume > 0 && data.audio.paused) {
      data.audio.play().catch(() => {})
    }
  })
  if (needUpdate) {
    requestAnimationFrame(updateVolume)
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 19:45:06