Add much more responsible Swiper.JS image carousel over self-made one

This commit is contained in:
2025-11-18 12:51:59 +13:00
parent 4992379487
commit b2657962e1
4 changed files with 85 additions and 49 deletions

20
web/package-lock.json generated
View File

@@ -12,6 +12,7 @@
"@tailwindcss/vite": "^4.1.17", "@tailwindcss/vite": "^4.1.17",
"astro": "^5.15.4", "astro": "^5.15.4",
"pocketbase": "^0.26.3", "pocketbase": "^0.26.3",
"swiper": "^12.0.3",
"tailwindcss": "^4.1.17" "tailwindcss": "^4.1.17"
} }
}, },
@@ -4966,6 +4967,25 @@
"url": "https://github.com/chalk/strip-ansi?sponsor=1" "url": "https://github.com/chalk/strip-ansi?sponsor=1"
} }
}, },
"node_modules/swiper": {
"version": "12.0.3",
"resolved": "https://registry.npmjs.org/swiper/-/swiper-12.0.3.tgz",
"integrity": "sha512-BHd6U1VPEIksrXlyXjMmRWO0onmdNPaTAFduzqR3pgjvi7KfmUCAm/0cj49u2D7B0zNjMw02TSeXfinC1hDCXg==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/swiperjs"
},
{
"type": "open_collective",
"url": "http://opencollective.com/swiper"
}
],
"license": "MIT",
"engines": {
"node": ">= 4.7.0"
}
},
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "4.1.17", "version": "4.1.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",

View File

@@ -13,6 +13,7 @@
"@tailwindcss/vite": "^4.1.17", "@tailwindcss/vite": "^4.1.17",
"astro": "^5.15.4", "astro": "^5.15.4",
"pocketbase": "^0.26.3", "pocketbase": "^0.26.3",
"swiper": "^12.0.3",
"tailwindcss": "^4.1.17" "tailwindcss": "^4.1.17"
} }
} }

View File

@@ -8,66 +8,81 @@ const pb = await authPB()
const photos = await pb.collection('photos').getFullList({ const photos = await pb.collection('photos').getFullList({
sort: '-created' sort: '-created'
}) })
const getImageLink = async (record: any) => {
return pb.files.getURL(record, record.image)
}
--- ---
<script> <script>
let pos = 0; import Swiper from 'swiper';
import { Navigation, Keyboard, EffectFade } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/effect-fade';
const dataElement = document.getElementById('carousel-data'); const dataElement = document.getElementById('carousel-data');
const photos = dataElement ? JSON.parse(dataElement.textContent || '[]') : []; const photos = dataElement ? JSON.parse(dataElement.textContent || '[]') : [];
const cap = photos.length - 1;
const img = document.getElementById('carousel-img') as HTMLImageElement;
const titleEl = document.getElementById('photo-title'); const titleEl = document.getElementById('photo-title');
const cameraEl = document.getElementById('photo-camera'); const cameraEl = document.getElementById('photo-camera');
const locationEl = document.getElementById('photo-location'); const locationEl = document.getElementById('photo-location');
const currentPosIndicator = document.getElementById('current-pos'); const currentPosIndicator = document.getElementById('current-pos');
const maxPosIndicator = document.getElementById('max-pos'); const maxPosIndicator = document.getElementById('max-pos');
maxPosIndicator!.innerText = (cap + 1) as string if (maxPosIndicator) {
currentPosIndicator!.innerText = (pos + 1) as string maxPosIndicator.innerText = String(photos.length);
}
function updatePhoto() { if (currentPosIndicator) {
const currentPhoto = photos[pos]; currentPosIndicator.innerText = '1';
}
currentPosIndicator!.innerText = (pos + 1) as string const swiper = new Swiper('.swiper', {
modules: [Navigation, Keyboard, EffectFade],
// Update image src effect: 'cards',
if (img && currentPhoto) { fadeEffect: {
const imageUrl = `/api/files/photos/${currentPhoto.id}/${currentPhoto.image}`; crossFade: true
img.src = imageUrl; },
speed: 400,
navigation: {
nextEl: '#inc-button',
prevEl: '#dec-button',
},
keyboard: {
enabled: true,
onlyInViewport: false,
},
loop: true,
on: {
slideChange: function() {
const realIndex = this.realIndex;
const currentPhoto = photos[realIndex];
if (currentPosIndicator) {
currentPosIndicator.innerText = String(realIndex + 1);
}
if (titleEl && currentPhoto) {
titleEl.textContent = currentPhoto.title || "Untitled";
}
if (cameraEl && currentPhoto) {
cameraEl.textContent = `📸 ${currentPhoto.camera}`;
}
if (locationEl && currentPhoto) {
locationEl.textContent = `📌 ${currentPhoto.location}`;
}
}
} }
});
// Update metadata
if (titleEl) {
titleEl.textContent = currentPhoto.title || "Untitled";
}
if (cameraEl) {
cameraEl.textContent = `📸 ${currentPhoto.camera}`
}
if (locationEl) {
locationEl.textContent = `📌 ${currentPhoto.location}`
}
}
function inc() {
pos = pos === cap ? 0 : pos + 1;
updatePhoto();
}
function dec() {
pos = pos === 0 ? cap : pos - 1;
updatePhoto();
}
// make functions globally accessible
(window as any).inc = inc;
(window as any).dec = dec;
</script> </script>
<!-- Hidden element to pass server data to client --> <!-- Hidden element to pass server data to client -->
<div class="hidden" id="carousel-data">{JSON.stringify(photos)}</div> <div class="hidden" id="carousel-data">{JSON.stringify(photos)}</div>
<img id="carousel-img" class="w-full md:h-[calc(100vh-7rem)] object-contain" src={ pb.files.getURL(photos[0], photos[0].image) } /> <div class="swiper w-full md:h-[calc(100vh-7rem)]">
<div class="swiper-wrapper">
{photos.map((photo) => (
<div class="swiper-slide flex items-center justify-center">
<img
src={pb.files.getURL(photo, photo.image)}
class="w-full md:h-[calc(100vh-7rem)] object-contain"
alt={photo.title || 'Photo'}
/>
</div>
))}
</div>
</div>

View File

@@ -17,9 +17,9 @@ const photos = await pb.collection('photos').getFullList({
<p id="photo-camera" class="text-sm">📸 {photos[0].camera}</p> <p id="photo-camera" class="text-sm">📸 {photos[0].camera}</p>
<p id="photo-location" class="text-sm">📌 {photos[0].location}</p> <p id="photo-location" class="text-sm">📌 {photos[0].location}</p>
<button id="dec-button" onclick="dec()">&lt;</button> <!-- <button id="dec-button" onclick="dec()">&lt;</button> -->
<span id="current-pos" /> of <span id="max-pos" /> <span id="current-pos" /> of <span id="max-pos" />
<button id="inc-button" onclick="inc()">&gt;</button> <!-- <button id="inc-button" onclick="inc()">&gt;</button> -->
<!-- <p class="text-2xl">Photography :)</p> <!-- <p class="text-2xl">Photography :)</p>