Skip to content

✨ add follow camera tracking animation#189

Merged
igotinfected merged 4 commits into
mainfrom
feature/add-tracking-animation-to-follow-mode
Jun 30, 2026
Merged

✨ add follow camera tracking animation#189
igotinfected merged 4 commits into
mainfrom
feature/add-tracking-animation-to-follow-mode

Conversation

@igotinfected

@igotinfected igotinfected commented Jun 30, 2026

Copy link
Copy Markdown
Member

Allow setting a tracking animation for the camera follow mode on top of the engage animation. This allows for smooth camera tracking when entities jump from position to position.

Summary by CodeRabbit

  • New Features

    • Added configurable camera follow tracking animation with Instant, Ease-in-out, and Linear options.
    • Camera follow now supports distinct animation for initial engage vs post-engage tracking updates.
    • Updated camera-follow and train-tracking samples with new tracking and motion controls.
  • Bug Fixes

    • Fixed tracking animation timing so continuous re-centering no longer restarts configured tracking on every frame.
    • Ensured “jumped” updates preserve the configured tracking animation (including correct Linear easing override).
  • Tests

    • Added integration and unit coverage for tracking animation behavior and serialization.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@igotinfected, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 39 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2387a75b-ea07-4c79-8b1b-436a37c103bd

📥 Commits

Reviewing files that changed from the base of the PR and between f1653ce and 0f18a03.

📒 Files selected for processing (2)
  • src/Spillgebees.Blazor.Map.Tests/Components/SgbMapOverlayContentTests.cs
  • src/Spillgebees.Blazor.Map/Components/SgbMap.razor.cs
📝 Walkthrough

Walkthrough

Adds TrackingAnimation to camera follow configuration, wires it through engine serialization and JS follow handling, and updates tests plus sample and integration UI to exercise eased tracking on jumped entity updates.

Changes

TrackingAnimation feature

Layer / File(s) Summary
Public API and wire contract
src/Spillgebees.Blazor.Map/Models/Follow/MapFollowCameraOptions.cs, src/Spillgebees.Blazor.Map/Models/Follow/MapFollowOptions.cs, src/Spillgebees.Blazor.Map/PublicAPI.Shipped.txt, src/Spillgebees.Blazor.Map.Assets/src/engine/ops.ts, src/Spillgebees.Blazor.Map/Engine/EngineOps.cs
Adds TrackingAnimation to MapFollowCameraOptions, updates follow documentation and shipped API, adds trackingAnimation to the JS follow config, and extends EngineFollowCamera with tracking animation.
C# coordinator and serialization mapping
src/Spillgebees.Blazor.Map/Engine/MapFollowCoordinator.cs, src/Spillgebees.Blazor.Map.Tests/Engine/CameraFollowOpSerializationTests.cs, src/Spillgebees.Blazor.Map.Tests/Engine/MapFollowCoordinatorTests.cs
Maps camera.TrackingAnimation into the engine follow camera and verifies the serialized camera.trackingAnimation payload in tests.
JS FollowController tracking animation logic
src/Spillgebees.Blazor.Map.Assets/src/engine/follow.ts
Adds timing gates and tracking animation state to choose animated or instant tracking moves.
JS tracking tests
src/Spillgebees.Blazor.Map.Assets/src/engine/follow.test.ts, src/Spillgebees.Blazor.Map.Assets/tests/browser/integration/engine-follow.spec.ts
Adds Vitest coverage for default, eased, linear, jumped, continuous, and engage-versus-tracking cases, plus a Playwright integration check for eased tracking during entity movement.
Integration test page wiring
src/Spillgebees.Blazor.Map.IntegrationTests/Pages/EngineFollowFunctionalTest.razor
Adds a tracking-animated follow action to the functional test page.
Demo sample UI update
src/Spillgebees.Blazor.Map.Docs/Samples/CameraFollow/CameraFollowExample.razor, src/Spillgebees.Blazor.Map.Docs/Samples/CameraFollow/CameraFollowExample.razor.cs
Adds tracking and train-motion controls, maps tracking animation into follow options, and conditionally applies train animation timing to the tracked entity layer.
Cosmetic reformatting
src/Spillgebees.Blazor.Map/Components/..., src/Spillgebees.Blazor.Map/Engine/..., src/Spillgebees.Blazor.Map/Models/..., src/Spillgebees.Blazor.Map.Tests/Engine/..., src/Spillgebees.Blazor.Map.Docs/Samples/TrainTracking/..., src/Spillgebees.Blazor.Map.IntegrationTests/StressQuery.cs
Formatting-only edits adjust method signatures, property getters, collection expressions, namespace declarations, string construction, and test assertion layout without changing behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Spillgebees/Blazor.Map#165: Introduced the camera follow path that this PR extends with trackingAnimation wiring and follow-controller timing logic.

