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