Skip to content

fix(studio): rotate from the angle a CSS-rotated element shows, without GSAP too - #4780

Closed
miguel-heygen wants to merge 2 commits into
fix/studio-edit-resize-size-holdfrom
fix/studio-rotate-from-css-rotation
Closed

miguel-heygen wants to merge 2 commits into
fix/studio-edit-resize-size-holdfrom
fix/studio-rotate-from-css-rotation

Conversation

@miguel-heygen

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

Copy link
Copy Markdown
Collaborator

What

A rotate gesture starts from the angle the element actually shows, and in a composition without GSAP the live draft shows the absolute angle the commit writes. Before this, a layer rotated by an authored CSS rotate: 30deg and turned another 25° was saved at 25°, and snapped back by 30° after the drop.

Why

The rotate base was GSAP's rotation plus Studio's old rotation variable, with || 0 when the page had no GSAP. The authored CSS rotate was ignored. The commit saved the absolute angle (0 + 25) as gsap.set({ rotation }), which replaces the CSS rotation once GSAP loads, while the draft had shown 30 + 25 by composing onto the authored value. The benchmark saw a 74.7 px drop on every CSS-rotated rotate cell without GSAP.

Related work

Stacked on #4776 (then #4774, #4766). Same reader as #4774: that PR reads position, this one adds rotation. Measured with the edit accuracy benchmark from #4760.

How

  • readRotationBase(element): GSAP's rotation (plus the old variable) when the page has GSAP. Without GSAP it is the rotation GSAP's CSSPlugin will parse from the CSS rotate, scale and transform: the same folded matrix the fix(studio): start every drag from one position reader and draft what it commits #4774 position reader uses, now also giving atan2(b, a). There is no || 0 fallback.
  • One rotation draft writer, applyRotationDraft, used for the drag and the release: GSAP's rotation, or without GSAP a CSS rotate of the target angle less what scale and transform already turn. A transform: rotate() or a mirrored element then drafts exactly what the commit will show.
  • One restore, restoreRotationDraft, used for no-change, failed-commit and cancel: the CSS snapshot plus GSAP's rotation without the old variable.
  • Deleted: readGsapRotation, the old composed rotation draft (applyStudioRotationDraft), and three "GSAP, else the old CSS channel" branches in the overlay gesture handler.
  • Net lines: source 0 (64 added, 64 deleted); tests +30.

Score

The edit accuracy benchmark was run on a dev machine: every rotate cell at 100% zoom, with no GSAP, a GSAP tween or a GSAP hold, px/pct/centre/xPercent placements, 0°/30°, root and nested. That is 44 cells. Base is #4776. Both runs wait 3 s before each snapshot.

Metric #4776 This PR
tracking 44/44 44/44
drop 38/44 44/44
reload 44/44 44/44
undo 44/44 44/44
all but smoothness 38/44 44/44

No cell got worse.

Before

A layer with an authored rotate: 30deg and no GSAP, rotated 25° more. After the drop it snaps back by 30°.

Before: the rotation snaps back after the drop

before-rot.webm

After

Same rotation: the layer stays at the angle it was dropped at.

After: the rotation is kept

after-rot.webm

Test plan

  • Unit tests added/updated: the rotate base without GSAP reads the authored CSS rotation and drafts the absolute angle, and the draft leaves the part of the angle a transform rotate or a mirror (scale: -1 1, scaleX(-1)) already draws. Each fails without the change. The old variable's snapshot/restore test now uses the committed path.
  • studio src/components/editor and src/hooks: all pass. tsc, oxfmt, oxlint and the comment ratchet are clean.
  • Manual testing performed: the 44 benchmark cells above, and a before/after capture.
  • Documentation updated (if applicable)
  • Comments follow CONTRIBUTING.md "Comments"

@miguel-heygen
miguel-heygen force-pushed the fix/studio-rotate-from-css-rotation branch from f1bbd05 to f85d658 Compare September 30, 2026 17:32
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Fixed on main by #4802: a rotate starts from the angle the element shows, its CSS rotation included, with or without GSAP. Every rotate case in the edit-accuracy gate, including those with a 30° CSS rotation, passes on main with no jump on drop. 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.

1 participant