Add ImageCarousel component with responsive design and functionality.

- Introduced a new `ImageCarousel` component to display image galleries with navigation and indicators.
- Included scoped CSS for custom styles and responsiveness.
- Integrated `ImageCarousel` into the homepage with sample image data.
This commit is contained in:
k
2025-07-20 12:57:40 +02:00
parent 15dedfabcf
commit 00213204c4
3 changed files with 306 additions and 0 deletions

View File

@ -0,0 +1,114 @@
---
// src/components/ImageCarousel.astro
import "../../styles/components/ImageCarousel.css";
interface Image {
src: string;
alt: string;
}
const { images = [] } = Astro.props as { images: Image[] };
---
<section class="image-carousel-container">
<h2 class="section-title">Galerie</h2>
<div class="image-carousel">
<button class="nav-button prev-button" aria-label="Previous image">
<span class="arrow">&#10094;</span>
</button>
<div class="carousel-images">
<div class="carousel-track">
{images.map((image, index) => (
<div class="carousel-slide" data-index={index}>
<img src={image.src} alt={image.alt} class="carousel-image" />
</div>
))}
</div>
</div>
<button class="nav-button next-button" aria-label="Next image">
<span class="arrow">&#10095;</span>
</button>
</div>
<div class="carousel-indicators">
{images.map((_, index) => (
<button
class="indicator-dot"
data-index={index}
aria-label={`Go to slide ${index + 1}`}
></button>
))}
</div>
</section>
<script>
// Initialize carousel functionality
function initCarousel() {
const carousel = document.querySelector('.image-carousel');
const track = document.querySelector('.carousel-track');
const slides = document.querySelectorAll('.carousel-slide');
const prevButton = document.querySelector('.prev-button');
const nextButton = document.querySelector('.next-button');
const indicators = document.querySelectorAll('.indicator-dot');
if (!carousel || !track || !slides.length || !prevButton || !nextButton) return;
let currentIndex = 0;
const slideCount = slides.length;
// Set initial active state
updateCarousel();
// Add event listeners
prevButton.addEventListener('click', () => {
currentIndex = (currentIndex - 1 + slideCount) % slideCount;
updateCarousel();
});
nextButton.addEventListener('click', () => {
currentIndex = (currentIndex + 1) % slideCount;
updateCarousel();
});
// Add click events to indicators
indicators.forEach((dot, index) => {
dot.addEventListener('click', () => {
currentIndex = index;
updateCarousel();
});
});
// Function to update carousel display
function updateCarousel() {
// Update active class on slides
slides.forEach((slide, index) => {
const position = index - currentIndex;
// Remove all position classes
slide.classList.remove('prev', 'current', 'next');
// Add appropriate position class
if (position === -1 || (position === slideCount - 1 && currentIndex === 0)) {
slide.classList.add('prev');
} else if (position === 0) {
slide.classList.add('current');
} else if (position === 1 || (position === -(slideCount - 1) && currentIndex === slideCount - 1)) {
slide.classList.add('next');
}
});
// Update indicators
indicators.forEach((dot, index) => {
dot.classList.toggle('active', index === currentIndex);
});
}
}
// Run initialization when DOM is loaded
document.addEventListener('DOMContentLoaded', initCarousel);
// Re-initialize on astro:page-load for Astro View Transitions
document.addEventListener('astro:page-load', initCarousel);
</script>

View File

@ -5,6 +5,7 @@ import Hero from "../components/Hero.astro";
import Welcome from "../components/Welcome.astro";
import EventsGrid from '../components/EventsGrid.astro';
import Drinks from "../components/Drinks.astro";
import ImageCarousel from "../components/ImageCarousel.astro";
const events = [
{image: '/images/Logo.png', title: 'Karaoke Night', date: 'Mi, 23. Juli 2025', description: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua' },
@ -12,6 +13,20 @@ const events = [
{image: '/images/Logo.png', title: 'Live-Musik', date: 'Sa, 26. Juli 2025', description: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod' },
{image: '/images/Logo.png', title: 'Cocktail-Abend', date: 'So, 27. Juli 2025', description: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua' }
];
const images = [
{ src: '/images/Logo.png', alt: 'Erstes Bild' },
{ src: '/images/Logo.png', alt: 'Zweites Bild' },
{ src: '/images/Logo.png', alt: 'Drittes Bild' },
{ src: '/images/Logo.png', alt: 'Viertes Bild' },
{ src: '/images/Logo.png', alt: 'Fünftes Bild' },
{ src: '/images/Logo.png', alt: 'Sechstes Bild' },
{ src: '/images/Logo.png', alt: 'Siebtes Bild' },
{ src: '/images/Logo.png', alt: 'Achtes Bild' },
{ src: '/images/Logo.png', alt: 'Neuntes Bild' },
{ src: '/images/Logo.png', alt: 'Zehntes Bild' }
];
---
<Layout>
@ -19,5 +34,6 @@ const events = [
<Hero />
<Welcome />
<EventsGrid events={events} />
<ImageCarousel images={images} />
<Drinks />
</Layout>

View File

@ -0,0 +1,176 @@
/* styles/components/ImageCarousel.css */
.image-carousel-container {
width: 100%;
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
}
.section-title {
text-align: center;
margin-bottom: 1.5rem;
font-size: 2rem;
font-weight: bold;
color: #333;
}
.image-carousel {
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
height: 400px;
}
.carousel-images {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
.carousel-track {
display: flex;
height: 100%;
position: relative;
}
.carousel-slide {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: all 0.5s ease;
display: flex;
align-items: center;
justify-content: center;
transform: scale(0.8);
z-index: 1;
}
/* Current slide - center and fully visible */
.carousel-slide.current {
opacity: 1;
transform: scale(1);
z-index: 3;
}
/* Previous slide - left side, partially visible */
.carousel-slide.prev {
opacity: 0.7;
transform: translateX(-30%) scale(0.85);
z-index: 2;
}
/* Next slide - right side, partially visible */
.carousel-slide.next {
opacity: 0.7;
transform: translateX(30%) scale(0.85);
z-index: 2;
}
.carousel-image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
/* Navigation buttons */
.nav-button {
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: rgba(255, 255, 255, 0.7);
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
transition: background-color 0.3s ease;
}
.nav-button:hover {
background-color: rgba(255, 255, 255, 0.9);
}
.prev-button {
left: 10px;
}
.next-button {
right: 10px;
}
.arrow {
font-size: 18px;
font-weight: bold;
}
/* Indicators */
.carousel-indicators {
display: flex;
justify-content: center;
margin-top: 1rem;
gap: 8px;
}
.indicator-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: #ccc;
border: none;
cursor: pointer;
transition: background-color 0.3s ease;
}
.indicator-dot.active {
background-color: #333;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.image-carousel {
height: 300px;
}
.carousel-slide.prev,
.carousel-slide.next {
opacity: 0.5;
transform: scale(0.7);
}
.carousel-slide.prev {
transform: translateX(-20%) scale(0.7);
}
.carousel-slide.next {
transform: translateX(20%) scale(0.7);
}
}
@media (max-width: 480px) {
.image-carousel {
height: 250px;
}
.carousel-slide.prev,
.carousel-slide.next {
display: none;
}
.nav-button {
width: 30px;
height: 30px;
}
}