fix: make native cursor click bounce visible

This commit is contained in:
EtienneLescot
2026-05-05 22:39:23 +02:00
parent 82bffefa54
commit f91300a1b7
2 changed files with 45 additions and 5 deletions
+34
View File
@@ -0,0 +1,34 @@
import { describe, expect, it } from "vitest";
import {
getNativeCursorClickBounceProgress,
getNativeCursorClickBounceScale,
} from "./nativeCursor";
describe("native cursor click bounce", () => {
it("keeps click progress visible across several frames", () => {
const recordingData = {
version: 2,
provider: "native" as const,
assets: [],
samples: [
{ timeMs: 0, cx: 0.5, cy: 0.5, interactionType: "move" as const },
{ timeMs: 100, cx: 0.5, cy: 0.5, interactionType: "click" as const },
{ timeMs: 133, cx: 0.5, cy: 0.5, interactionType: "move" as const },
{ timeMs: 166, cx: 0.5, cy: 0.5, interactionType: "move" as const },
{ timeMs: 200, cx: 0.5, cy: 0.5, interactionType: "move" as const },
{ timeMs: 300, cx: 0.5, cy: 0.5, interactionType: "move" as const },
],
};
expect(getNativeCursorClickBounceProgress(recordingData, 133)).toBeGreaterThan(0);
expect(getNativeCursorClickBounceProgress(recordingData, 200)).toBeGreaterThan(0);
expect(getNativeCursorClickBounceProgress(recordingData, 400)).toBe(0);
});
it("applies a visible press and rebound scale at high intensity", () => {
expect(getNativeCursorClickBounceScale(5, 1)).toBe(1);
expect(getNativeCursorClickBounceScale(5, 0.82)).toBeLessThan(0.9);
expect(getNativeCursorClickBounceScale(5, 0.28)).toBeGreaterThan(1.05);
expect(getNativeCursorClickBounceScale(5, 0)).toBe(1);
});
});
+11 -5
View File
@@ -57,7 +57,7 @@ function clamp(value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value));
}
const NATIVE_CURSOR_CLICK_ANIMATION_MS = 140;
const NATIVE_CURSOR_CLICK_ANIMATION_MS = 260;
const NATIVE_CURSOR_MOTION_BLUR_MAX_PX = 6;
const nativeCursorAssetMapCache = new WeakMap<
CursorRecordingData,
@@ -329,7 +329,7 @@ export function getNativeCursorClickBounceProgress(
recordingData: CursorRecordingData | null | undefined,
timeMs: number,
) {
if (!hasNativeCursorRecordingData(recordingData)) {
if (!recordingData || recordingData.provider !== "native" || recordingData.samples.length === 0) {
return 0;
}
@@ -357,9 +357,15 @@ export function getNativeCursorClickBounceScale(clickBounce: number, progress: n
return 1;
}
const bounceAmount = Math.sin(progress * Math.PI);
const amplitude = clamp(clickBounce, 0, 4) * 0.08;
return Math.max(0.72, 1 - bounceAmount * amplitude);
const intensity = clamp(clickBounce, 0, 5) / 5;
const elapsed = 1 - clamp(progress, 0, 1);
if (elapsed < 0.38) {
const pressProgress = Math.sin((elapsed / 0.38) * Math.PI);
return 1 - pressProgress * intensity * 0.24;
}
const reboundProgress = Math.sin(((elapsed - 0.38) / 0.62) * Math.PI);
return 1 + reboundProgress * intensity * 0.16;
}
export function getNativeCursorMotionBlurPx({