diff --git a/src/app/_samples/mediaco/components/carousel/carousel.component.html b/src/app/_samples/mediaco/components/carousel/carousel.component.html
index 11caed7a..c6ef11f6 100644
--- a/src/app/_samples/mediaco/components/carousel/carousel.component.html
+++ b/src/app/_samples/mediaco/components/carousel/carousel.component.html
@@ -1,15 +1,28 @@
diff --git a/src/app/_samples/mediaco/components/carousel/carousel.component.scss b/src/app/_samples/mediaco/components/carousel/carousel.component.scss
index 3b689750..52b52b75 100644
--- a/src/app/_samples/mediaco/components/carousel/carousel.component.scss
+++ b/src/app/_samples/mediaco/components/carousel/carousel.component.scss
@@ -84,3 +84,34 @@
}
}
}
+.skeleton-shimmer {
+ width: 100%;
+ height: 100%;
+ background: #e0e0e0;
+ position: relative;
+ overflow: hidden;
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ transform: translateX(-100%);
+ background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.1) 50%, transparent 100%);
+ animation: shimmer 1.5s infinite;
+ }
+}
+.skeleton-text {
+ height: 16px;
+ width: 80%;
+ background: rgba(255, 255, 255, 0.3);
+ border-radius: 4px;
+}
+
+@keyframes shimmer {
+ 100% {
+ transform: translateX(100%);
+ }
+}
diff --git a/src/app/_samples/mediaco/components/carousel/carousel.component.ts b/src/app/_samples/mediaco/components/carousel/carousel.component.ts
index fb75ac21..b1701e65 100644
--- a/src/app/_samples/mediaco/components/carousel/carousel.component.ts
+++ b/src/app/_samples/mediaco/components/carousel/carousel.component.ts
@@ -31,6 +31,8 @@ export class CarouselComponent implements AfterViewInit, OnDestroy, OnChanges {
originalItems: any[] = [];
displayItems: any[] = [];
+ isLoading: boolean = true;
+ skeletonItems: any[] = new Array(6).fill(0);
constructor(
private ngZone: NgZone,
@@ -38,8 +40,22 @@ export class CarouselComponent implements AfterViewInit, OnDestroy, OnChanges {
) {}
ngOnChanges(changes: SimpleChanges) {
- if (changes['data'] && this.data) {
- this.buildCarouselItems();
+ if (changes['data']) {
+ if (this.data && this.data.length > 0) {
+ this.buildCarouselItems();
+ } else {
+ this.isLoading = true;
+ }
+ }
+ }
+
+ initializeScroll() {
+ const container = this.scrollContainer?.nativeElement;
+ if (container && container.scrollWidth > 0) {
+ const singleSetWidth = container.scrollWidth / 3;
+ container.scrollLeft = singleSetWidth;
+ // Trigger one calculation to set initial sizes
+ this.onScroll({ target: container } as any);
}
}
@@ -51,8 +67,10 @@ export class CarouselComponent implements AfterViewInit, OnDestroy, OnChanges {
...item
};
});
+
this.originalItems = mappedData;
let loopList = [...mappedData];
+
// If you have 2 items, we duplicate them until we have at least 12.
const MIN_ITEMS = 12;
if (loopList.length > 0) {
@@ -60,8 +78,46 @@ export class CarouselComponent implements AfterViewInit, OnDestroy, OnChanges {
loopList = [...loopList, ...loopList];
}
}
- //CREATE 3 SETS: [Left Buffer] [Middle (Active)] [Right Buffer]
+
this.displayItems = [...loopList, ...loopList, ...loopList];
+
+ this.preloadImages();
+ }
+
+ //Logic to download all images before showing UI
+ preloadImages() {
+ this.isLoading = true;
+ const uniqueUrls = [...new Set(this.displayItems.map(item => item.img))].filter(url => url);
+
+ let loadedCount = 0;
+ const total = uniqueUrls.length;
+
+ if (total === 0) {
+ this.finishLoading();
+ return;
+ }
+
+ uniqueUrls.forEach(url => {
+ const img = new Image();
+ img.src = url;
+
+ const onImageComplete = () => {
+ loadedCount++;
+ if (loadedCount === total) {
+ this.finishLoading();
+ }
+ };
+
+ img.onload = onImageComplete;
+ img.onerror = onImageComplete;
+ });
+ }
+
+ finishLoading() {
+ this.isLoading = false;
+ setTimeout(() => {
+ this.initializeScroll();
+ }, 0);
}
ngAfterViewInit() {
@@ -69,13 +125,6 @@ export class CarouselComponent implements AfterViewInit, OnDestroy, OnChanges {
const container = this.scrollContainer?.nativeElement;
if (container) {
container.addEventListener('scroll', this.onScroll.bind(this));
- setTimeout(() => {
- if (container.scrollWidth > 0) {
- const singleSetWidth = container.scrollWidth / 3;
- container.scrollLeft = singleSetWidth;
- this.onScroll({ target: container } as any);
- }
- }, 50);
}
});
}
@@ -88,6 +137,8 @@ export class CarouselComponent implements AfterViewInit, OnDestroy, OnChanges {
}
onScroll(event: Event) {
+ if (this.isLoading) return;
+
const container = event.target as HTMLElement;
if (!container) return;
@@ -101,6 +152,7 @@ export class CarouselComponent implements AfterViewInit, OnDestroy, OnChanges {
} else if (currentScroll >= singleSetWidth * 2 - 100) {
container.scrollLeft = currentScroll - singleSetWidth;
}
+
const containerRect = container.getBoundingClientRect();
if (containerRect.width === 0) return;