You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于点击事件增删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>

问题分析

  1. 动画结束后重置状态:默认animation-fill-mode为none,动画执行完元素会回到初始样式,导致切换后位置不对。
  2. 动画未重新触发:切换类时,浏览器认为同一动画已执行过,不会再次运行。
  3. 初始状态不匹配:初始类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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:52:40