improve image handling
Some checks failed
/ build (pull_request) Failing after 34s

- add blurred fallback image
- define images in dict
This commit is contained in:
kritzl 2026-10-06 19:45:18 +02:00
commit 3b9873e288
Signed by: kritzl
SSH key fingerprint: SHA256:5BmINP9VjZWaUk5Z+2CTut1KFhwLtd0ZynMekKbtViM
3 changed files with 157 additions and 71 deletions

View file

@ -2,38 +2,70 @@
import Footer from "../../../components/Footer.astro"
import Layout from "../../../layouts/Layout.astro"
import {File} from "@lucide/astro"
import path from "../../../utils/path.ts"
import customPath from "../../../utils/path.ts"
import {Picture} from "astro:assets"
import {readFile} from "node:fs/promises"
import {getPlaiceholder} from "plaiceholder"
import traumweh_1 from "../../../assets/media/hamburg/2026/traumweh_1.png"
const impressions = [
{
type Image = {
image: any,
alt: string,
attribution: string,
blur?: string,
}
const images_raw: Record<string, Image> = {
traumweh_1: {
image: traumweh_1,
alt: "Ein Foto der Veranstaltung. Eine Speakerin steht auf einer Bühe. Auf einer Leinwand ist eine Präsentation zu sehen. Auf Bannern steht DIDAYS 2026 Hamburg.",
attribution: "CC-4.0 BY-SA traumweh",
},
{
traumweh_2: {
image: traumweh_1,
alt: "Ein Foto der Veranstaltung. Eine Speakerin steht auf einer Bühe. Auf einer Leinwand ist eine Präsentation zu sehen. Auf Bannern steht DIDAYS 2026 Hamburg.",
attribution: "CC-4.0 BY-SA traumweh",
},
{
traumweh_3: {
image: traumweh_1,
alt: "Ein Foto der Veranstaltung. Eine Speakerin steht auf einer Bühe. Auf einer Leinwand ist eine Präsentation zu sehen. Auf Bannern steht DIDAYS 2026 Hamburg.",
attribution: "CC-4.0 BY-SA traumweh",
},
{
traumweh_4: {
image: traumweh_1,
alt: "Ein Foto der Veranstaltung. Eine Speakerin steht auf einer Bühe. Auf einer Leinwand ist eine Präsentation zu sehen. Auf Bannern steht DIDAYS 2026 Hamburg.",
attribution: "CC-4.0 BY-SA traumweh",
},
{
traumweh_5: {
image: traumweh_1,
alt: "Ein Foto der Veranstaltung. Eine Speakerin steht auf einer Bühe. Auf einer Leinwand ist eine Präsentation zu sehen. Auf Bannern steht DIDAYS 2026 Hamburg.",
attribution: "CC-4.0 BY-SA traumweh",
},
]
}
const images: Record<string, Image> = {}
for (const image_name in images_raw) {
const image = images_raw[image_name]
let buffer
if (import.meta.env.DEV) {
const imagePath = new URL(image.image.src, Astro.url)
const imageResponse = await fetch(imagePath)
buffer = Buffer.from(await imageResponse.arrayBuffer())
} else {
const imagePath = "./dist" + image.image.src
buffer = await readFile(imagePath)
}
image["blur"] = (await getPlaiceholder(buffer, {
size: 10,
})).base64
images[image_name] = image
}
const impressions = ["traumweh_1", "traumweh_2", "traumweh_3", "traumweh_4", "traumweh_5"]
const slides = [
{
@ -58,7 +90,8 @@ const slides = [
<Layout>
<div class="flex flex-col">
<h1 class="text-6xl font-bold mt-8 mb-4 flex justify-center">
<img src={path(import.meta.env.BASE_URL, "/hamburg/2026/logo.svg")} alt="Logo von den DiDays 2026 in Hamburg"
<img src={customPath(import.meta.env.BASE_URL, "/hamburg/2026/logo.svg")}
alt="Logo von den DiDays 2026 in Hamburg"
class="h-full"/>
</h1>
<div class="mask-[url(/transitions/big.svg)] bg-base-200 h-30 mask-repeat-x mask-[auto_100%]"/>
@ -72,21 +105,31 @@ const slides = [
</div>
</div>
<div class="bg-white relative">
<div class="bg-white relative overflow-hidden">
<div
class="mask-[url(/transitions/small.svg)] bg-base-200 h-10 mask-repeat-x mask-[auto_100%] absolute top-0 w-full"/>
class="mask-[url(/transitions/small.svg)] bg-base-200 h-10 mask-repeat-x mask-[auto_100%] absolute top-0 w-full z-20"/>
<img
aria-hidden
alt=""
src={images["traumweh_1"].blur}
class="absolute inset-0 w-full h-full transform scale-150 filter blur-2xl"
/>
<Picture
formats={["avif", "webp", "jpg"]}
widths={[540, 720, traumweh_1.width]}
src={traumweh_1}
class="w-full"
alt="Ein Foto der Veranstaltung. Eine Speakerin steht auf einer Bühe. Auf einer Leinwand ist eine Präsentation zu sehen. Auf Bannern steht DIDAYS 2026 Hamburg."
widths={[540, 720, images["traumweh_1"].image.width]}
src={images["traumweh_1"].image}
alt={images["traumweh_1"].alt}
layout="full-width"
class="w-full max-h-[70vh] object-cover"
pictureAttributes={{class: "relative z-10 text-transparent"}}
/>
<span class="absolute px-3 text-xs py-1 bg-white/50 bottom-11 right-0 rounded-l text-black">
CC-4.0 BY-SA traumweh
</span>
{images["traumweh_1"].attribution &&
<span class="absolute z-10 px-3 text-xs py-1 bg-white/50 bottom-11 right-0 rounded-l text-black">
{images["traumweh_1"].attribution}
</span>
}
<div
class="mask-[url(/transitions/small.svg)] bg-base-200 h-10 mask-repeat-x mask-[auto_100%] rotate-180 absolute bottom-0 w-full"/>
class="mask-[url(/transitions/small.svg)] bg-base-200 h-10 mask-repeat-x mask-[auto_100%] rotate-180 absolute bottom-0 w-full z-20"/>
</div>
<div class="bg-base-200 pb-20 w-screen overflow-x-hidden">
@ -119,42 +162,54 @@ const slides = [
Eindrücke vom Event
</h2>
<div class="flex gap-4 overflow-x-auto w-full justify-start py-4">
{impressions.map((impression, index) =>
<button command="show-modal" commandfor={`impression-${index}`}
class="mx-auto shrink-0 w-lg overflow-hidden shadow-lg rounded-xl relative">
<Picture
formats={["avif", "webp", "jpg"]}
widths={[540, 720, impression.image.width]}
src={impression.image}
alt={impression.alt}
layout="full-width"
/>
{impression.attribution &&
<span class="absolute px-3 text-xs py-1 bg-white/50 bottom-0 right-0 rounded-tl text-black">
{impression.attribution}
</span>}
</button>
<dialog id={`impression-${index}`}
class="mx-auto my-auto rounded-xl max-w-6xl bg-base-100 backdrop:bg-black/50"
closedby="any">
<Picture
src={impression.image}
alt={impression.alt}
/>
<div class="p-2 flex justify-between items-center">
{
impression.attribution &&
<span class="px-3 text-xs py-1">
{impression.attribution}
</span>
}
<button commandfor={`impression-${index}`} command="close" class="btn btn-base-200 ml-auto">
Schließen
{impressions.map(async (impression_name) => {
const impression = images[impression_name]
return (
<button command="show-modal" commandfor={`impression-${impression_name}`}
class="mx-auto shrink-0 w-lg overflow-hidden shadow-lg rounded-xl relative">
<img
aria-hidden
alt=""
src={impression.blur}
class="absolute inset-0 w-full h-full transform scale-150 filter blur-2xl"
/>
<Picture
formats={["avif", "webp", "jpg"]}
widths={[540, 720, impression.image.width]}
src={impression.image}
alt={impression.alt}
layout="full-width"
pictureAttributes={{class: "relative z-10 text-transparent"}}
/>
{impression.attribution &&
<span
class="absolute z-10 px-3 text-xs py-1 bg-white/50 bottom-0 right-0 rounded-tl text-black">
{impression.attribution}
</span>}
</button>
</div>
</dialog>
,
<dialog id={`impression-${impression_name}`}
class="mx-auto my-auto rounded-xl max-w-6xl bg-base-100 backdrop:bg-black/50"
closedby="any">
<Picture
src={impression.image}
alt={impression.alt}
/>
<div class="p-2 flex justify-between items-center">
{
impression.attribution &&
<span class="px-3 text-xs py-1">
{impression.attribution}
</span>
}
<button commandfor={`impression-${impression_name}`} command="close"
class="btn btn-base-200 ml-auto">
Schließen
</button>
</div>
</dialog>)
},
)}
</div>
</div>
@ -171,7 +226,7 @@ const slides = [
</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 w-full py-6">
{slides.map(slide =>
<a href={path(import.meta.env.BASE_URL, slide.path)}
<a href={customPath(import.meta.env.BASE_URL, slide.path)}
class="card shrink-0 bg-base-100 card-md shadow-lg border-base-200 border-2">
<div class="card-body">
<h2 class="card-title">{slide.name}</h2>