feat(framework): kinetic scroller with iOS-feel physics#163
Draft
doodlewind wants to merge 1 commit into
Draft
Conversation
framework/src/kinetics.ts (@pocketjs/framework/kinetics): one axis, one deterministic state machine — tracking (finger-follow with the classic rubber-band curve, slope 0.55, capped overscroll), fling (per-tick decay 0.9672 ≡ UIScrollView 0.998/ms), spring (edge bounce with the engine's K=170/C=26 preset, CARRYING the incoming velocity — the one place spring-with-initial-velocity is needed, kept JS-side), chase (byte-for-byte the apps/im pump: 0.3 of remaining distance, snap under 0.6 px), and tween (cubic ease-out scrollTo, optional snap-points at release). Fling and spring integrate per core tick with settle checks INSIDE the tick loop, so the stop tick is hz-invariant and a 30 Hz trajectory is exactly the 60 Hz one subsampled (asserted in tests). Every formula is IEEE + − * / with literal constants — no transcendentals in the sim path. Settled offsets round to 1/64 px so resting framebuffers hash identically. bindDpadScroll() wraps the im d-pad/analog semantics (6 px per held frame, 10 px nub) over any Scroller, gated by an `active` accessor. rebase() shifts the offset and every in-flight anchor for prepend-without-jump; isAtEnd() judges intent (the chase/tween target) per the im at-bottom rule. 21 unit tests: literal trajectories, subsampling theorem, rubber curve values, mid-bounce catch, exact chase/snap/tween arithmetic, dpad binding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 2/6 of the touch-input series (stacked on #161).
What
framework/src/kinetics.ts—createScroller(): a single-axis, fully deterministic scroll physics state machine, offset published as a Solid signal (translateY: -offset()= one paint-only setProp per moving frame, no relayout). States:(1 − 1/((x·0.55/d)+1))·d, capped (default 48 px), invertible for mid-bounce catchesv *= 0.9672(≡ iOS 0.998/ms);projectFlingfor snap functionspos += d·0.3, snap < 0.6 px (d-pad / stick-to-bottom)scrollTo, snap-points at releaseSettle checks run inside the tick loop, so the stop tick is hz-invariant: the 30 Hz trajectory is exactly the 60 Hz one subsampled (asserted). Settled offsets round to 1/64 px for stable framebuffer hashes. No
Math.pow/transcendentals anywhere.Also
bindDpadScroll()(im d-pad/analog semantics over any Scroller),rebase()(prepend-without-jump),isAtEnd()(target-judged, the im at-bottom rule).Why JS-side, not engine springs
Windowing, near-edge fetch, and stick-to-bottom all need the live offset every frame, and there is no layout/anim readback op — the integrator must live guest-side. Cost is identical (one setProp per moving frame);
contracts/spec/spec.tsstays untouched.Tests
21 cases in
tests/kinetics.test.ts: literal trajectory determinism, 30≡60 Hz subsampling incl. rest position, rubber curve values (-24.9745at 50 px past edge), overscroll cap/hard-clamp, mid-bounce catch continuity, exact chase arithmetic (30, 51, …), tween frame-count and landings, snap projection, rebase, settle callbacks, dpad binding with analog + gating.🤖 Generated with Claude Code