Посібник для Angular
Дізнайтеся, як будувати слайдери з @reelkit/angular.
Компонент rk-reel
The rk-reel загортає контролер слайдера з ядра. Автономний, із ChangeDetectionStrategy.OnPush.
import { Component } from '@angular/core';
import { ReelComponent, ReelIndicatorComponent, RkReelItemDirective } from '@reelkit/angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReelComponent, ReelIndicatorComponent, RkReelItemDirective],
template: `
<rk-reel
[count]="items.length"
style="width: 100%; height: 100dvh"
direction="vertical"
[enableWheel]="true"
(afterChange)="onAfterChange($event)"
>
<ng-template rkReelItem let-i let-size="size">
<div [style.width.px]="size[0]" [style.height.px]="size[1]"
[style.background]="items[i].color"
style="display:flex;flex-direction:column;align-items:center;justify-content:center;color:#fff">
<div style="font-size:1.5rem;font-weight:bold">{{ items[i].title }}</div>
<div style="font-size:0.875rem;opacity:0.8">{{ items[i].subtitle }}</div>
</div>
</ng-template>
<div style="position:absolute;right:12px;top:50%;transform:translateY(-50%);z-index:10">
<rk-reel-indicator direction="vertical" />
</div>
</rk-reel>
`,
})
export class AppComponent {
items = [
{ title: 'Virtualized', subtitle: 'Only 3 slides in DOM', color: '#6366f1' },
{ title: 'Touch First', subtitle: 'Native swipe gestures', color: '#8b5cf6' },
{ title: 'Zero Deps', subtitle: 'Tiny bundle size', color: '#7c3aed' },
{ title: 'Keyboard Nav', subtitle: 'Full a11y support', color: '#ec4899' },
{ title: 'SSR Ready', subtitle: 'Works everywhere', color: '#14b8a6' },
{ title: '60fps', subtitle: 'Smooth animations', color: '#f59e0b' },
];
onAfterChange(event: { index: number; indexInRange: number }) {
console.log('Current index:', event.index);
}
}Auto-sizing
The size необов’язковий. Якщо його не передати, rk-reel сам вимірює свій контейнер через ResizeObserver і підлаштовується під макет, задани й CSS. Розмір контейнера має задавати батьківський елемент — наприклад, flex, grid або явні розміри в CSS.
<!-- Explicit size (fixed) -->
<rk-reel [count]="items.length" [size]="[400, 600]">
<ng-template rkReelItem let-i let-size="size"> ... </ng-template>
</rk-reel>
<!-- Auto-size (responsive — sized by CSS) -->
<rk-reel [count]="items.length" style="width: 100%; height: 100dvh">
<ng-template rkReelItem let-i let-size="size"> ... </ng-template>
</rk-reel>Шаблонний патерн rkReelItem
Замість render prop із React в Angular використовується структурна директива rkReelItem на ng-template. Саме це дає віртуалізацію: створюються лише видимі слайди. Контекст шаблону надає три змінні:
<ng-template rkReelItem let-i let-indexInRange="indexInRange" let-size="size">
<!--
$implicit (let-i) : number — absolute slide index (0 to count-1)
indexInRange : number — position in visible window (0, 1, or 2)
size : [number, number] — [width, height] of the container
-->
<app-slide [data]="items[i]"
[style.width.px]="size[0]"
[style.height.px]="size[1]" />
</ng-template>Навігація
Вбудовані способи навігації:
- Дотик / свайп: Тягніть, щоб гортати — з інерцією та прилипанням
- Клавіатура: Стрілки та Escape
- Колесо миші: Увімкніть
[enableWheel]="true" - Програмно: Скористайтеся пропсом
(apiReady)щоб отриматиnext(),prev(),goTo()
import { Component } from '@angular/core';
import { ReelComponent, RkReelItemDirective, type ReelApi } from '@reelkit/angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReelComponent, RkReelItemDirective],
template: `
<rk-reel
[count]="10"
[size]="[400, 600]"
(apiReady)="api = $event"
>
<ng-template rkReelItem let-i let-size="size">
<app-slide [index]="i" [size]="size" />
</ng-template>
</rk-reel>
<button (click)="api?.prev()">Prev</button>
<button (click)="api?.next()">Next</button>
<button (click)="api?.goTo(5)">Go to 5</button>
`,
})
export class AppComponent {
api: ReelApi | undefined;
}Стан в URL
createOverlayUrlState будує контролер стану в URL для оверлея й повертає його цілком, а ви передаєте його в *UrlOverlay компонента як його вхід [controller] . Викликайте його в контексті впровадження — в ініціалізаторі поля; він під’єднується одразу й звільняється через DestroyRef. Стан відкриття належить URL, тож прив’язаний оверлей відкривається сам, а посилання — звичайний спосіб його відкрити. Перший запис відсутнього параметра додає один запис в історію, кожен наступний його замінює, тож гортання ніколи не ховає кнопку «назад». Тримайте контролер, щоб читати value/позицією і керувати ним програмно: set(position) відкриває, set(null) закриває, а set — той самий низькорівневий запис, який оверлей робить усередині під час зміни слайда.
import { Component, signal } from '@angular/core';
import { RouterLink } from '@angular/router';
import { RkLightboxUrlOverlayComponent } from '@reelkit/angular-lightbox';
import { createOverlayUrlState, urlIndexKey } from '@reelkit/angular';
@Component({
imports: [RkLightboxUrlOverlayComponent, RouterLink],
template: `
@for (image of images(); track image.src; let i = $index) {
<a [routerLink]="[]" [queryParams]="{ photo: i }">
<img [src]="image.src" alt="" />
</a>
}
<rk-lightbox-url-overlay [controller]="photo" [items]="images()" />
`,
})
export class GalleryComponent {
protected readonly images = signal(photos);
// Attaches now, releases on destroy.
protected readonly photo = createOverlayUrlState({
param: 'photo',
...urlIndexKey(() => this.images().length),
});
}Застосунок із роутером — передайте адаптер на базі Router, інакше власне місцеположення роутера застаріє і наступна навігація втратить параметр:
import { createRouterUrlAdapter } from '@reelkit/angular/ng-router-url-adapter';
protected readonly photo = createOverlayUrlState({
param: 'photo',
adapter: createRouterUrlAdapter(),
...urlIndexKey(() => this.images().length),
});Об’єкт опцій приймає param, codec, та locator (усі три обов’язкові) плюс необов’язковий adapter. codec та locator — узгоджена пара з однаковим Id, so a plain ?photo=3 галерея розгортає ...urlIndexKey(() => images().length), який повертає обидві половин и одразу. urlIndexKey обмежує індекс живою кількістю, яку повертає геттер, тож застарілий ?photo=99 відхиляється й сам зникає з URL замість того, щоб відкрити слайд, якого ніхто не називав. Посторінкова стрічка або галерея з адресацією за ідентичністю передає власну узгоджену пару codec + locator . Повна таблиця опцій — у довіднику API для Angular.
ReelIndicator
Необов’язковий компонент, що показує індикатори прогресу в стилі Instagram із поточною позицією в слайдері. Якщо він усередині rk-reel, він сам під’єднується до count та active значення через токен впровад ження RK_REEL_CONTEXT — нічого зв’язувати вручну не треба.
<!-- Auto-connect: count and active are inherited from parent rk-reel -->
<rk-reel [count]="10" [size]="[400, 600]">
<ng-template rkReelItem let-i let-size="size"> ... </ng-template>
<rk-reel-indicator direction="vertical" />
</rk-reel>
<!-- Manual usage: pass count and active explicitly (e.g. outside a rk-reel) -->
<rk-reel-indicator [count]="10" [active]="currentIndex" />Вихід apiReady — патерн на сигналах
The (apiReady) спрацьовує один раз після того, як компонент змонтовано й виміряно. Він видає об’єкт ReelApi який можна зберегти й використовувати для імперативної навігації. Тримати це посилання в сигналах Angular добре поєднується з виявленням змін OnPush .
import { Component, signal } from '@angular/core';
import { ReelComponent, RkReelItemDirective, type ReelApi } from '@reelkit/angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReelComponent, RkReelItemDirective],
template: `
<rk-reel
[count]="items.length"
style="width: 100%; height: 100dvh"
direction="vertical"
[enableWheel]="true"
(apiReady)="reelApi.set($event)"
(afterChange)="currentIndex.set($event.index)"
>
<ng-template rkReelItem let-i let-size="size">
<div [style.width.px]="size[0]" [style.height.px]="size[1]">
{{ items[i].title }}
</div>
</ng-template>
</rk-reel>
<div style="position:absolute;bottom:16px;left:50%;transform:translateX(-50%)">
<button (click)="reelApi()?.prev()"
[disabled]="currentIndex() === 0">Prev</button>
<button (click)="reelApi()?.next()"
[disabled]="currentIndex() === items.length - 1">Next</button>
</div>
`,
})
export class AppComponent {
readonly items = [
{ title: 'Slide 1', color: '#6366f1' },
{ title: 'Slide 2', color: '#8b5cf6' },
{ title: 'Slide 3', color: '#ec4899' },
];
readonly reelApi = signal<ReelApi | undefined>(undefined);
readonly currentIndex = signal(0);
}Головне
- Автономний компонент
Import
ReelComponent,RkReelItemDirective, and optionallyReelIndicatorComponentпросто вimportsarray - ng-template і rkReelItem
Відповідник в Angular для пропса
itemBuilderіз React — дає віртуалізацію - apiReady
Вихід, що спрацьовує один раз із імперативним API навігації — жодних запитів через ViewChild
- afterChange
Emits
{ index, indexInRange }— стежить за поточним індексом для оновлення інтерфейсу - OnPush за замовчуванням
Усі компоненти використовують
ChangeDetectionStrategy.OnPushі сигнали Angular заради максимальної продуктивності
Поради щодо продуктивності
- Тримайте шаблони слайдів легкими
The
rkReelItemвиконується для кожного видимого слайда (зазвичай трьох одночасно). Уникайте важких обчислень і глибоко вкладених структур усередині. - Вантажте дані ближче до краю
Використовуйте
(afterChange)щоб помітити наближення до кінця й підвантажити наступний пакет, доки слайди не скінчилися — так виходить нескінченна стрічка. - Тримайте імперативний стан у сигналах
Зберігайте посилання
ReelApiта поточний індекс у сигналах Angular — це дає точкову реактивність без повного перерендеру компонента. - Вимикайте колесо на сторінках із прокручуванням
Set
[enableWheel]="false"коли слайдер вбудований у макет із прокручуванням, щоб не перехоплювати прокручування сторінки.
Наступні кроки
- Довідник API для Angular - усі доступні входи, виходи та методи
- Reel Player - відеоплеєр у стилі TikTok / Reels
- Lightbox - галерея зображень і відео
- Stories Player - переглядач історій у стилі Instagram