You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 08:10:03