基于点击事件增删CSS类触发关键帧动画的问题排查
解决CSS关键帧动画切换抖动及失效问题
我尝试通过jQuery点击事件添加/移除CSS类,用CSS关键帧动画实现区块上下移动。但首次点击橙色div会抖动,后续点击完全没效果。我加了不同颜色验证点击事件已触发、类也正确添加,但颜色不是重点。我想严格用CSS动画(不用jQuery动画),还要用到CSS的reverse动画特性。
原代码
CSS
.swipeable { background: orange; position: absolute; bottom: 0; width: 100%; } @keyframes updown { 0% { transform: translateY(0px); } 100% { transform: translateY(40px); } } .swipeable-open { animation: updown; animation-direction: normal; animation-duration: 0.5s; background: red; } .swipeable-close { animation: updown; animation-direction: reverse; animation-duration: 0.5s; background: yellow; }
jQuery
$(document).on('click', '.swipeable', function() { const swipeable = $(this); if (swipeable.hasClass('swipeable-open')) { swipeable.removeClass('swipeable-open'); swipeable.addClass('swipeable-close'); } else { swipeable.addClass('swipeable-open'); swipeable.removeClass('swipeable-close'); } })
HTML
<div class="swipeable swipeable-close"> <h1> This is a header </h1> <p> Clicking on this orange area should move the orange area up. Clicking on it again should move the orange area down. </p> </div>
问题分析
- 动画结束后重置状态:默认
animation-fill-mode为none,动画执行完元素会回到初始样式,导致切换后位置不对。 - 动画未重新触发:切换类时,浏览器认为同一动画已执行过,不会再次运行。
- 初始状态不匹配:初始类
swipeable-close用reverse方向,但元素初始位置是translateY(0),反向动画会从100%帧(translateY(40px))往回走,导致首次点击时的抖动。
修复方案
修改后的CSS
.swipeable { background: orange; position: absolute; bottom: 0; width: 100%; /* 初始设为关闭状态,对应动画结束位置 */ transform: translateY(40px); } @keyframes updown { 0% { transform: translateY(40px); } 100% { transform: translateY(0px); } } .swipeable-open { animation: updown 0.5s normal forwards; background: red; } .swipeable-close { animation: updown 0.5s reverse forwards; background: yellow; }
修改后的jQuery
$(document).on('click', '.swipeable', function() { const swipeable = $(this); // 先移除所有动画类 swipeable.removeClass('swipeable-open swipeable-close'); // 强制浏览器重绘,确保动画能重新触发 void swipeable[0].offsetWidth; // 用data属性跟踪状态,避免类判断的延迟问题 if (swipeable.data('isOpen')) { swipeable.addClass('swipeable-close'); swipeable.data('isOpen', false); } else { swipeable.addClass('swipeable-open'); swipeable.data('isOpen', true); } })
修改后的HTML
<div class="swipeable"> <h1>This is a header</h1> <p>Clicking on this orange area should move the orange area up. Clicking on it again should move the orange area down.</p> </div>
核心改进点
animation-fill-mode: forwards:让动画结束后保持最终帧的位置,不会重置回初始样式。- 强制重绘:通过读取
offsetWidth触发浏览器重排,确保移除旧动画类后,新类的动画能重新执行。 - 调整初始位置与动画逻辑:把元素初始位置设为关闭状态,动画从关闭到打开是向上移动,反向动画则是向下移动,逻辑更直观,解决首次点击抖动问题。
内容的提问来源于stack exchange,提问作者Darren Gates
相关产品推荐
相关产品推荐

