如何实现悬停触发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
相关产品推荐
相关产品推荐

