如何实现点击单选按钮累加百分比更新进度条动画
核心问题定位
你原来的代码有两个致命缺陷导致效果不符合预期:
- 所有进度动画全是硬编码的CSS关键帧,动画起始值、结束值都是写死的固定值,根本不会读取进度条当前的实际宽度。每次新增动画class都会强制进度条从预设的起始值开始播放,必然出现重置跳变,根本做不到基于当前值的增减。
- 事件绑定逻辑完全混乱:全局注册click事件,每次页面任意位置点击都会重复执行domReady回调,给单选按钮重复绑定click事件,点的次数越多绑定的处理器越多,且动画class只增不减,逻辑完全失控。
修复方案
实现思路
彻底抛弃硬编码关键帧的方案,改用「JS动态计算总进度 + CSS transition平滑过渡」的模式:
- 给每个单选按钮自定义
data-progress属性,存储当前选项对应的进度权重 - 每次选选项时,遍历所有已选中的单选按钮,累加权重得到总进度值
- 直接给进度条设置新的宽度值,靠CSS的transition属性自动从当前宽度平滑过渡到目标宽度,从根源上避免跳变重置
- 事件只在DOM加载完成后绑定一次,没有重复绑定、冗余class的问题,不需要刷新页面,任意顺序点击都能正常响应。
完整修改后代码
CSS部分
删除所有硬编码的.progress-value-xx类和对应的@keyframes动画,只保留基础样式和过渡属性:
body{ padding:12px; } .col-6 label{ border:1px solid #333; padding: 8px 12px; border-radius: 4px; cursor: pointer; display: block; margin-bottom: 8px; } .col-6 input[type=radio]:checked + label{ border:2px solid #0d6efd; } .progress { background: #e9ecef; justify-content: flex-start; border-radius: 100px; align-items: center; position: relative; display: flex; height: 10px; width: 100%; margin-bottom:24px; } .progress-value { border-radius: 100px; background: #0d6efd; height: 100%; width: 0; /* 核心:自动从当前宽度平滑过渡到新宽度,无需写死动画帧 */ transition: width 1s ease; }
HTML部分
删除label上冗余的内联onclick事件,给每个单选按钮加data-progress属性标记对应进度值,顺便把过老的jQuery1.4.4换成稳定的3.x版本:
<head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> </head> <body> <div class="wrapp"> Speed <div class="progress"> <div class="progress-value"></div> </div> </div> <div class="row"> <label>Group 1</label> <div class="col-6"> <input type="radio" style="display:none;" id="js1" data-price="146.99" data-progress="25" value="option1a" name="ONE"> <label for="js1"> Option 1a </label> </div> <div class="col-6"> <input type="radio" style="display:none;" id="js2" data-price="123.99" data-progress="50" value="option2a" name="ONE"> <label for="js2"> Option 2a </label> </div> <hr style="margin-top:24px;"> <label>Group 2</label> <div class="col-6"> <input type="radio" style="display:none;" id="js3" data-price="116.99" data-progress="25" value="option1b" name="TWO"> <label for="js3"> Option 1b </label> </div> <div class="col-6"> <input type="radio" style="display:none;" id="js4" data-price="93.99" data-progress="40" value="option2b" name="TWO"> <label for="js4"> Option 2b </label> </div> </div> </body>
JS部分
删除所有冗余的全局点击监听、分散的事件绑定函数,统一在DOM加载完成后绑定一次事件:
$(document).ready(function() { // 给所有单选按钮绑定change事件,只绑一次 $('input[type=radio]').on('change', function() { let total = 0; // 遍历所有已选中的单选,累加进度 $('input[type=radio]:checked').each(function() { total += Number($(this).data('progress')); }) // 进度上限锁100%,防止溢出 total = Math.min(total, 100); // 更新进度条宽度,transition自动处理动画 $('.progress-value').width(total + '%'); }) // 初始化默认选中首选项,加载页面就显示初始进度 $('#js1').prop('checked', true).trigger('change'); })
扩展说明
- 后续如果要加新的单选组,只需要给对应input加
data-progress属性设置权重即可,不需要修改JS和CSS代码 - 动画时长、缓动效果直接改
.progress-value的transition属性就行,全局生效 - 进度值可以根据业务需求自由调整,比如要让某个选项占更高权重,直接改
data-progress的数字即可,不需要写新的动画关键帧
内容的提问来源于stack exchange,提问作者DaTZcodie
相关产品推荐
相关产品推荐

