mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 23:03:09 +00:00
fix(studio): harden flat keyframe retiming
This commit is contained in:
@@ -58,6 +58,29 @@ describe("resolveKeyframeRetime — move (within the tween window)", () => {
|
||||
expect(r.duration).toBe(3);
|
||||
expect(r.pctRemap).toEqual([]);
|
||||
});
|
||||
|
||||
it("no-ops an unexpected interior percentage on a flat tween", () => {
|
||||
expect(
|
||||
resolveKeyframeRetime({
|
||||
...WINDOW,
|
||||
keyframes: [],
|
||||
draggedTweenPct: 50,
|
||||
dropAbsTime: 5,
|
||||
}).kind,
|
||||
).toBe("noop");
|
||||
});
|
||||
|
||||
it("no-ops instead of rounding a sub-10ms flat tween to zero", () => {
|
||||
expect(
|
||||
resolveKeyframeRetime({
|
||||
tweenStart: 2,
|
||||
tweenDuration: 4,
|
||||
keyframes: [],
|
||||
draggedTweenPct: 100,
|
||||
dropAbsTime: 2.0004,
|
||||
}).kind,
|
||||
).toBe("noop");
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveKeyframeRetime — resize (past the tween boundary)", () => {
|
||||
|
||||
@@ -51,6 +51,8 @@ export interface KeyframeRetimeResult {
|
||||
const NOOP_EPSILON_PCT = 0.1;
|
||||
/** Slack (seconds) for the within-tween boundary test. */
|
||||
const EPSILON_TIME = 1e-4;
|
||||
/** Smallest authored tween window; avoids sub-millisecond/round-to-zero durations. */
|
||||
const MIN_TWEEN_DURATION = 0.01;
|
||||
|
||||
const round3 = (n: number) => Math.round(n * 1000) / 1000;
|
||||
const round1 = (n: number) => Math.round(n * 10) / 10; // 0.1% precision
|
||||
@@ -65,16 +67,20 @@ function resolveFlatTweenBoundaryRetime(opts: {
|
||||
dropAbsTime: number;
|
||||
}): KeyframeRetimeResult | null {
|
||||
const { keyframeCount, draggedTweenPct, tweenStart, tweenEnd, dropAbsTime } = opts;
|
||||
if (keyframeCount > 0 || (draggedTweenPct !== 0 && draggedTweenPct !== 100)) return null;
|
||||
if (keyframeCount > 0) return null;
|
||||
// Flat tweens have only synthesized boundary diamonds. Never delegate an
|
||||
// unexpected interior percentage to the authored-keyframe move path.
|
||||
if (draggedTweenPct !== 0 && draggedTweenPct !== 100) return { kind: "noop" };
|
||||
const draggedTime = draggedTweenPct === 0 ? tweenStart : tweenEnd;
|
||||
if (Math.abs(dropAbsTime - draggedTime) <= EPSILON_TIME) return { kind: "noop" };
|
||||
const newStart = draggedTweenPct === 0 ? dropAbsTime : tweenStart;
|
||||
const newEnd = draggedTweenPct === 100 ? dropAbsTime : tweenEnd;
|
||||
if (newEnd <= newStart + EPSILON_TIME) return { kind: "noop" };
|
||||
const newDuration = newEnd - newStart;
|
||||
if (newDuration < MIN_TWEEN_DURATION) return { kind: "noop" };
|
||||
return {
|
||||
kind: "resize",
|
||||
position: round3(newStart),
|
||||
duration: round3(newEnd - newStart),
|
||||
duration: round3(newDuration),
|
||||
pctRemap: [],
|
||||
};
|
||||
}
|
||||
@@ -127,7 +133,7 @@ export function resolveKeyframeRetime(opts: {
|
||||
|
||||
const newStart = Math.min(dropAbsTime, tweenStart);
|
||||
const newEnd = Math.max(dropAbsTime, tweenEnd);
|
||||
const newDuration = Math.max(0.01, newEnd - newStart);
|
||||
const newDuration = Math.max(MIN_TWEEN_DURATION, newEnd - newStart);
|
||||
|
||||
// The dragged keyframe is the one whose tween-% is closest to draggedTweenPct.
|
||||
let draggedIdx = 0;
|
||||
|
||||
Reference in New Issue
Block a user