From a4761b8b8ebf4757109dd1d87c846d36bdd0ec79 Mon Sep 17 00:00:00 2001 From: malangfox Date: Wed, 21 Aug 2024 19:04:04 +0900 Subject: [PATCH 1/3] fix: apply ResizeObserver to camera and panels --- src/Flicking.ts | 13 +++++- src/core/AutoResizer.ts | 17 ++++++- src/renderer/Renderer.ts | 6 +++ test/unit/Flicking.spec.ts | 70 +++++++++++++++++++++++++++++ test/unit/renderer/Renderer.spec.ts | 4 +- 5 files changed, 106 insertions(+), 4 deletions(-) diff --git a/src/Flicking.ts b/src/Flicking.ts index 22d6af9e3d..2ca51f9ee3 100644 --- a/src/Flicking.ts +++ b/src/Flicking.ts @@ -218,6 +218,13 @@ class Flicking extends Component { * @see Viewport */ public get viewport() { return this._viewport; } + /** + * {@link AutoResizer} instance of the Flicking + * @ko 현재 Flicking에 활성화된 {@link AutoResizer} 인스턴스 + * @internal + * @readonly + */ + public get autoResizer() { return this._autoResizer; } // Internal States /** * Whether Flicking's {@link Flicking#init init()} is called. @@ -906,6 +913,10 @@ class Flicking extends Component { public set autoResize(val: FlickingOptions["autoResize"]) { this._autoResize = val; + if (!this._initialized) { + return; + } + if (val) { this._autoResizer.enable(); } else { @@ -916,7 +927,7 @@ class Flicking extends Component { public set useResizeObserver(val: FlickingOptions["useResizeObserver"]) { this._useResizeObserver = val; - if (this._autoResize) { + if (this._initialized && this._autoResize) { this._autoResizer.enable(); } } diff --git a/src/core/AutoResizer.ts b/src/core/AutoResizer.ts index 56551dbe3d..bd37f0ecc4 100644 --- a/src/core/AutoResizer.ts +++ b/src/core/AutoResizer.ts @@ -4,6 +4,10 @@ */ import Flicking from "../Flicking"; +/** + * A component that detects size change and trigger resize method when the autoResize option is used + * @ko autoResize 옵션을 사용할 때 크기 변화를 감지하고 Flicking의 resize를 호출하는 컴포넌트 + */ class AutoResizer { private _flicking: Flicking; private _enabled: boolean; @@ -36,7 +40,11 @@ class AutoResizer { ? new ResizeObserver(this._skipFirstResize) : new ResizeObserver(this._onResize); - resizeObserver.observe(flicking.viewport.element); + [ + flicking.viewport.element, + flicking.camera.element, + ...flicking.panels.map(panel => panel.element) + ].forEach((element) => { resizeObserver.observe(element); }); this._resizeObserver = resizeObserver; } else { @@ -64,6 +72,13 @@ class AutoResizer { return this; } + public observe(element: Element): this { + if (this._resizeObserver) { + this._resizeObserver.observe(element); + } + return this; + } + private _onResize = () => { const flicking = this._flicking; const resizeDebounce = flicking.resizeDebounce; diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 9df336ea7c..b78a54e175 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -335,6 +335,12 @@ abstract class Renderer { // Update camera & control this._updateCameraAndControl(); + if (flicking.autoResize && flicking.useResizeObserver) { + panelsAdded.forEach((panel) => { + flicking.autoResizer.observe(panel.element); + }); + } + void this.render(); if (!flicking.animating) { diff --git a/test/unit/Flicking.spec.ts b/test/unit/Flicking.spec.ts index a5a3eb8fb9..ad49d609d8 100644 --- a/test/unit/Flicking.spec.ts +++ b/test/unit/Flicking.spec.ts @@ -908,6 +908,76 @@ describe("Flicking", () => { expect(resizeSpy.calledOnce).to.be.true; }); + + ["viewport", "camera", "panel"].forEach(element => { + it(`should call resize when size of ${element} is changed`, async () => { + const flicking = await createFlicking( + El.viewport("1000px", "1000px").add( + El.camera("1000px", "1000px").add( + El.panel("800px", "1000px"), + El.panel("800px", "1000px"), + El.panel("800px", "1000px"), + ) + ), + { autoResize: true, useResizeObserver: true } + ); + const afterResizeSpy = sinon.spy(); + const beforeResizeSpy = sinon.spy(); + + // wait for initial resize + await waitTime(100); + + flicking.on(EVENTS.AFTER_RESIZE, afterResizeSpy); + flicking.on(EVENTS.BEFORE_RESIZE, beforeResizeSpy); + + switch (element) { + case "viewport": + flicking.element.style.height = "3000px"; + break; + case "camera": + flicking.camera.element.style.height = "3000px"; + break; + case "panel": + flicking.panels[2].element.style.height = "3000px"; + break; + } + + await waitEvent(flicking, EVENTS.AFTER_RESIZE); + + expect(afterResizeSpy.calledOnce).to.be.true; + expect(beforeResizeSpy.calledOnce).to.be.true; + }); + }); + + it("should observe size of panel element if panel element is added later", async () => { + const flicking = await createFlicking( + El.viewport("1000px", "1000px").add( + El.camera("1000px", "1000px").add( + El.panel("800px", "1000px"), + El.panel("800px", "1000px"), + El.panel("800px", "1000px"), + ) + ), + { autoResize: true, useResizeObserver: true } + ); + const afterResizeSpy = sinon.spy(); + const beforeResizeSpy = sinon.spy(); + + // wait for initial resize + await waitTime(100); + + flicking.on(EVENTS.AFTER_RESIZE, afterResizeSpy); + flicking.on(EVENTS.BEFORE_RESIZE, beforeResizeSpy); + + flicking.append(flicking.panels[0].element.outerHTML); + + flicking.panels[3].element.style.height = "3000px"; + + await waitEvent(flicking, EVENTS.AFTER_RESIZE); + + expect(afterResizeSpy.calledOnce).to.be.true; + expect(beforeResizeSpy.calledOnce).to.be.true; + }); }); describe("preventClickOnDrag", () => { diff --git a/test/unit/renderer/Renderer.spec.ts b/test/unit/renderer/Renderer.spec.ts index 726cf44e06..f1acf3d439 100644 --- a/test/unit/renderer/Renderer.spec.ts +++ b/test/unit/renderer/Renderer.spec.ts @@ -423,7 +423,7 @@ describe("Renderer", () => { it("should resize the panel with image when it's loaded", async () => { const flicking = await createFlicking(El.viewport("200px", "200px").add( - El.camera().add( + El.camera("100%", "100%").add( El.imgPanel("100%", "100%") ) ), { resizeOnContentsReady: true }); @@ -438,7 +438,7 @@ describe("Renderer", () => { it("should update the camera range after the image's loaded", async () => { const flicking = await createFlicking(El.viewport("200px", "200px").add( - El.camera().add( + El.camera("100%", "100%").add( El.imgPanel("100%", "100%") ) ), { resizeOnContentsReady: true }); From 3763e799852c27d97af3b10d0d01f19364385a5d Mon Sep 17 00:00:00 2001 From: malangfox Date: Fri, 23 Aug 2024 20:59:15 +0900 Subject: [PATCH 2/3] fix: unobserve removed panel when panel change --- src/core/AutoResizer.ts | 7 +++++++ src/renderer/Renderer.ts | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/core/AutoResizer.ts b/src/core/AutoResizer.ts index bd37f0ecc4..c83d720c83 100644 --- a/src/core/AutoResizer.ts +++ b/src/core/AutoResizer.ts @@ -79,6 +79,13 @@ class AutoResizer { return this; } + public unobserve(element: Element): this { + if (this._resizeObserver) { + this._resizeObserver.unobserve(element); + } + return this; + } + private _onResize = () => { const flicking = this._flicking; const resizeDebounce = flicking.resizeDebounce; diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index b78a54e175..c630953a93 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -339,6 +339,9 @@ abstract class Renderer { panelsAdded.forEach((panel) => { flicking.autoResizer.observe(panel.element); }); + panelsRemoved.forEach((panel) => { + flicking.autoResizer.unobserve(panel.element); + }); } void this.render(); From 16b1c05bc06ddba686a89e787fd1e7dbc92aaf65 Mon Sep 17 00:00:00 2001 From: malangfox Date: Fri, 30 Aug 2024 11:36:38 +0900 Subject: [PATCH 3/3] fix: add defense code to check if element exists --- src/renderer/Renderer.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index c630953a93..901b769e1d 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -337,10 +337,14 @@ abstract class Renderer { if (flicking.autoResize && flicking.useResizeObserver) { panelsAdded.forEach((panel) => { - flicking.autoResizer.observe(panel.element); + if (panel.element) { + flicking.autoResizer.observe(panel.element); + } }); panelsRemoved.forEach((panel) => { - flicking.autoResizer.unobserve(panel.element); + if (panel.element) { + flicking.autoResizer.unobserve(panel.element); + } }); }