Poem

🐇 Hop, hop, the camera knows the way,
A gentle track for jumps at play.
EaseInOut or Linear, bright,
While quick little steps stay light.
The rabbit cheers: “No bump, no scare—
Just smooth map glides through the air!” 🎈

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding tracking animation support to camera follow mode.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/add-tracking-animation-to-follow-mode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/Spillgebees.Blazor.Map.Docs/Samples/CameraFollow/CameraFollowExample.razor.cs (1)

273-278: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move TrainMotionChoice above DisposeAsync or move DisposeAsync to the end.

This new enum keeps the component out of the required .razor.cs member order. As per coding guidelines, **/*.razor.cs: Member order in C# components: [Inject] → [CascadingParameter] → [Parameter] first, then all other public members before private, with nested types and DisposeAsync last; keep fields/consts at the top of their group.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/Spillgebees.Blazor.Map.Docs/Samples/CameraFollow/CameraFollowExample.razor.cs`
around lines 273 - 278, The new TrainMotionChoice nested enum is out of the
required component member order in CameraFollowExample; move TrainMotionChoice
so it appears before DisposeAsync, or move DisposeAsync to the very end of the
type. Keep the .razor.cs ordering rule in mind for the component:
injected/cascading/parameter members first, then other public members, then
private members, with nested types and DisposeAsync last.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Spillgebees.Blazor.Map.Assets/src/engine/follow.ts`:
- Around line 275-276: `applyTrackMove` is updating `lastMoveAt` for every
camera correction, which keeps the movement timer fresh even when
`positionMoved` is false. Update the tracking flow in `follow.ts` so
`lastMoveAt` only advances on real entity position changes, and keep
bearing/zoom/pitch drift corrections separate from the discrete move timestamp.
Apply the same adjustment in the related `matchheading`/tracking logic around
the other affected block so `shouldAnimateTrackMove(...)` still allows
`camera.trackingAnimation` for the next true jump.

In `@src/Spillgebees.Blazor.Map/Models/Follow/MapFollowCameraOptions.cs`:
- Around line 20-28: MapFollowCameraOptions only exposes the new 8-parameter
primary constructor, which breaks callers compiled against the older 7-parameter
shape. Update MapFollowCameraOptions to preserve binary compatibility by keeping
the existing constructor signature available and exposing TrackingAnimation
through a separate compat path, such as an explicit overload or init-only
property, while leaving the older parameter order and defaults intact.

---

Nitpick comments:
In
`@src/Spillgebees.Blazor.Map.Docs/Samples/CameraFollow/CameraFollowExample.razor.cs`:
- Around line 273-278: The new TrainMotionChoice nested enum is out of the
required component member order in CameraFollowExample; move TrainMotionChoice
so it appears before DisposeAsync, or move DisposeAsync to the very end of the
type. Keep the .razor.cs ordering rule in mind for the component:
injected/cascading/parameter members first, then other public members, then
private members, with nested types and DisposeAsync last.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bbbbc847-15ee-42ab-b479-a963a5dda0c8

📥 Commits

Reviewing files that changed from the base of the PR and between 91794c1 and e96e0c9.

📒 Files selected for processing (40)
  • src/Spillgebees.Blazor.Map.Assets/src/engine/follow.test.ts
  • src/Spillgebees.Blazor.Map.Assets/src/engine/follow.ts
  • src/Spillgebees.Blazor.Map.Assets/src/engine/ops.ts
  • src/Spillgebees.Blazor.Map.Assets/tests/browser/integration/engine-follow.spec.ts
  • src/Spillgebees.Blazor.Map.Docs/Samples/CameraFollow/CameraFollowExample.razor
  • src/Spillgebees.Blazor.Map.Docs/Samples/CameraFollow/CameraFollowExample.razor.cs
  • src/Spillgebees.Blazor.Map.Docs/Samples/TrainTracking/TrainSampleState.cs
  • src/Spillgebees.Blazor.Map.Docs/Samples/TrainTracking/TrainTrackingExample.razor.cs
  • src/Spillgebees.Blazor.Map.IntegrationTests/Pages/EngineFollowFunctionalTest.razor
  • src/Spillgebees.Blazor.Map.IntegrationTests/StressQuery.cs
  • src/Spillgebees.Blazor.Map.Tests/Engine/CameraFollowOpSerializationTests.cs
  • src/Spillgebees.Blazor.Map.Tests/Engine/EngineOpsSerializationTests.cs
  • src/Spillgebees.Blazor.Map.Tests/Engine/MapFollowCoordinatorTests.cs
  • src/Spillgebees.Blazor.Map.Tests/Engine/MotionFrameEncoderTests.cs
  • src/Spillgebees.Blazor.Map/Components/DisplayMapControl.razor.cs
  • src/Spillgebees.Blazor.Map/Components/GeolocateMapControl.cs
  • src/Spillgebees.Blazor.Map/Components/Layers/GeoJsonSource.cs
  • src/Spillgebees.Blazor.Map/Components/Layers/TrackedEntityLayer.cs
  • src/Spillgebees.Blazor.Map/Components/LegendMapControl.razor.cs
  • src/Spillgebees.Blazor.Map/Components/MapCircle.cs
  • src/Spillgebees.Blazor.Map/Components/MapControls.cs
  • src/Spillgebees.Blazor.Map/Components/MapFeatures.cs
  • src/Spillgebees.Blazor.Map/Components/MapMarker.cs
  • src/Spillgebees.Blazor.Map/Components/MapOverlay.cs
  • src/Spillgebees.Blazor.Map/Components/MapOverlays.cs
  • src/Spillgebees.Blazor.Map/Components/MapPolyline.cs
  • src/Spillgebees.Blazor.Map/Components/MapSources.cs
  • src/Spillgebees.Blazor.Map/Components/NavigationMapControl.cs
  • src/Spillgebees.Blazor.Map/Components/OverlayMapControl.razor.cs
  • src/Spillgebees.Blazor.Map/Engine/EngineJson.cs
  • src/Spillgebees.Blazor.Map/Engine/EngineOps.cs
  • src/Spillgebees.Blazor.Map/Engine/EngineStyleJson.cs
  • src/Spillgebees.Blazor.Map/Engine/MapEngineChannel.cs
  • src/Spillgebees.Blazor.Map/Engine/MapFollowCoordinator.cs
  • src/Spillgebees.Blazor.Map/Models/Controls/MapControlOptions.cs
  • src/Spillgebees.Blazor.Map/Models/Display/MapDisplayState.cs
  • src/Spillgebees.Blazor.Map/Models/Follow/MapFollowCameraOptions.cs
  • src/Spillgebees.Blazor.Map/Models/Follow/MapFollowOptions.cs
  • src/Spillgebees.Blazor.Map/Models/Legends/MapLegendItemTemplateContext.cs
  • src/Spillgebees.Blazor.Map/PublicAPI.Shipped.txt

Comment thread src/Spillgebees.Blazor.Map.Assets/src/engine/follow.ts Outdated
Comment thread src/Spillgebees.Blazor.Map/Models/Follow/MapFollowCameraOptions.cs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Spillgebees.Blazor.Map/Components/SgbMap.razor.cs (1)

633-647: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Drain active control sync before tearing down map resources.

MarkDisposing() blocks new syncs, but an already-entered sync can pass Line 676 and still run _controls.Sync() while DisposeAsync() continues to DisposeMapAsync() and Router.Dispose(). Wait on the control-sync semaphore after marking disposal and before tearing down shared resources.

Proposed fix
 public async ValueTask DisposeAsync()
 {
     MarkDisposing();
+    await DrainControlSyncAsync();
     _readyTcs.TrySetResult(false);
     _subscribedDisplay?.Changed -= HandleDisplayChanged;
+private async ValueTask DrainControlSyncAsync()
+{
+    if (!TryEnterControlSyncDrain())
+    {
+        return;
+    }
+
+    try
+    {
+        await _controlSyncLock.WaitAsync();
+        _controlSyncLock.Release();
+    }
+    finally
+    {
+        ExitControlSync();
+    }
+}
+
+private bool TryEnterControlSyncDrain()
+{
+    lock (_controlSyncStateLock)
+    {
+        if (_controlSyncLockDisposed)
+        {
+            return false;
+        }
+
+        _controlSyncUsers++;
+        return true;
+    }
+}

Also applies to: 666-687

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Spillgebees.Blazor.Map/Components/SgbMap.razor.cs` around lines 633 -
647, Drain any in-flight control sync before disposing shared map resources in
DisposeAsync. After MarkDisposing() blocks new syncs, wait for the control-sync
semaphore in the SgbMap component so an already-entered sync cannot continue
into _controls.Sync() while MapEngineJs.DisposeMapAsync() and Router.Dispose()
are running. Keep the ordering in DisposeAsync so the drain happens before
teardown, and preserve the existing cleanup flow afterward.
🧹 Nitpick comments (1)
src/Spillgebees.Blazor.Map/Components/SgbMap.razor.cs (1)

690-745: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep DisposeAsync last in the component partial.

The new private control-sync helpers are placed after DisposeAsync; move helpers before disposal and leave DisposeAsync at the end of the component. As per coding guidelines, "**/*.razor.cs: Member order in C# components: [Inject] → [CascadingParameter] → [Parameter] first, then all other public members before private, with nested types and DisposeAsync last; keep fields/consts at the top of their group."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Spillgebees.Blazor.Map/Components/SgbMap.razor.cs` around lines 690 -
745, The component member order is off: the new private control-sync helpers in
SgbMap should be moved before the disposal method so DisposeAsync remains last
in the partial. Relocate MarkDisposing, IsControlSyncDisposing,
TryEnterControlSync, ExitControlSync, and DisposeControlSyncLockIfIdle to the
private-member area above DisposeAsync, keeping DisposeAsync at the end of the
component.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/Spillgebees.Blazor.Map/Components/SgbMap.razor.cs`:
- Around line 633-647: Drain any in-flight control sync before disposing shared
map resources in DisposeAsync. After MarkDisposing() blocks new syncs, wait for
the control-sync semaphore in the SgbMap component so an already-entered sync
cannot continue into _controls.Sync() while MapEngineJs.DisposeMapAsync() and
Router.Dispose() are running. Keep the ordering in DisposeAsync so the drain
happens before teardown, and preserve the existing cleanup flow afterward.

---

Nitpick comments:
In `@src/Spillgebees.Blazor.Map/Components/SgbMap.razor.cs`:
- Around line 690-745: The component member order is off: the new private
control-sync helpers in SgbMap should be moved before the disposal method so
DisposeAsync remains last in the partial. Relocate MarkDisposing,
IsControlSyncDisposing, TryEnterControlSync, ExitControlSync, and
DisposeControlSyncLockIfIdle to the private-member area above DisposeAsync,
keeping DisposeAsync at the end of the component.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b270a34e-2b37-436a-b66c-fc5f57f61b26

📥 Commits

Reviewing files that changed from the base of the PR and between e96e0c9 and f1653ce.

📒 Files selected for processing (5)
  • src/Spillgebees.Blazor.Map.Assets/src/engine/follow.test.ts
  • src/Spillgebees.Blazor.Map.Assets/src/engine/follow.ts
  • src/Spillgebees.Blazor.Map.Docs/Samples/CameraFollow/CameraFollowExample.razor.cs
  • src/Spillgebees.Blazor.Map.Tests/Components/SgbMapOverlayContentTests.cs
  • src/Spillgebees.Blazor.Map/Components/SgbMap.razor.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Spillgebees.Blazor.Map.Assets/src/engine/follow.ts
  • src/Spillgebees.Blazor.Map.Docs/Samples/CameraFollow/CameraFollowExample.razor.cs
  • src/Spillgebees.Blazor.Map.Assets/src/engine/follow.test.ts

@igotinfected igotinfected merged commit 9376578 into main Jun 30, 2026
5 checks passed
@igotinfected igotinfected deleted the feature/add-tracking-animation-to-follow-mode branch June 30, 2026 04:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant