src/components/common/ImageCollage.astro 2.7 K raw
1
---
2
import { Image } from "astro:assets";
3
import type { ImageMetadata } from "astro";
4
5
interface Props {
6
	images: (ImageMetadata | string)[];
7
	alts: string[];
8
}
9
10
const { images, alts } = Astro.props;
11
---
12
13
<div class="image-collage">
14
	<div class="collage-grid">
15
		{images.map((img, index) => (
16
			<div class="collage-item" data-index={index}>
17
				{typeof img === "string" ? (
18
					<img src={img} alt={alts[index]} loading="lazy" />
19
				) : (
20
					<Image src={img} alt={alts[index]} loading="lazy" />
21
				)}
22
			</div>
23
		))}
24
	</div>
25
	<div class="collage-overlay" id="collage-overlay">
26
		<img src="" alt="Expanded image preview" id="expanded-image" />
27
	</div>
28
</div>
29
30
<style>
31
	.image-collage {
32
		position: relative;
33
		width: 100%;
34
	}
35
36
	.collage-grid {
37
		display: grid;
38
		grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
39
		gap: 0.5rem;
40
		width: 100%;
41
	}
42
43
	.collage-item {
44
		position: relative;
45
		overflow: hidden;
46
		cursor: pointer;
47
		aspect-ratio: 1;
48
		transition: transform 0.2s ease;
49
	}
50
51
	.collage-item:hover {
52
		transform: scale(1.02);
53
	}
54
55
	.collage-item img {
56
		width: 100%;
57
		height: 100%;
58
		object-fit: cover;
59
		display: block;
60
	}
61
62
	.collage-overlay {
63
		position: fixed;
64
		top: 0;
65
		left: 0;
66
		width: 100vw;
67
		height: 100dvh;
68
		background-color: rgba(0, 0, 0, 0);
69
		display: flex;
70
		align-items: center;
71
		justify-content: center;
72
		opacity: 0;
73
		pointer-events: none;
74
		transition: all 0.3s ease;
75
		z-index: 9999;
76
	}
77
78
	.collage-overlay.active {
79
		background-color: rgba(0, 0, 0, 0.9);
80
		opacity: 1;
81
		pointer-events: all;
82
	}
83
84
	.collage-overlay img {
85
		max-width: 90%;
86
		max-height: 90vh;
87
		object-fit: contain;
88
		transform: scale(0.8);
89
		transition: transform 0.3s ease;
90
	}
91
92
	.collage-overlay.active img {
93
		transform: scale(1);
94
	}
95
96
	@media (max-width: 768px) {
97
		.collage-item {
98
			cursor: default;
99
		}
100
101
		.collage-item:hover {
102
			transform: none;
103
		}
104
	}
105
</style>
106
107
<script>
108
	document.addEventListener('DOMContentLoaded', () => {
109
		const isMobile = () => window.innerWidth <= 768;
110
111
		const collageItems = document.querySelectorAll('.collage-item');
112
		const overlay = document.getElementById('collage-overlay');
113
		const expandedImage = document.getElementById('expanded-image') as HTMLImageElement;
114
115
		collageItems.forEach((item) => {
116
			item.addEventListener('click', () => {
117
				if (isMobile()) return;
118
119
				const img = item.querySelector('img') as HTMLImageElement;
120
				if (img && overlay && expandedImage) {
121
					expandedImage.src = img.src;
122
					expandedImage.alt = img.alt;
123
					overlay.classList.add('active');
124
				}
125
			});
126
		});
127
128
		overlay?.addEventListener('click', () => {
129
			overlay.classList.remove('active');
130
			setTimeout(() => {
131
				if (expandedImage) {
132
					expandedImage.src = '';
133
				}
134
			}, 300);
135
		});
136
	});
137
</script>