The Images Page
the images page on my site lists every single image I've ever published on my website. I'm actually quite happy with how it all works, and I realised I've never broken it down before!
Before uploading any images to my site, I need to compress them. Images out of my camera are huge and I don't want people downloading 25mb+ images on random pages of my site. I use ImageMagick and a little helper function I wrote to do this.
I generate a sm
, md
and
lg
version of each image with the following commands:
# A thumbnail version of the image
convert originalImage.jpg -strip -interlace Plane -scale 10% -blur 0x2.5 -resize 1000% -resize 180 "sm/${1%.*}.jpg"
# A suprisingly good quality version of the image
convert originalImage.jpg -strip -interlace Plane -gaussian-blur 0.05 -quality 85% -resize 720 "md/${1%.*}.jpg"
# A nicer "HD" version of the image
convert originalImage.jpg -strip -quality 85% -interlace Plane -resize 1800 "lg/${1%.*}.jpg"
Once I've generated all the images, they're added to my sites
storage in folders. My site generator then reads all the files in the
sm
folder, and generates a html for all the images. The
html for each image looks something like this:
<img
src="https://assets.pfy.ch/sm/image.jpg"
finalSrc="https://assets.pfy.ch/md/image.jpg"
hdSrc="https://assets.pfy.ch/lg/image.jpg"
/>
By default the sm
version of the image will load, when
the images is in view, I swap out the src
with the value
in the finalSrc
attribute. This is done using an
Intersection Observer.
const images = document.querySelectorAll('.galleryImage');
const loadImage = (image: HTMLImageElement) => {
image.src = image.getAttribute('finalSrc') || '';
};
const handleIntersection = (entries: IntersectionObserverEntry[]) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0) {
loadImage(entry.target as HTMLImageElement);
}
});
};
const observer = new IntersectionObserver(handleIntersection, {
root: null,
rootMargin: '0px',
threshold: 0.05,
});
images.forEach((img) => {
observer.observe(img);
img.addEventListener('click', (event) => {
const image = event.target as HTMLImageElement;
image.onerror = () => {
console.warn('Could not get LG version of image!');
image.src = image.getAttribute('finalSrc') || '';
};
image.src = image.getAttribute('hdSrc') || '';
});
});
I would love to have the images popup or go fullscreen when they're clicked, but I didn't want to make it too complex.
Since they're just plain HTML images, you can just open them in a new tab or window. Which seems to work fine for most people who browse my photos!