Skip to content
Closed
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
28 changes: 21 additions & 7 deletions packages/runtime-dom/src/components/TransitionGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ import {
} from '@vue/runtime-core'
import { extend } from '@vue/shared'

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

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

Expand Down Expand Up @@ -145,10 +150,7 @@ const TransitionGroupImpl: ComponentOptions = /*@__PURE__*/ decorate({
instance,
),
)
positionMap.set(
child,
(child.el as Element).getBoundingClientRect(),
)
positionMap.set(child, calculatePosition(child.el as Element))
}
}
}
Expand Down Expand Up @@ -188,8 +190,20 @@ function callPendingCbs(c: VNode) {
}
}

function calculatePosition(el: Element) {
const rect = el.getBoundingClientRect()
const style = getComputedStyle(el)

const matrix = new DOMMatrixReadOnly(style.transform)
const [ox, oy] = style.transformOrigin.split(' ').map(v => parseFloat(v))
const point = new DOMPoint(ox, oy)
const transformed = point.matrixTransform(matrix)

return { left: transformed.x + rect.left, top: transformed.y + rect.top }
Comment on lines +198 to +202
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Transform origin parsing breaks for percentages/keywords

parseFloat on style.transformOrigin treats 50% as 50 (interpreted as 50 px) and returns NaN for keywords such as center, left, etc. That makes calculatePosition report incorrect or NaN offsets when authors use the default 50% 50% origin or keyword-based origins, so moves are computed from the wrong point and transitions visibly jump. Please resolve the transform-origin string into actual pixel offsets (handle %, the keyword set, and fall back to the axis size) before feeding it to the DOMMatrix.

🤖 Prompt for AI Agents
In packages/runtime-dom/src/components/TransitionGroup.ts around lines 198-202,
the transform-origin parsing currently uses parseFloat which treats "50%" as 50
and returns NaN for keywords; update calculatePosition to resolve
transformOrigin tokens into pixel offsets before creating the DOMPoint: split
the transformOrigin into x/y (support single-value shorthand by using '50%' for
missing axis), for each token if it endsWith('%') compute
(parseFloat(token)/100) * axisSize (rect.width for x, rect.height for y), else
if it is a keyword map 'left'->0%,'center'->50%,'right'->100% and similarly
'top'->0%,'center'->50%,'bottom'->100%, else parseFloat and treat as pixels,
then use those computed ox/oy pixel values to construct the DOMPoint and
continue with matrixTransform and return offsets.

}

function recordPosition(c: VNode) {
newPositionMap.set(c, (c.el as Element).getBoundingClientRect())
newPositionMap.set(c, calculatePosition(c.el as Element))
}

function applyTranslation(c: VNode): VNode | undefined {
Expand Down
Loading