@@ -62,6 +62,96 @@ describe("gsap adapter on a step", () => {
6262} ) ;
6363
6464describe ( "gsap adapter at a tween's start" , ( ) => {
65+ // Issue #5122: a render seeks every frame in order; only the from-vars set transformOrigin.
66+ it ( "keeps a fromTo's from-only values from the frame it starts on, with immediateRender off" , ( ) => {
67+ const box = document . body . appendChild ( document . createElement ( "div" ) ) ;
68+ const timeline = gsap . timeline ( { paused : true } ) ;
69+ timeline . fromTo (
70+ box ,
71+ { scale : 1.5 , transformOrigin : "0% 0%" } ,
72+ { scale : 1 , duration : 0.45 , ease : "none" , immediateRender : false } ,
73+ 0.1 ,
74+ ) ;
75+ timeline . set ( { } , { } , 1 ) ;
76+ const adapter = createGsapAdapter ( {
77+ getTimeline : ( ) => timeline as unknown as RuntimeTimelineLike ,
78+ } ) ;
79+ const origins = [ 2 , 3 , 4 , 5 ] . map ( ( frame ) => {
80+ adapter . seek ( { time : frame / 30 } ) ;
81+ return box . style . transformOrigin ;
82+ } ) ;
83+ expect ( origins ) . toEqual ( [ "" , "0% 0%" , "0% 0%" , "0% 0%" ] ) ;
84+ expect ( gsap . getProperty ( box , "scaleX" ) ) . toBeCloseTo ( 1.4259 , 3 ) ;
85+ } ) ;
86+
87+ it ( "keeps a keyframed fromTo's from-only values over an earlier tween's on the same element" , ( ) => {
88+ const box = document . body . appendChild ( document . createElement ( "div" ) ) ;
89+ const timeline = gsap . timeline ( { paused : true } ) ;
90+ const fromTo = ( origin : string , to : gsap . TweenVars , at : number ) =>
91+ timeline . fromTo (
92+ box ,
93+ { scale : 1.5 , transformOrigin : origin } ,
94+ { scale : 1 , immediateRender : false , ...to } ,
95+ at ,
96+ ) ;
97+ fromTo ( "0% 0%" , { duration : 0.1 } , 0 ) ;
98+ fromTo ( "100% 100%" , { duration : 0.1 } , 0.1 ) ;
99+ fromTo ( "0% 0%" , { keyframes : { y : [ 0 , 40 ] } , duration : 0.5 } , 0.2 ) ;
100+ timeline . set ( { } , { } , 1 ) ;
101+ const adapter = createGsapAdapter ( {
102+ getTimeline : ( ) => timeline as unknown as RuntimeTimelineLike ,
103+ } ) ;
104+ const origins = [ 0 , 2 / 30 , 0.1 , 4 / 30 , 0.2 , 7 / 30 ] . map ( ( time ) => {
105+ adapter . seek ( { time } ) ;
106+ return box . style . transformOrigin ;
107+ } ) ;
108+ expect ( origins . slice ( 1 ) ) . toEqual ( [ "0% 0%" , "100% 100%" , "100% 100%" , "0% 0%" , "0% 0%" ] ) ;
109+ } ) ;
110+
111+ it ( "keeps each staggered target's from-only values from the frame it starts on" , ( ) => {
112+ const film = ( ) => {
113+ const boxes = [ 0 , 1 , 2 ] . map ( ( ) => document . body . appendChild ( document . createElement ( "div" ) ) ) ;
114+ const timeline = gsap . timeline ( { paused : true } ) ;
115+ timeline . fromTo (
116+ boxes ,
117+ { scale : 1.5 , transformOrigin : "0% 0%" } ,
118+ { scale : 1 , duration : 0.3 , stagger : 0.1 , ease : "none" , immediateRender : false } ,
119+ 0.1 ,
120+ ) ;
121+ timeline . set ( { } , { } , 1 ) ;
122+ return { boxes, timeline } ;
123+ } ;
124+ const played = film ( ) ;
125+ const seeked = film ( ) ;
126+ const adapter = createGsapAdapter ( {
127+ getTimeline : ( ) => seeked . timeline as unknown as RuntimeTimelineLike ,
128+ } ) ;
129+ for ( let frame = 0 ; frame <= 12 ; frame ++ ) {
130+ played . timeline . totalTime ( frame / 30 , false ) ;
131+ adapter . seek ( { time : frame / 30 } ) ;
132+ const origins = ( film : { boxes : HTMLElement [ ] } ) =>
133+ film . boxes . map ( ( box ) => box . style . transformOrigin ) ;
134+ expect ( origins ( seeked ) , `frame ${ frame } ` ) . toEqual ( origins ( played ) ) ;
135+ }
136+ } ) ;
137+
138+ it ( "still lets a set authored later on the tween's start win over its from-values" , ( ) => {
139+ const box = document . body . appendChild ( document . createElement ( "div" ) ) ;
140+ const timeline = gsap . timeline ( { paused : true } ) ;
141+ timeline . fromTo (
142+ box ,
143+ { opacity : 0 , transformOrigin : "0% 0%" } ,
144+ { opacity : 1 , duration : 0.5 , immediateRender : false } ,
145+ 0.1 ,
146+ ) ;
147+ timeline . set ( box , { opacity : 0.3 , transformOrigin : "100% 100%" } , 0.1 ) ;
148+ timeline . set ( { } , { } , 1 ) ;
149+ createGsapAdapter ( { getTimeline : ( ) => timeline as unknown as RuntimeTimelineLike } ) . seek ( {
150+ time : 0.1 ,
151+ } ) ;
152+ expect ( [ box . style . opacity , box . style . transformOrigin ] ) . toEqual ( [ "0.3" , "100% 100%" ] ) ;
153+ } ) ;
154+
65155 // An edit at 2 s turns the later tween into keyframes; its first keyframe must win over the from() end.
66156 it . each ( [ 0 , 1 , 2 , 2.5 , 3 ] ) (
67157 "shows the tween that starts at the seek time, seeking from %s s" ,
0 commit comments