Add much more responsible Swiper.JS image carousel over self-made one
This commit is contained in:
20
web/package-lock.json
generated
20
web/package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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()"><</button>
|
<!-- <button id="dec-button" onclick="dec()"><</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()">></button>
|
<!-- <button id="inc-button" onclick="inc()">></button> -->
|
||||||
|
|
||||||
|
|
||||||
<!-- <p class="text-2xl">Photography :)</p>
|
<!-- <p class="text-2xl">Photography :)</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user