fix(mask): stop requestAnimationFrame loop on dispose to prevent memory leak

The #moveCursorToTarget() method recursively schedules itself via
requestAnimationFrame, creating a continuous animation loop for the
AI cursor. However, dispose() only removes the DOM wrapper element
without stopping this loop, causing:

- CPU waste: rAF callback continues executing every frame (~60fps)
  after the mask is disposed, performing unnecessary calculations
  on a detached cursor element.
- Resource leak: Each SimulatorMask instance creates an unrecoverable
  animation loop that persists for the lifetime of the page.
- Console noise: style assignments to removed DOM nodes may produce
  browser warnings.

Fix: Add a #disposed boolean flag, checked at the top of
#moveCursorToTarget() to short-circuit the recursion. Set the flag
to true in dispose() before removing DOM elements.

Changes:
- Add #disposed field (default false)
- Guard #moveCursorToTarget() with early return when #disposed
- Set #disposed = true in dispose() before cleanup
This commit is contained in:
liuguiyuan
2026-04-08 18:53:42 +08:00
parent 3bffd76626
commit 9104064e8c

View File

@@ -10,6 +10,8 @@ export class SimulatorMask extends EventTarget {
wrapper = document.createElement('div')
motion: Motion | null = null
#disposed = false
#cursor = document.createElement('div')
#currentCursorX = 0
@@ -129,6 +131,7 @@ export class SimulatorMask extends EventTarget {
}
#moveCursorToTarget() {
if (this.#disposed) return
const newX = this.#currentCursorX + (this.#targetCursorX - this.#currentCursorX) * 0.2
const newY = this.#currentCursorY + (this.#targetCursorY - this.#currentCursorY) * 0.2
@@ -200,6 +203,7 @@ export class SimulatorMask extends EventTarget {
}
dispose() {
this.#disposed = true
console.log('dispose SimulatorMask')
this.motion?.dispose()
this.wrapper.remove()