Skip to content

fix(studio): a drag in a composition without GSAP keeps the element's CSS translate - #4750

Closed
miguel-heygen wants to merge 4 commits into
mainfrom
fix/studio-no-gsap-stylesheet-translate-drag
Closed

miguel-heygen wants to merge 4 commits into
mainfrom
fix/studio-no-gsap-stylesheet-translate-drag

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

What

In a composition that does not load GSAP, dragging an element that a stylesheet places with translate (or a transform translation) no longer jumps when you let go. A 30px drag now moves it 30px, while dragging, after the drop and after a reload.

Why

Before this change, the element followed the pointer during the drag and then jumped by its stylesheet offset on release. For example, with #title { translate: 0 -200px }, a 30px drag down landed it about 200 composition pixels lower. The wrong position was saved to the file, so a reload showed it too.

The first move of an element in such a composition is saved as gsap.set("#title", { x, y }), and the server adds GSAP to the file for that write. That part is intended: a static element's position lives in one GSAP set. When GSAP loads, it folds the element's CSS translate and its transform's translation into x/y, so the saved x/y has to include them. The drag took its starting x/y from gsap.getProperty. With no GSAP on the page yet, it used 0, so the saved value dropped the stylesheet offset.

To reproduce on main: take a composition with no GSAP script and #title { translate: 0 -200px }, then drag #title 30px down. It jumps on drop, and the file gets y: 87 where it should get y: -113.

Related work

Refs #4734. That PR changes the drag teardown in the same file; this change is only in the drag setup, and the two do not overlap.

How

createManualOffsetDragMember reads its starting x/y from gsap.getProperty when GSAP is on the page. When it is not, it now computes the value GSAP's CSS parser will report for the element once the commit loads it:

  • the computed CSS translate (px, or % of the element's box), plus
  • the translation of rotate, scale and the computed transform combined in that order, as GSAP builds them, minus
  • a -50% centering, which GSAP keeps in xPercent/yPercent rather than x/y.

When the browser cannot parse that combination (for example rotate: x 30deg), GSAP keeps only the plain transform, and so does this.

Nothing changes when GSAP is loaded.

Test plan

  • Unit tests added: a stylesheet translate, a stylesheet transform, and a -50% centering. The first two fail on main (expected { x: 40, y: 30 } to deeply equal { x: 40, y: -170 }). The third fails if the centering rule is removed.
  • Also tested: a CSS rotate or scale turns or scales the transform's translation, a combination the browser cannot parse falls back to the plain transform without throwing, and with GSAP on the page the base still comes from GSAP. Checked against real GSAP 3.12.5 in Chrome on 27 element styles: all match.
  • Manual testing performed: walked in Studio on fixture compositions. Each walk did two moves, then undo, redo and reload. Walks covered a no-GSAP translate element, a no-GSAP transform: translate(-50%, -50%) element, and a GSAP composition with a stylesheet translate to check for regressions.
  • Documentation updated (if applicable)
  • Comments follow CONTRIBUTING.md "Comments"

Walk results. Screen px in a 1600x900 Studio, from where the element started; the pointer moved +40,+30 and then -60,+20.

Composition Build Saved first move After drop After two moves Undo / redo After reload
No GSAP, translate: 0 -200px main y: 87 40, 99 -20, 50 40, 99 / -20, 119 -20, 119
No GSAP, translate: 0 -200px this PR y: -113 40, 30 -20, -19 40, 30 / -20, 50 -20, 50
No GSAP, transform: translate(-50%, -50%) main x: 116 66, 30 6, 50 66, 30 / 6, 50 6, 50
No GSAP, transform: translate(-50%, -50%) this PR x: 42 40, 30 -20, 50 40, 30 / -20, 50 -20, 50
GSAP, translate: 0 -200px main and this PR (same) y: -113 40, -39 -20, -19 40, 30 / -20, 50 -20, 50

The saved values in the GSAP composition are the same on main and on this PR. The live -39 there, and the -19 after the second no-GSAP move on this PR, is the stylesheet translate showing again under GSAP's transform after a drop. That is #4734's bug. #4734 is now on main, and on main plus this PR every cell in both compositions is exact: 40, 30 / -20, 50.

Before

No GSAP, translate: 0 -200px. Held at the end of the drag, the title is where the pointer put it:

Before: title held under the pointer at the end of the drag

After the drop it jumps down, and that is what gets saved:

Before: title jumped down after the drop

No GSAP, transform: translate(-50%, -50%). After the drop it jumps right:

Before: centered title jumped right after the drop

After

No GSAP, translate: 0 -200px. After the drop the title stays where the pointer left it:

After: title stays under the pointer after the drop

No GSAP, transform: translate(-50%, -50%). It stays put too:

After: centered title stays put after the drop

@miguel-heygen
miguel-heygen force-pushed the fix/studio-no-gsap-stylesheet-translate-drag branch from c67c0d2 to a5228f6 Compare September 30, 2026 04:37
@miguel-heygen
miguel-heygen marked this pull request as ready for review September 30, 2026 05:17
@miguel-heygen
miguel-heygen marked this pull request as draft September 30, 2026 05:21
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Fixed on main by #4799: a move in a composition without GSAP now saves the element's own CSS translate and adds no GSAP, so a stylesheet translate is kept. The edit-accuracy gate's move cases on this fixture (a stylesheet translate, no GSAP) all pass on main. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants