如何让元素保持CSS关键帧动画结束后的最终状态?
解决关键帧动画保持结束状态+重复触发的问题
核心问题拆解
你现在的痛点有两个:一是需要让关键帧动画结束后保持最终状态,二是隐藏后再次显示时动画无法重新触发。之前用JS设置行内样式的方式会覆盖CSS规则,导致第二次触发时动画初始状态失效。
最优方案:用CSS animation-fill-mode: forwards 替代JS设置样式
这是最简洁的解决办法,完全不需要JS干预元素样式:
- 修改动画的CSS规则,添加
forwards属性:
.block-a.is-visible .block-a-1 { animation: fadeIn 2s linear 2s forwards; /* 新增forwards */ }
animation-fill-mode: forwards的作用就是让元素在动画结束后,保持关键帧最后一帧的所有属性,完美替代你之前在animationend事件里手动设置opacity:1的操作。
- 删掉JS里的
animationend监听代码,因为不需要了。
解决重复触发动画的问题
现在移除is-visible再重新添加时,浏览器会认为动画状态没有变化,不会重新执行。解决这个问题很简单,只需要在移除类后强制浏览器重绘一次:
修改你的点击事件代码:
const $btn = document.querySelector('button'); const $block = document.querySelector('.block-a'); $btn.addEventListener('click', function() { const hasVisibleClass = $block.classList.contains("is-visible"); if (hasVisibleClass) { $block.classList.remove("is-visible"); // 强制浏览器重绘,重置动画状态 void $block.offsetHeight; } else { $block.classList.add("is-visible"); } })
void $block.offsetHeight会让浏览器立即计算元素的高度,触发重绘,这样下次添加is-visible类时,浏览器会重新识别动画,从头执行。
完整可运行代码
HTML
<button>Do Block 1</button> <div class="block-a"> <span class="block-a-1">Block A-1</span> <h1>Block A</h1> </div>
CSS
body { height: 100vh; display: flex; align-items: center; justify-content: center; flex-direction: column; } button { font-size: 32px; margin-bottom: 24px; cursor: pointer; } .block-a { width: 25vw; height: 50vh; background-color: red; padding: 20px; color: #fff; transform: translateY(150%); transition: all .25s linear; } .block-a.is-visible { transform: translateY(0); transition: all .1s ease-in; } .block-a-1 { font-size: 32px; font-weight: bold; color: white; background-color: blue; padding: 10px; opacity: 0; } .block-a.is-visible .block-a-1 { animation: fadeIn 2s linear 2s forwards; } @keyframes fadeIn { 0% { opacity: 0; } 100% { opacity: 1; } }
JS
const $btn = document.querySelector('button'); const $block = document.querySelector('.block-a'); $btn.addEventListener('click', function() { const hasVisibleClass = $block.classList.contains("is-visible"); if (hasVisibleClass) { $block.classList.remove("is-visible"); void $block.offsetHeight; } else { $block.classList.add("is-visible"); } })
为什么原来的方法失效?
你之前用JS设置style.opacity = 1会给元素添加行内样式,行内样式的优先级远高于CSS类规则。当你隐藏再显示时,虽然is-visible类重新添加了动画,但行内的opacity:1会覆盖动画初始帧的opacity:0,导致动画看起来没触发。而animation-fill-mode: forwards是通过CSS规则维持状态,移除类后元素会自动回到初始的opacity:0,不会有样式冲突。
内容的提问来源于stack exchange,提问作者Daveh0
相关产品推荐
相关产品推荐

