Initial design based off original website, some things still to do
This commit is contained in:
126
src/components/ImageCarousel.astro
Normal file
126
src/components/ImageCarousel.astro
Normal file
@@ -0,0 +1,126 @@
|
||||
---
|
||||
import { Image } from "astro:assets";
|
||||
import type { ImageMetadata } from "astro";
|
||||
|
||||
import { shuffleArray } from "@lib/utils";
|
||||
|
||||
interface Props {
|
||||
images: Array<ImageMetadata>;
|
||||
className: string;
|
||||
altText?: string | ((index: number) => string);
|
||||
interval?: number;
|
||||
backgroundColour?: string;
|
||||
backgroundOpacity?: string;
|
||||
transitionStyle?: string;
|
||||
transitionDuration?: string;
|
||||
foreground?: boolean;
|
||||
foregroundColour?: string;
|
||||
foregroundOpacity?: string;
|
||||
quality?: number;
|
||||
height?: number;
|
||||
shuffle?: boolean;
|
||||
}
|
||||
|
||||
const {
|
||||
images,
|
||||
className,
|
||||
altText = null,
|
||||
interval = 5000,
|
||||
backgroundColour = "bg-black",
|
||||
backgroundOpacity = "opacity-100",
|
||||
transitionStyle = "ease-in-out",
|
||||
transitionDuration = "duration-2000",
|
||||
foreground = false,
|
||||
foregroundColour = "bg-black",
|
||||
foregroundOpacity = "opacity-50",
|
||||
quality = "80",
|
||||
height,
|
||||
shuffle = false
|
||||
} = Astro.props as Props;
|
||||
|
||||
const IDs: string[] = [];
|
||||
|
||||
const imagesArray = shuffle ? shuffleArray(images) : images;
|
||||
---
|
||||
|
||||
<div class:list={[className]}>
|
||||
<div class="relative h-full w-full">
|
||||
<div class:list={[backgroundColour, backgroundOpacity, "absolute inset-0"]}>
|
||||
</div>
|
||||
|
||||
{
|
||||
imagesArray.map((image, index) => {
|
||||
const ID = `image-carousel-${Math.random().toString(36)}`;
|
||||
IDs.push(ID);
|
||||
|
||||
return (
|
||||
<Image
|
||||
data-id={`${ID}`}
|
||||
class:list={[
|
||||
"absolute h-full w-full object-cover object-center transition-opacity",
|
||||
transitionStyle,
|
||||
transitionDuration,
|
||||
index > 0 ? "opacity-0" : "",
|
||||
index < 2 ? "" : "hidden"
|
||||
]}
|
||||
src={image}
|
||||
alt={
|
||||
typeof altText === "function" ? altText(index) : (altText ?? "")
|
||||
}
|
||||
loading={index < 2 ? "eager" : "lazy"}
|
||||
aria-hidden={index === 0 ? "false" : "true"}
|
||||
layout="full-width"
|
||||
fit="cover"
|
||||
style="height: 100% !important;"
|
||||
quality={quality}
|
||||
height={height === undefined ? undefined : height}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
foreground && (
|
||||
<div
|
||||
class:list={[foregroundColour, foregroundOpacity, "absolute inset-0"]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script define:vars={{ imagesArray, interval, IDs }}>
|
||||
let currentIndex = 0;
|
||||
|
||||
setInterval(() => {
|
||||
const total = imagesArray.length;
|
||||
const current = document.querySelectorAll(
|
||||
`[data-id="${IDs[currentIndex]}"]`
|
||||
)[0];
|
||||
const nextIndex = (currentIndex + 1) % total;
|
||||
const next = document.querySelectorAll(`[data-id="${IDs[nextIndex]}"]`)[0];
|
||||
|
||||
if (!current || !next) return;
|
||||
|
||||
// Fade out current, fade in next
|
||||
current.classList.add("opacity-0");
|
||||
current.setAttribute("aria-hidden", "true");
|
||||
|
||||
next.classList.remove("opacity-0", "hidden");
|
||||
next.setAttribute("aria-hidden", "false");
|
||||
|
||||
const preloadIndex = (nextIndex + 1) % total;
|
||||
const preloadImage = document.querySelectorAll(
|
||||
`[data-id="${IDs[preloadIndex]}"]`
|
||||
)[0];
|
||||
if (preloadImage) {
|
||||
preloadImage.classList.add("opacity-0");
|
||||
preloadImage.classList.remove("hidden");
|
||||
preloadImage.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
|
||||
currentIndex = nextIndex;
|
||||
}, interval);
|
||||
</script>
|
Reference in New Issue
Block a user