fix: refine fullscreen photo overlay
This commit is contained in:
@@ -32,6 +32,7 @@ let scheduledRenderTimer = null;
|
|||||||
let scheduledRenderOptions = { fitMap: false };
|
let scheduledRenderOptions = { fitMap: false };
|
||||||
let lastRenderAt = 0;
|
let lastRenderAt = 0;
|
||||||
const SHARE_PARAM_NAME = "share";
|
const SHARE_PARAM_NAME = "share";
|
||||||
|
let overlayMapView = null;
|
||||||
|
|
||||||
const map = L.map("map", { zoomControl: true }).setView([52.5208, 13.4095], 13);
|
const map = L.map("map", { zoomControl: true }).setView([52.5208, 13.4095], 13);
|
||||||
const lightMapLayer = L.tileLayer("https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", {
|
const lightMapLayer = L.tileLayer("https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", {
|
||||||
@@ -248,6 +249,27 @@ function setMapTheme(theme) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function captureMapView() {
|
||||||
|
const center = map.getCenter();
|
||||||
|
return {
|
||||||
|
center: [center.lat, center.lng],
|
||||||
|
zoom: map.getZoom()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function rememberOverlayMapView() {
|
||||||
|
overlayMapView = captureMapView();
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreOverlayMapView() {
|
||||||
|
if (!overlayMapView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
map.setView(overlayMapView.center, overlayMapView.zoom, { animate: false });
|
||||||
|
overlayMapView = null;
|
||||||
|
}
|
||||||
|
|
||||||
function setRouteVisible(visible) {
|
function setRouteVisible(visible) {
|
||||||
routeVisible = Boolean(visible);
|
routeVisible = Boolean(visible);
|
||||||
localStorage.setItem(ROUTE_VISIBLE_STORAGE_KEY, routeVisible ? "true" : "false");
|
localStorage.setItem(ROUTE_VISIBLE_STORAGE_KEY, routeVisible ? "true" : "false");
|
||||||
@@ -494,6 +516,7 @@ function closeOverlayView() {
|
|||||||
overlayLoading.classList.remove("error");
|
overlayLoading.classList.remove("error");
|
||||||
overlayLoadingText.textContent = "Loading full-size image...";
|
overlayLoadingText.textContent = "Loading full-size image...";
|
||||||
overlayImage.removeAttribute("src");
|
overlayImage.removeAttribute("src");
|
||||||
|
restoreOverlayMapView();
|
||||||
}
|
}
|
||||||
|
|
||||||
closeOverlay.addEventListener("click", closeOverlayView);
|
closeOverlay.addEventListener("click", closeOverlayView);
|
||||||
@@ -503,7 +526,7 @@ overlay.addEventListener("click", (event) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateRouteView(photos) {
|
function updateRouteView(photos, options = {}) {
|
||||||
const routePoints = photos
|
const routePoints = photos
|
||||||
.filter((photo) => photo.latitude !== null && photo.longitude !== null && photo.capturedAt)
|
.filter((photo) => photo.latitude !== null && photo.longitude !== null && photo.capturedAt)
|
||||||
.sort((a, b) => String(a.capturedAt).localeCompare(String(b.capturedAt)))
|
.sort((a, b) => String(a.capturedAt).localeCompare(String(b.capturedAt)))
|
||||||
@@ -511,6 +534,10 @@ function updateRouteView(photos) {
|
|||||||
|
|
||||||
route.setLatLngs(routePoints);
|
route.setLatLngs(routePoints);
|
||||||
|
|
||||||
|
if (options.preserveMapView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (routePoints.length === 1) {
|
if (routePoints.length === 1) {
|
||||||
map.setView(routePoints[0], 13);
|
map.setView(routePoints[0], 13);
|
||||||
} else if (routePoints.length > 1) {
|
} else if (routePoints.length > 1) {
|
||||||
@@ -671,7 +698,8 @@ function renderVisiblePhotos(options = {}) {
|
|||||||
marker.on("mouseover", () => marker.openPopup());
|
marker.on("mouseover", () => marker.openPopup());
|
||||||
marker.on("click", () => {
|
marker.on("click", () => {
|
||||||
state.activePhotoId = photo.id;
|
state.activePhotoId = photo.id;
|
||||||
renderVisiblePhotos({ fitMap: false });
|
rememberOverlayMapView();
|
||||||
|
renderVisiblePhotos({ fitMap: false, preserveMapView: true });
|
||||||
openOverlay(photo);
|
openOverlay(photo);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -685,14 +713,15 @@ function renderVisiblePhotos(options = {}) {
|
|||||||
'<img src="' + escapeHtml(photo.thumbUrl) + '" alt="' + escapeHtml(photo.name) + '" />';
|
'<img src="' + escapeHtml(photo.thumbUrl) + '" alt="' + escapeHtml(photo.name) + '" />';
|
||||||
item.addEventListener("click", () => {
|
item.addEventListener("click", () => {
|
||||||
state.activePhotoId = photo.id;
|
state.activePhotoId = photo.id;
|
||||||
renderVisiblePhotos({ fitMap: false });
|
rememberOverlayMapView();
|
||||||
|
renderVisiblePhotos({ fitMap: false, preserveMapView: true });
|
||||||
openOverlay(photo);
|
openOverlay(photo);
|
||||||
});
|
});
|
||||||
photoList.appendChild(item);
|
photoList.appendChild(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRouteView(visiblePhotos);
|
updateRouteView(visiblePhotos, options);
|
||||||
renderTimeline();
|
renderTimeline();
|
||||||
|
|
||||||
if (options.fitMap && visiblePhotos.length) {
|
if (options.fitMap && visiblePhotos.length) {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export const APP_STYLES = ` :root {
|
|||||||
--photo-active-bg: rgba(51, 65, 85, 0.76);
|
--photo-active-bg: rgba(51, 65, 85, 0.76);
|
||||||
--photo-active-border: rgba(148, 163, 184, 0.34);
|
--photo-active-border: rgba(148, 163, 184, 0.34);
|
||||||
--empty-border: rgba(148, 163, 184, 0.22);
|
--empty-border: rgba(148, 163, 184, 0.22);
|
||||||
--overlay-bg: rgba(2, 6, 23, 0.9);
|
--overlay-bg: rgba(2, 6, 23, 0.7);
|
||||||
--card-border: rgba(148, 163, 184, 0.16);
|
--card-border: rgba(148, 163, 184, 0.16);
|
||||||
--timeline-surface: rgba(15, 23, 42, 0.88);
|
--timeline-surface: rgba(15, 23, 42, 0.88);
|
||||||
--timeline-surface-soft: rgba(15, 23, 42, 0.76);
|
--timeline-surface-soft: rgba(15, 23, 42, 0.76);
|
||||||
@@ -97,7 +97,7 @@ export const APP_STYLES = ` :root {
|
|||||||
--photo-active-bg: rgba(100, 116, 139, 0.12);
|
--photo-active-bg: rgba(100, 116, 139, 0.12);
|
||||||
--photo-active-border: rgba(100, 116, 139, 0.32);
|
--photo-active-border: rgba(100, 116, 139, 0.32);
|
||||||
--empty-border: rgba(15, 23, 42, 0.16);
|
--empty-border: rgba(15, 23, 42, 0.16);
|
||||||
--overlay-bg: rgba(8, 12, 18, 0.78);
|
--overlay-bg: rgba(8, 12, 18, 0.7);
|
||||||
--card-border: rgba(15, 23, 42, 0.11);
|
--card-border: rgba(15, 23, 42, 0.11);
|
||||||
--timeline-surface: rgba(255, 255, 255, 0.84);
|
--timeline-surface: rgba(255, 255, 255, 0.84);
|
||||||
--timeline-surface-soft: rgba(255, 255, 255, 0.94);
|
--timeline-surface-soft: rgba(255, 255, 255, 0.94);
|
||||||
@@ -587,6 +587,7 @@ export const APP_STYLES = ` :root {
|
|||||||
display: none;
|
display: none;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
background: var(--overlay-bg);
|
background: var(--overlay-bg);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
@@ -598,11 +599,13 @@ export const APP_STYLES = ` :root {
|
|||||||
.overlay-card {
|
.overlay-card {
|
||||||
display: inline-grid;
|
display: inline-grid;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
max-width: 100%;
|
max-width: min(100%, calc(100vw - 48px));
|
||||||
|
gap: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
background: var(--timeline-surface-soft);
|
background: transparent;
|
||||||
border-color: var(--timeline-border);
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
color: var(--timeline-text);
|
color: var(--timeline-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,25 +613,32 @@ export const APP_STYLES = ` :root {
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
min-height: 48vh;
|
min-height: 0;
|
||||||
background: var(--timeline-surface-strong);
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-card header {
|
.overlay-card header {
|
||||||
padding: 16px 18px;
|
padding: 0 4px;
|
||||||
border-bottom: 1px solid var(--timeline-border);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: var(--timeline-surface-strong);
|
background: transparent;
|
||||||
color: var(--timeline-text);
|
color: var(--timeline-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.overlay-card header strong {
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: var(--timeline-surface-soft);
|
||||||
|
border: 1px solid var(--timeline-border);
|
||||||
|
backdrop-filter: blur(14px);
|
||||||
|
}
|
||||||
|
|
||||||
.overlay-card img {
|
.overlay-card img {
|
||||||
display: block;
|
display: block;
|
||||||
width: auto;
|
width: auto;
|
||||||
max-width: min(1120px, calc(100vw - 48px));
|
max-width: min(1440px, calc(100vw - 48px));
|
||||||
max-height: 78vh;
|
max-height: calc(100vh - 112px);
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
background: var(--timeline-surface-strong);
|
background: var(--timeline-surface-strong);
|
||||||
transition: opacity 120ms ease;
|
transition: opacity 120ms ease;
|
||||||
@@ -677,6 +687,27 @@ export const APP_STYLES = ` :root {
|
|||||||
color: var(--timeline-text);
|
color: var(--timeline-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.overlay-close {
|
||||||
|
position: fixed;
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
|
z-index: 1001;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--timeline-surface-soft);
|
||||||
|
color: var(--timeline-text);
|
||||||
|
border: 1px solid var(--timeline-border);
|
||||||
|
box-shadow: var(--shadow-soft);
|
||||||
|
backdrop-filter: blur(14px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-close:hover {
|
||||||
|
background: var(--timeline-surface-strong);
|
||||||
|
border-color: var(--timeline-border-strong);
|
||||||
|
}
|
||||||
|
|
||||||
.thumb {
|
.thumb {
|
||||||
width: min(210px, 100%);
|
width: min(210px, 100%);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|||||||
@@ -140,12 +140,12 @@ ${APP_STYLES}
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="overlay" id="overlay" aria-hidden="true">
|
<div class="overlay" id="overlay" aria-hidden="true">
|
||||||
|
<button class="close overlay-close" id="close-overlay" type="button" aria-label="Close photo">
|
||||||
|
x
|
||||||
|
</button>
|
||||||
<div class="overlay-card">
|
<div class="overlay-card">
|
||||||
<header>
|
<header>
|
||||||
<strong id="overlay-title">Photo</strong>
|
<strong id="overlay-title">Photo</strong>
|
||||||
<button class="close" id="close-overlay" type="button" aria-label="Close photo">
|
|
||||||
x
|
|
||||||
</button>
|
|
||||||
</header>
|
</header>
|
||||||
<div class="overlay-media">
|
<div class="overlay-media">
|
||||||
<div class="overlay-loading" id="overlay-loading" aria-live="polite" aria-atomic="true">
|
<div class="overlay-loading" id="overlay-loading" aria-live="polite" aria-atomic="true">
|
||||||
|
|||||||
Reference in New Issue
Block a user