From e36261b5529bdc5d0c359c4a3b36fdae9409ab93 Mon Sep 17 00:00:00 2001
From: Mees Frensel <33722705+meesfrensel@users.noreply.github.com>
Date: Fri, 28 Nov 2025 18:50:16 +0100
Subject: [PATCH] fix(web): integrate zoom toggle button into panorama photo
viewer (#24189)
---
.../asset-viewer/asset-viewer.svelte | 2 +-
.../asset-viewer/image-panorama-viewer.svelte | 8 ++--
.../photo-sphere-viewer-adapter.svelte | 39 ++++++++++++++++---
3 files changed, 40 insertions(+), 9 deletions(-)
diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte
index 7570278e51..b657f34ece 100644
--- a/web/src/lib/components/asset-viewer/asset-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte
@@ -512,7 +512,7 @@
{:else if asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR || (asset.originalPath && asset.originalPath
.toLowerCase()
.endsWith('.insp'))}
-
+
{:else if isShowEditor && selectedEditType === 'crop'}
{:else}
diff --git a/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte b/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte
index 7334aab4d4..08ba43526d 100644
--- a/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte
@@ -7,11 +7,12 @@
import { t } from 'svelte-i18n';
import { fade } from 'svelte/transition';
- interface Props {
+ type Props = {
asset: AssetResponseDto;
- }
+ zoomToggle?: (() => void) | null;
+ };
- const { asset }: Props = $props();
+ let { asset, zoomToggle = $bindable() }: Props = $props();
const loadAssetData = async (id: string) => {
const data = await viewAsset({ ...authManager.params, id, size: AssetMediaSize.Preview });
@@ -24,6 +25,7 @@
{:then [data, { default: PhotoSphereViewer }]}
+ import { shortcuts } from '$lib/actions/shortcut';
import { boundingBoxesArray, type Faces } from '$lib/stores/people.store';
import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store';
+ import { photoZoomState } from '$lib/stores/zoom-image.store';
import {
EquirectangularAdapter,
Viewer,
@@ -24,15 +26,23 @@
strokeLinejoin: 'round',
};
- interface Props {
+ type Props = {
panorama: string | { source: string };
originalPanorama?: string | { source: string };
adapter?: AdapterConstructor | [AdapterConstructor, unknown];
plugins?: (PluginConstructor | [PluginConstructor, unknown])[];
navbar?: boolean;
- }
+ zoomToggle?: (() => void) | null;
+ };
- let { panorama, originalPanorama, adapter = EquirectangularAdapter, plugins = [], navbar = false }: Props = $props();
+ let {
+ panorama,
+ originalPanorama,
+ adapter = EquirectangularAdapter,
+ plugins = [],
+ navbar = false,
+ zoomToggle = $bindable(),
+ }: Props = $props();
let container: HTMLDivElement | undefined = $state();
let viewer: Viewer;
@@ -93,6 +103,14 @@
}
});
+ zoomToggle = () => {
+ if (!viewer) {
+ return;
+ }
+ viewer.animate({ zoom: $photoZoomState.currentZoom > 1 ? 50 : 83.3, speed: 250 });
+ };
+
+ let hasChangedResolution: boolean = false;
onMount(() => {
if (!container) {
return;
@@ -139,10 +157,15 @@
const resolutionPlugin = viewer.getPlugin(ResolutionPlugin);
const zoomHandler = ({ zoomLevel }: events.ZoomUpdatedEvent) => {
// zoomLevel range: [0, 100]
- if (Math.round(zoomLevel) >= 75) {
+ photoZoomState.set({
+ ...$photoZoomState,
+ currentZoom: zoomLevel / 50,
+ });
+
+ if (Math.round(zoomLevel) >= 75 && !hasChangedResolution) {
// Replace the preview with the original
void resolutionPlugin.setResolution('original');
- viewer.removeEventListener(events.ZoomUpdatedEvent.type, zoomHandler);
+ hasChangedResolution = true;
}
};
@@ -158,7 +181,13 @@
viewer.destroy();
}
boundingBoxesUnsubscribe();
+ // zoomHandler is not called on initial load. Viewer initial zoom is 1, but photoZoomState could be != 1.
+ photoZoomState.set({
+ ...$photoZoomState,
+ currentZoom: 1,
+ });
});
+