前端进度条实现与优化

进度条随着任务的进程而进行增长,任务暂停,进度条的动画也会随之暂停。比如视频进度条的主要实现功能:

  • 支持播放、暂停、重播
  • 播放结束后,播放次数+1,并重新开始播放

推荐的实现

component:

// index.jsx
import { useState } from 'react'
import './index.css'

let totalTime = 3000  // 假设视频播放为3s

function App() {
    const [isPlay, setIsPlay] = useState(false)  // 是否播放
    const [count, setCount] = useState(0)  // 播放次数
    const [type, setType] = useState(0)   // 使用哪个动画。0: @keyframes play; 1: @keyframes replay;
    
    // 暂停 && 播放
    const handleVideo = () => setIsPlay(!isPlay);
    
    // 重播
    const replay = () => {
        setIsPlay(true)
        setType(type ? 0 : 1)
    }
    
    // 动画结束时触发的事件
    const end = () => {
        setCount(count + 1)  // 播放次数 +1
        replay()   // 重新开始播放
    }
    
    return (
        <div id="root">
            <button onClick={handleVideo}>{ isPlay ? '暂停' : '播放' }</button>
            <button onClick={replay}>重播</button>
            <span>{ `播放次数为:${count}` }</span>
            <div className="container">
                <div 
                    className={`progress ${isPlay ? 'play' : 'pause'}`} 
                    style={{
                        animationDuration: `${totalTime}ms`,
                        animationName: `${type ? 'replay' : 'play'}`
                    }}
                    onAnimationEnd={end}  // 动画结束时的事件
                />
            </div>
        </div>
    )
}

CSS:

@keyframes play {   
    to {
        width: 100%;
    }
}

@keyframes replay {
    to {
        width: 100%;
    }
}

.container {
    height: 10px;
    border-radius: 5px;
    border: 1px solid black;
}

.progress {
    height: 100%;
    width: 0;
    background-color: red;
    animation-timing-function: linear;
}

.progress.play {     /* 使animation动画启动 */
    animation-play-state: running;
}

.progress.pause {    /* 使animation动画暂停 */
    animation-play-state: paused;
}

1. 设置了两个@keyframes动画在使进度条重新播放时进行切换,即点击 "重播" 时,直接切换到另一个动画,很好的实现进度条从0开始递增。

2. 使用两个类名的样式,分别用于控制动画的播放和暂停。

此方案不需要去一直修改数据来驱动视图的改变,减少了框架内的大量计算,提升了不少的性能。

相比如果通过定时器来快速递增变量progress以此来实现进度增加——变量每次改变都会驱动视图重新计算渲染,必然是性能很差的。

不过两种方案均会引发频繁的重排和重绘,仍有优化的空间

优化

启用GPU加速,避开重排和重绘的环节,将进度条单独提升到一个图层,即不影响其它元素

只需要改动其css内容即可:

@keyframes play {     /* 通过transform来启用GPU加速,跳过重排重绘阶段 */
    0% {  
        transform: translateX(-50%) scaleX(0);  /* 用 scaleX 来代替 width */
    }

    to {
        transform: translateX(0) scaleX(1);
    }
}

@keyframes replay {
    0% {
        transform: translateX(-50%) scaleX(0);
    }

    to {
        transform: translateX(0) scaleX(1);
    }
}

.container {
    height: 10px;
    border-radius: 5px;
    border: 1px solid black;
}

.progress {
    height: 100%;
    width: 100%;   /* 初始宽度为100%,因为要对其缩放 */
    background-color: red;
    will-change: transform;   /* 通过will-change告知浏览器提前做好优化准备 */
    animation-timing-function: linear;
}

.progress.play {    
    animation-play-state: running;
}

.progress.pause {   
    animation-play-state: paused;
}

设置进度条 width: 100%,当进度走到 50%的时候,通过scaleX(0.5)将其缩放一半,此时进度条长度为容器的一半且居中,此时通过translateX(-25%)将其向左平移到最左端(因为进度条占了容器的一半且居中,表明左右的留白正好分别是 (100% - 50%) / 2 = 25%,所以x = -(100% - 50%) / 2 = -25%),所以当初始状态scaleX(0)时,translateX的值为 -(100% - 0%) / 2 = -50%。

总结

通过启用GPU加速,最终视频进度条的性能可以提升了大约 50% 左右。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的猫96

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值