Skip to content

fix(runtime-dom): use offsetLeft and offsetTop instead of getBoundingClientRect to avoid transform scale affect animation #6108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions packages/runtime-dom/src/components/TransitionGroup.ts
Original file line number Diff line number Diff line change
@@ -28,10 +28,16 @@ import {
} from '@vue/runtime-core'
import { extend } from '@vue/shared'

const positionMap = new WeakMap<VNode, DOMRect>()
const newPositionMap = new WeakMap<VNode, DOMRect>()
interface Position {
top: number
left: number
}

const positionMap = new WeakMap<VNode, Position>()
const newPositionMap = new WeakMap<VNode, Position>()
const moveCbKey = Symbol('_moveCb')
const enterCbKey = Symbol('_enterCb')

export type TransitionGroupProps = Omit<TransitionProps, 'mode'> & {
tag?: string
moveClass?: string
@@ -127,10 +133,10 @@ const TransitionGroupImpl: ComponentOptions = {
instance,
),
)
positionMap.set(
child,
(child.el as Element).getBoundingClientRect(),
)
positionMap.set(child, {
left: (child.el as HTMLElement).offsetLeft,
top: (child.el as HTMLElement).offsetTop,
})
}
}
}
@@ -184,7 +190,10 @@ function callPendingCbs(c: VNode) {
}

function recordPosition(c: VNode) {
newPositionMap.set(c, (c.el as Element).getBoundingClientRect())
newPositionMap.set(c, {
left: (c.el as HTMLElement).offsetLeft,
top: (c.el as HTMLElement).offsetTop,
})
}

function applyTranslation(c: VNode): VNode | undefined {
56 changes: 56 additions & 0 deletions packages/vue/__tests__/e2e/TransitionGroup.spec.ts
Original file line number Diff line number Diff line change
@@ -630,4 +630,60 @@ describe('e2e: TransitionGroup', () => {
},
E2E_TIMEOUT,
)

// #6105
test(
'with scale',
async () => {
await page().evaluate(() => {
const { createApp, ref, onMounted } = (window as any).Vue
createApp({
template: `
<div id="container">
<div class="scale" style="transform: scale(2) translateX(50%) translateY(50%)">
<transition-group tag="ul">
<li v-for="item in items" :key="item">{{item}}</li>
</transition-group>
<button id="toggleBtn" @click="click">button</button>
</div>
</div>
`,
setup: () => {
const items = ref(['a', 'b', 'c'])
const click = () => {
items.value.reverse()
}

onMounted(() => {
const styleNode = document.createElement('style')
styleNode.innerHTML = `.v-move {
transition: transform 0.5s ease;
}`
document.body.appendChild(styleNode)
})

return { items, click }
},
}).mount('#app')
})

const original_top = await page().$eval('ul li:nth-child(1)', node => {
return node.getBoundingClientRect().top
})
const new_top = await page().evaluate(() => {
const el = document.querySelector('ul li:nth-child(1)')
const p = new Promise(resolve => {
el!.addEventListener('transitionstart', () => {
const new_top = el!.getBoundingClientRect().top
resolve(new_top)
})
})
;(document.querySelector('#toggleBtn') as any)!.click()
return p
})

expect(original_top).toBeLessThan(new_top as number)
},
E2E_TIMEOUT,
)
})