multi-image comics

This commit is contained in:
mi
2025-11-15 18:52:14 +10:00
parent f71720c156
commit 91b6d4efeb
7 changed files with 356 additions and 66 deletions

View File

@@ -431,6 +431,30 @@ main {
margin: 0 auto;
}
/* Multi-image comics (webtoon style) */
.comic-image-multi {
padding: 0; /* Remove padding for seamless stacking */
}
.comic-image-multi img {
display: block;
width: 100%;
margin: 0;
padding: 0;
vertical-align: bottom; /* Removes tiny gap below images */
}
/* Lazy-loaded images - placeholder while loading */
.comic-image-multi img.lazy-load {
min-height: 200px;
background: var(--color-hover-bg);
}
.comic-image-multi img.lazy-load.loaded {
min-height: auto;
background: none;
}
/* Comic Navigation */
.comic-navigation {
border-top: var(--border-width-thin) solid var(--border-color);

View File

@@ -5,6 +5,7 @@
let totalComics = 0;
let comicName = ''; // Will be extracted from initial page title
let currentComicNumber = 0;
let lazyLoadObserver = null;
// Fetch and display a comic
async function loadComic(comicId) {
@@ -79,8 +80,15 @@
const comicImageDiv = document.querySelector('.comic-image');
updateComicImage(comicImageDiv, comic, title);
// Update or create/remove the link wrapper
updateComicImageLink(comic.number);
// Update or create/remove the link wrapper (only for single-image comics)
if (!comic.is_multi_image) {
updateComicImageLink(comic.number);
}
// Initialize lazy loading for multi-image comics
if (comic.is_multi_image) {
initLazyLoad();
}
// Update author note
let transcriptDiv = document.querySelector('.comic-transcript');
@@ -137,35 +145,96 @@
}
}
// Initialize lazy loading for multi-image comics
function initLazyLoad() {
// Disconnect existing observer if any
if (lazyLoadObserver) {
lazyLoadObserver.disconnect();
}
// Create Intersection Observer for lazy loading
lazyLoadObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
const src = img.getAttribute('data-src');
if (src) {
img.src = src;
img.removeAttribute('data-src');
img.classList.add('loaded');
lazyLoadObserver.unobserve(img);
}
}
});
}, {
rootMargin: '50px' // Start loading 50px before image enters viewport
});
// Observe all lazy-load images
document.querySelectorAll('.lazy-load').forEach(img => {
lazyLoadObserver.observe(img);
});
}
// Update or create comic image with optional mobile version
function updateComicImage(comicImageDiv, comic, title) {
// Clear all existing content
comicImageDiv.innerHTML = '';
// Create new image element(s)
if (comic.mobile_filename) {
// Create picture element with mobile source
const picture = document.createElement('picture');
const source = document.createElement('source');
source.media = '(max-width: 768px)';
source.srcset = `/static/images/comics/${comic.mobile_filename}`;
const img = document.createElement('img');
img.src = `/static/images/comics/${comic.filename}`;
img.alt = title;
img.title = comic.alt_text;
picture.appendChild(source);
picture.appendChild(img);
comicImageDiv.appendChild(picture);
// Update container class for multi-image
if (comic.is_multi_image) {
comicImageDiv.classList.add('comic-image-multi');
} else {
// Create regular img element
const img = document.createElement('img');
img.src = `/static/images/comics/${comic.filename}`;
img.alt = title;
img.title = comic.alt_text;
comicImageDiv.appendChild(img);
comicImageDiv.classList.remove('comic-image-multi');
}
// Create new image element(s)
if (comic.is_multi_image) {
// Multi-image comic (webtoon style)
comic.filenames.forEach((filename, index) => {
const img = document.createElement('img');
if (index === 0) {
// First image loads immediately
img.src = `/static/images/comics/${filename}`;
img.loading = 'eager';
} else {
// Subsequent images lazy load
img.setAttribute('data-src', `/static/images/comics/${filename}`);
img.classList.add('lazy-load');
img.loading = 'lazy';
}
img.alt = comic.alt_texts[index] || '';
img.title = comic.alt_texts[index] || '';
comicImageDiv.appendChild(img);
});
} else {
// Single image comic
if (comic.mobile_filename) {
// Create picture element with mobile source
const picture = document.createElement('picture');
const source = document.createElement('source');
source.media = '(max-width: 768px)';
source.srcset = `/static/images/comics/${comic.mobile_filename}`;
const img = document.createElement('img');
img.src = `/static/images/comics/${comic.filename}`;
img.alt = title;
img.title = comic.alt_text;
picture.appendChild(source);
picture.appendChild(img);
comicImageDiv.appendChild(picture);
} else {
// Create regular img element
const img = document.createElement('img');
img.src = `/static/images/comics/${comic.filename}`;
img.alt = title;
img.title = comic.alt_text;
comicImageDiv.appendChild(img);
}
}
}
@@ -377,7 +446,17 @@
const dateDisplay = document.querySelector('.comic-date-display');
const formattedDate = dateDisplay ? dateDisplay.textContent : null;
updateNavButtons(currentNumber, formattedDate);
updateComicImageLink(currentNumber);
// Check if current comic is multi-image
const comicImageDiv = document.querySelector('.comic-image');
const isMultiImage = comicImageDiv && comicImageDiv.classList.contains('comic-image-multi');
if (!isMultiImage) {
updateComicImageLink(currentNumber);
} else {
// Initialize lazy loading for multi-image comics on page load
initLazyLoad();
}
}
// Handle browser back/forward