@@ -351,33 +351,13 @@ export async function tryGsapResizeIntercept(
351351 const dropPoint = scaleDraftDropPoint ;
352352 const measured = draw ( ( ) => {
353353 clearStudioBoxSize ( draftEl ) ;
354- // Put the committed scale on the live element before measuring.
355- //
356- // This step reads where the commit lands the box and shifts the position
357- // hold by the difference. That only works if the commit has actually
358- // rendered, and whether it had was luck: on the FIRST resize of an element
359- // the timeline had not re-seeked yet, so this measured the element at its
360- // natural size, still sitting on the drop point, computed a residual of
361- // zero, and skipped the correction entirely. The scale then landed, GSAP
362- // rendered it around the element's centre, and the element jumped by the
363- // whole drag distance. Elements that had been resized before got a
364- // correction only because their PREVIOUS scale made the residual non-zero.
365- //
366- // Setting it here costs nothing when the commit has already rendered (same
367- // value) and makes the measurement below mean what it says either way.
354+ // Draw the committed scale before measuring: on a first resize the timeline has not re-seeked yet,
355+ // so the box would read at its natural size and the correction would be skipped.
368356 if ( committedScale ) {
369357 setElementGsapScale ( draftEl , committedScale . x , committedScale . y ) ;
370358 }
371- // Measure from the pre-gesture position, not the draft one.
372- //
373- // The resize draft translates the element to keep the dragged corner under
374- // the cursor, but the scale route never persists that translation — the
375- // element renders back at its pre-gesture position as soon as the commit
376- // lands. Measuring while the draft translation was still applied made the
377- // residual carry the whole drag distance, and the position commit then
378- // composed that residual onto the pre-gesture base (it reads the gesture's
379- // own base attributes, not the live value), so the element landed a full
380- // drag away from the drop point on every scale resize.
359+ // Measure from the pre-gesture position: the scale route never saves the draft's translation, and
360+ // the position write composes the residual onto that same base.
381361 const gsapPos = readGsapPositionFromIframe ( iframe , selector ) ?? { x : 0 , y : 0 } ;
382362 const { baseGsapX, baseGsapY } = computeDraggedGsapPosition (
383363 selection . element ,
@@ -410,13 +390,8 @@ export async function tryGsapResizeIntercept(
410390 base,
411391 corrected,
412392 } ) ;
413- // Correct the LIVE runtime NOW, synchronously: the soft reload above just
414- // rendered the committed scale around the element center — NOT at the drop
415- // point — and everything up to here runs in the same microtask chain as
416- // that reload, so no frame has painted the uncorrected position yet. The
417- // server persist below costs network round-trips; without this set, the
418- // element visibly sits at the wrong spot for those frames (the drop
419- // "jump"). The persisted commit re-applies the same values (idempotent).
393+ // Correct the live box in the same task as the measurement, so no frame shows it off the drop
394+ // point while the position write is in flight.
420395 setElementGsapPosition ( draftEl , corrected . x , corrected . y ) ;
421396 return { base, corrected } ;
422397 } ) ;
0 commit comments