You need to enable JavaScript to run this app. 如何实现悬停触发CSS动画完成后反向播放,中途停悬不跳转变回初始状态?
优惠活动
大模型
产品
解决方案
定价
更多

如何实现悬停触发CSS动画完成后反向播放,中途停悬不跳转变回初始状态?

问题根源

当前遇到的跳变问题,核心原因有三个:

  • 原有动画设置了infinite alternate的无限循环模式,会自动往返播放,和你想要的「悬停才触发」的交互逻辑不匹配
  • 自行调整时把动画属性只绑定在:hover选择器上,鼠标移开时hover样式失效,动画被直接移除,浏览器会立刻把元素样式重置为CSS默认的初始状态,不会保留过渡动画
  • 单独用:after伪类选择器不生效,是因为伪类没有和.box元素绑定同一个动画实例,无法同步动画进度
实现方案

全程不需要写JavaScript,纯CSS就能实现目标交互,核心思路是从头到尾把同一个动画实例绑定在.box元素上,任何状态下都不把动画属性移除,只通过调整播放状态、播放方向控制动画进度,这样浏览器会持续追踪当前的动画进度,不会出现跳变。
具体调整点:

  • 移除原有动画的infinite、alternate参数,把无限循环改成单次可往返的动画
  • 开启animation-fill-mode: both,让动画在暂停、播放阶段都保留当前进度对应的样式,不会自动跳回默认值
  • 非悬停状态下设置动画为反向播放,鼠标移开时动画自动从当前所在的进度位置反向倒放回初始状态
  • 微调动画延迟参数,消除动画完全播完后切方向的微小视觉跳变,人眼无法感知差异
修正后的完整代码
* {
  margin: 0;
  padding: 0;
}
.container {
  background: #f0f0f0;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100vw;
  height: 100vh;
}
.box {
  border-radius: 20px;
  width: 60px;
  height: 60px;
  background: #f0f0f0;
  box-shadow: 0 0 0 #cccccc,
              0 0 0 #ffffff,
              10px 10px 10px #cccccc inset,
              -10px -10px 10px #ffffff inset;
  /* 动画全程绑定在元素上,不做移除 */
  animation: anime 3s cubic-bezier(0.16, 1, 0.3, 1) both;
  /* 初始状态:正向方向、暂停,停在0%关键帧的小方块位置 */
  animation-direction: normal;
  animation-play-state: paused;
  animation-delay: 0s;
}

/* 悬停状态:正向运行,从当前进度走到展开态 */
.box:hover {
  animation-direction: normal;
  animation-play-state: running;
  animation-delay: 0s;
}

/* 非悬停状态:反向运行,从当前进度收回到初始态 */
.box:not(:hover) {
  animation-direction: reverse;
  animation-play-state: running;
  /* 微调延迟消除切方向的微小跳变,人眼无法感知 */
  animation-delay: -0.01s;
}

/* 原有keyframes保持不变即可 */
@keyframes anime {
  0% {
    width: 60px;
    height: 60px;
    background: #f0f0f0;
    box-shadow: 0 0 0 #cccccc,
                0 0 0 #ffffff,
                10px 10px 10px #cccccc inset,
                -10px -10px 10px #ffffff inset;
  }
  25% {
    width: 60px;
    height: 60px;
    background: #f8f8f8;
    box-shadow: 10px 10px 10px #cccccc,
                10px 10px 10px #ffffff,
                0 0 0 #cccccc inset,
                0 0 0 #ffffff inset;
  }
  50% {
    width: 60px;
    height: 240px;
    background: #f8f8f8;
    box-shadow: 10px 10px 10px #cccccc,
                10px 10px 10px #ffffff,
                0 0 0 #cccccc inset,
                0 0 0 #ffffff inset;
  }
  100% {
    width: 480px;
    height: 240px;
    background: #fafafa;
    box-shadow: 40px 40px 40px #cccccc,
                0 0 0 #ffffff,
                0 0 0 #cccccc inset,
                2px 2px 2px #ffffff inset;
  }
}
注意事项
  • 不要在非悬停状态下删除元素上的animation属性,一旦动画实例被移除,浏览器就会直接把样式重置为默认值,触发硬跳变
  • 所有状态下的动画名称、时长、缓动函数必须保持完全一致,不能给不同状态设不一样的动画参数,否则会触发进度重置
  • 如果需要保留原来的1s延迟效果,只要在所有状态的animation参数里加上对应的延迟值即可,不需要改其他逻辑
  • 不需要使用:after伪类,因为动画直接绑定在.box本身,伪类的样式不会继承当前的动画进度

内容的提问来源于stack exchange,提问作者Sparkylc2

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.07 20:06:03