Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 9.6Intermediate ~25 min · shot 6 of 7

Drag, Throw and Inertia

In one line

Pointer events follow a finger or a mouse, pointer capture keeps the drag alive, and the speed of the last 100 ms of movement is handed to a spring when the finger lets go.

0/7 · 0%

Think of it like this

Pushing a shopping trolley. While your hand is on it, it goes where you push. When you let go it keeps rolling at the speed your hand gave it and slows down. Nobody expects it to stop the moment you let go, or to jump to the nearest bay.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

pointer events
One family of events (pointerdown, pointermove, pointerup) for mouse, touch and pen.
pointer capture
Telling the browser to send all later events of one pointer to your element.
touch-action
A CSS property saying which touch gestures the browser may handle and which are yours.
rubber banding
Resistance that grows as you drag past an edge.

Step by step

01Throw the card

Drag the card a short way and let go slowly: it returns. Flick it: it carries on to the far end. The message line prints the release speed.

02Use the keyboard

Tab to the card (blue outline), then ArrowRight and ArrowLeft. aria-valuetext says "open" or "closed".

03Read the rule table

The console of "Gesture rules": five movements classified, the rubber band values, three speeds and the pointer log.

04Hold, then release

Drag to the middle, hold for a second, let go: the message reads released at speed 0 px/s.

Try it yourself

  1. 1

    Switch the momentum off

    In "Throw the card" change x + v * 0.15 to x + v * 0 in decide. A 40 px flick at 1250 px/s used to carry the card to the far end; now it falls back to 0, because only the position (40, short of 90) counts.

Code & diagrams

Throw the card

A card with two resting places. Release hands its speed to the spring of Topic 9.2.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

touch-action: pan-y
keyboard: ArrowRight and ArrowLeft do what a throw does
Gesture rules

The same rules the drawer uses, run on scripted values so the numbers are exact.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

moved (0, 0) → tap or scroll
moved (4, 2) → tap or scroll
moved (12, 3) → drag
moved (12, 40) → tap or scroll
moved (-30, 0) → drag
pulled past the edge by 10, 50, 100, 300, 1000 px → shown 8.5, 26.4, 35.9, 47.2, 53.0 px
flick: 1000 px/s
same drag, finger rested 200 ms: 0 px/s
one sample only: 0 px/s
pointer 1 grabs; pointer 2 ignored; cancel: settle with speed 0

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Use a stale speed

In "Throw the card" replace performance.now() - b.t <= 80 in the moving line with true.

terminal
$ Open the card, drag it back 40 px to 140, hold 300 ms, release (scripted events)
── what you'll see ──
With the check the card rests at 180 (open, speed 0). Without it the card rests at 0: the release used the -1250 px/s of the movement 300 ms earlier.

Myth vs fact

Myth

touch-action: none on everything is the safe choice.

Fact

It stops the page scrolling and zooming from that element. pan-y keeps vertical scrolling and still gives you the sideways drag.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Our samples use performance.now() when the handler runs. event.timeStamp is the hardware's time and is more exact on a busy thread, but the course's clock cannot drive it. getCoalescedEvents() returns the moves a browser batched into one frame.

Remember this

  1. 1

    The problem this solves: following a finger is easy. The hard part is the release. If the drawer snaps to the nearest edge, a flick feels ignored. If it ignores that you stopped before letting go, it flies off at a stale speed. And it must work with mouse, touch, pen and keyboard.

  2. 2

    Pointer events and capture. pointerdown, pointermove and pointerup cover mouse, touch and pen, each pointer with an id. setPointerCapture(id) sends that pointer's later events to your element, even when the finger leaves it or the window. It can throw if the pointer is gone, so wrap it in try.

  3. 3

    **touch-action decides who owns a swipe.** By default a moving touch belongs to the browser: it scrolls and your element gets pointercancel. touch-action: pan-y leaves vertical scrolling to the browser and gives you sideways movement. none gives you everything: use it only where needed.

  4. 4

    A tap is not a drag. A finger wobbles. Wait for 6 px of mostly sideways movement: (4, 2) is still a tap, (12, 3) is a drag, (12, 40) is a vertical scroll. After a real drag, swallow the click that follows, so it cannot press a button under the finger.

  5. 5

    Speed from the last 100 ms. Keep recent positions with their times. The speed is distance over time between the first and last sample within 100 ms. If the last sample is older than about 80 ms the finger rested: speed 0. In the second example a flick gives 1000 px/s, and the same drag after a 200 ms rest gives 0.

  6. 6

    Where a throw ends. Add 0.15 s of the release speed to the position and see which side of the middle it lands on. In the first example a 40 px flick at 1250 px/s opens (40 + 188 is past the middle); a slow 60 px drag held for 200 ms falls back. Then start a spring there with that speed.

  7. 7

    Edges. Rubber banding resists past the edge: 56 × (1 - 1 ÷ (past ÷ 56 + 1)) gives 8.5 px for a 10 px pull, 35.9 for 100, 53.0 for 1000. Follow one pointer and ignore others. On pointercancel settle with speed 0. Every drag needs a keyboard twin: the card has role="slider" and moves on the arrow keys like a throw.

Explain it without notes

01

Why does the drawer capture the pointer, and what does touch-action change?

02

How is the release speed computed, and why check the age of the last sample?

Practice

01

Make the card rubber-band when it is dragged past either end.

02

Add Home and End keys to the card.

Done when you can

  • You can capture a pointer and say what touch-action does

  • You can tell a tap from a drag

  • You can compute a release speed and say when it is zero