From fc1c38d06e364a2c1a2d507c059e9173a677c973 Mon Sep 17 00:00:00 2001 From: Theodore Kruczek Date: Wed, 20 Dec 2023 22:12:47 -0500 Subject: [PATCH] fix: :bug: fix zoom functionality Closes #25 --- src/viewer/index.ts | 108 ++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 34 deletions(-) diff --git a/src/viewer/index.ts b/src/viewer/index.ts index cb16862..1b778b1 100644 --- a/src/viewer/index.ts +++ b/src/viewer/index.ts @@ -42,6 +42,16 @@ class Viewer { satellites?: Satellites; orbits?: Orbits; earth?: Earth; + targetZoom = 5; + + /** The maximum zoom level for the viewer. This controls how close the camera can get to the earth. */ + private readonly maxZoomLevel = 60; + /** The minimum zoom level for the viewer. This controls how far the camera can get from the earth. */ + private readonly minZoomLevel = 2; + /** The ideal number of frames to take to zoom in or out. Higher number slows down the zoom animation. */ + private readonly framesPerZoomUpdate = 25; + /** The allowable margin for zooming in or out. If within this margin, the zoom level is set directly to the target zoom. */ + private readonly zoomAllowableMargin = 0.01; constructor (config?: Record) { this.config = { ...config, ...this.config }; @@ -79,6 +89,17 @@ class Viewer { } } + /** + * Handles the scroll wheel event. + */ + onWheel (event: WheelEvent) { + if (event.deltaY > 0) { + this.zoomOut(); + } else { + this.zoomIn(); + } + } + onSatDataLoaded (satData: Record) { this.eventManager.fireEvent('satdataloaded', satData); this.ready = true; @@ -240,10 +261,8 @@ class Viewer { } this.camera.position.y = 42; - this.controls.minDistance = 4; - this.controls.maxDistance = 100; this.controls.enablePan = false; - this.controls.zoomSpeed = 0.5; + this.controls.enableZoom = false; // this.controls.enableDamping = true; // this.controls.autoRotate = true; // this.controls.autoRotateSpeed = 0.5; @@ -251,6 +270,7 @@ class Viewer { this.camera.updateProjectionMatrix(); window.addEventListener('resize', this.onWindowResize.bind(this)); + window.addEventListener('wheel', this.onWheel.bind(this)); const canvasElement = this.renderer.domElement; canvasElement.addEventListener('click', this.onClick.bind(this)); @@ -263,6 +283,8 @@ class Viewer { animate () { requestAnimationFrame(this.animate.bind(this)); + this.updateCamera(); + for (let i = 0; i < this.sceneComponents.length; i++) { this.sceneComponents[i].update(this.scene); } @@ -276,6 +298,26 @@ class Viewer { } } + /** + * Updates the camera zoom based on the target zoom value. + * If the zoom target is different from the current zoom, it gradually zooms towards the target. + * If the zoom target is within a margin of 0.1 from the current zoom, it directly sets the zoom to the target. + * After updating the zoom, it clamps the zoom value and updates the camera's projection matrix. + */ + private updateCamera () { + if (this.camera) { + if (Math.abs(this.camera.zoom - this.targetZoom) > this.zoomAllowableMargin) { + this.camera.zoom += (this.targetZoom - this.camera.zoom) / this.framesPerZoomUpdate; + this.clampZoom(); + this.camera.updateProjectionMatrix(); + } else if (this.camera.zoom !== this.targetZoom) { + this.camera.zoom = this.targetZoom; + this.clampZoom(); + this.camera.updateProjectionMatrix(); + } + } + } + getSatelliteStore () { return this.satelliteStore; } @@ -291,44 +333,42 @@ class Viewer { } } - zoomIn () { + /** + * Clamps the zoom level of the camera to ensure it stays within a certain range. + * The targetZoom is adjusted to be no more than 5 times the current zoom level + * and no less than 1/5th of the current zoom level. The camera's zoom level is then + * clamped to the specified minimum and maximum zoom levels. + */ + private clampZoom () { if (this.camera) { - const targetZoom = this.camera.zoom + 1.2; - const timeout = 20; - const zoomFn = () => { - if (!this.camera) { - return; - } - - if (this.camera.zoom < targetZoom) { - this.camera.zoom += 0.08; - this.camera.updateProjectionMatrix(); - setTimeout(zoomFn, timeout); - } - }; + if (this.targetZoom > this.camera.zoom * 5) { + this.targetZoom = this.camera.zoom * 5; + } else if (this.targetZoom < this.camera.zoom / 5) { + this.targetZoom = this.camera.zoom / 5; + } - setTimeout(zoomFn, timeout); + this.camera.zoom = Math.min(Math.max(this.camera.zoom, this.minZoomLevel), this.maxZoomLevel); + this.targetZoom = Math.min(Math.max(this.targetZoom, this.minZoomLevel), this.maxZoomLevel); } } - zoomOut () { + /** + * Zooms in the camera. Fired when the user scrolls up or presses the zoom in button. + */ + zoomIn ():void { if (this.camera) { - const targetZoom = this.camera.zoom - 1.2; - const timeout = 20; - - const zoomFn = () => { - if (!this.camera) { - return; - } - - if (this.camera.zoom > targetZoom) { - this.camera.zoom -= 0.08; - this.camera.updateProjectionMatrix(); - setTimeout(zoomFn, timeout); - } - }; + this.targetZoom += (this.camera.zoom / 4); + this.clampZoom(); + } + } - setTimeout(zoomFn, timeout); + /** + * Zooms out the camera. Fired when the user scrolls down or presses the zoom out button. + */ + zoomOut ():void { + if (this.camera) { + this.targetZoom -= (this.camera.zoom / 6); + this.clampZoom(); } }