feat(web): bounding box for faces when hovering over the face in photo view (#26667)
* feat(web): when hovering over a face already deteced, display the bounding box also shown when hovering over the person in the details-pane. * prevent lint error * fix unused varpull/26691/head
parent
8279e1078a
commit
5caa7e1902
|
|
@ -11,7 +11,7 @@
|
||||||
import { imageManager } from '$lib/managers/ImageManager.svelte';
|
import { imageManager } from '$lib/managers/ImageManager.svelte';
|
||||||
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
||||||
import { ocrManager } from '$lib/stores/ocr.svelte';
|
import { ocrManager } from '$lib/stores/ocr.svelte';
|
||||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
import { boundingBoxesArray, type Faces } from '$lib/stores/people.store';
|
||||||
import { SlideshowLook, SlideshowState, slideshowLookCssMapping, slideshowStore } from '$lib/stores/slideshow.store';
|
import { SlideshowLook, SlideshowState, slideshowLookCssMapping, slideshowStore } from '$lib/stores/slideshow.store';
|
||||||
import { getAssetUrl, targetImageSize as getTargetImageSize, handlePromiseError } from '$lib/utils';
|
import { getAssetUrl, targetImageSize as getTargetImageSize, handlePromiseError } from '$lib/utils';
|
||||||
import { canCopyImageToClipboard, copyImageToClipboard } from '$lib/utils/asset-utils';
|
import { canCopyImageToClipboard, copyImageToClipboard } from '$lib/utils/asset-utils';
|
||||||
|
|
@ -195,6 +195,42 @@
|
||||||
}
|
}
|
||||||
lastUrl = imageLoaderUrl;
|
lastUrl = imageLoaderUrl;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const faceToNameMap = $derived.by(() => {
|
||||||
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
||||||
|
const map = new Map<Faces, string>();
|
||||||
|
for (const person of asset.people ?? []) {
|
||||||
|
for (const face of person.faces ?? []) {
|
||||||
|
map.set(face, person.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
|
||||||
|
const faces = $derived(Array.from(faceToNameMap.keys()));
|
||||||
|
|
||||||
|
const handleImageMouseMove = (event: MouseEvent) => {
|
||||||
|
$boundingBoxesArray = [];
|
||||||
|
if (!assetViewerManager.imgRef || !element || isFaceEditMode.value || ocrManager.showOverlay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const containerRect = element.getBoundingClientRect();
|
||||||
|
const mouseX = event.clientX - containerRect.left;
|
||||||
|
const mouseY = event.clientY - containerRect.top;
|
||||||
|
|
||||||
|
const faceBoxes = getBoundingBox(faces, overlayMetrics);
|
||||||
|
|
||||||
|
for (const [index, box] of faceBoxes.entries()) {
|
||||||
|
if (mouseX >= box.left && mouseX <= box.left + box.width && mouseY >= box.top && mouseY <= box.top + box.height) {
|
||||||
|
$boundingBoxesArray.push(faces[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImageMouseLeave = () => {
|
||||||
|
$boundingBoxesArray = [];
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AssetViewerEvents {onCopy} {onZoom} />
|
<AssetViewerEvents {onCopy} {onZoom} />
|
||||||
|
|
@ -218,6 +254,9 @@
|
||||||
class="relative h-full w-full select-none"
|
class="relative h-full w-full select-none"
|
||||||
bind:clientWidth={containerWidth}
|
bind:clientWidth={containerWidth}
|
||||||
bind:clientHeight={containerHeight}
|
bind:clientHeight={containerHeight}
|
||||||
|
role="presentation"
|
||||||
|
onmousemove={handleImageMouseMove}
|
||||||
|
onmouseleave={handleImageMouseLeave}
|
||||||
>
|
>
|
||||||
{#if !imageLoaded}
|
{#if !imageLoaded}
|
||||||
<div id="spinner" class="flex h-full items-center justify-center">
|
<div id="spinner" class="flex h-full items-center justify-center">
|
||||||
|
|
@ -248,11 +287,20 @@
|
||||||
: slideshowLookCssMapping[$slideshowLook]}"
|
: slideshowLookCssMapping[$slideshowLook]}"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
/>
|
/>
|
||||||
{#each getBoundingBox($boundingBoxesArray, overlayMetrics) as boundingbox (boundingbox.id)}
|
{#each getBoundingBox($boundingBoxesArray, overlayMetrics) as boundingbox, index (boundingbox.id)}
|
||||||
<div
|
<div
|
||||||
class="absolute border-solid border-white border-3 rounded-lg"
|
class="absolute border-solid border-white border-3 rounded-lg"
|
||||||
style="top: {boundingbox.top}px; left: {boundingbox.left}px; height: {boundingbox.height}px; width: {boundingbox.width}px;"
|
style="top: {boundingbox.top}px; left: {boundingbox.left}px; height: {boundingbox.height}px; width: {boundingbox.width}px;"
|
||||||
></div>
|
></div>
|
||||||
|
{#if faceToNameMap.get($boundingBoxesArray[index])}
|
||||||
|
<div
|
||||||
|
class="absolute bg-white/90 text-black px-2 py-1 rounded text-sm font-medium whitespace-nowrap pointer-events-none shadow-lg"
|
||||||
|
style="top: {boundingbox.top + boundingbox.height + 4}px; left: {boundingbox.left +
|
||||||
|
boundingbox.width}px; transform: translateX(-100%);"
|
||||||
|
>
|
||||||
|
{faceToNameMap.get($boundingBoxesArray[index])}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#each ocrBoxes as ocrBox (ocrBox.id)}
|
{#each ocrBoxes as ocrBox (ocrBox.id)}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue