feat: Lightroom-style workspace UI #2
@@ -42,6 +42,7 @@
|
||||
let isFullscreen = $state(false);
|
||||
let zoomed = $state(false);
|
||||
let scale = $state(1);
|
||||
let hiResLoaded = $state(false);
|
||||
|
||||
let showTop = $state(false);
|
||||
let showBottom = $state(false);
|
||||
@@ -60,17 +61,41 @@
|
||||
let startL = 0;
|
||||
let startT = 0;
|
||||
|
||||
const previewSrc = $derived(
|
||||
fresh?.preview_url ?? fresh?.original_url ?? current?.preview_url ?? current?.original_url ?? ''
|
||||
);
|
||||
const fullSrc = $derived(fresh?.original_url ?? current?.original_url ?? '');
|
||||
const displaySrc = $derived(zoomed && hiResLoaded && fullSrc ? fullSrc : previewSrc);
|
||||
|
||||
$effect(() => {
|
||||
const id = current?.id;
|
||||
fresh = null;
|
||||
zoomed = false;
|
||||
scale = 1;
|
||||
hiResLoaded = false;
|
||||
if (!id) return;
|
||||
api<{ photo: Photo }>(`/photos/${id}`)
|
||||
.then((data) => (fresh = data.photo))
|
||||
.catch(() => {});
|
||||
});
|
||||
|
||||
// In zoom mode, preload the full-resolution original and swap it in once loaded.
|
||||
$effect(() => {
|
||||
if (!zoomed || hiResLoaded) return;
|
||||
const url = fullSrc;
|
||||
if (!url) return;
|
||||
const im = new Image();
|
||||
let alive = true;
|
||||
im.onload = () => {
|
||||
if (alive) hiResLoaded = true;
|
||||
};
|
||||
im.src = url;
|
||||
return () => {
|
||||
alive = false;
|
||||
im.onload = null;
|
||||
};
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
const el = scroller;
|
||||
if (!el) return;
|
||||
@@ -205,10 +230,10 @@
|
||||
}
|
||||
|
||||
function baseWidth(): number {
|
||||
return imgEl?.naturalWidth || fresh?.width || current?.width || 0;
|
||||
return fresh?.width || current?.width || imgEl?.naturalWidth || 0;
|
||||
}
|
||||
function baseHeight(): number {
|
||||
return imgEl?.naturalHeight || fresh?.height || current?.height || 0;
|
||||
return fresh?.height || current?.height || imgEl?.naturalHeight || 0;
|
||||
}
|
||||
|
||||
function prev() {
|
||||
@@ -219,8 +244,7 @@
|
||||
}
|
||||
|
||||
function toggleActual() {
|
||||
const atHundred = zoomed && Math.abs(scale - 1) < 0.01;
|
||||
if (atHundred) {
|
||||
if (zoomed) {
|
||||
zoomed = false;
|
||||
scale = 1;
|
||||
if (scroller) {
|
||||
@@ -434,10 +458,7 @@
|
||||
{#if current}
|
||||
<img
|
||||
bind:this={imgEl}
|
||||
src={fresh?.preview_url ??
|
||||
fresh?.original_url ??
|
||||
current.preview_url ??
|
||||
current.original_url}
|
||||
src={displaySrc}
|
||||
alt={current.original_filename}
|
||||
draggable="false"
|
||||
style={zoomed ? `width: ${baseWidth() * scale}px; height: auto;` : ''}
|
||||
|
||||
Reference in New Issue
Block a user