All files / src/components/VirtualWalkthrough vr-annotation-scroll.ts

100% Statements 14/14
100% Branches 7/7
100% Functions 2/2
100% Lines 14/14

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41  2x     2x                       2x 4x 4x 3x     1x             2x 9x 9x 3x     6x 6x   6x    
/** Deflection the stick must pass before the text moves, so a resting thumb doesn't drift it. */
export const SCROLL_DEADZONE = 0.15;
 
/** Canvas px per second at full deflection. */
export const SCROLL_SPEED = 1200;
 
interface ThumbstickSource {
  handedness: string;
  gamepad?: { axes: ArrayLike<number> } | null;
}
 
/**
 * Thumbstick Y for a source that can scroll the panel. Only the right hand scrolls: the left
 * stick drives locomotion, and right-stick Y is free because snap-vertical is turned off.
 * Hand-tracked sources report a gamepad with no axes, which would read as NaN.
 */
export const readScrollAxis = (inputSource: ThumbstickSource): number => {
  const axes = inputSource.gamepad?.axes;
  if (inputSource.handedness !== 'right' || !axes || axes.length < 4) {
    return 0;
  }
 
  return axes[3];
};
 
/**
 * Advances the scroll position for one frame of stick deflection, ramping from a standstill at
 * the deadzone edge. A forward push reports negative Y and moves further into the text.
 */
export const nextScrollY = (scrollY: number, axisY: number, dt: number, maxScroll: number): number => {
  const magnitude = Math.abs(axisY);
  if (magnitude <= SCROLL_DEADZONE) {
    return Math.min(Math.max(scrollY, 0), maxScroll);
  }
 
  const ramped = (magnitude - SCROLL_DEADZONE) / (1 - SCROLL_DEADZONE);
  const delta = -Math.sign(axisY) * ramped * SCROLL_SPEED * dt;
 
  return Math.min(Math.max(scrollY + delta, 0), maxScroll);
};