Performance fix
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { env } from '$env/dynamic/public';
|
import { env } from '$env/dynamic/public';
|
||||||
|
|
||||||
// Svelte 5 Runes for Reactivity
|
// Svelte 5 Runes for Core Reactivity
|
||||||
let photos = $state([]);
|
let photos = $state([]);
|
||||||
let nextToken = $state(null);
|
let nextToken = $state(null);
|
||||||
let loading = $state(false);
|
let loading = $state(false);
|
||||||
@@ -10,18 +10,20 @@
|
|||||||
let activePhoto = $state(null);
|
let activePhoto = $state(null);
|
||||||
let observerTarget;
|
let observerTarget;
|
||||||
|
|
||||||
// Lightbox Pan & Zoom Matrix State
|
// Lightbox Control Flags
|
||||||
let isZoomed = $state(false);
|
let isZoomed = $state(false);
|
||||||
let lightboxContainer = $state(null);
|
let lightboxContainer = $state(null);
|
||||||
let isDragging = $state(false);
|
let imageMatrixWrapper = $state(null); // Explicit ref to bypass reactive state lag
|
||||||
|
let isDragging = false; // Internal mutable flag (not a reactive rune)
|
||||||
|
|
||||||
// Coordinate state vectors for custom 2D transform panning
|
// Coordinate Vector Matrix (using raw numbers to bypass Svelte state loops on fast cycles)
|
||||||
let panX = $state(0);
|
let currentX = 0;
|
||||||
let panY = $state(0);
|
let currentY = 0;
|
||||||
let startX = 0;
|
let startX = 0;
|
||||||
let startY = 0;
|
let startY = 0;
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
// Touch tracking coordinates for mobile swipes
|
// Touch tracking coordinates for layout pagination swipes
|
||||||
let touchStartX = 0;
|
let touchStartX = 0;
|
||||||
let touchEndX = 0;
|
let touchEndX = 0;
|
||||||
|
|
||||||
@@ -75,8 +77,11 @@
|
|||||||
function resetZoom() {
|
function resetZoom() {
|
||||||
isZoomed = false;
|
isZoomed = false;
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
panX = 0;
|
currentX = 0;
|
||||||
panY = 0;
|
currentY = 0;
|
||||||
|
if (imageMatrixWrapper) {
|
||||||
|
imageMatrixWrapper.style.transform = `translate3d(0px, 0px, 0px)`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextPhoto() {
|
function nextPhoto() {
|
||||||
@@ -100,11 +105,11 @@
|
|||||||
activePhoto = null;
|
activePhoto = null;
|
||||||
}
|
}
|
||||||
if (e.key === 'ArrowRight' && !isZoomed) {
|
if (e.key === 'ArrowRight' && !isZoomed) {
|
||||||
e.preventDefault(); // 👈 Stops the underlying grid buttons from changing focus
|
e.preventDefault();
|
||||||
nextPhoto();
|
nextPhoto();
|
||||||
}
|
}
|
||||||
if (e.key === 'ArrowLeft' && !isZoomed) {
|
if (e.key === 'ArrowLeft' && !isZoomed) {
|
||||||
e.preventDefault(); // 👈 Stops the underlying grid buttons from changing focus
|
e.preventDefault();
|
||||||
prevPhoto();
|
prevPhoto();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,34 +123,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic Pointer Interface Input Routing (Desktop Mouse & Mobile Touch Dragging)
|
// HIGH PERFORMANCE NATIVE INTERFACE PIPELINE (60FPS Engine)
|
||||||
function pointerDown(clientX, clientY, target) {
|
function pointerDown(clientX, clientY, target) {
|
||||||
if (!isZoomed) return;
|
if (!isZoomed) return;
|
||||||
if (target.tagName === 'BUTTON' || target.closest('button')) return;
|
if (target.tagName === 'BUTTON' || target.closest('button')) return;
|
||||||
|
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
startX = clientX - panX;
|
startX = clientX - currentX;
|
||||||
startY = clientY - panY;
|
startY = clientY - currentY;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pointerMove(clientX, clientY, preventDefaultFunc) {
|
function pointerMove(clientX, clientY) {
|
||||||
if (!isDragging) return;
|
if (!isDragging) return;
|
||||||
if (typeof preventDefaultFunc === 'function') preventDefaultFunc();
|
|
||||||
|
|
||||||
panX = clientX - startX;
|
currentX = clientX - startX;
|
||||||
panY = clientY - startY;
|
currentY = clientY - startY;
|
||||||
|
|
||||||
|
// requestAnimationFrame schedules updates with the browser screen refresh cycle
|
||||||
|
if (!ticking) {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
if (imageMatrixWrapper) {
|
||||||
|
imageMatrixWrapper.style.transform = `translate3d(${currentX}px, ${currentY}px, 0px)`;
|
||||||
|
}
|
||||||
|
ticking = false;
|
||||||
|
});
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pointerUp() {
|
function pointerUp() {
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Desktop Mouse Adapters
|
// Desktop Mouse Interface Subscriptions
|
||||||
function handleMouseDown(e) { pointerDown(e.clientX, e.clientY, e.target); }
|
function handleMouseDown(e) { pointerDown(e.clientX, e.clientY, e.target); }
|
||||||
function handleMouseMove(e) { pointerMove(e.clientX, e.clientY, () => e.preventDefault()); }
|
function handleMouseMove(e) {
|
||||||
|
if(isDragging) e.preventDefault();
|
||||||
|
pointerMove(e.clientX, e.clientY);
|
||||||
|
}
|
||||||
|
|
||||||
// Mobile Touch Panning Adapters
|
// Mobile Touch Interface Subscriptions
|
||||||
// Mobile Touch Panning Adapters
|
|
||||||
function handleTouchStart(e) {
|
function handleTouchStart(e) {
|
||||||
if (isZoomed) {
|
if (isZoomed) {
|
||||||
const touch = e.touches[0];
|
const touch = e.touches[0];
|
||||||
@@ -156,9 +173,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleTouchMove(e) {
|
function handleTouchMove(e) {
|
||||||
if (isZoomed && e.touches.length === 1) { // 👈 Ensures single finger panning behaves correctly
|
if (isZoomed && e.touches.length === 1) {
|
||||||
|
e.preventDefault(); // Kill native momentum bouncing
|
||||||
const touch = e.touches[0];
|
const touch = e.touches[0];
|
||||||
pointerMove(touch.clientX, touch.clientY, () => e.preventDefault());
|
pointerMove(touch.clientX, touch.clientY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +236,7 @@
|
|||||||
onclick={(e) => { if (e.target === e.currentTarget) activePhoto = null; }}
|
onclick={(e) => { if (e.target === e.currentTarget) activePhoto = null; }}
|
||||||
onkeydown={handleKeyDown}
|
onkeydown={handleKeyDown}
|
||||||
ontouchstart={handleTouchStart}
|
ontouchstart={handleTouchStart}
|
||||||
ontouchmove={handleTouchMove}
|
ontouchmove={handleTouchMove}
|
||||||
ontouchend={handleTouchEnd}
|
ontouchend={handleTouchEnd}
|
||||||
>
|
>
|
||||||
<div class="w-full flex justify-between items-center z-50 p-2 pointer-events-none">
|
<div class="w-full flex justify-between items-center z-50 p-2 pointer-events-none">
|
||||||
@@ -315,16 +333,17 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="max-w-full max-h-full flex items-center justify-center p-2 transition-transform duration-300 ease-out"
|
bind:this={imageMatrixWrapper}
|
||||||
style="transform: translate3d({panX}px, {panY}px, 0px);"
|
class="max-w-full max-h-full flex items-center justify-center p-2 ease-out will-change-transform"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={activePhoto.fullUrl}
|
src={activePhoto.fullUrl}
|
||||||
alt="Gallery Content Visual"
|
alt="Gallery Content Visual"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
class="max-h-[70vh] max-w-full rounded shadow-2xl select-none pointer-events-none transition-transform duration-300 will-change-transform"
|
class="max-h-[70vh] max-w-full rounded shadow-2xl select-none pointer-events-none transition-transform duration-200 ease-out will-change-transform"
|
||||||
style="transform: scale({isZoomed ? 3.8 : 1});"
|
style="transform: scale({isZoomed ? 3.8 : 1});"
|
||||||
/> </div>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full max-w-4xl mx-auto z-40 bg-zinc-950/90 border border-zinc-900 px-4 py-3 rounded-xl backdrop-blur-md pointer-events-auto">
|
<div class="w-full max-w-4xl mx-auto z-40 bg-zinc-950/90 border border-zinc-900 px-4 py-3 rounded-xl backdrop-blur-md pointer-events-auto">
|
||||||
|
|||||||
Reference in New Issue
Block a user