improve touchscreen scrolling on carousel
All checks were successful
publish.yml / publish (push) Successful in 1m5s
All checks were successful
publish.yml / publish (push) Successful in 1m5s
This commit is contained in:
@@ -10,8 +10,6 @@
|
|||||||
(mouseleave)="onHoverEnd()"
|
(mouseleave)="onHoverEnd()"
|
||||||
(focusin)="onHoverStart()"
|
(focusin)="onHoverStart()"
|
||||||
(focusout)="onCarouselFocusOut($event)"
|
(focusout)="onCarouselFocusOut($event)"
|
||||||
(touchstart)="onHoverStart()"
|
|
||||||
(touchend)="onHoverEnd()"
|
|
||||||
(click)="onInteractionClick()"
|
(click)="onInteractionClick()"
|
||||||
>
|
>
|
||||||
<button class="carousel-btn" type="button" aria-label="Previous technologies" (click)="scrollStack(-1)">
|
<button class="carousel-btn" type="button" aria-label="Previous technologies" (click)="scrollStack(-1)">
|
||||||
@@ -24,6 +22,9 @@
|
|||||||
[class.is-auto-scrolling]="isAutoScrolling"
|
[class.is-auto-scrolling]="isAutoScrolling"
|
||||||
#stackCarousel
|
#stackCarousel
|
||||||
(scroll)="onCarouselScroll()"
|
(scroll)="onCarouselScroll()"
|
||||||
|
(touchstart)="onTouchStart()"
|
||||||
|
(touchend)="onTouchEnd()"
|
||||||
|
(touchcancel)="onTouchCancel()"
|
||||||
role="region"
|
role="region"
|
||||||
aria-label="Tech stack carousel"
|
aria-label="Tech stack carousel"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -26,9 +26,12 @@ export class TechStackCarousel implements AfterViewInit, OnDestroy {
|
|||||||
private currentAutoScrollSpeedPxPerSecond = 0;
|
private currentAutoScrollSpeedPxPerSecond = 0;
|
||||||
private targetAutoScrollSpeedPxPerSecond = 0;
|
private targetAutoScrollSpeedPxPerSecond = 0;
|
||||||
private isPausePendingStop = false;
|
private isPausePendingStop = false;
|
||||||
|
private isTouchInteracting = false;
|
||||||
|
private isProgrammaticScrollUpdate = false;
|
||||||
private readonly autoScrollSpeedPxPerSecond = 72;
|
private readonly autoScrollSpeedPxPerSecond = 72;
|
||||||
private readonly speedRampDurationMs = 420;
|
private readonly speedRampDurationMs = 420;
|
||||||
private readonly speedStopThresholdPxPerSecond = 0.5;
|
private readonly speedStopThresholdPxPerSecond = 0.5;
|
||||||
|
private readonly touchScrollResumeDelayMs = 1200;
|
||||||
isAutoScrolling = false;
|
isAutoScrolling = false;
|
||||||
showLeftFade = false;
|
showLeftFade = false;
|
||||||
showRightFade = false;
|
showRightFade = false;
|
||||||
@@ -89,7 +92,32 @@ export class TechStackCarousel implements AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onCarouselScroll(): void {
|
onCarouselScroll(): void {
|
||||||
|
if (this.isProgrammaticScrollUpdate) {
|
||||||
|
this.isProgrammaticScrollUpdate = false;
|
||||||
this.updateFadeState();
|
this.updateFadeState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep autoscroll from fighting touch/momentum scrolling on mobile.
|
||||||
|
this.pauseAutoScrollImmediately();
|
||||||
|
this.scheduleResume(this.touchScrollResumeDelayMs);
|
||||||
|
this.updateFadeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
onTouchStart(): void {
|
||||||
|
this.isTouchInteracting = true;
|
||||||
|
this.clearScheduledResume();
|
||||||
|
this.pauseAutoScrollImmediately();
|
||||||
|
}
|
||||||
|
|
||||||
|
onTouchEnd(): void {
|
||||||
|
this.isTouchInteracting = false;
|
||||||
|
this.scheduleResume(this.touchScrollResumeDelayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
onTouchCancel(): void {
|
||||||
|
this.isTouchInteracting = false;
|
||||||
|
this.scheduleResume(this.touchScrollResumeDelayMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
onHoverStart(): void {
|
onHoverStart(): void {
|
||||||
@@ -118,6 +146,11 @@ export class TechStackCarousel implements AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resumeAutoScroll(): void {
|
resumeAutoScroll(): void {
|
||||||
|
if (this.isTouchInteracting) {
|
||||||
|
this.scheduleResume(this.touchScrollResumeDelayMs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.isPausePendingStop = false;
|
this.isPausePendingStop = false;
|
||||||
this.targetAutoScrollSpeedPxPerSecond = this.autoScrollSpeedPxPerSecond;
|
this.targetAutoScrollSpeedPxPerSecond = this.autoScrollSpeedPxPerSecond;
|
||||||
this.syncAutoScrollState();
|
this.syncAutoScrollState();
|
||||||
@@ -242,6 +275,7 @@ export class TechStackCarousel implements AfterViewInit, OnDestroy {
|
|||||||
// Normalize only when moving past the duplicated track boundary to avoid abrupt remaps on quick re-entry.
|
// Normalize only when moving past the duplicated track boundary to avoid abrupt remaps on quick re-entry.
|
||||||
this.virtualScrollLeft = nextVirtualScrollLeft >= loopWidth ? nextVirtualScrollLeft - loopWidth : nextVirtualScrollLeft;
|
this.virtualScrollLeft = nextVirtualScrollLeft >= loopWidth ? nextVirtualScrollLeft - loopWidth : nextVirtualScrollLeft;
|
||||||
|
|
||||||
|
this.isProgrammaticScrollUpdate = true;
|
||||||
carousel.scrollLeft = this.virtualScrollLeft;
|
carousel.scrollLeft = this.virtualScrollLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,5 +311,20 @@ export class TechStackCarousel implements AfterViewInit, OnDestroy {
|
|||||||
this.targetAutoScrollSpeedPxPerSecond > this.speedStopThresholdPxPerSecond ||
|
this.targetAutoScrollSpeedPxPerSecond > this.speedStopThresholdPxPerSecond ||
|
||||||
this.currentAutoScrollSpeedPxPerSecond > this.speedStopThresholdPxPerSecond;
|
this.currentAutoScrollSpeedPxPerSecond > this.speedStopThresholdPxPerSecond;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private pauseAutoScrollImmediately(): void {
|
||||||
|
this.clearScheduledResume();
|
||||||
|
|
||||||
|
if (this.autoScrollFrameId !== null) {
|
||||||
|
cancelAnimationFrame(this.autoScrollFrameId);
|
||||||
|
this.autoScrollFrameId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isPausePendingStop = false;
|
||||||
|
this.currentAutoScrollSpeedPxPerSecond = 0;
|
||||||
|
this.targetAutoScrollSpeedPxPerSecond = 0;
|
||||||
|
this.lastAutoScrollTime = 0;
|
||||||
|
this.syncAutoScrollState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user