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

Vue3 模态框leave阶段过渡动画失效问题求助

模态框关闭时Vue Transition leave动画不触发的解决方案

我在主组件中使用自定义模态框组件,添加appear属性后,模态框打开时过渡动画生效,但关闭时leave阶段的过渡无效果,完全没触发过渡类。相关代码如下:

模态框组件原代码

<template>
    <div
        v-if="show"
        class="fixed top-0 left-0 w-full h-full flex items-center justify-center bg-gray-900 bg-opacity-60 z-20"
    >
        <div class="fixed inset-0 bg-transparent" @click="zoomOutModal"></div>
        <Transition name="slide-fade" appear>
            <div
                class="max-w-lg bg-[#151f32] w-full p-5 rounded-lg shadow-lg z-30"
                :class="{
                    'transition-all scale-105 ease-linear duration-700': zoomIn,
                }"
            >
                <h2 class="text-lg font-medium mb-3 text-center">
                    <slot>Titre du modal</slot>
                </h2>
                <slot name="content"></slot>
            </div>
        </Transition>
    </div>
</template>

<script setup>
import { ref } from 'vue'
import Buttons from '@/Components/Buttons.vue'

const show = ref(true)
const zoomIn = ref(false)

const zoomOutModal = () => {
    zoomIn.value = true
    setTimeout(() => {
        zoomIn.value = false
    }, 300)
}
</script>

<style>
.slide-fade-enter-active,
.slide-fade-leave-active {
    transition: all 0.3s ease-out;
}

.slide-fade-enter-from {
    transform: translateY(100vh);
    opacity: 0;
}

.slide-fade-leave-to {
    transform: translateY(0vh);
    opacity: 0;
    transition: all 0.3s ease-out;
}
</style>

主组件原代码片段

<template>
....
....
<div class="mt-6">
    <Buttons @click="showModal = true"> Ajouter Type</Buttons>
    <Modal v-if="showModal" @close="showModal = false">
        Add Type
        <template v-slot:content>
            <form
                @keydown.enter="sumitAddType"
                @submit.prevent="sumitAddType"
                class="space-y-3"
            >
                <input
                    class="px-3 py-2 bg-dark-item-bg text-white border-none rounded-xl w-full placeholder-gray focus:focus:ring-blue/30"
                    type="text"
                    placeholder="Tapez le text le nom"
                    v-model="modalForm.name"
                />
                <div v-if="errors" class="mt-5 space-y-2">
                    <div
                        v-for="error in errors"
                        v-text="error"
                        class="text-red text-xs"
                    ></div>
                </div>
                <div class="flex gap-4">
                    <CancelButtons @click="showModal = false"
                        >Annuler</CancelButtons
                    >

                    <Buttons :disabled="modalForm.processing">
                        <div v-if="modalForm.processing">
                            <Spinner />
                        </div>
                        <div v-else>Ajouter</div>
                    </Buttons>
                </div>
            </form>
        </template>
    </Modal>
</div>
....
....
</template>

问题根源

  1. 模态框外层容器用v-if="show"直接绑定硬编码的true,且父组件用v-if="showModal"直接销毁组件,导致内部Transition来不及执行leave动画就被移除。
  2. 自定义的zoomIn逻辑和Vue Transition的过渡体系冲突,且关闭时未关联模态框的实际销毁时机。
  3. slide-fade-leave-to的transform: translateY(0vh)没有视觉变化,即使触发动画也看不到效果。

修复方案

1. 重构模态框组件

让组件接收父组件的显示状态,内部维护过渡生命周期,确保动画完成后再通知父组件关闭:

<template>
    <!-- 用v-show保持容器存在,避免直接销毁 -->
    <div
        class="fixed top-0 left-0 w-full h-full flex items-center justify-center bg-gray-900 bg-opacity-60 z-20"
        v-show="props.show"
    >
        <div class="fixed inset-0 bg-transparent" @click="handleClose"></div>
        <!-- Transition包裹的元素用v-if控制,触发leave动画 -->
        <Transition name="slide-fade" appear @after-leave="emit('close')">
            <div
                v-if="isVisible"
                class="max-w-lg bg-[#151f32] w-full p-5 rounded-lg shadow-lg z-30"
            >
                <h2 class="text-lg font-medium mb-3 text-center">
                    <slot>Titre du modal</slot>
                </h2>
                <slot name="content"></slot>
            </div>
        </Transition>
    </div>
</template>

<script setup>
import { ref, watch } from 'vue'
import Buttons from '@/Components/Buttons.vue'

// 接收父组件的显示状态
const props = defineProps({
    show: {
        type: Boolean,
        default: false
    }
})
const emit = defineEmits(['close'])

// 内部维护显示状态,控制Transition的触发
const isVisible = ref(props.show)

// 监听父组件的show变化,同步内部状态
watch(() => props.show, (newVal) => {
    if (newVal) isVisible.value = true
})

// 触发关闭逻辑:先隐藏元素执行leave动画
const handleClose = () => {
    isVisible.value = false
}
</script>

<style>
.slide-fade-enter-active,
.slide-fade-leave-active {
    transition: all 0.3s ease-out;
}

.slide-fade-enter-from {
    transform: translateY(100vh);
    opacity: 0;
}

.slide-fade-leave-to {
    /* 修改为和进入方向相反的动画 */
    transform: translateY(100vh);
    opacity: 0;
}
</style>

2. 调整主组件使用方式

移除v-if,改用:show传递状态,让模态框内部处理销毁时机:

<template>
....
....
<div class="mt-6">
    <Buttons @click="showModal = true"> Ajouter Type</Buttons>
    <!-- 用:show传递状态,不再用v-if直接销毁组件 -->
    <Modal :show="showModal" @close="showModal = false">
        Add Type
        <template v-slot:content>
            <form
                @keydown.enter="sumitAddType"
                @submit.prevent="sumitAddType"
                class="space-y-3"
            >
                <input
                    class="px-3 py-2 bg-dark-item-bg text-white border-none rounded-xl w-full placeholder-gray focus:focus:ring-blue/30"
                    type="text"
                    placeholder="Tapez le text le nom"
                    v-model="modalForm.name"
                />
                <div v-if="errors" class="mt-5 space-y-2">
                    <div
                        v-for="error in errors"
                        v-text="error"
                        class="text-red text-xs"
                    ></div>
                </div>
                <div class="flex gap-4">
                    <CancelButtons @click="showModal = false"
                        >Annuler</CancelButtons
                    >

                    <Buttons :disabled="modalForm.processing">
                        <div v-if="modalForm.processing">
                            <Spinner />
                        </div>
                        <div v-else>Ajouter</div>
                    </Buttons>
                </div>
            </form>
        </template>
    </Modal>
</div>
....
....
</template>

关键修复说明

  • 保留容器实例:用v-show维持模态框外层容器的存在,避免父组件直接销毁组件导致Transition失效。
  • 动画生命周期联动:通过@after-leave事件监听动画完成,再通知父组件更新状态,确保leave动画完整执行。
  • 修正过渡样式:将slide-fade-leave-to的transform改为和进入方向相反的translateY(100vh),让关闭动画有视觉效果。

内容的提问来源于stack exchange,提问作者dev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 12:15:33