Skip to content

feat(renderer): scale-aware camera animation duration #71

@LeslieOA

Description

@LeslieOA

Background

Split out from the cinematic double-tap zoom umbrella (#36, item 5 of the zoom-rendering audit). A first implementation was attempted and deliberately reverted — read the retro on #36 before picking this up.

Original intent (from the audit)

CAMERA_ANIM_DURATION_MS = 300 regardless of how big the zoom change is. Going from 0.3× to 3× (a 10× zoom step) takes the same 300ms as going from 0.9× to 1.0×. The audit's claim: bigger jumps feel rushed, smaller ones sluggish. Proposed formula:

const scaleRatio = Math.max(scaleStart / scaleEnd, scaleEnd / scaleStart);
const durationMs = Math.min(500, 250 + Math.log2(scaleRatio) * 50);

What happened when it was tried

Implemented on feat/cinematic-double-tap-zoom (PR #42, closed unmerged) and reverted. Feel-testing in the harness did not bear out the premise: users perceive deceleration, not absolute duration. easeOutCubic front-loads enough that a fixed 300ms reads as comfortable for both 1.1× and 10× steps — the eye locks onto the soft landing. Stretching big jumps to ~415ms made them feel slower, not more cinematic.

Remaining live scope

Revisit only after #39 (native UI-thread camera interpolator, post-Rust-port) lands. Today every camera-animation frame pays a JS-thread bridge cost, which confounds any timing feel-test; once the tween runs off the JS thread, the per-frame cost profile changes and the question is worth re-asking with clean conditions. Treat the audit's formula as a starting point, not a spec.

Acceptance criteria

  • Re-evaluated after perf(renderer): native UI-thread camera interpolator (post-Rust-port) #39 lands, with side-by-side feel-testing in the harness at small (~1.1×) and large (~10×) zoom steps
  • Decision documented on this issue — "keep fixed 300ms" is a valid outcome
  • If a scale-aware duration is adopted, the curve and bounds are recorded alongside the feel-test notes
  • All existing animation tests still pass

Refs: #36
Blocked by: #39

Agent prompt

Repo: workspace-sh/react-native-jsoncanvas. Read issue #36 in full (especially the
two retro comments — PR #42 implemented this and was reverted after feel-testing),
then issue #39, then this issue.

Precondition: #39 (native UI-thread camera interpolator) must be merged. If it is
not, stop and report — the JS-thread tween's per-frame bridge cost confounds any
duration feel-test.

Task: re-evaluate scale-aware camera animation duration under the new interpolator.
1. Locate CAMERA_ANIM_DURATION_MS and the camera animation path (formerly the
   animateCamera rAF tween in src/renderer/; #39 will have moved or replaced it).
2. Prototype the log2-based formula from the issue body behind a quick toggle.
3. Feel-test in the harness app: small (~1.1×) and large (~10×) zoom steps, fixed
   300ms vs scale-aware, with the existing easeOutCubic easing unchanged.
4. The prior finding was that perception tracks deceleration, not duration, and
   fixed 300ms won. Only adopt a change if it clearly reads better; "keep 300ms"
   is a valid outcome. Document the decision as a comment on this issue either way.
5. If adopted: branch + PR (Conventional Commits), "Refs: #<this issue>", run the
   animation tests. Do not modify pan/pinch/scroll-wheel gesture handlers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions