Frontmatter
| id | 8781 |
| title | Fix duplicate TicketCanvas animation loops via reactive animationId |
| state | Closed |
| labels | bugai |
| assignees | tobiu |
| createdAt | Jan 18, 2026, 5:33 PM |
| updatedAt | Jan 18, 2026, 5:37 PM |
| githubUrl | https://github.com/neomjs/neo/issues/8781 |
| author | tobiu |
| commentsCount | 2 |
| parentIssue | null |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [] |
| closedAt | Jan 18, 2026, 5:37 PM |
Fix duplicate TicketCanvas animation loops via reactive animationId
tobiu assigned to @tobiu on Jan 18, 2026, 5:36 PM

tobiu
Jan 18, 2026, 5:37 PM
Input from Gemini 3 Pro:
✦ Refactoring complete.
- Reactive Config:
animationIdis nowanimationId_, a reactive config.- Auto-Cleanup:
afterSetAnimationIdnow automatically cancels the previous frame (or timeout) whenever the ID changes.- Render Loop: The
rendermethod now correctly assignsme.animationIdto the result ofrequestAnimationFrame(orsetTimeout), ensuring the loop is tracked.- Cleanup:
clearGraphnow setsme.animationId = nullto stop the loop when unmounting.This guarantees that a new loop cannot start without killing the old one, resolving the duplicate render issue.

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
The
TicketCanvassingleton (Canvas Worker) fails to track itsanimationIdcorrectly, causing it to remainnulleven after an animation loop starts. This leads toupdateGraphDataspawning 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.mjsto:animationIdclass field into a reactive config:animationId_.afterSetAnimationIdto handle cleanup (canceling the previous frame) automatically when the ID changes.this.animationId.clearGraphproperly nullifiesanimationIdto stop the loop.This ensures only one render loop is ever active at a time.