CSS/JS圆形进度条reload重构后中点过渡动画不流畅问题求解
圆形任务进度条实现与问题修复
功能说明
我实现了一款展示已完成任务进度的CSS/JS圆形进度条,特性如下:
- 进度发生增减变化时,会在0.5s内完成平滑过渡动画
- 进度条由左右两半环形结构拼接实现,JS中额外添加了延迟和过渡时间调整逻辑,适配总任务数为奇数的场景
问题与根因
初始加载页面后运行完全正常,点击reload按钮清空进度条容器、重新插入所有DOM元素重构进度条后,进度条走到中点位置时过渡动画不再流畅,刷新页面后中点过渡效果就会恢复正常。
更新:已定位问题根因为JS的reload事件逻辑中,插入新的进度条DOM后,遗漏了重新执行bar_transition = $('.circle .bar_transition .sub-progress')获取新DOM元素的步骤,导致后续动画逻辑操作的是已被移除的旧DOM节点。
完整实现代码
JavaScript 代码
function updateSubtaskProgressBar(subs_completed, total_subs, progress_bar_transition, toggle, mod, reload_btn) { var left_side = $(".sub-progress-bar_transition .circle .left .sub-progress"); var right_side = $(".sub-progress-bar_transition .circle .right .sub-progress"); progress = subs_completed / total_subs * 360; transition = 500; delay = transition / 2; rot_reminder = 0; if (progress < 180) { rot_right = 0; right_side.css({ 'transform': 'rotate(' + rot_right + 'deg)' }); rot_left = progress rot_reminder = 180 - rot_left if (rot_reminder != 0 && mod != 0 && toggle == 0) { progress_bar_transition.css({ 'transition': 'all ' + delay / 1000 + 's ease-in' }); setTimeout(function () { progress_bar_transition.css({ 'transition': 'all ' + delay / 1000 + 's ease-out' }); left_side.css({ 'transform': 'rotate(' + rot_left + 'deg)' }); }, delay); toggle = 1 - toggle reload_btn.data('toggle', toggle) } else { progress_bar_transition.css({ 'transition': 'all ' + transition / 1000 + 's ease-in-out' }); left_side.css({ 'transform': 'rotate(' + rot_left + 'deg)' }); } } else { rot_left = 180; left_side.css({ 'transform': 'rotate(' + rot_left + 'deg)' }); rot_right = progress - 180; rot_reminder = rot_right if (rot_reminder != 0 && mod != 0 && toggle == 1) { progress_bar_transition.css({ 'transition': 'all ' + delay / 1000 + 's ease-in' }); setTimeout(function () { progress_bar_transition.css({ 'transition': 'all ' + delay / 1000 + 's ease-out' }); right_side.css({ 'transform': 'rotate(' + rot_right + 'deg)' }); }, delay); toggle = 1 - toggle reload_btn.data('toggle', toggle) } else { progress_bar_transition.css({ 'transition': 'all ' + transition / 1000 + 's ease-in-out' }); right_side.css({ 'transform': 'rotate(' + rot_right + 'deg)' }); } } } function setSubtaskProgressBar(subs_completed, total_subs, bar_transition) { modulo = 0; current_progress = 0; var left_side = $(".sub-progress-bar_transition .circle .left .sub-progress"); var right_side = $(".sub-progress-bar_transition .circle .right .sub-progress"); modulo = total_subs%2 current_progress = subs_completed / total_subs * 360; var reload = $('.reload') reload.data('completed', subs_completed) reload.data('total', total_subs) reload.data('modulo', modulo) if (current_progress < 180) { rot_left = current_progress rot_right = 0 left_side.css({ 'transform': 'rotate(' + rot_left + 'deg)' }); reload.data('toggle', 1) } else { rot_left = 180 rot_right = current_progress - 180 left_side.css({ 'transform': 'rotate(' + rot_left + 'deg)' }); right_side.css({ 'transform': 'rotate(' + rot_right + 'deg)' }); reload.data('toggle', 0) } bar_transition.css({ 'transition': 'none' }); } $(document).ready(function () { subs_completed = 3; total_subs = 5; bar_transition = $('.circle .bar_transition .sub-progress') number = $('.number') number.html(subs_completed + '/' + total_subs) setSubtaskProgressBar(subs_completed, total_subs, bar_transition) $(document).on('click', '.add', function () { subs_completed += 1 $('.number').html(subs_completed + '/' + total_subs) reload = $('.reload') reload.data('completed', subs_completed) modulo = reload.data('modulo') toggle = reload.data('toggle') total = reload.data('total') updateSubtaskProgressBar(subs_completed, total, bar_transition, toggle, modulo, reload) }) $(document).on('click', '.remove', function () { subs_completed -= 1; $('.number').html(subs_completed + '/' + total_subs) reload = $('.reload') reload.data('completed', subs_completed) modulo = reload.data('modulo') toggle = reload.data('toggle') total = reload.data('total') updateSubtaskProgressBar(subs_completed, total, bar_transition, toggle, modulo, reload) }) $(document).on('click', '.reload', function () { num = $(this).data('completed') + '/' + $(this).data('total') data = '<div class="subtask-circular-progress sub-progress-bar_transition" data-completed="'+$(this).data('completed')+'" data-total="'+$(this).data('total')+'" data-modulo="'+$(this).data('modulo')+'" data-toggle="'+$(this).data('toggle')+'"><div class="inner"></div><div class="number">'+num+'</div><div class="circle"><div class="bar_transition left"><div class="sub-progress"></div></div><div class="bar_transition right"><div class="sub-progress"></div></div></div></div>' wrapper = $('.wrapper').empty(); wrapper.append(data) // 修复:重新获取新插入的进度条DOM元素 bar_transition = $('.circle .bar_transition .sub-progress') setSubtaskProgressBar(subs_completed, total_subs, bar_transition) console.log('reload') }) });
CSS 代码
body{ background-color: #333; } .subtask-circular-progress { position: absolute; left: 50%; top: 40%; height: 100px; width: 100px; background-color: red; } .subtask-circular-progress .inner { position: absolute; z-index: 6; top: 50%; left: 50%; height: 80px; width: 80px; margin: -40px 0 0 -40px; border-radius: 100%; background: #454545; } .subtask-circular-progress .number { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10; font-size: 15px; font-weight: 500; color: white; } .subtask-circular-progress .bar_transition { position: absolute; height: 100%; width: 100%; background: #fff; -webkit-border-radius: 100%; border-radius: 100%; clip: rect(0px, 100px, 100px, 50px); } .circle .bar_transition .sub-progress { position: absolute; height: 100%; width: 100%; -webkit-border-radius: 100%; border-radius: 100%; clip: rect(0px, 50px, 100px, 0px); background: #4158d0; transition: all 0.5s ease-in-out; } .circle .left .sub-progress { transform: rotate(0deg); } .circle .right { transform: rotate(180deg); z-index: 3; } .circle .right .sub-progress { transform: rotate(0deg); } .buttons{ position: absolute; left: 50%; top: 60%; }
HTML 代码
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="wrapper"> <div class="subtask-circular-progress sub-progress-bar_transition"> <div class="inner"></div> <div class="number">100%</div> <div class="circle"> <div class="bar_transition left"> <div class="sub-progress"></div> </div> <div class="bar_transition right"> <div class="sub-progress"></div> </div> </div> </div> </div> <div class="d-flex buttons"> <button type="button" class="btn btn-success add">+</button> <button type="button" class="btn btn-success remove ms-4">-</button> <button type="button" class="btn btn-success reload ms-4" data-completed="" data-total="" data-modulo="" data-toggle="">reload</button> </div>
内容的提问来源于stack exchange,提问作者Luka Reeson
相关产品推荐
相关产品推荐

