解决小程序视频进度条卡顿:基于CSS Transition的平滑方案

作者:像素猫时间:2026-10-24 13:50:35浏览:562

针对微信小程序中视频进度条因TimeUpdate事件触发频率低(100-350ms)导致的卡顿问题,本文提出一种基于CSS Transition的优化方案。通过固定动画时长(如0.5s)并预测性更新进度条宽度,利用CSS平滑过渡特性消除生硬跳变,在暂停或拖动时可能存在微小延迟,但整体体验显著提升。

解决进度条显示不流畅的问题

大多数前端开发者都曾开发过音频或视频播放器,实现基础功能并不困难。近期在微信小程序开发中,我遇到了类似微博视频流的需求,其中包含自定义进度条的功能。初版实现后发现进度条跳动明显、不够顺滑。尝试搜索网络上的解决方案,但未找到合适的方法。随后观察了微信小程序中“微博”应用的进度条表现,发现其动画效果同样生硬。下方GIF展示了该现象,开发者可自行搜索微信小程序中的微博视频进行验证。

解决进度条显示不流畅的问题

常规实现方式分析

决定对该问题进行优化,首先梳理现有的常规实现方案。

  • 监听TimeUpdate事件
  • 获取当前播放时间,结合视频总时长计算进度百分比(公式:currentTime / duration * 100)
  • 将计算出的百分比赋值给进度条的width属性

现有方案依赖事件获取当前播放时间,该事件的触发间隔约为100至350毫秒。以下记录的小程序事件对象队列展示了这一现象。


[
    {"detail":{"currentTime":0.10509,"duration":5.83}},
    {"detail":{"currentTime":0.364527,"duration":5.83}},
    {"detail":{"currentTime":0.613648,"duration":5.83}},
]

当前方案的主要缺陷在于:每次事件触发即直接更新进度条,缺乏过渡动画,导致视觉效果生硬。以下展示了总时长为5秒的进度条变化过程。

解决进度条显示不流畅的问题

核心代码逻辑如下:


const onProgress = (e, $dom) => {
    const updateFunc = (percent) => {
        $dom.style.width = percent+'%'
    }
    let percent = ((e.detail.currentTime / e.detail.duration) * 100).toFixed(1)
    updateFunc(percent)
}

利用CSS Transition属性优化

我们可以利用CSS动画属性进行优化,为实现灵活控制,选择使用transition。transition允许定义动画执行时长,当改变width属性时,transition会在指定时间内以动画形式平滑改变进度条宽度。关键在于:动画执行时长必须固定,且在上一个动画结束前应避免再次修改width,否则可能引发冲突,导致动画效果异常。

  • 设定合理的transition执行时间:0.5秒
  • 根据视频总时长,计算0.5秒在进度条中所占的百分比(公式:100 / duration / 2)
  • 在第一次TimeUpdate事件触发时,执行width改变,将进度条直接设置到0.5秒对应的位置:width = 100 / duration / 2
  • 在非第一次TimeUpdate事件中,仅当currentTime超过上一次进度条位置时,才更新当前进度条百分比

上述逻辑可能较为抽象,通过图示说明:

解决进度条显示不流畅的问题

  1. 当第一次TimeUpdate事件触发时(例如在0.1336秒,具体值随机,可能在0.1至0.3秒之间),我们将width设置为对应0.5秒的位置。此时进度条与视频播放同步运动,与真实进度的偏差仅为约0.1秒。在动画执行的0.5秒期间,TimeUpdate事件会多次触发。
  2. 当某次TimeUpdate的currentTime(例如0.7123秒,同样为随机值)大于上次执行的0.5秒位置时,此时进度条大致位于0.5秒附近。我们再次触发下一个0.5秒的动画,即将width设置为对应1秒的进度条位置。
  3. 随后,当下次迭代中currentTime大于1秒时,width设置为对应1.5秒的位置。以此类推,循环执行。

核心代码实现如下:


const playControl = {
  percent: 0,
  time: 0,
  duration: 0,
  first: true
}
const onProgress = (e, $dom) => {
    const updateFunc = (percent) => {
        playControl.percent = percent
        playControl.time = e.detail.currentTime
        $dom.style.width = percent+'%'
    }
    //当前视频进度第一次更新
    if (playControl.first) {
        playControl.duration = e.detail.duration
        playControl.first = false
        updateFunc(100 / e.detail.duration / 2)
    } else {
        let percent = ((e.detail.currentTime / e.detail.duration) * 100).toFixed(1)
        if (percent - playControl.percent > 0 || e.detail.currentTime >= e.detail.duration) {
            updateFunc(percent)
        }
    }
}

最终效果对比(注:GIF图像可能存在压缩折损)

解决进度条显示不流畅的问题

60秒版本的进度条看起来与普通版本差异不大?若将另一个60秒版本遮挡进行对比,仍可发现细微区别。

若解释仍显晦涩或未能完全理解,可直接查看GitHub仓库代码,代码可直接运行:https://github.com/zimv/smooth-progress

此方案在部分场景下(如暂停、拖动操作)可能会产生短暂延迟,但个人总体认为其带来的体验提升利大于弊。

至此,关于JS实现进度条顺滑版的详细方案介绍完毕。如需了解更多相关JS进度条顺滑版内容,请搜索本站历史文章或继续浏览下方相关文章。感谢大家的支持!

作者最新文章