feat: reusable image viewer
This commit is contained in:
parent
5684129873
commit
8bc1493821
5 changed files with 68 additions and 51 deletions
|
@ -1,51 +1,22 @@
|
|||
---
|
||||
interface Image {
|
||||
src: string
|
||||
alt: string
|
||||
}
|
||||
interface Props {
|
||||
images: Image[]
|
||||
}
|
||||
const { images } = Astro.props
|
||||
const rowCount = Math.ceil(images.length/3)
|
||||
// A component that allows you to open images in full screen.
|
||||
// All you need to do is place it anywhere on the page.
|
||||
---
|
||||
|
||||
<div>
|
||||
<div id="gallery" style={`grid-template-rows: repeat(${rowCount}, 263px);`}>
|
||||
{images.map(img => (
|
||||
<img src={img.src} alt={img.alt} loading="lazy" />
|
||||
))}
|
||||
</div>
|
||||
<div id="fullscreenGallery" style="opacity: 0;">
|
||||
<img id="fullscreenImage" />
|
||||
<div id="fullscreenAlt"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#gallery {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#gallery img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#fullscreenGallery {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
background-color: #0008;
|
||||
padding: 1em;
|
||||
backdrop-filter: blur(42px);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -55,24 +26,23 @@ const rowCount = Math.ceil(images.length/3)
|
|||
transition: opacity 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
#fullscreenAlt {
|
||||
font-style: italic;
|
||||
text-shadow: 0 0 4px rgba(0,0,0,.5);
|
||||
}
|
||||
|
||||
#fullscreenImage {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
max-height: calc(100% - 22px - 3em);
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
#fullscreenAlt {
|
||||
font-style: italic;
|
||||
text-shadow: 0 0 4px rgba(0,0,0,.5);
|
||||
height: 22px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
window.onload = () => {
|
||||
const gallery = document.getElementById("gallery")
|
||||
if (!gallery) return
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
const fsContainer = document.getElementById("fullscreenGallery")
|
||||
if (!fsContainer) return
|
||||
|
||||
|
@ -87,7 +57,8 @@ const rowCount = Math.ceil(images.length/3)
|
|||
})
|
||||
|
||||
const isOpen = () => fsContainer.style.opacity !== "0"
|
||||
const images = Array.from(gallery.children) as HTMLImageElement[]
|
||||
const images = (Array.from(document.querySelectorAll("main img")) as HTMLImageElement[])
|
||||
.filter(i => !i.src.endsWith("svg") && i.id != "fullscreenImage")
|
||||
|
||||
for (const img of images) {
|
||||
img.addEventListener("click", () => {
|
||||
|
@ -127,5 +98,5 @@ const rowCount = Math.ceil(images.length/3)
|
|||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
35
src/components/PhotoGrid.astro
Normal file
35
src/components/PhotoGrid.astro
Normal file
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
interface Image {
|
||||
src: string
|
||||
alt: string
|
||||
}
|
||||
interface Props {
|
||||
images: Image[]
|
||||
}
|
||||
const { images } = Astro.props
|
||||
const rowCount = Math.ceil(images.length/3)
|
||||
---
|
||||
|
||||
<div>
|
||||
<div class="gallery" style={`grid-template-rows: repeat(${rowCount}, 263px);`}>
|
||||
{images.map(img => (
|
||||
<img src={img.src} alt={img.alt} loading="lazy" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.gallery {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gallery img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
|
@ -28,7 +28,9 @@ const { title, description } = Astro.props
|
|||
<body>
|
||||
<div class="limiter">
|
||||
<Navbar />
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import Gallery from '../components/Gallery.astro'
|
||||
import PhotoGrid from '../components/PhotoGrid.astro'
|
||||
import ImageViewer from '../components/ImageViewer.astro'
|
||||
---
|
||||
|
||||
<Layout
|
||||
|
@ -9,7 +10,7 @@ import Gallery from '../components/Gallery.astro'
|
|||
>
|
||||
<h1>Кинотеатр</h1>
|
||||
<p>Мои любимые кадры из кино, сериалов и игр.</p>
|
||||
<Gallery images={[
|
||||
<PhotoGrid images={[
|
||||
{src: "/cinema/scott-pilgrim-takes-off/1.avif", alt: "Scott Pilgrim Takes Off (2023)"},
|
||||
{src: "/cinema/angels-egg/1.avif", alt: "Яйцо ангела (1985)"},
|
||||
{src: "/cinema/spider-man-across-the-spider-verse/1.avif", alt: "Человек-паук: Паутина вселенных (2023)"},
|
||||
|
@ -27,4 +28,5 @@ import Gallery from '../components/Gallery.astro'
|
|||
{src: "/cinema/night-in-the-woods/1.avif", alt: "Night in the Woods (2017)"},
|
||||
{src: "/cinema/night-in-the-woods/2.avif", alt: "Night in the Woods (2017)"},
|
||||
]} />
|
||||
<ImageViewer />
|
||||
</Layout>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { getCollection } from 'astro:content'
|
||||
import Layout from '../../layouts/Layout.astro'
|
||||
import Tag from '../../components/Tag.astro'
|
||||
import ImageViewer from '../../components/ImageViewer.astro'
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const blogEntries = await getCollection('notes')
|
||||
|
@ -32,6 +33,8 @@ const formatDate = (date: Date) => {
|
|||
<div class="content">
|
||||
<Content />
|
||||
</div>
|
||||
|
||||
<ImageViewer />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
|
@ -51,6 +54,10 @@ const formatDate = (date: Date) => {
|
|||
.content {
|
||||
img {
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
margin-left: calc((100% - min(100%, 700px)) / 2);
|
||||
margin-bottom: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #444;
|
||||
|
|
Loading…
Reference in a new issue