mediumReact Native#148

Worklet-style interpolate and clamp

Prompt

The two math primitives inside every Reanimated gesture:

  1. clamp(value, lower, upper)
  2. interpolate(value, [inMin, inMax], [outMin, outMax], extrapolate) — linear mapping; extrapolate is 'extend' (default, keep the line going) or 'clamp' (pin to the output range)

Example: drag progress 0→1 mapping to translateY 0→−300.

Solution

function clamp(value, lower, upper) {
  return Math.min(Math.max(value, lower), upper)
}

function interpolate(value, [inMin, inMax], [outMin, outMax], extrapolate = 'extend') {
  const progress = (value - inMin) / (inMax - inMin)
  const result = outMin + progress * (outMax - outMin)
  if (extrapolate === 'clamp') {
    // clamp in OUTPUT space, handling inverted ranges (e.g. 0 → -300)
    const lo = Math.min(outMin, outMax)
    const hi = Math.max(outMin, outMax)
    return clamp(result, lo, hi)
  }
  return result
}
Mentor's take

Every gesture-driven animation is this one line of math — normalize to progress, project onto the output — run once per frame on the UI thread. Interviewers like it because it's small enough to write live and deep enough to expose whether you understand what a worklet is allowed to be.

The load-bearing details:

  • Normalize, then project. (value − inMin) / (inMax − inMin) is progress ∈ [0,1] only inside the input range — outside it, progress goes negative or past 1, and that's not a bug: 'extend' deliberately keeps the line going (a drag past the threshold keeps moving the card). 'clamp' is the choice for opacity — extrapolated opacity 1.4 is nonsense.
  • Clamp in output space, min/max the bounds first. The natural mistake is clamp(result, outMin, outMax) — but output ranges are routinely inverted ([0, -300] for drag-up), and clamping between (0, −300) with min=0 returns 0 forever. Ordering the bounds is the difference between "works in the demo" and "works when the designer flips the direction."
  • Why this must be pure math: these run as Reanimated worklets on the UI-thread runtime — no closures over React state, no side effects, just number → number. That purity is what lets the gesture track the finger while the JS thread is blocked parsing JSON. Pure functions aren't a style preference here; they're the execution model.

Red flag: hardcoding the pixel math inside the gesture handler (translateY = gestureY * -0.6 sprinkled around). Interpolate centralizes the mapping so the input range, output range, and edge policy are declared once — when the design changes, one array changes.

Say it: "Interpolate is normalize-then-project with an explicit edge policy — extend for spatial motion, clamp for bounded properties like opacity — and it's pure math because it has to run per-frame on the UI thread."