mirror of https://github.com/vuejs/core.git
fix(transition): reflow before leave-active class after leave-from (#12288)
re-fix #2593
This commit is contained in:
parent
a20a4cb36a
commit
4b479db61d
|
@ -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) {
|
||||
|
|
|
@ -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: `
|
||||
<div id="container">
|
||||
<transition name="test-reflow">
|
||||
<div v-if="toggle" class="test-reflow">content</div>
|
||||
</transition>
|
||||
</div>
|
||||
<button id="toggleBtn" @click="click">button</button>
|
||||
`,
|
||||
setup: () => {
|
||||
const toggle = ref(false)
|
||||
const click = () => (toggle.value = !toggle.value)
|
||||
return {
|
||||
toggle,
|
||||
click,
|
||||
}
|
||||
},
|
||||
}).mount('#app')
|
||||
})
|
||||
|
||||
// if transition starts while there's v-leave-active added along with v-leave-from, its bad, it has to start when it doesnt have the v-leave-from
|
||||
|
||||
// enter
|
||||
await classWhenTransitionStart()
|
||||
await transitionFinish()
|
||||
|
||||
// leave
|
||||
expect(await classWhenTransitionStart()).toStrictEqual([
|
||||
'test-reflow',
|
||||
'test-reflow-leave-from',
|
||||
'test-reflow-leave-active',
|
||||
])
|
||||
|
||||
expect(await style('.test-reflow', 'opacity')).toStrictEqual('0.9')
|
||||
|
||||
await nextFrame()
|
||||
expect(await classList('.test-reflow')).toStrictEqual([
|
||||
'test-reflow',
|
||||
'test-reflow-leave-active',
|
||||
'test-reflow-leave-to',
|
||||
])
|
||||
|
||||
await transitionFinish()
|
||||
expect(await html('#container')).toBe('<!--v-if-->')
|
||||
})
|
||||
|
||||
test('warn when used on multiple elements', async () => {
|
||||
createApp({
|
||||
render() {
|
||||
|
|
|
@ -39,6 +39,7 @@ interface PuppeteerUtils {
|
|||
value(selector: string): Promise<string>
|
||||
html(selector: string): Promise<string>
|
||||
classList(selector: string): Promise<string[]>
|
||||
style(selector: string, property: keyof CSSStyleDeclaration): Promise<any>
|
||||
children(selector: string): Promise<any[]>
|
||||
isVisible(selector: string): Promise<boolean>
|
||||
isChecked(selector: string): Promise<boolean>
|
||||
|
@ -120,6 +121,19 @@ export function setupPuppeteer(args?: string[]): PuppeteerUtils {
|
|||
return page.$eval(selector, (node: any) => [...node.children])
|
||||
}
|
||||
|
||||
async function style(
|
||||
selector: string,
|
||||
property: keyof CSSStyleDeclaration,
|
||||
): Promise<any> {
|
||||
return await page.$eval(
|
||||
selector,
|
||||
(node, property) => {
|
||||
return window.getComputedStyle(node)[property]
|
||||
},
|
||||
property,
|
||||
)
|
||||
}
|
||||
|
||||
async function isVisible(selector: string): Promise<boolean> {
|
||||
const display = await page.$eval(selector, node => {
|
||||
return window.getComputedStyle(node).display
|
||||
|
@ -195,6 +209,7 @@ export function setupPuppeteer(args?: string[]): PuppeteerUtils {
|
|||
value,
|
||||
html,
|
||||
classList,
|
||||
style,
|
||||
children,
|
||||
isVisible,
|
||||
isChecked,
|
||||
|
|
|
@ -16,11 +16,21 @@
|
|||
.test-appear,
|
||||
.test-enter,
|
||||
.test-leave-active,
|
||||
.test-reflow-enter,
|
||||
.test-reflow-leave-to,
|
||||
.hello,
|
||||
.bye.active,
|
||||
.changed-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
.test-reflow-leave-active,
|
||||
.test-reflow-enter-active {
|
||||
-webkit-transition: opacity 50ms ease;
|
||||
transition: opacity 50ms ease;
|
||||
}
|
||||
.test-reflow-leave-from {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.test-anim-enter-active {
|
||||
animation: test-enter 50ms;
|
||||
-webkit-animation: test-enter 50ms;
|
||||
|
|
Loading…
Reference in New Issue