Revert "fix: performance"

This reverts commit cc39abed41.
This commit is contained in:
2026-04-24 18:19:54 +02:00
parent cc39abed41
commit c52856aaba
58 changed files with 140 additions and 195 deletions
+7 -26
View File
@@ -64,28 +64,13 @@
[title]="portraitHighlights.length > 1 ? 'Show next image' : 'Single image'"
>
<div class="portrait-media" [class.portrait-media-switching]="isPortraitSwitching">
<picture>
<source
type="image/avif"
[attr.srcset]="currentPortraitHighlight.avifSrcset"
sizes="(max-width: 640px) calc(100vw - 2rem), (max-width: 900px) min(560px, calc(100vw - 2rem)), 560px"
/>
<source
type="image/webp"
[attr.srcset]="currentPortraitHighlight.webpSrcset"
sizes="(max-width: 640px) calc(100vw - 2rem), (max-width: 900px) min(560px, calc(100vw - 2rem)), 560px"
/>
<img
class="hero-feature-image"
[src]="currentPortraitHighlight.image"
[attr.width]="currentPortraitHighlight.width"
[attr.height]="currentPortraitHighlight.height"
[alt]="currentPortraitHighlight.alt"
fetchpriority="high"
loading="eager"
decoding="async"
/>
</picture>
<img
class="hero-feature-image"
[src]="currentPortraitHighlight.image"
width="560"
height="420"
[alt]="global.firstname + ' highlight'"
/>
@if (currentPortraitHighlightIndex === 0) {
<span class="portrait-click-hint">Click me!</span>
}
@@ -163,11 +148,7 @@
class="project-icon"
[class.project-icon-circle]="project.CircleIcon !== false"
[src]="project.icon"
[attr.width]="project.iconWidth ?? 108"
[attr.height]="project.iconHeight ?? 108"
[alt]="project.title + ' icon'"
loading="lazy"
decoding="async"
/>
}
+81 -13
View File
@@ -22,6 +22,7 @@ import { FooterComponent } from '../footer/footer.component';
export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
private readonly sanitizer = inject(DomSanitizer);
private readonly zone = inject(NgZone);
private preloadedImages: HTMLImageElement[] = [];
private tallestPortraitAspectRatio: number | null = null;
private lockedHeroSectionMinHeight: number = 0;
private projectEntryObserver: IntersectionObserver | null = null;
@@ -81,7 +82,7 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
}
ngOnInit(): void {
this.initPortraitAspectRatio();
this.preloadImageAssets();
this.initNameGradientScrollAnimation();
this.zone.runOutsideAngular((): void => {
@@ -180,21 +181,88 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
this.scrollToElementById('contact-links', this.getResponsiveScrollOffset(0.05));
}
private initPortraitAspectRatio(): void {
const minAspectRatio: number = this.portraitHighlights.reduce(
(minRatio: number, highlight: PortraitHighlight): number => {
if (highlight.width <= 0 || highlight.height <= 0) {
return minRatio;
}
private preloadImageAssets(): void {
if (typeof window === 'undefined') {
return;
}
const currentRatio: number = highlight.width / highlight.height;
return minRatio === 0 ? currentRatio : Math.min(minRatio, currentRatio);
},
0,
const projectIcons: string[] = this.projects
.map((project: PortfolioProject): string | undefined => project.icon)
.filter((icon: string | undefined): icon is string => Boolean(icon));
const uniqueImageUrls: string[] = Array.from(
new Set<string>([
...this.portraitHighlights.map((highlight: PortraitHighlight): string => highlight.image),
...projectIcons,
]),
);
if (minAspectRatio > 0) {
this.tallestPortraitAspectRatio = minAspectRatio;
this.preloadedImages = uniqueImageUrls.map((url: string): HTMLImageElement => {
const image = new Image();
image.decoding = 'async';
image.loading = 'eager';
image.src = url;
return image;
});
this.syncHeroHeightToLargestPortraitImage();
}
private syncHeroHeightToLargestPortraitImage(): void {
if (typeof window === 'undefined') {
return;
}
const portraitImageUrls: Set<string> = new Set<string>(
this.portraitHighlights.map(
(highlight: PortraitHighlight): string =>
new URL(highlight.image, window.location.href).href,
),
);
const portraitImages: HTMLImageElement[] = this.preloadedImages.filter(
(image: HTMLImageElement): boolean => portraitImageUrls.has(image.src),
);
if (portraitImages.length === 0) {
return;
}
const finalizeAspectRatio = (): void => {
const minAspectRatio: number = portraitImages.reduce(
(minRatio: number, image: HTMLImageElement): number => {
if (image.naturalWidth === 0 || image.naturalHeight === 0) {
return minRatio;
}
const currentRatio: number = image.naturalWidth / image.naturalHeight;
return minRatio === 0 ? currentRatio : Math.min(minRatio, currentRatio);
},
0,
);
if (minAspectRatio > 0) {
this.tallestPortraitAspectRatio = minAspectRatio;
this.scheduleHeroSectionHeightUpdate();
}
};
let pendingLoads: number = portraitImages.length;
const onImageSettled = (): void => {
pendingLoads -= 1;
if (pendingLoads === 0) {
finalizeAspectRatio();
}
};
for (const image of portraitImages) {
if (image.complete) {
onImageSettled();
continue;
}
image.addEventListener('load', onImageSettled, { once: true });
image.addEventListener('error', onImageSettled, { once: true });
}
}