Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d4e080d17 | |||
| 2a4c605a77 | |||
| da9d1954d6 | |||
| 4fc00098f2 | |||
| 2df7aece5c | |||
| d65be95e95 |
@@ -22,6 +22,8 @@ interface HeroVideoProps {
|
||||
badge?: string;
|
||||
}
|
||||
|
||||
import placeholders from '@/lib/placeholders.json';
|
||||
|
||||
export function HeroVideo(props: HeroVideoProps) {
|
||||
const { data } = props;
|
||||
const title = props.title || data?.title || 'DIE EXPERTEN FÜR KABELNETZBAU';
|
||||
@@ -37,6 +39,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
|
||||
const posterSrc = props.backgroundImage?.url || props.posterImage?.url || data?.posterImage?.url || defaultPoster;
|
||||
const posterAlt = props.backgroundImage?.alt || data?.posterImage?.alt || "E-TIB Gruppe Baustelle";
|
||||
const blurDataURL = (placeholders as Record<string, string>)[posterSrc] || undefined;
|
||||
|
||||
const ctaLabel = props.ctaLabel || props.linkText || data?.ctaLabel || 'Unternehmen entdecken';
|
||||
const ctaHref = props.ctaHref || props.linkHref || data?.ctaHref || '#unternehmen';
|
||||
@@ -87,13 +90,15 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
className="object-cover z-[1] pointer-events-none filter contrast-125 saturate-110 brightness-90"
|
||||
sizes="100vw"
|
||||
priority
|
||||
placeholder={blurDataURL ? "blur" : "empty"}
|
||||
blurDataURL={blurDataURL}
|
||||
/>
|
||||
|
||||
{/* Render video on top if available, but defer src to avoid blocking LCP */}
|
||||
{videoUrl && (
|
||||
<video
|
||||
key={`${pathname}-${videoUrl}`}
|
||||
className="absolute inset-0 w-full h-full object-cover z-[2] pointer-events-none filter contrast-125 saturate-110 brightness-90"
|
||||
className={`absolute inset-0 w-full h-full object-cover z-[2] pointer-events-none filter contrast-125 saturate-110 brightness-90 transition-opacity duration-1000 ${videoSrcLoaded ? 'opacity-100' : 'opacity-0'}`}
|
||||
src={videoSrcLoaded ? videoUrl : undefined}
|
||||
autoPlay
|
||||
muted
|
||||
|
||||
@@ -46,6 +46,22 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
const scrollLeftRef = React.useRef(0);
|
||||
const isDraggingRef = React.useRef(false);
|
||||
const [isDragging, setIsDragging] = React.useState(false);
|
||||
const [paddingX, setPaddingX] = React.useState(24);
|
||||
|
||||
React.useEffect(() => {
|
||||
const updatePadding = () => {
|
||||
const containerEl = document.querySelector('#referenzen .container');
|
||||
if (containerEl) {
|
||||
const rect = containerEl.getBoundingClientRect();
|
||||
const computedStyle = window.getComputedStyle(containerEl);
|
||||
const pLeft = parseFloat(computedStyle.paddingLeft || '0');
|
||||
setPaddingX(rect.left + pLeft);
|
||||
}
|
||||
};
|
||||
updatePadding();
|
||||
window.addEventListener('resize', updatePadding);
|
||||
return () => window.removeEventListener('resize', updatePadding);
|
||||
}, []);
|
||||
|
||||
const badge = props.badge || data?.badge || t('badge');
|
||||
const title = props.title || data?.title || t('title');
|
||||
@@ -115,8 +131,11 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseUp={onMouseUp}
|
||||
onMouseMove={onMouseMove}
|
||||
className={`select-none flex gap-6 overflow-x-auto pb-8 pt-4 px-6 md:px-[max(2rem,calc((100vw_-_1280px)_/_2))] lg:px-[max(2.5rem,calc((100vw_-_1280px)_/_2))] [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden ${isDragging ? 'cursor-grabbing snap-none' : 'cursor-grab snap-x snap-mandatory'}`}
|
||||
className={`select-none flex overflow-x-auto pb-8 pt-4 [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden ${isDragging ? 'cursor-grabbing snap-none' : 'cursor-grab snap-x snap-mandatory'}`}
|
||||
>
|
||||
{/* Robust Left Spacer for Pixel-Perfect Alignment */}
|
||||
<div style={{ minWidth: paddingX }} className="flex-shrink-0 pointer-events-none" aria-hidden="true" />
|
||||
|
||||
{references.map((ref, i) => {
|
||||
const imgSrc = ref.image
|
||||
? (typeof ref.image === 'string' ? ref.image : ref.image.url)
|
||||
@@ -129,7 +148,7 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true, margin: "-50px" }}
|
||||
transition={{ delay: i * 0.1, duration: 1.0, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="flex-shrink-0 w-[320px] md:w-[480px] snap-start group pointer-events-auto"
|
||||
className="flex-shrink-0 w-[320px] md:w-[480px] snap-start group pointer-events-auto mr-6"
|
||||
>
|
||||
<Link
|
||||
href={`/${locale}/referenzen#${ref.slug}`}
|
||||
|
||||
7
lib/placeholders.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"/assets/photos/DJI_0048.JPG": "data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAADwAQCdASoKAAcABUB8JYwCdH8AGDNIbaAA92kbyv6vgOVMTIAAAA==",
|
||||
"/assets/videos/web/hero-bahnkreuzung-poster.jpg": "data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoKAAYABUB8JaQAA3AA/vDUoAA=",
|
||||
"/assets/videos/web/hero-bohrung-poster.jpg": "data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACwAQCdASoKAAYABUB8JZACdAEOO2gAAN5Rr3co8j7CDYNKNCAAAA==",
|
||||
"/assets/videos/web/hero-kabelpflug-poster.jpg": "data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoKAAYABUB8JaQAA3AA/vDUoAA=",
|
||||
"/assets/videos/web/hero-messe-poster.jpg": "data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoKAAYABUB8JaQAA3AA/vDUoAA="
|
||||
}
|
||||
@@ -15,7 +15,6 @@ const nextConfig = {
|
||||
pagesBufferLength: 2,
|
||||
},
|
||||
experimental: {
|
||||
optimizeCss: true,
|
||||
staleTimes: {
|
||||
dynamic: 0,
|
||||
static: 30,
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
"prepare": "husky",
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
},
|
||||
"version": "2.2.35",
|
||||
"version": "2.2.38",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@parcel/watcher",
|
||||
|
||||
BIN
public/assets/photos/2025-09-23_12.11.43.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 11 MiB After Width: | Height: | Size: 1007 KiB |
BIN
public/assets/photos/2025-09-23_12.12.37.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.0 MiB After Width: | Height: | Size: 777 KiB |
BIN
public/assets/photos/DJI_0037.JPG
Executable file → Normal file
|
Before Width: | Height: | Size: 9.3 MiB After Width: | Height: | Size: 522 KiB |
BIN
public/assets/photos/DJI_0048.JPG
Executable file → Normal file
|
Before Width: | Height: | Size: 7.8 MiB After Width: | Height: | Size: 412 KiB |
BIN
public/assets/photos/DJI_0243.JPG
Executable file → Normal file
|
Before Width: | Height: | Size: 8.6 MiB After Width: | Height: | Size: 488 KiB |
BIN
public/assets/photos/DJI_0301.JPG
Executable file → Normal file
|
Before Width: | Height: | Size: 9.8 MiB After Width: | Height: | Size: 574 KiB |
BIN
public/assets/photos/DSC00373.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 304 KiB |
BIN
public/assets/photos/DSC00375.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 304 KiB |
BIN
public/assets/photos/DSC00388.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 342 KiB |
BIN
public/assets/photos/DSC00400.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 322 KiB |
BIN
public/assets/photos/DSC00430.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.5 MiB After Width: | Height: | Size: 540 KiB |
BIN
public/assets/photos/DSC00440.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 358 KiB |
BIN
public/assets/photos/DSC00452.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 6.5 MiB After Width: | Height: | Size: 618 KiB |
BIN
public/assets/photos/DSC02028.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.4 MiB After Width: | Height: | Size: 314 KiB |
BIN
public/assets/photos/DSC02057.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.7 MiB After Width: | Height: | Size: 345 KiB |
BIN
public/assets/photos/DSC02069.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 9.0 MiB After Width: | Height: | Size: 597 KiB |
BIN
public/assets/photos/DSC02083.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 7.2 MiB After Width: | Height: | Size: 493 KiB |
BIN
public/assets/photos/DSC02089.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.8 MiB After Width: | Height: | Size: 371 KiB |
BIN
public/assets/photos/DSC02114.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.7 MiB After Width: | Height: | Size: 240 KiB |
BIN
public/assets/photos/DSC02135.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.0 MiB After Width: | Height: | Size: 325 KiB |
BIN
public/assets/photos/DSC02150.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 8.7 MiB After Width: | Height: | Size: 629 KiB |
BIN
public/assets/photos/DSC02152.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 9.4 MiB After Width: | Height: | Size: 659 KiB |
BIN
public/assets/photos/DSC02156.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 9.3 MiB After Width: | Height: | Size: 664 KiB |
BIN
public/assets/photos/DSC02164.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 9.2 MiB After Width: | Height: | Size: 648 KiB |
BIN
public/assets/photos/DSCF8687.JPG
Executable file → Normal file
|
Before Width: | Height: | Size: 8.2 MiB After Width: | Height: | Size: 442 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-100.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 218 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-12.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 345 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-14.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.5 MiB After Width: | Height: | Size: 355 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-15.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 336 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-2.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.4 MiB After Width: | Height: | Size: 346 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-24.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 174 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-27.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 120 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-28.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 277 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-3.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 181 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-45.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 276 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-47.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.9 MiB After Width: | Height: | Size: 305 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-54.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 194 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-6.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 216 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-60.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 220 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-86.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 108 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(219_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 376 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(225_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 250 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(227_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 256 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(235_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 326 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(247_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.2 MiB After Width: | Height: | Size: 408 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(249_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 339 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(24_von_99)_groß2.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 11 MiB After Width: | Height: | Size: 188 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(250_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.0 MiB After Width: | Height: | Size: 353 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(257_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 159 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(26_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.7 MiB After Width: | Height: | Size: 130 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(2_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 197 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(38_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.7 MiB After Width: | Height: | Size: 171 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(66_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.4 MiB After Width: | Height: | Size: 206 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(6_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.1 MiB After Width: | Height: | Size: 290 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(71_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.2 MiB After Width: | Height: | Size: 189 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(80_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.0 MiB After Width: | Height: | Size: 185 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(83_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.6 MiB After Width: | Height: | Size: 285 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(95_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.7 MiB After Width: | Height: | Size: 218 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(98_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 623 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 774 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 601 KiB |
BIN
public/assets/photos/Screenshot_2025-09-30_085237.png
Executable file → Normal file
|
Before Width: | Height: | Size: 5.3 MiB After Width: | Height: | Size: 455 KiB |
BIN
public/assets/photos/Screenshot_2025-09-30_085339.png
Executable file → Normal file
|
Before Width: | Height: | Size: 5.2 MiB After Width: | Height: | Size: 474 KiB |
BIN
public/assets/photos/Screenshot_2025-09-30_085736.png
Executable file → Normal file
|
Before Width: | Height: | Size: 6.0 MiB After Width: | Height: | Size: 722 KiB |
BIN
public/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-256.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 139 KiB |
BIN
public/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-27.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 658 KiB After Width: | Height: | Size: 238 KiB |
BIN
public/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-294.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.9 MiB After Width: | Height: | Size: 233 KiB |
BIN
public/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-298.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.6 MiB After Width: | Height: | Size: 252 KiB |
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 324 KiB |
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 390 KiB |
44
scripts/generate-placeholders.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import sharp from 'sharp';
|
||||
|
||||
const PUBLIC_DIR = path.join(process.cwd(), 'public');
|
||||
const IMAGES_TO_PROCESS = [
|
||||
'/assets/photos/DJI_0048.JPG',
|
||||
'/assets/videos/web/hero-bahnkreuzung-poster.jpg',
|
||||
'/assets/videos/web/hero-bohrung-poster.jpg',
|
||||
'/assets/videos/web/hero-kabelpflug-poster.jpg',
|
||||
'/assets/videos/web/hero-messe-poster.jpg',
|
||||
];
|
||||
|
||||
async function generatePlaceholders() {
|
||||
const placeholders: Record<string, string> = {};
|
||||
|
||||
for (const imagePath of IMAGES_TO_PROCESS) {
|
||||
const fullPath = path.join(PUBLIC_DIR, imagePath);
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
console.warn(`File not found: ${fullPath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const buffer = await sharp(fullPath)
|
||||
.resize(10, 10, { fit: 'inside' })
|
||||
.blur(2) // Adding slight blur
|
||||
.webp({ quality: 20 })
|
||||
.toBuffer();
|
||||
|
||||
const base64 = `data:image/webp;base64,${buffer.toString('base64')}`;
|
||||
placeholders[imagePath] = base64;
|
||||
console.log(`Generated placeholder for ${imagePath}`);
|
||||
} catch (e) {
|
||||
console.error(`Failed to generate placeholder for ${imagePath}`, e);
|
||||
}
|
||||
}
|
||||
|
||||
const outputPath = path.join(process.cwd(), 'lib', 'placeholders.json');
|
||||
fs.writeFileSync(outputPath, JSON.stringify(placeholders, null, 2));
|
||||
console.log(`Saved placeholders to ${outputPath}`);
|
||||
}
|
||||
|
||||
generatePlaceholders();
|
||||
41
scripts/optimize-images.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import sharp from 'sharp';
|
||||
|
||||
const PHOTOS_DIR = path.join(process.cwd(), 'public', 'assets', 'photos');
|
||||
|
||||
async function optimizeImages() {
|
||||
const files = fs.readdirSync(PHOTOS_DIR);
|
||||
let optimized = 0;
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.match(/\.(jpg|jpeg|png)$/i)) continue;
|
||||
|
||||
const filePath = path.join(PHOTOS_DIR, file);
|
||||
const metadata = await sharp(filePath).metadata();
|
||||
|
||||
// If image is larger than 1920px wide, or is a massive file
|
||||
if ((metadata.width && metadata.width > 1920) || fs.statSync(filePath).size > 1024 * 1024) {
|
||||
console.log(`Optimizing ${file} (Width: ${metadata.width}, Size: ${Math.round(fs.statSync(filePath).size / 1024)}KB)`);
|
||||
|
||||
const tmpPath = `${filePath}.tmp`;
|
||||
try {
|
||||
await sharp(filePath)
|
||||
.resize({ width: 1920, withoutEnlargement: true })
|
||||
.jpeg({ quality: 80, progressive: true })
|
||||
.toFile(tmpPath);
|
||||
|
||||
fs.renameSync(tmpPath, filePath);
|
||||
console.log(`✅ Optimized ${file} (New Size: ${Math.round(fs.statSync(filePath).size / 1024)}KB)`);
|
||||
optimized++;
|
||||
} catch (err) {
|
||||
console.error(`Failed to optimize ${file}`, err);
|
||||
if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Finished optimizing ${optimized} images.`);
|
||||
}
|
||||
|
||||
optimizeImages();
|
||||