Reset carousels on page load, tidy navbar state across transitions, proper preloading for carousels
This commit is contained in:
3 files changed
+51
-15
No files matched your search
@@ -43,6 +43,9 @@ const {
|
||||
} = Astro.props as Props;
|
||||
|
||||
const imagesArray = shuffle ? shuffleArray(images) : images;
|
||||
|
||||
// Slides kept in the layout (first visible, rest preloaded) so their eager loads actually fire; anything past this stays hidden until its turn.
|
||||
const preloadCount = Math.max(eagerCount, 2);
|
||||
---
|
||||
|
||||
<div class:list={[className, "relative overflow-hidden"]}>
|
||||
@@ -60,7 +63,7 @@ const imagesArray = shuffle ? shuffleArray(images) : images;
|
||||
transitionStyle,
|
||||
transitionDuration,
|
||||
index > 0 ? "opacity-0" : "",
|
||||
index < 2 ? "" : "hidden"
|
||||
index < preloadCount ? "" : "hidden"
|
||||
]}
|
||||
src={image}
|
||||
alt={
|
||||
@@ -89,27 +92,53 @@ const imagesArray = shuffle ? shuffleArray(images) : images;
|
||||
</div>
|
||||
|
||||
<script
|
||||
define:vars={{ imageCount: imagesArray.length, carouselId: id, interval }}
|
||||
define:vars={{
|
||||
imageCount: imagesArray.length,
|
||||
carouselId: id,
|
||||
interval,
|
||||
preloadCount
|
||||
}}
|
||||
>
|
||||
let currentIndex = 0;
|
||||
window.__carousels = window.__carousels || {};
|
||||
|
||||
// Honour reduced-motion: leave the first image showing, no rotation.
|
||||
const reduceMotion = window.matchMedia(
|
||||
"(prefers-reduced-motion: reduce)"
|
||||
).matches;
|
||||
// Reset the carousel and (re)start it. Runs on initial load and after
|
||||
// every page transition, so navigation always restarts the fade sequence.
|
||||
function initCarousel() {
|
||||
const state = (window.__carousels[carouselId] = window.__carousels[
|
||||
carouselId
|
||||
] || { index: 0, timer: null });
|
||||
state.index = 0;
|
||||
if (state.timer) {
|
||||
clearInterval(state.timer);
|
||||
state.timer = null;
|
||||
}
|
||||
|
||||
if (!reduceMotion) {
|
||||
setInterval(() => {
|
||||
const slides = Array.from(
|
||||
document.querySelectorAll(`[data-id^="${carouselId}-"]`)
|
||||
);
|
||||
slides.forEach((slide, index) => {
|
||||
slide.classList.toggle("opacity-0", index !== 0);
|
||||
slide.classList.toggle("hidden", index >= preloadCount);
|
||||
slide.setAttribute("aria-hidden", index === 0 ? "false" : "true");
|
||||
});
|
||||
|
||||
// Honour reduced-motion: leave the first image showing, no rotation.
|
||||
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
||||
|
||||
state.timer = setInterval(() => {
|
||||
const current = document.querySelectorAll(
|
||||
`[data-id="${carouselId}-${currentIndex}"]`
|
||||
`[data-id="${carouselId}-${state.index}"]`
|
||||
)[0];
|
||||
const nextIndex = (currentIndex + 1) % imageCount;
|
||||
const nextIndex = (state.index + 1) % imageCount;
|
||||
const next = document.querySelectorAll(
|
||||
`[data-id="${carouselId}-${nextIndex}"]`
|
||||
)[0];
|
||||
|
||||
if (!current || !next) return;
|
||||
|
||||
// Wait until the incoming image has loaded, or the fade pops in.
|
||||
if (next.tagName === "IMG" && !next.complete) return;
|
||||
|
||||
// Fade out current, fade in next
|
||||
current.classList.add("opacity-0");
|
||||
current.setAttribute("aria-hidden", "true");
|
||||
@@ -127,7 +156,10 @@ const imagesArray = shuffle ? shuffleArray(images) : images;
|
||||
preloadImage.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
|
||||
currentIndex = nextIndex;
|
||||
state.index = nextIndex;
|
||||
}, interval);
|
||||
}
|
||||
|
||||
document.addEventListener("astro:page-load", initCarousel);
|
||||
initCarousel();
|
||||
</script>
|
||||
@@ -64,7 +64,7 @@ const { navbarDisplay = "normal" } = Astro.props as Props;
|
||||
<div class="md:hidden">
|
||||
<button
|
||||
id="menu-toggle"
|
||||
class="text-2xl focus:outline-none"
|
||||
class="focus-visible:ring-primary text-2xl focus:outline-none focus-visible:ring-2"
|
||||
aria-label="Toggle navigation menu"
|
||||
aria-expanded="false"
|
||||
aria-controls="mobile-menu"
|
||||
@@ -139,6 +139,10 @@ const { navbarDisplay = "normal" } = Astro.props as Props;
|
||||
nav = document.getElementById("nav");
|
||||
toggle = document.getElementById("menu-toggle");
|
||||
|
||||
// The nav persists across transitions: always start with a closed menu.
|
||||
nav?.setAttribute("data-mobile-menu", "hidden");
|
||||
toggle?.setAttribute("aria-expanded", "false");
|
||||
|
||||
if (toggle && !toggle.dataset.bound) {
|
||||
toggle.dataset.bound = "true";
|
||||
toggle.addEventListener("click", () => {
|
||||
|
||||
@@ -120,7 +120,7 @@ const tracks = (
|
||||
quality={5}
|
||||
height={192}
|
||||
shuffle={true}
|
||||
eagerCount={0}
|
||||
eagerCount={4}
|
||||
><div class="flex h-full w-full items-center">
|
||||
<a
|
||||
href="/contact/"
|
||||
@@ -208,7 +208,7 @@ const tracks = (
|
||||
quality={5}
|
||||
height={384}
|
||||
shuffle={true}
|
||||
eagerCount={0}
|
||||
eagerCount={4}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in new issue
Block a user