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>
问题根源
- 模态框外层容器用
v-if="show"直接绑定硬编码的true,且父组件用v-if="showModal"直接销毁组件,导致内部Transition来不及执行leave动画就被移除。 - 自定义的
zoomIn逻辑和Vue Transition的过渡体系冲突,且关闭时未关联模态框的实际销毁时机。 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
相关产品推荐
相关产品推荐

