mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-07 18:26:17 +00:00
fix(studio): seek ruler clicks to the pressed position and round retime percentages
A ruler press with no pointer movement settled the playhead at t=0 instead of the clicked time. handlePointerUp replays pendingClientXRef, which only the pointermove path wrote, so a plain click fell back to the ref's initial 0 and overwrote the correct pointerdown seek. Seed the ref on pointerdown. The keyframe retime move branch also returned the raw quotient while the resize branch rounded to 3dp, so values like 74.81203007518799% landed in the user's source and churned the diff on every drag. Round at the point of computation so the no-op test and the written value agree.
This commit is contained in:
@@ -180,3 +180,45 @@ describe("resolveKeyframeRetime — guards", () => {
|
||||
expect(r.pctRemap).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveKeyframeRetime — move percentages are rounded like the resize path", () => {
|
||||
// The move branch used to return the raw quotient, so `74.81203007518799%`
|
||||
// landed in the user's source and churned the diff on every drag.
|
||||
const decimals = (n: number): number => String(n).split(".")[1]?.length ?? 0;
|
||||
|
||||
it("rounds a repeating quotient to 3dp", () => {
|
||||
const r = resolveKeyframeRetime({
|
||||
tweenStart: 2,
|
||||
tweenDuration: 3,
|
||||
keyframes: KEYFRAMES,
|
||||
draggedTweenPct: 0,
|
||||
dropAbsTime: 3, // (3-2)/3 = 33.333333333333336%
|
||||
});
|
||||
expect(r.kind).toBe("move");
|
||||
expect(r.toTweenPct).toBe(33.333);
|
||||
});
|
||||
|
||||
it("never emits more than 3 decimal places", () => {
|
||||
for (const tweenDuration of [3, 7, 9, 11, 133]) {
|
||||
const r = resolveKeyframeRetime({
|
||||
tweenStart: 2,
|
||||
tweenDuration,
|
||||
keyframes: KEYFRAMES,
|
||||
draggedTweenPct: 0,
|
||||
dropAbsTime: 3,
|
||||
});
|
||||
expect(r.kind).toBe("move");
|
||||
expect(decimals(r.toTweenPct ?? 0)).toBeLessThanOrEqual(3);
|
||||
}
|
||||
});
|
||||
|
||||
it("leaves an already-short percentage untouched", () => {
|
||||
const r = resolveKeyframeRetime({
|
||||
...WINDOW,
|
||||
keyframes: KEYFRAMES,
|
||||
draggedTweenPct: 0,
|
||||
dropAbsTime: 3, // (3-2)/4 = exactly 25%
|
||||
});
|
||||
expect(r.toTweenPct).toBe(25);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -121,7 +121,10 @@ export function resolveKeyframeRetime(opts: {
|
||||
|
||||
// Within the tween window → plain move (re-key the tween-%).
|
||||
if (dropAbsTime >= tweenStart - EPSILON_TIME && dropAbsTime <= tweenEnd + EPSILON_TIME) {
|
||||
const toTweenPct = clamp(((dropAbsTime - tweenStart) / tweenDuration) * 100, 0, 100);
|
||||
// Round here, not at the return: the no-op test below and the value written
|
||||
// to source must be the same number. The resize branch already rounds, so
|
||||
// this keeps both write paths at the authored 3dp precision.
|
||||
const toTweenPct = round3(clamp(((dropAbsTime - tweenStart) / tweenDuration) * 100, 0, 100));
|
||||
if (Math.abs(toTweenPct - draggedTweenPct) < NOOP_EPSILON_PCT) return { kind: "noop" };
|
||||
return { kind: "move", toTweenPct };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user