Some checks are pending
Build and Deploy to Web Server / deploy (push) Has started running
125 lines
3.2 KiB
Plaintext
125 lines
3.2 KiB
Plaintext
---
|
|
import { Image } from "astro:assets";
|
|
import type { ImageMetadata } from "astro";
|
|
|
|
import { shuffleArray } from "@lib/utils";
|
|
|
|
interface Props {
|
|
images: Array<ImageMetadata>;
|
|
class: 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,
|
|
class: 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, "relative overflow-hidden"]}>
|
|
<div>
|
|
<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"
|
|
quality={quality}
|
|
height={height === undefined ? undefined : height}
|
|
/>
|
|
);
|
|
})
|
|
}
|
|
|
|
{
|
|
foreground && (
|
|
<div
|
|
class:list={[foregroundColour, foregroundOpacity, "absolute inset-0"]}
|
|
/>
|
|
)
|
|
}
|
|
</div>
|
|
<slot />
|
|
</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>
|