LearnNewsExamplesServices
Frontmatter
id8781
titleFix duplicate TicketCanvas animation loops via reactive animationId
stateClosed
labels
bugai
assigneestobiu
createdAtJan 18, 2026, 5:33 PM
updatedAtJan 18, 2026, 5:37 PM
githubUrlhttps://github.com/neomjs/neo/issues/8781
authortobiu
commentsCount2
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJan 18, 2026, 5:37 PM

Fix duplicate TicketCanvas animation loops via reactive animationId

Closed v11.23.0 bugai
tobiu
tobiu commented on Jan 18, 2026, 5:33 PM

The TicketCanvas singleton (Canvas Worker) fails to track its animationId correctly, causing it to remain null even after an animation loop starts. This leads to updateGraphData spawning a new, parallel render loop every time the timeline data refreshes (e.g., navigating between tickets), resulting in visual glitches and performance degradation.

This ticket refactors TicketCanvas.mjs to:

  1. Convert the animationId class field into a reactive config: animationId_.
  2. Implement afterSetAnimationId to handle cleanup (canceling the previous frame) automatically when the ID changes.
  3. Ensure the render loop correctly assigns the new frame ID to this.animationId.
  4. Ensure clearGraph properly nullifies animationId to stop the loop.

This ensures only one render loop is ever active at a time.

tobiu added the bug label on Jan 18, 2026, 5:33 PM
tobiu added the ai label on Jan 18, 2026, 5:33 PM
tobiu referenced in commit 637e76c - "fix: TicketCanvas prevent duplicate render loops via reactive animationId (#8781)" on Jan 18, 2026, 5:36 PM
tobiu assigned to @tobiu on Jan 18, 2026, 5:36 PM
tobiu
tobiu Jan 18, 2026, 5:37 PM

Input from Gemini 3 Pro:

✦ Refactoring complete.

  • Reactive Config: animationId is now animationId_, a reactive config.
  • Auto-Cleanup: afterSetAnimationId now automatically cancels the previous frame (or timeout) whenever the ID changes.
  • Render Loop: The render method now correctly assigns me.animationId to the result of requestAnimationFrame (or setTimeout), ensuring the loop is tracked.
  • Cleanup: clearGraph now sets me.animationId = null to stop the loop when unmounting.

This guarantees that a new loop cannot start without killing the old one, resolving the duplicate render issue.

tobiu
tobiu Jan 18, 2026, 5:37 PM

Input from Gemini 3 Pro:

✦ Closing ticket as the fix has been implemented and pushed.

tobiu closed this issue on Jan 18, 2026, 5:37 PM
tobiu cross-referenced by #8782 on Jan 18, 2026, 5:43 PM