Vue3中基于CSS Keyframes的淡入淡出动画集成求助
Vue3 集成CSS缩放弹跳动画方案
需求说明
需要将原生HTML+CSS+JS实现的缩放弹跳动画集成到Vue3项目中,核心是结合Vue的响应式机制与DOM更新逻辑,实现和原生效果一致的动画切换。
原生实现代码
原生环境下通过requestAnimationFrame()确保动画流畅触发,代码如下:
HTML
<!DOCTYPE html> <html> <head> <title>Rectangle Animation</title> </head> <body> <div id="myRectangle"></div> <button id="toggleButton">Toggle Animation</button> </body> </html>
CSS
#myRectangle { width: 100px; height: 150px; background-color: green; margin: 20px; transform: scale(0); } .animation-out { animation-duration: 4s; animation-timing-function: ease; animation-direction: normal; animation-fill-mode: forwards; animation-name: scale-easeInOutBounce; } .animation-in { animation-duration: 4s; animation-timing-function: ease; animation-direction: reverse; animation-fill-mode: forwards; animation-name: scale-easeInOutBounce; } @keyframes scale-easeInOutBounce { 0% { transform: scale(1); } 2% { transform: scale(0.99); } 4% { transform: scale(1); } 10% { transform: scale(0.97); } 14% { transform: scale(0.99); } 22% { transform: scale(0.88); } 32% { transform: scale(0.99); } 42% { transform: scale(0.6); } 50% { transform: scale(0.5); } 58% { transform: scale(0.4); } 68% { transform: scale(0.01); } 78% { transform: scale(0.12); } 86% { transform: scale(0.01); } 90% { transform: scale(0.03); } 96% { transform: scale(0); } 98% { transform: scale(0.01); } 100% { transform: scale(0); } }
JavaScript
document.getElementById('toggleButton').addEventListener('click', function() { var rectangle = document.getElementById('myRectangle'); if (rectangle.classList.contains('animation-in')) { rectangle.classList.remove('animation-in'); requestAnimationFrame(() => {rectangle.classList.add('animation-out');}) } else { rectangle.classList.remove('animation-out'); requestAnimationFrame(() => {rectangle.classList.add('animation-in');}) } });
Vue3集成问题分析
原Vue3基础代码直接通过切换active响应式变量来切换类名,无法触发动画的原因是:
- Vue的响应式更新会批量处理DOM修改,类名切换会在同一帧完成,浏览器无法识别状态变化
- 缺少类似原生
requestAnimationFrame()的时机控制,无法确保移除旧类后触发重绘再添加新类
修正后的Vue3实现方案
结合Vue的nextTick和原生requestAnimationFrame,确保动画状态切换的时机正确,同时修复原代码中的变量未声明问题:
<!DOCTYPE html> <html> <head> <script src="https://unpkg.com/vue@next"></script> <style> .element { width: 100px; height: 150px; background-color: green; margin: 20px; transform: scale(0); } .animation-out { animation-duration: 4s; animation-timing-function: ease; animation-delay: 0s; animation-iteration-count: 1; animation-direction: normal; animation-fill-mode: forwards; animation-play-state: running; animation-name: scale-easeInOutBounce; } .animation-in { animation-duration: 4s; animation-timing-function: ease; animation-delay: 0s; animation-iteration-count: 1; animation-direction: reverse; animation-fill-mode: forwards; animation-play-state: running; animation-name: scale-easeInOutBounce; } @keyframes scale-easeInOutBounce { 0% { transform: scale(1); } 2% { transform: scale(0.99); } 4% { transform: scale(1); } 10% { transform: scale(0.97); } 14% { transform: scale(0.99); } 22% { transform: scale(0.88); } 32% { transform: scale(0.99); } 42% { transform: scale(0.6); } 50% { transform: scale(0.5); } 58% { transform: scale(0.4); } 68% { transform: scale(0.01); } 78% { transform: scale(0.12); } 86% { transform: scale(0.01); } 90% { transform: scale(0.03); } 96% { transform: scale(0); } 98% { transform: scale(0.01); } 100% { transform: scale(0); } } </style> </head> <body> <div id="app"></div> <template id="app-template"> <div class="element" :class="currentAnimationClass"></div> <button @click="trigger()" style="padding:5px;margin:10px;width:200px;height:30px; position: absolute; bottom: 10px; left: 50px;">{{triggerLabel}}</button> </template> <script> const { createApp, ref, computed, nextTick } = Vue; const App = { template: '#app-template', setup() { const active = ref(false); const currentAnimationClass = ref(''); const triggerLabel = computed(() => { return !active.value ? '开始进入动画' : '开始退出动画'; }); const trigger = async () => { // 先清空当前动画类 currentAnimationClass.value = ''; // 等待Vue完成DOM更新 await nextTick(); // 在下一次重绘前添加目标动画类 requestAnimationFrame(() => { active.value = !active.value; currentAnimationClass.value = active.value ? 'animation-in' : 'animation-out'; }); }; return { currentAnimationClass, triggerLabel, trigger }; } }; createApp(App).mount('#app'); </script> </body> </html>
关键修改点
- 使用
nextTick等待Vue完成DOM更新,确保旧动画类已移除 - 通过
requestAnimationFrame在下一次浏览器重绘前添加新动画类,让浏览器识别状态变化并触发动画 - 改用响应式变量
currentAnimationClass管理动画类,修复原代码中变量未声明的问题
补充说明
requestAnimationFrame()是浏览器提供的动画API,用于通知浏览器在下一次重绘前执行动画更新逻辑,相比setTimeout或setInterval,它能同步浏览器的重绘节奏,避免动画卡顿,实现更流畅的视觉效果。
内容的提问来源于stack exchange,提问作者GrumpyDog
相关产品推荐
相关产品推荐

