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;
|
} = Astro.props as Props;
|
||||||
|
|
||||||
const imagesArray = shuffle ? shuffleArray(images) : images;
|
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"]}>
|
<div class:list={[className, "relative overflow-hidden"]}>
|
||||||
@@ -60,7 +63,7 @@ const imagesArray = shuffle ? shuffleArray(images) : images;
|
|||||||
transitionStyle,
|
transitionStyle,
|
||||||
transitionDuration,
|
transitionDuration,
|
||||||
index > 0 ? "opacity-0" : "",
|
index > 0 ? "opacity-0" : "",
|
||||||
index < 2 ? "" : "hidden"
|
index < preloadCount ? "" : "hidden"
|
||||||
]}
|
]}
|
||||||
src={image}
|
src={image}
|
||||||
alt={
|
alt={
|
||||||
@@ -89,27 +92,53 @@ const imagesArray = shuffle ? shuffleArray(images) : images;
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script
|
<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.
|
// Reset the carousel and (re)start it. Runs on initial load and after
|
||||||
const reduceMotion = window.matchMedia(
|
// every page transition, so navigation always restarts the fade sequence.
|
||||||
"(prefers-reduced-motion: reduce)"
|
function initCarousel() {
|
||||||
).matches;
|
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) {
|
const slides = Array.from(
|
||||||
setInterval(() => {
|
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(
|
const current = document.querySelectorAll(
|
||||||
`[data-id="${carouselId}-${currentIndex}"]`
|
`[data-id="${carouselId}-${state.index}"]`
|
||||||
)[0];
|
)[0];
|
||||||
const nextIndex = (currentIndex + 1) % imageCount;
|
const nextIndex = (state.index + 1) % imageCount;
|
||||||
const next = document.querySelectorAll(
|
const next = document.querySelectorAll(
|
||||||
`[data-id="${carouselId}-${nextIndex}"]`
|
`[data-id="${carouselId}-${nextIndex}"]`
|
||||||
)[0];
|
)[0];
|
||||||
|
|
||||||
if (!current || !next) return;
|
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
|
// Fade out current, fade in next
|
||||||
current.classList.add("opacity-0");
|
current.classList.add("opacity-0");
|
||||||
current.setAttribute("aria-hidden", "true");
|
current.setAttribute("aria-hidden", "true");
|
||||||
@@ -127,7 +156,10 @@ const imagesArray = shuffle ? shuffleArray(images) : images;
|
|||||||
preloadImage.setAttribute("aria-hidden", "true");
|
preloadImage.setAttribute("aria-hidden", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
currentIndex = nextIndex;
|
state.index = nextIndex;
|
||||||
}, interval);
|
}, interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener("astro:page-load", initCarousel);
|
||||||
|
initCarousel();
|
||||||
</script>
|
</script>
|
||||||
@@ -64,7 +64,7 @@ const { navbarDisplay = "normal" } = Astro.props as Props;
|
|||||||
<div class="md:hidden">
|
<div class="md:hidden">
|
||||||
<button
|
<button
|
||||||
id="menu-toggle"
|
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-label="Toggle navigation menu"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-controls="mobile-menu"
|
aria-controls="mobile-menu"
|
||||||
@@ -139,6 +139,10 @@ const { navbarDisplay = "normal" } = Astro.props as Props;
|
|||||||
nav = document.getElementById("nav");
|
nav = document.getElementById("nav");
|
||||||
toggle = document.getElementById("menu-toggle");
|
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) {
|
if (toggle && !toggle.dataset.bound) {
|
||||||
toggle.dataset.bound = "true";
|
toggle.dataset.bound = "true";
|
||||||
toggle.addEventListener("click", () => {
|
toggle.addEventListener("click", () => {
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ const tracks = (
|
|||||||
quality={5}
|
quality={5}
|
||||||
height={192}
|
height={192}
|
||||||
shuffle={true}
|
shuffle={true}
|
||||||
eagerCount={0}
|
eagerCount={4}
|
||||||
><div class="flex h-full w-full items-center">
|
><div class="flex h-full w-full items-center">
|
||||||
<a
|
<a
|
||||||
href="/contact/"
|
href="/contact/"
|
||||||
@@ -208,7 +208,7 @@ const tracks = (
|
|||||||
quality={5}
|
quality={5}
|
||||||
height={384}
|
height={384}
|
||||||
shuffle={true}
|
shuffle={true}
|
||||||
eagerCount={0}
|
eagerCount={4}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in new issue
Block a user