mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
fix(studio): author every new tween against one element
U3 fixed "add keyframe at playhead" widening a write to every sibling sharing a class, but wired writeTargetSelector into only two paths. The same bug was still reachable from the add-animation button, drag, resize, rotate, gesture recording, and the property panel: each derived its target from selectorFromSelection, which hands back a bare class for an id-less element, so one edit authored a tween over all five siblings and the timeline collapsed their rows into one. Route every path that authors a NEW tween through the existing ladder: - ensureElementAddressable now accepts selection.selector only when it addresses exactly one element, so the id-minting fallback right below it (previously unreachable whenever any selector was present) does the work. - gsapDragCommit's five new-tween branches go through one newTweenTarget helper; instant patches reuse the written target so the runtime moves the element the source write names. - useGestureCommit and useAnimatedPropertyCommit keep the existing selector for matching/retargeting and author new tweens with a separate write selector. Retargets of an EXISTING tween are deliberately untouched: they keep anim.targetSelector, so a tween aimed at a whole group stays aimed at it. Narrowing the write alone regressed idempotency, verified by test: the "is there already a write for this element" lookups matched targetSelector by string, so the next nudge missed the write it had just made and appended a second, conflicting one. The read half now falls back to the live DOM (tweenTargetsElement, same contract as getAnimationsForElement), which also still matches a deliberate group tween. Tests reproduce each site through a real writer, re-parse with the real parser, and resolve through resolveSelectorElementIds (what feeds the keyframe cache and the lanes), plus pins for the new-tween vs retarget-existing distinction so a future change cannot collapse the two.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { GsapAnimation } from "@hyperframes/core/gsap-parser";
|
||||
import type { RuntimeTweenChange, SetPatchProps } from "./gsapRuntimePatch";
|
||||
import { isInstantHold } from "./gsapShared";
|
||||
import { isInstantHold, tweenTargetsElement } from "./gsapShared";
|
||||
|
||||
/** The shape of an `update-property` mutation a static-set nudge POSTs. */
|
||||
interface UpdatePropertyMutation {
|
||||
@@ -34,12 +34,13 @@ export function setPatchFromUpdateProperty(
|
||||
function findPositionSetAnimation(
|
||||
animations: GsapAnimation[],
|
||||
selector: string,
|
||||
element?: Element | null,
|
||||
): GsapAnimation | null {
|
||||
return (
|
||||
animations.find(
|
||||
(a) =>
|
||||
a.method === "set" &&
|
||||
a.targetSelector === selector &&
|
||||
tweenTargetsElement(a.targetSelector, selector, element) &&
|
||||
("x" in a.properties || "y" in a.properties),
|
||||
) ?? null
|
||||
);
|
||||
@@ -61,12 +62,16 @@ function findPositionSetAnimation(
|
||||
export function findExistingPositionWrite(
|
||||
animations: GsapAnimation[],
|
||||
selector: string,
|
||||
element?: Element | null,
|
||||
): GsapAnimation | null {
|
||||
const set = findPositionSetAnimation(animations, selector);
|
||||
const set = findPositionSetAnimation(animations, selector, element);
|
||||
if (set) return set;
|
||||
return (
|
||||
animations.find(
|
||||
(a) => a.targetSelector === selector && a.propertyGroup === "position" && isInstantHold(a),
|
||||
(a) =>
|
||||
tweenTargetsElement(a.targetSelector, selector, element) &&
|
||||
a.propertyGroup === "position" &&
|
||||
isInstantHold(a),
|
||||
) ?? null
|
||||
);
|
||||
}
|
||||
@@ -74,10 +79,14 @@ export function findExistingPositionWrite(
|
||||
export function findRotationSetAnimation(
|
||||
animations: GsapAnimation[],
|
||||
selector: string,
|
||||
element?: Element | null,
|
||||
): GsapAnimation | null {
|
||||
return (
|
||||
animations.find(
|
||||
(a) => isInstantHold(a) && a.targetSelector === selector && "rotation" in a.properties,
|
||||
(a) =>
|
||||
isInstantHold(a) &&
|
||||
tweenTargetsElement(a.targetSelector, selector, element) &&
|
||||
"rotation" in a.properties,
|
||||
) ?? null
|
||||
);
|
||||
}
|
||||
@@ -85,12 +94,13 @@ export function findRotationSetAnimation(
|
||||
export function findSizeSetAnimation(
|
||||
animations: GsapAnimation[],
|
||||
selector: string,
|
||||
element?: Element | null,
|
||||
): GsapAnimation | null {
|
||||
return (
|
||||
animations.find(
|
||||
(a) =>
|
||||
isInstantHold(a) &&
|
||||
a.targetSelector === selector &&
|
||||
tweenTargetsElement(a.targetSelector, selector, element) &&
|
||||
("width" in a.properties || "height" in a.properties),
|
||||
) ?? null
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user