Inertia & momentum

관성·모멘텀

Motion that carries the velocity you had at the moment of release, then gradually slows under friction instead of stopping dead — the "it slides as far as you threw it" feel after a flick or scroll release.

Also known as: Flick gestureMomentum scrollingFriction decay
···
html
<div class="track">
  <div class="rail"></div>
  <i class="hand" id="hand"></i>
  <i class="ball" id="ball"></i>
</div>
css
.track{position:relative;width:min(84%,440px);height:40px}
.rail{position:absolute;top:50%;left:0;right:0;height:2px;margin-top:-1px;background:var(--line);border-radius:2px}
.ball{position:absolute;top:50%;left:0;width:28px;height:28px;margin-top:-14px;border-radius:50%;background:var(--accent);box-shadow:0 4px 10px rgba(0,0,0,.2)}
.hand{position:absolute;top:-14px;left:0;width:14px;height:14px;border-radius:50% 50% 50% 4px;background:var(--accent-2);opacity:0;transform:rotate(-45deg)}
js
const ball = document.getElementById('ball');
const hand = document.getElementById('hand');
const track = document.querySelector('.track');
let x = 0, v = 0, w = 300, ballW = 28;
function measure() { w = track.clientWidth - ballW; }
measure();
window.addEventListener('resize', measure);

function flick() {
  v = w * (0.045 + Math.random() * 0.02);
  hand.style.opacity = '1';
  hand.style.transition = 'none';
  hand.style.left = '0px';
  requestAnimationFrame(() => {
    hand.style.transition = 'left .22s ease-out, opacity .22s ease-out .12s';
    hand.style.left = Math.min(w, x + v * 6) + 'px';
    hand.style.opacity = '0';
  });
}

let waiting = 0;
function loop() {
  if (v > 0.03) {
    x = Math.min(w, x + v);
    v *= 0.955;
    if (x >= w) v = 0;
  } else if (waiting <= 0) {
    x = 0;
    flick();
    waiting = 60;
  } else {
    waiting--;
  }
  ball.style.left = x + 'px';
  ball.style.transform = 'scale(' + (1 + Math.min(v, 6) / 40) + ',' + (1 - Math.min(v, 6) / 60) + ')';
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

The core is keeping one piece of state alive across frames: velocity. While the user drags, you estimate velocity from the last few frames' movement; the instant they release, you keep updating position by that velocity (position += velocity) every frame. Multiply velocity by a friction coefficient (0.9–0.97) each frame so it decays toward zero, and stop the loop once velocity drops below a small threshold (say 0.02).

A coefficient closer to 1 slides longer; closer to 0 stops sooner. Part of why iOS scrolling feels distinctly "slippery" is exactly this coefficient being tuned. If the motion hits a boundary, damping it back the other way instead of just stopping connects naturally into a rubber-band effect.

Unlike duration-based animation, you never decide "how long this takes" up front — only the physical quantities. Throw hard and it travels far; nudge gently and it barely moves. That proportionality between input strength and outcome is the whole point of an inertia model.

When to use

Use it for released drags — lists, carousels, map panning — where input strength should show up in the result. Click-triggered transitions don't need it; a click has no "how hard you threw it."