diff --git a/CHANGELOG.md b/CHANGELOG.md index 50edfb4df..a324b7dae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +- Fixed issue where window resize events were handled twice when using window-based display modes (FitScreen, FitScreenAndFill, FitScreenAndZoom, FillScreen), causing double resolution/viewport computation and double canvas size writes - Fixed issue where `scaleTo({…})` and `scaleBy({…})` actions would cause entities to keep scaling indefinitely after the action completed, due to a copy-paste bug that zeroed `angularVelocity` instead of `scaleFactor` - Fixed issue where `scaleTo({…})` and `scaleBy({…})` actions used a live reference to the entity's scale vector as the interpolation start point, causing the easing curve to be corrupted if the entity's scale changed during the action - Fixed issue where the first action in a sequence would not execute after calling `clearActions()` mid-execution. All action types now properly reset their initialization state when stopped, resolving issue #3468 diff --git a/src/engine/screen.ts b/src/engine/screen.ts index e9a587bfa..f8f42b24d 100644 --- a/src/engine/screen.ts +++ b/src/engine/screen.ts @@ -328,7 +328,9 @@ export class Screen { if (this._resizeObserver) { this._resizeObserver.disconnect(); } - this.parent.removeEventListener('resize', this._resizeHandler); + if (!(this.parent instanceof Window)) { + this.parent.removeEventListener('resize', this._resizeHandler); + } // Safari <=13.1 workaround if (this._mediaQueryList.removeEventListener) { this._mediaQueryList.removeEventListener('change', this._pixelRatioChangeHandler); @@ -1171,8 +1173,8 @@ export class Screen { this._resizeHandler(); }); this._resizeObserver.observe(this.parent); + this.parent.addEventListener('resize', this._resizeHandler); } - this.parent.addEventListener('resize', this._resizeHandler); } /** diff --git a/src/spec/vitest/loader-spec.ts b/src/spec/vitest/loader-spec.ts index 9bcd0f5ef..a8d0e048e 100644 --- a/src/spec/vitest/loader-spec.ts +++ b/src/spec/vitest/loader-spec.ts @@ -286,12 +286,25 @@ describe('A loader', () => { const oldPos = [loader.playButtonRootElement.style.left, loader.playButtonRootElement.style.top]; - engine.screen.viewport = { width: 100, height: 100 }; + const originalGetBoundingClientRect = engine.canvas.getBoundingClientRect; + engine.canvas.getBoundingClientRect = () => ({ + x: 0, + y: 0, + width: 500, + height: 500, + top: 0, + left: 0, + right: 500, + bottom: 500, + toJSON: () => {} + }); engine.browser.window.nativeComponent.dispatchEvent(new Event('resize')); const newPos = [loader.playButtonRootElement.style.left, loader.playButtonRootElement.style.top]; + engine.canvas.getBoundingClientRect = originalGetBoundingClientRect; + expect(oldPos).not.toEqual(newPos); }); diff --git a/src/spec/vitest/screen-spec.ts b/src/spec/vitest/screen-spec.ts index bd74cd1ec..fb66e9a55 100644 --- a/src/spec/vitest/screen-spec.ts +++ b/src/spec/vitest/screen-spec.ts @@ -996,4 +996,21 @@ describe('A Screen', () => { ' (read more here https://excaliburjs.com/docs/screens#understanding-viewport--resolution).' ]); }); + + it('should only register window resize listener once for Window parent (not twice)', () => { + const addEventListenerSpy = vi.spyOn(window, 'addEventListener'); + + const sut = new ex.Screen({ + canvas, + context, + browser, + displayMode: ex.DisplayMode.FitScreen, + viewport: { width: 800, height: 600 } + }); + + const resizeListenerCalls = addEventListenerSpy.mock.calls.filter((call) => call[0] === 'resize'); + expect(resizeListenerCalls.length).toBe(1); + + addEventListenerSpy.mockRestore(); + }); });