Let me compare the original code with the optimized version and explain each adjustment in detail:
1. HTML Structure Changes
Original:
- Simple HTML structure with no loading indicators
- No placeholder elements for thumbnails
Optimized:
- Added loading indicator in the main content area:
<div id="loading" class="loading-indicator">載入中...</div>
- Added styles for thumbnail placeholders and loading indicators
- Added CSS for active thumbnail highlighting
2. CSS Additions
Original:
- Basic styling for thumbnails with no states for loading or active items
Optimized:
- Added styles for placeholder elements:
.thumbnail .placeholder
- Added loading indicator styling:
.loading-indicator
- Added active state for thumbnails:
.thumbnail.active img
- Added background color for image placeholders to improve visual appearance during loading
3. JavaScript Logic Changes
Thumbnail Loading Strategy
Original:
javascript
albumData.data.forEach((photo, index) => {
const thumbDiv = document.createElement('div');
thumbDiv.className = 'thumbnail';
const img = document.createElement('img');
img.src = albumData.path + photo;
img.alt = albumData.description[index];
thumbDiv.appendChild(img);
thumbDiv.addEventListener('click', () => displayPhoto(album, index));
thumbnailBar.appendChild(thumbDiv);
});
Optimized:
javascript
albumData.data.forEach((photo, index) => {
const thumbDiv = document.createElement('div');
thumbDiv.className = 'thumbnail';
thumbDiv.dataset.index = index;
const placeholder = document.createElement('div');
placeholder.className = 'placeholder';
placeholder.textContent = `#${index + 1}`;
thumbDiv.appendChild(placeholder);
thumbDiv.addEventListener('click', () => displayPhoto(album, index));
thumbnailBar.appendChild(thumbDiv);
});
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
}
});
}, {
root: thumbnailBar,
rootMargin: '0px 100px 0px 100px',
threshold: 0.1
});
Key Changes:
- Created placeholder elements with numbers first instead of loading all images immediately
- Used Intersection Observer API to detect when thumbnails enter the viewport
- Added data attributes to thumbnails for easier reference
- Added a 100px margin to load thumbnails just outside the visible area for smoother scrolling
Image Loading and Caching
Original:
- No image caching
- No preloading strategy
- Directly sets image source without waiting for load completion
Optimized:
javascript
function preloadImage(url) {
return new Promise((resolve, reject) => {
if (imageCache[url]) {
resolve(imageCache[url]);
return;
}
const img = new Image();
img.onload = () => {
imageCache[url] = img;
resolve(img);
};
img.onerror = reject;
img.src = url;
});
}
Key Changes:
- Added an image cache object to store loaded images
- Created a preloadImage function that returns a Promise
- Images are preloaded as thumbnails become visible
- Main image loading shows a loading indicator
Display Photo Improvements
Original:
javascript
function displayPhoto(album, index) {
const albumData = sources[album];
fullImage.src = albumData.path + albumData.data[index];
description.textContent = albumData.description[index];
currentIndex = index;
}
Optimized:
javascript
function displayPhoto(album, index) {
const albumData = sources[album];
const imgSrc = albumData.path + albumData.data[index];
showLoading();
description.textContent = albumData.description[index];
currentIndex = index;
updateActiveThumbnail(index);
if (imageCache[imgSrc]) {
fullImage.src = imgSrc;
hideLoading();
} else {
preloadImage(imgSrc)
.then(() => {
fullImage.src = imgSrc;
hideLoading();
})
.catch(() => {
description.textContent = "圖片載入失敗";
hideLoading();
});
}
}
Key Changes:
- Added loading indicator during image loading
- Uses the cache to avoid reloading already loaded images
- Handles image loading errors
- Updates the active thumbnail visually
- Auto-scrolls to ensure the current thumbnail is visible
User Experience Improvements
Original:
- No visual feedback for current thumbnail
- No keyboard navigation beyond spacebar
- No loading indicators
Optimized:
- Added active state highlighting for the current thumbnail
- Added keyboard navigation with left and right arrow keys
- Added loading indicators for main image
- Auto-scrolling to keep the active thumbnail visible
javascript
function updateActiveThumbnail(index) {
document.querySelectorAll('.thumbnail').forEach(thumb => {
thumb.classList.remove('active');
});
const currentThumb = document.querySelector(`.thumbnail[data-index="${index}"]`);
if (currentThumb) {
currentThumb.classList.add('active');
currentThumb.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'center'});
}
}
document.addEventListener('keydown', (event) => {
if (event.code === 'Space') {
event.preventDefault();
togglePlayPause();
} else if (event.code === 'ArrowLeft' && currentAlbum) {
currentIndex = (currentIndex - 1 + sources[currentAlbum].data.length) % sources[currentAlbum].data.length;
displayPhoto(currentAlbum, currentIndex);
} else if (event.code === 'ArrowRight' && currentAlbum) {
currentIndex = (currentIndex + 1) % sources[currentAlbum].data.length;
displayPhoto(currentAlbum, currentIndex);
}
});
4. Loading Order Optimization
Original:
- Loads all thumbnails first
- Only after all thumbnails are loaded does it display the main photo
- No separation between UI display and content loading
Optimized:
- Shows the UI immediately
- Displays the first photo right away
- Loads thumbnails progressively as they come into view
- Pre-loads images that are likely to be viewed next
5. Technical Benefits
- Faster Initial Load Time
- The app becomes interactive almost immediately rather than waiting for all thumbnails to load
- Users can see and interact with the interface without delay
- Reduced Memory Usage
- Only loads images when needed rather than all at once
- Caches images to prevent redundant loading
- Better Performance with Large Albums
- The original code would struggle with albums containing many photos
- The optimized version scales well even with hundreds of photos
- Improved Bandwidth Usage
- Only loads images that the user actually sees
- Reduces unnecessary data transfer for users on limited connections
This optimization approach follows modern web development best practices for image-heavy applications, ensuring a responsive user experience even when dealing with large photo collections.