diff --git a/packages/runtime-dom/src/components/Transition.ts b/packages/runtime-dom/src/components/Transition.ts index 3a1a661d90b..6c6344bfcac 100644 --- a/packages/runtime-dom/src/components/Transition.ts +++ b/packages/runtime-dom/src/components/Transition.ts @@ -181,7 +181,13 @@ export function resolveTransitionProps( onAppearCancelled = onEnterCancelled, } = baseProps - const finishEnter = (el: Element, isAppear: boolean, done?: () => void) => { + const finishEnter = ( + el: Element & { _enterCancelled?: boolean }, + isAppear: boolean, + done?: () => void, + isCancelled?: boolean, + ) => { + el._enterCancelled = isCancelled removeTransitionClass(el, isAppear ? appearToClass : enterToClass) removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass) done && done() @@ -240,7 +246,10 @@ export function resolveTransitionProps( }, onEnter: makeEnterHook(false), onAppear: makeEnterHook(true), - onLeave(el: Element & { _isLeaving?: boolean }, done) { + onLeave( + el: Element & { _isLeaving?: boolean; _enterCancelled?: boolean }, + done, + ) { el._isLeaving = true const resolve = () => finishLeave(el, done) addTransitionClass(el, leaveFromClass) @@ -249,9 +258,14 @@ export function resolveTransitionProps( } // add *-leave-active class before reflow so in the case of a cancelled enter transition // the css will not get the final state (#10677) - addTransitionClass(el, leaveActiveClass) - // force reflow so *-leave-from classes immediately take effect (#2593) - forceReflow() + if (!el._enterCancelled) { + // force reflow so *-leave-from classes immediately take effect (#2593) + forceReflow() + addTransitionClass(el, leaveActiveClass) + } else { + addTransitionClass(el, leaveActiveClass) + forceReflow() + } nextFrame(() => { if (!el._isLeaving) { // cancelled @@ -269,11 +283,11 @@ export function resolveTransitionProps( callHook(onLeave, [el, resolve]) }, onEnterCancelled(el) { - finishEnter(el, false) + finishEnter(el, false, undefined, true) callHook(onEnterCancelled, [el]) }, onAppearCancelled(el) { - finishEnter(el, true) + finishEnter(el, true, undefined, true) callHook(onAppearCancelled, [el]) }, onLeaveCancelled(el) { diff --git a/packages/vue/__tests__/e2e/Transition.spec.ts b/packages/vue/__tests__/e2e/Transition.spec.ts index c0863a75991..66be3a4af57 100644 --- a/packages/vue/__tests__/e2e/Transition.spec.ts +++ b/packages/vue/__tests__/e2e/Transition.spec.ts @@ -3,7 +3,7 @@ import path from 'node:path' import { Transition, createApp, h, nextTick, ref } from 'vue' describe('e2e: Transition', () => { - const { page, html, classList, isVisible, timeout, nextFrame, click } = + const { page, html, classList, style, isVisible, timeout, nextFrame, click } = setupPuppeteer() const baseUrl = `file://${path.resolve(__dirname, './transition.html')}` @@ -2986,6 +2986,55 @@ describe('e2e: Transition', () => { ) }) + test('reflow after *-leave-from before *-leave-active', async () => { + await page().evaluate(() => { + const { createApp, ref } = (window as any).Vue + createApp({ + template: ` +