Simple Angular wrapper for embla-carousel.
WARNING: CURRENTLY IN ACTIVE DEVELOPMENT, API MAY CHANGE
- 🖐️ SSR in mind
- 🔥 Performant
- 🛠 Super-lightweight: embla-carousel 6.7kb + angular directive 1kb gzipped
@zip-fa/ng-embla-carousel | Angular |
---|---|
0.x | >= 16.0 |
npm i @zip-fa/ng-embla-carousel
Provide global options in your app.config.ts:
import { provideEmblaGlobalOptions } from '@zip-fa/ng-embla-carousel';
providers: [
provideEmblaGlobalOptions({
skipSnaps: true,
loop: true
})
]
component.ts:
import { EmblaCarouselDirective } from '@zip-fa/ng-embla-carousel';
@Component({
imports: [EmblaCarouselDirective]
})
component.html:
Control flow (angular v17+):
<div class="embla">
<div class="embla__viewport"
emblaCarousel
>
<div class="embla__container">
@for (slide of slides; track $index) {
<div class="embla__slide">
<div class="embla__slide__number"><span>{{ $index + 1 }}</span></div>
<img class="embla__slide__img" [src]="slide" alt="Your alt text">
</div>
}
</div>
</div>
</div>
*ngFor:
<div class="embla">
<div class="embla__viewport"
emblaCarousel
>
<div class="embla__container">
<div class="embla__slide"
*ngFor="let slide of slides"
>
<div class="embla__slide__number"><span>{{ $index + 1 }}</span></div>
<img class="embla__slide__img" [src]="slide" alt="Your alt text">
</div>
</div>
</div>
</div>
component.scss:
.embla {
--slide-spacing: 1rem;
--slide-size: 100%;
--slide-height: 19rem;
&__viewport {
overflow: hidden;
}
&__container {
backface-visibility: hidden;
display: flex;
touch-action: pan-y;
margin-left: calc(var(--slide-spacing) * -1);
}
&__slide {
flex: 0 0 var(--slide-size);
min-width: 0;
padding-left: var(--slide-spacing);
position: relative;
}
&__slide__img {
display: block;
height: var(--slide-height);
width: 100%;
object-fit: cover;
}
}
component.ts:
import { Component, ViewChild } from '@angular/core';
import { EmblaCarouselDirective } from '@zip-fa/ng-embla-carousel';
import type { EmblaOptionsType, EmblaPluginType, EmblaEventType } from 'embla-carousel';
import Autoplay from 'embla-carousel-autoplay';
@Component()
export class MyComponent {
@ViewChild(EmblaCarouselDirective, { static: true })
private emblaCarousel!: EmblaCarouselDirective;
public readonly options: EmblaOptionsType = {
loop: true,
duration: 25
};
public readonly plugins: EmblaPluginType[] = [Autoplay({ delay: 4000 })];
public readonly subscribeToEvents: EmblaEventType[] = [
'init',
'pointerDown',
'pointerUp',
'slidesChanged',
'slidesInView',
'select',
'settle',
'destroy',
'reInit',
'resize',
'scroll'
];
onEmblaChanged(event: EmblaEventType): void {
console.log(`Embla event triggered: ${event}`, this.emblaCarousel.emblaApi);
}
}
<div class="embla">
<div class="embla__viewport"
#emblaRef="emblaCarousel"
[emblaCarousel]="options"
[emblaPlugins]="plugins"
[subscribeToEvents]="subscribeToEvents"
[eventsThrottleTime]="100"
(emblaChange)="onEmblaChanged($event)"
>
...
</div>
<button type="button" (click)="emblaRef.scrollPrev()">Prev slide</button>
<button type="button" (click)="emblaRef.scrollNext()">Next slide</button>
<button type="button" (click)="emblaRef.scrollTo(3)">To third slide</button>
</div>
Tip: you can change embla
prefix to whatever you want. Please configure everything by your needs
Name | Type | Reference |
---|---|---|
emblaCarousel | EmblaOptionsType | view |
emblaPlugins | EmblaPluginType[] | view |
subscribeToEvents | EmblaEventType[] | which events will be emitted from (emblaChange) |
eventsThrottleTime | number (ms) | trhottle for rapid embla events, such as scroll; 100 ms by default |
Name | Type |
---|---|
(emblaChange) | EmblaEventType |
Name | Description | Reference |
---|---|---|
scrollNext(jump?: boolean) | Scroll to next slide | view |
scrollPrev(jump?: boolean) | Scroll to prev slide | view |
scrollTo(index: number, jump?: boolean) | Scroll to given slide | view |
emblaApi.on()
, emblaApi.scrollNext()
, emblaApi.scrollPrev()
, emblaApi.scrollTo()
calls will trigger too much ChangeDetection, which will lead to serious performance issues.
Consider using EmblaCarouselDirective.scrollPrev()
, EmblaCarouselDirective.scrollNext()
, EmblaCarouselDirective.scrollTo()
- they are wrapped with ngZone.runOutsideAngular()
.
@Component()
export class MyComponent {
@ViewChild(EmblaCarouselDirective, { static: true })
private emblaCarousel!: EmblaCarouselDirective;
someAction(): void {
const { emblaApi } = this.emblaCarousel; // EmblaCarouselType
console.log(emblaApi.canScrollPrev());
}
}