Public: defer category image fetch and unblock card navigation

Avoid navigation stalls on cold loads by delaying thumbnail requests until cards are near viewport. Stop in-flight page downloads on card click so opening a photo page is immediate even while list images are still loading.
This commit is contained in:
Alexander Andreev 2026-02-20 19:06:18 +03:00
parent 92e606a87e
commit 0eefcf6b04

View File

@ -239,7 +239,7 @@ function outputWatermarked(string $path, string $mime): never
<div class="cards"> <div class="cards">
<?php foreach($photos as $p): ?> <?php foreach($photos as $p): ?>
<a class="card" href="?photo_id=<?= (int)$p['id'] ?>&section_id=<?= (int)$activeSectionId ?><?= $viewerToken!=='' ? '&viewer=' . urlencode($viewerToken) : '' ?>" style="text-decoration:none;color:inherit;position:relative"> <a class="card" href="?photo_id=<?= (int)$p['id'] ?>&section_id=<?= (int)$activeSectionId ?><?= $viewerToken!=='' ? '&viewer=' . urlencode($viewerToken) : '' ?>" style="text-decoration:none;color:inherit;position:relative">
<?php if (!empty($p['before_file_id'])): ?><div class="img-box thumb-img-box is-loading"><img class="js-public-image" src="?action=image&file_id=<?= (int)$p['before_file_id'] ?>" alt="" loading="lazy" decoding="async" fetchpriority="low"></div><?php endif; ?> <?php if (!empty($p['before_file_id'])): ?><div class="img-box thumb-img-box is-loading"><img class="js-public-image js-card-image" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==" data-src="?action=image&file_id=<?= (int)$p['before_file_id'] ?>" alt="" loading="lazy" decoding="async" fetchpriority="low"></div><?php endif; ?>
<?php if (!empty($p['after_file_id'])): ?><span title="Есть обработанная версия" style="position:absolute;top:8px;right:8px;background:rgba(31,111,235,.92);color:#fff;font-size:11px;line-height:1;padding:6px 7px;border-radius:999px">AI</span><?php endif; ?> <?php if (!empty($p['after_file_id'])): ?><span title="Есть обработанная версия" style="position:absolute;top:8px;right:8px;background:rgba(31,111,235,.92);color:#fff;font-size:11px;line-height:1;padding:6px 7px;border-radius:999px">AI</span><?php endif; ?>
<div class="cap"><strong><?= h((string)$p['code_name']) ?></strong><br><span class="muted"><?= h((string)($p['description'] ?? '')) ?></span></div> <div class="cap"><strong><?= h((string)$p['code_name']) ?></strong><br><span class="muted"><?= h((string)($p['description'] ?? '')) ?></span></div>
</a> </a>
@ -257,6 +257,34 @@ function outputWatermarked(string $path, string $mime): never
</div> </div>
<script> <script>
(() => { (() => {
const loadCardImage = (img) => {
const src = img.dataset.src;
if (!src) {
return;
}
img.src = src;
img.removeAttribute('data-src');
};
const deferred = document.querySelectorAll('.js-card-image[data-src]');
if ('IntersectionObserver' in window) {
const io = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) {
return;
}
loadCardImage(entry.target);
io.unobserve(entry.target);
});
}, { rootMargin: '240px 0px' });
deferred.forEach((img) => io.observe(img));
} else {
deferred.forEach((img) => loadCardImage(img));
}
document.querySelectorAll('img').forEach((img) => { document.querySelectorAll('img').forEach((img) => {
img.addEventListener('contextmenu', (e) => e.preventDefault()); img.addEventListener('contextmenu', (e) => e.preventDefault());
img.addEventListener('dragstart', (e) => e.preventDefault()); img.addEventListener('dragstart', (e) => e.preventDefault());
@ -275,10 +303,27 @@ function outputWatermarked(string $path, string $mime): never
img.addEventListener('load', clearLoading, { once: true }); img.addEventListener('load', clearLoading, { once: true });
img.addEventListener('error', clearLoading, { once: true }); img.addEventListener('error', clearLoading, { once: true });
if (img.complete) { if (img.complete && !img.dataset.src) {
clearLoading(); clearLoading();
} }
}); });
document.querySelectorAll('a.card').forEach((link) => {
link.addEventListener('click', (e) => {
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
return;
}
const href = link.getAttribute('href');
if (!href) {
return;
}
e.preventDefault();
window.stop();
window.location.assign(href);
});
});
})(); })();
(() => { (() => {