问题
需要实现一个下载按钮的动画,要求在鼠标进入容器元素(container的mouseenter事件)时触发弹跳动画(使用的是tailwind的预设动画animate-bounce
),在离开时结束动画。具体实现时遇到了一个尴尬的问题:动画初始帧和结束帧与默认css不一致时,进入和退出动画都会顿一下,效果如下。
需求和解决方案
现在要求是鼠标进出容器时动画都流畅地显示,这里先给出tailwindanimate-bounce
的默认实现。
animation: bounce 1s infinite;
@keyframes bounce {
0%, 100% {
transform: translateY(-25%);
animationTimingFunction: cubic-bezier(0.8, 0, 1, 1);
}
50% {
transform: translateY(0);
animationTimingFunction: cubic-bezier(0, 0, 0.2, 1);
}
}
解决开始动画问题
1. 重写关键帧和过渡函数
首先想到的是自己实现一个animate-bounce
,使得动画在0% 100%
的时候,transform
的值为translateY(0)
,除此外要满足原来的动画效果(即动画下部分节奏较快),还需要调整贝塞尔曲线,这无疑是比较麻烦的,所以这里就不实现了。
2. animation-delay
动画延迟一般用于实现一些重复效果的序列动画。如:
源代码codepen.io
易看出这个动画是由一个个带有正值animation-delay的小圆组成,那么如果animation-delay设置为负值会怎么样呢?
给原来的弹跳动画增加一个负的延迟,则动画提前执行,初始帧将从后面开始。
animation-delay: -0.5s
/* 0.5s恰好为animation-duration的一半 */
效果:
动画初始帧虽然正常了,但是鼠标移出时动画戛然而止,同样也很突兀。
解决结束动画问题
关键点在于移出类名animate-bounce
的时机,animation提供了一个animationiteration
的事件回调,在完整走完一次动画时会触发这个事件。借助它可以在恰当的时机结束动画。
// 父容器绑定鼠标离开事件
container.addEventListener("mounseleave", () => {
animateTarget.addEventlistener("animationiteration", () => {
// 因为tailwind这个动画的结束在translateY(-25%)的位置
// 为了满足结束时translateY为0,需要延迟500ms
setTimeout(() => {
animateTarget.classList.remove("animate-bounce");
}, 500);
}, { once: true })
})
此时无论何时鼠标进入或者离开容器,都能流畅的控制动画效果。 效果:
总结
- 进入动画:使用负值animation-delay对齐初始帧。
- 退出动画:监听
animationiteration
事件,使动画完整走完一个流程再结束。 完整代码: stackblitz
首次写文,如果觉得有用,麻烦点个赞吧。