mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 14:50:02 +00:00
fix(studio): compute keyframe percentages in the tween's own time frame
A sub-composition tween's resolvedStart is composition-local, while the timeline element resolved for it is the sub-comp HOST, whose start is main-timeline absolute. toClipPercentage subtracted the two frames from each other, so a host mounted at 1.5s cached its 0s tween at -12% and its last tween's end keyframe at 88% instead of 100%. A clip-relative percentage can never be negative. resolveClipTimingBasis now returns the clip start in the frame the tween's own times are measured in: the composition mount (expandedParentStart for an expanded child, the parent composition clip's start otherwise, 0 for a root-composition element) is subtracted, and a sub-comp inner element that falls back to its host's window starts at 0 in that window. It moves to gsapShared so the post-commit cache writer can share it instead of resolving its own basis, which also gives that writer the sub-comp host fallback it was missing.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Reading a composition file's GSAP tweens into the keyframe cache: fetch,
|
||||
* selector -> element id resolution, and the clip-relative timing basis.
|
||||
* Reading a composition file's GSAP tweens into the keyframe cache: fetch and
|
||||
* selector -> element id resolution.
|
||||
* Split from useGsapTweenCache to keep that file under the 600-line limit.
|
||||
*/
|
||||
import type { GsapAnimation, GsapKeyframesData, ParsedGsap } from "@hyperframes/core/gsap-parser";
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
elementCacheKeys,
|
||||
writeGsapAnimationsForElement,
|
||||
} from "./gsapKeyframeCacheHelpers";
|
||||
import { idFromSelector, toClipKeyframes } from "./gsapShared";
|
||||
import { idFromSelector, resolveClipTimingBasis, toClipKeyframes } from "./gsapShared";
|
||||
import {
|
||||
deduplicateKeyframes,
|
||||
isStaticPositionHold,
|
||||
@@ -103,37 +103,6 @@ export async function fetchParsedAnimations(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clip-relative timing basis for an element. Sub-composition internals (e.g. pills
|
||||
* inside a scene) aren't timeline clips themselves — they're derived at expand time
|
||||
* — so they're absent from `elements`. Without a basis, elDuration defaulted to 1
|
||||
* and clip-relative keyframe percentages blew past 100% (rendering off the clip).
|
||||
* Fall back to the sub-comp HOST's bounds, resolved via domClipChildren (the host's
|
||||
* data-composition-src is stripped in the rendered DOM, so we can't query it).
|
||||
*/
|
||||
export function resolveClipTimingBasis(
|
||||
elementId: string,
|
||||
sourceFile: string,
|
||||
elements: ReadonlyArray<{
|
||||
domId?: string;
|
||||
key?: string;
|
||||
id: string;
|
||||
start: number;
|
||||
duration: number;
|
||||
}>,
|
||||
domClipChildren: ReadonlyArray<{ id: string; hostId: string }>,
|
||||
): { elStart: number; elDuration: number } {
|
||||
const direct = elements.find(
|
||||
(el) => el.domId === elementId || (el.key ?? el.id) === `${sourceFile}#${elementId}`,
|
||||
);
|
||||
if (direct) return { elStart: direct.start, elDuration: direct.duration };
|
||||
const hostId = domClipChildren.find((c) => c.id === elementId)?.hostId;
|
||||
const host = hostId
|
||||
? elements.find((el) => el.domId === hostId || (el.key ?? el.id) === `index.html#${hostId}`)
|
||||
: undefined;
|
||||
return { elStart: host?.start ?? 0, elDuration: host?.duration ?? 1 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one composition file's tweens into the keyframe cache. Split out of the
|
||||
* hook so the effect can run it per file without re-nesting the whole body.
|
||||
|
||||
Reference in New Issue
Block a user