Vue3过渡动画仅滑入生效,滑出及遮罩无动画问题求助
Vue3 菜单过渡动画失效问题排查与修复
问题描述
使用Vue3父子组件实现菜单过渡动画时,点击Toggle按钮菜单可正常滑入,但无法触发滑出动画;同时遮罩层(cartbar-overlay)完全没有过渡效果。
错误原因
- 父组件提前销毁子组件:父组件给子组件添加了
v-show="cartVisible",当cartVisible变为false时,子组件会被立即隐藏销毁,内部的<transition>还未执行完离开动画就被移除,导致滑出和遮罩过渡失效。 - 双重显示控制冲突:子组件内部已经通过
v-show="props.visible"配合<transition>控制元素显示,父组件的外部v-show会打断内部过渡流程。
修复代码
修改后的父组件
<template> <div> <button @click="cartToggle">Toggle</button> </div> <div> <!-- 移除子组件上的v-show,让子组件内部自行控制过渡逻辑 --> <Child :visible="cartVisible" @update:visible="cartToggle" /> </div> </template> <script setup> import { ref } from "vue"; import Child from "./Child.vue"; const cartVisible = ref(false); const cartToggle = () => { cartVisible.value = !cartVisible.value; }; </script> <style scoped> span { cursor: pointer; } </style>
修改后的子组件
<script setup lang="ts"> import { toRefs, watchEffect, onBeforeUnmount } from "vue"; const props = defineProps({ visible: { type: Boolean, required: true, default: false, }, }); const { visible } = toRefs(props); const emit = defineEmits(["update:visible"]); const hideCartBar = () => { emit("update:visible", false); }; watchEffect(() => { document.body.style.overflow = visible.value ? "hidden" : "auto"; document.body.style.paddingRight = visible.value ? "15px" : "0px"; }); onBeforeUnmount(() => { document.body.style.overflow = "auto"; document.body.style.paddingRight = "0px"; }); </script> <template> <div class="cartbar"> <!-- 外层容器通过v-show控制,确保过渡完成后再隐藏整体 --> <div class="cartbar-wrapper" v-show="visible"> <transition name="fade"> <div v-show="visible" class="cartbar-overlay" @click="hideCartBar" ></div> </transition> <transition name="slide"> <div v-show="visible" class="cartbar-container"> <div class="cartbar__header"> <h2>Menu</h2> <span @click="hideCartBar" class="cursor-pointer">Close</span> </div> </div> </transition> </div> </div> </template> <style scoped> /* 过渡动画样式 */ .fade-enter-from, .fade-leave-to { opacity: 0; } .fade-enter-to, .fade-leave-from { opacity: 1; } .fade-enter-active, .fade-leave-active { transition: opacity 500ms; } .slide-enter-from, .slide-leave-to { transform: translateX(100%); } .slide-enter-to, .slide-leave-from { transform: translateX(0); } .slide-enter-active, .slide-leave-active { transition: transform 500ms; } /* 菜单容器样式 */ .cursor-pointer { cursor: pointer; } .cartbar-wrapper { z-index: 1300; position: fixed; top: 0; right: 0; bottom: 0; left: 0; display: flex; width: 100%; padding-left: calc(4px * 9); } .cartbar-overlay { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(0, 0, 0, 0.7); opacity: 1; cursor: pointer; } .cartbar-container { position: relative; display: flex; flex-direction: column; width: 100%; max-width: 460px; height: 100%; overflow: hidden; margin-left: auto; padding: calc(4px * 7); background-color: #f4f2f1; color: #706b5f; } </style>
修复说明
- 父组件移除子组件上的
v-show,避免提前销毁子组件中断过渡流程。 - 子组件的
cartbar-wrapper通过v-show="visible"控制整体显示,确保内部<transition>能完整执行进入和离开动画后,再隐藏整个容器。 - 保留子组件内部过渡元素的
v-show="visible",配合<transition>实现遮罩淡入淡出、菜单滑入滑出的完整动画效果。
内容的提问来源于stack exchange,提问作者Mr.P
相关产品推荐
相关产品推荐

