fix(runtime): useAnimation interval preserves fractional values, matching Ink (#104)
normalizeInterval rounded the interval (Math.round), so a 60fps interval (16.67ms) became 17ms and 8.4ms became 8ms — drifting frame=floor(elapsed/interval) and the scheduler's nextDueTime over time. Ink's normalizeAnimationInterval (use-animation.ts:147-151) does not round. Removed Math.round; the clamp (>=1, <=MAX_TIMER_INTERVAL) is unchanged and the scheduler already ceil()s the setTimeout delay so a fractional interval doesn't busy-loop. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,15 @@ describe.sequential("normalizeInterval", () => {
|
||||
expect(normalizeInterval(Number.POSITIVE_INFINITY)).toBe(100);
|
||||
expect(normalizeInterval(Number.MAX_SAFE_INTEGER)).toBe(2_147_483_647);
|
||||
});
|
||||
|
||||
test("preserves fractional intervals, matching Ink (no rounding)", () => {
|
||||
// Ink's normalizeAnimationInterval does NOT round (use-animation.ts:147-151),
|
||||
// so a 60fps interval (16.67ms) or 8.4ms stays fractional; rounding would drift
|
||||
// frame=floor(elapsed/interval) and the scheduler's nextDueTime over time.
|
||||
expect(normalizeInterval(16.67)).toBe(16.67);
|
||||
expect(normalizeInterval(8.4)).toBe(8.4);
|
||||
expect(normalizeInterval(0.5)).toBe(1); // still clamped to >= 1
|
||||
});
|
||||
});
|
||||
|
||||
describe.sequential("createAnimationScheduler", () => {
|
||||
|
||||
@@ -3,7 +3,10 @@ const MAX_TIMER_INTERVAL = 2_147_483_647;
|
||||
|
||||
export function normalizeInterval(interval: number | undefined): number {
|
||||
if (interval === undefined || !Number.isFinite(interval)) return DEFAULT_INTERVAL;
|
||||
return Math.min(Math.max(1, Math.round(interval)), MAX_TIMER_INTERVAL);
|
||||
// No rounding — Ink's normalizeAnimationInterval (use-animation.ts:147-151) preserves
|
||||
// fractional intervals (e.g. 16.67ms for 60fps); rounding would drift frame counts and
|
||||
// the scheduler's nextDueTime over time. The scheduler already ceil()s the setTimeout delay.
|
||||
return Math.min(Math.max(1, interval), MAX_TIMER_INTERVAL);
|
||||
}
|
||||
|
||||
type AnimationSubscriber = {
|
||||
|
||||
Reference in New Issue
Block a user