heroweb/examples/images/index3.html
2024-10-31 07:24:12 +01:00

297 lines
9.3 KiB
HTML

<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Grid</title>
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
body, html {
margin: 0;
padding: 0;
min-height: 100vh;
}
.container {
max-width: 100% !important;
padding: 0.5rem !important;
margin: 0 !important;
}
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5rem;
}
.grid-item {
position: relative;
cursor: pointer;
overflow: hidden;
border-radius: 4px;
}
.grid img {
width: 100%;
height: calc(33.33vh - 1rem);
object-fit: cover;
margin: 0;
transition: transform 0.3s ease;
}
.grid-item:hover img {
transform: scale(1.05);
}
/* Image overlay styles */
.image-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
padding: 1rem;
text-align: center;
transform: translateY(100%);
transition: transform 0.3s ease;
}
.grid-item:hover .image-overlay {
transform: translateY(0);
}
.image-overlay h3 {
margin: 0;
font-size: 1rem;
color: white;
margin-bottom: 0.5rem;
}
.image-overlay p {
margin: 0;
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.8);
line-height: 1.2;
}
/* Modal styles */
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.95);
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.modal.active {
opacity: 1;
visibility: visible;
}
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
height: 90%;
display: flex;
justify-content: center;
align-items: center;
}
.modal img {
max-width: 90%;
max-height: 90vh;
object-fit: contain;
animation: zoomIn 0.3s ease;
}
.close-btn {
position: absolute;
top: 20px;
right: 30px;
color: #fff;
font-size: 40px;
font-weight: bold;
cursor: pointer;
z-index: 1002;
transition: transform 0.3s ease;
}
.close-btn:hover {
transform: scale(1.1);
}
/* Enhanced navigation buttons */
.nav-button {
position: fixed;
top: 50%;
transform: translateY(-50%);
background: rgba(255, 255, 255, 0.1);
border: none;
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
color: white;
cursor: pointer;
border-radius: 50%;
z-index: 1002;
transition: all 0.3s ease;
}
.nav-button:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-50%) scale(1.1);
}
.nav-button.prev {
left: 30px;
}
.nav-button.next {
right: 30px;
}
.nav-button i {
font-size: 28px;
}
@keyframes zoomIn {
from {
opacity: 0;
transform: scale(0.3);
}
to {
opacity: 1;
transform: scale(1);
}
}
@media (max-width: 768px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
.grid img {
height: calc(25vh - 1rem);
}
.nav-button.prev { left: 10px; }
.nav-button.next { right: 10px; }
.nav-button {
width: 50px;
height: 50px;
}
}
@media (max-width: 480px) {
.grid {
grid-template-columns: 1fr;
}
.grid img {
height: calc(20vh - 1rem);
}
}
</style>
</head>
<body>
<main class="container" x-data="imageGallery()">
<div class="grid">
<template x-for="image in displayedImages" :key="image.uniqueId">
<div class="grid-item" @click="openModal(image)">
<img :src="image.url" :alt="image.alt">
<template x-if="image.title && image.description">
<div class="image-overlay">
<h3 x-text="image.title"></h3>
<p x-text="image.description"></p>
</div>
</template>
</div>
</template>
</div>
<!-- Modal -->
<div id="imageModal"
class="modal"
:class="{ 'active': modalOpen }"
@click.self="closeModal()"
@keydown.window.escape="closeModal()"
@keydown.window.arrow-right="nextImage()"
@keydown.window.arrow-left="previousImage()">
<span class="close-btn" @click="closeModal()">&times;</span>
<div class="modal-content">
<template x-if="currentImage">
<img :src="currentImage.url"
:alt="currentImage.alt"
style="transition: opacity 0.3s ease"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 transform scale-90"
x-transition:enter-end="opacity-100 transform scale-100">
</template>
</div>
<button class="nav-button prev" @click.stop="previousImage">
<i class="bi bi-chevron-left"></i>
</button>
<button class="nav-button next" @click.stop="nextImage">
<i class="bi bi-chevron-right"></i>
</button>
</div>
</main>
<script>
function imageGallery() {
return {
images: [],
displayedImages: [],
modalOpen: false,
currentImage: null,
currentIndex: 0,
async init() {
try {
const response = await fetch('data.json');
const data = await response.json();
this.images = data.images;
// Create array of 12 random images
let tempImages = [];
while (tempImages.length < 12) {
const randomImage = this.images[Math.floor(Math.random() * this.images.length)];
// Create a new object with unique ID to avoid duplicate keys
tempImages.push({
...randomImage,
uniqueId: randomImage.id + '_' + tempImages.length
});
}
this.displayedImages = tempImages;
} catch (error) {
console.error('Error loading images:', error);
}
},
openModal(image) {
this.modalOpen = true;
this.currentImage = image;
this.currentIndex = this.displayedImages.findIndex(img => img.uniqueId === image.uniqueId);
document.body.style.overflow = 'hidden';
},
closeModal() {
this.modalOpen = false;
document.body.style.overflow = 'auto';
},
nextImage() {
if (!this.modalOpen) return;
this.currentIndex = (this.currentIndex + 1) % this.displayedImages.length;
this.currentImage = this.displayedImages[this.currentIndex];
},
previousImage() {
if (!this.modalOpen) return;
this.currentIndex = (this.currentIndex - 1 + this.displayedImages.length) % this.displayedImages.length;
this.currentImage = this.displayedImages[this.currentIndex];
}
}
}
</script>
</body>
</html>