Reel Player для Angular

Повноекранний вертикальний медіаплеєр у стилі Instagram чи TikTok для Angular на основі @reelkit/angular-reel-player.

Подивитися демо наживо →

Features

Вертикальний свайп
Дотик, перетягування, клавіатура, колесо
Автовідтворення відео
Грає, коли слайд видимий
Перемикач звуку
Безперервність на iOS
Multi-Media
Вкладені горизонтальні каруселі
Пам’ять позиції
Продовжує з місця, де зупинилися
Знімок кадру
Плавний перехід від постера до відео
Віртуалізований
Лише 3 слайди в DOM
Співвідношення сторін
9:16 на десктопі, на весь екран на мобільних
Навігація на десктопі
Кнопки-стрілки
Узагальнені типи
Власні моделі даних вмісту
Customizable
Шаблонні слоти для всього
Обробка помилок
Виявлення зіпсованого медіа з LRU-кешем
Стан в URL
Посилання, якими можна ділитися, і закриття кнопкою «назад»

Встановлення

bash
Icons
Стандартні елементи керування використовують lucide-angular для іконок (закриття, звук, стрілки навігації). Якщо ви віддаєте перевагу іншій бібліотеці іконок, скористайтеся слотами rkPlayerControls та rkPlayerNavigation щоб передати власні.

Базове використання

Імпортуйте таблицю стилів і автономний RkReelPlayerOverlayComponent у imports array.

reel-feed.component.ts

Шаблонні слоти

Шість директив шаблонних слотів дають налаштувати будь-яку частину інтерфейсу плеєра. Кожна отримує строго типізований об’єкт контексту. Задавайте лише ті слоти, які хочете перекрити — для решти лишиться стандартний вигляд.

DirectiveТип контекстуОпис
[rkPlayerControls]PlayerControlsContext<T>Власна загальна смуга керування (закриття, перемикач звуку тощо)
[rkPlayerError]{ $implicit: activeIndex, item, innerActiveIndex }Шаблонний слот власного індикатора помилки
[rkPlayerLoading]{ $implicit: activeIndex, item, innerActiveIndex }Шаблонний слот власного індикатора завантаження
[rkPlayerNavigation]PlayerNavigationContextВласні стрілки навігації вперед і назад
[rkPlayerNestedNavigation]PlayerNestedNavigationContextВласні стрілки навігації для внутрішнього горизонтального слайдера
[rkPlayerNestedSlide]PlayerNestedSlideContextВласний вміст кожного слайда всередині внутрішнього горизонтального слайдера
[rkPlayerSlide]PlayerSlideContext<T>Повністю власний вміст слайда, що замінює стандартний медіаслайд
[rkPlayerSlideOverlay]PlayerSlideOverlayContext<T>Оверлей для кожного слайда (дані автора, лайки, опис тощо)
[rkPlayerTimeline]PlayerTimelineContext<T>Власна смуга таймлайну відтворення. Показується лише тоді, коли за правилом (режим timeline плюс мінімальна тривалість) вивелася б стандартна смуга — логіка auto/always/never та сама.
typescript

Власний таймлайн

The rkPlayerTimeline викликається лише тоді, коли за правилами показу оверлей вивів би стандартну смугу (та сама логіка timeline плюс timelineMinDurationSeconds), so you don't re-implement it. Reuse the .rk-reel-timeline на своєму корені, щоб успадкувати притиснення до низу, відступи безпечної зони та проміжок на дотикових пристроях. Викличте state.bindInteractions(el) на своїй доріжці перемотування, щоб підключити перемотування вказівником і клавіатурою.

Вкладений слайдер (мультимедійні елементи)

Коли ContentItem містить кілька записів media , плеєр показує їх у вкладеному горизонтальному слайдері (як карусель Instagram). Через слот rkPlayerNestedSlide можна налаштувати вміст внутрішнього слайда.

typescript

Завантаження вмісту та обробка помилок

Плеєр стежить за станом завантаження й помилок кожного слайда. Поки вміст вантажиться, показується хвильовий індикатор; для зіпсованого медіа — значок помилки. URL з помилками кешуються, тож повторний перехід одразу показує помилку без нової спроби.

Колбеки життєвого циклу

Коли використовуєте слот rkPlayerSlide , керуйте індикатором завантаження через колбеки контексту:

КолбекКоли викликати
onReadyЗображення завантажилося або відео почало грати. Скидає стани завантаження й помилки.
onWaitingВідео буферизується посеред відтворення. Показує індикатор завантаження.
onErrorВміст не завантажився. Показує оверлей помилки й кешує URL як зіпсований.
html

Власний інтерфейс завантаження та помилок

Замініть стандартний хвильовий індикатор і значок помилки власними шаблонами:

html

Таймлайн

Оверлей показує вбудовану смугу таймлайну відтворення над активним відео. Керуйте показом через пропс timeline : 'auto' (типово) показує її, коли активне медіа — відео, довше за timelineMinDurationSeconds (default 30), 'always' щойно активне відео, 'never' вимикає її. Для повністю власної смуги перемотування беріть слот rkPlayerTimeline ; його контекст відкриває timelineState на основі TimelineController.

html

Тему задавайте через власні властивості CSS --rk-reel-timeline-* власними властивостями CSS. Для прямого керування у власних компонентах-споживачах впроваджуйте TimelineStateService.

RkTimelineBarComponent

Стандартний компонент смуги перемотування. Використовує TimelineStateService (його надає RkReelPlayerOverlayComponent) and renders the track, buffered ranges, progress fill, and scrub pill. Selector: rk-timeline-bar. Входи: class?: string, style?: Record<string, string>. Використовуйте всередині шаблону rkPlayerTimeline щоб загорнути або доповнити стандартну смугу; окремо — лише всередині споживача, який надає цей сервіс.

typescript

SoundStateService

Надається на рівні RkReelPlayerOverlayComponent . Впроваджується стандартною кнопкою звуку й відкривається в контексті шаблонного слота елементів керування. Можна впровадити у власні елементи керування, що є children оверлея, для прямого доступу.

typescript
ЧленТипОпис
muted()Signal<boolean>Чи вимкнено звук просто зараз
disabled()Signal<boolean>True, коли на активному слайді немає відео або триває перехід
toggle()() => voidПеремикає стан звуку

Стан в URL

RkReelPlayerUrlOverlayComponent — окремий компонент, стан відкриття якого живе в адресному рядку. Побудуйте контролер через createOverlayUrlState у контексті впровадження й передайте його як [controller]: плеєр відкривається, коли параметр називає слайд, і закривається, коли параметр зникає. Посиланнями можна ділитися, а кнопка «назад» закриває плеєр. RkReelPlayerOverlayComponent stays [isOpen], тож кожен компонент має рівно один драйвер стану відкриття.

Вбудовані клавіші
Слайди можна адресувати вбудованим ключем — розгорніть urlIndexKey (за позицією) або urlStableIdKey (за стабільним id) into the controller — both re-exported from @reelkit/angular. See the посібник зі стану в URL та API ядра.

Застосунок із роутером передає адаптер на базі Router, щоб той лишався єдиним джерелом правди про навігацію: запис в історію повз нього лишає його місцеположення застарілим і втрачає параметр на наступній навігації. createRouterUrlAdapter from @reelkit/angular/ng-router-url-adapter — готовий адаптер.

typescript
  • Відкриття додає один запис в історію. Свайп стрічки замінює його, тож N свайпів не додають записів, і один крок назад завжди виходить із плеєра. «Назад» закриває, а не гортає слайди.
  • «Назад» закриває лише тоді, коли плеєр відкрили всередині застосунку — посилання додало запис. За надісланим посиланням у новій вкладці історії позаду немає, тож кнопка «назад» виведе із сайту; кнопка ✕ або Escape прибирає параметр на місці й лишає вас на сторінці.
  • Пряме посилання ?reel=3 відкриває плеєр одразу на цьому слайді.
  • Параметр, який не називає жодного слайда — застаріла закладка, змінене вручну значення, — прибирається з URL, а не лишає в адресному рядку слайд, який не відкриється.
  • Глибина URL залежить від ключа контролера: одна вісь для самого допису або дві (urlIndexTwoAxisKey) to also carry a multi-media post’s inner image index. Pick one key per app; the shapes do not cross-decode.

Full createOverlayUrlState опції — у довіднику API для Angular.

Один ключ чи два — оберіть глибину URL

Той самий RkReelPlayerUrlOverlayComponent працює з обома формами; він розрізняє їх під час виконання за position контролера, тож жодного входу режиму немає. Ключ обирайте, коли будуєте контролер:

KeyWireCarries
urlIndexKey(…)?reel=3Лише вертикальний допис.
urlIndexTwoAxisKey(…)?reel=3.2Допис та та індекс внутрішнього медіа каруселі.

Дві форми запису навмисно різні — двовісний ключ строго з крапкою (3.0, never a bare 3), so a bare one-axis link does not cross-decode. Switching an app between keys therefore invalidates any previously shared links. Pick one shape and keep it.

typescript

Стабільні посилання. Індекс адресує за позицією, тож збережений у закладках ?reel=3 відкриє інший допис, щойно стрічку перевпорядкують — а для стрічки це радше правило. urlStableIdKey адресує за стабільним id, scanning the live feed — one call covers the common case.

typescript

Pass hashCodec: base64UrlCodec щоб закодувати id в URL у base64url — оборотне маскування, а не криптографічний хеш.

Адресуєте за іншим полем ( slug), or page an infinite feed with locateAsync, and build the codec/locator самі. Дві окремі задачі: codec записує ідентичність в URL, locator знаходить, де ця ідентичність лежить.

typescript

Нескінченні стрічки. locate синхронний, тож відповідає лише за вже завантажені дописи — надіслане посилання на допис 400 у стрічці, де завантажено 20, нічого не знайде. locateAsync — запасний варіант, що викликається лише коли locate не знаходить: завантажте потрібні сторінки й поверніть індекс, який ця ідентичність отримала.

Shortcut
Keying by the item’s id? Не пишіть кодек і локатор вручну — передайте locateAsync просто в urlStableIdKey({ items, locateAsync }) (він вантажить дані, якщо не знайшов, і повертає індекс). Розгорнутий варіант нижче — для адресації за іншим полем або для повного контролю.
typescript
  • While locateAsync у процесі, плеєр лишається закритим, а параметр — недоторканим, тож пряме посилання переживає запит. null або відмова прибирає параметр.
  • Відповідь, що приходить після зміни URL, після закриття або після демонтажу, відкидається — повільний запит не відкриє слайд, якого ніхто не просив.
  • Поки триває очікування, нічого не рендериться: цей стан завантаження вже належить сторінці, тож малюйте власний скелетон.
  • Тайм-ауту немає — плеєр не може знати, яка стрічка завдовжки. Завершуйте значенням null коли сторінки скінчилися, інакше оверлей лишиться закритим назавжди.

Власні типи даних

Extend BaseContentItem щоб узяти власну доменну модель. Компонент узагальнений: RkReelPlayerOverlayComponent<T extends BaseContentItem>.

typescript

Входи RkReelPlayerOverlayComponent

InputТипТипове значенняОпис
ariaLabelstring'Video player'Доступна назва області діалогу
aspectRationumber | undefinedundefinedСпіввідношення ширини до висоти контейнера на десктопі. Типово 9/16. На мобільних плеєр займає всю область перегляду.
contentT[] (extends BaseContentItem)requiredМасив елементів вмісту для показу в плеєрі
enableNavKeysbooleantrueВмикає навігацію стрілками клавіатури
enableWheelbooleantrueВмикає навігацію колесом миші
initialIndexnumber0Індекс початково видимого елемента, від нуля
initialInnerIndexnumber0Індекс внутрішнього медіа, з якого відкриватися, лише для початково видимого допису — дає змогу двовісному URL вести просто до потрібного зображення мультимедійного допису. Ігнорується, щойно користувач починає гортати.
isOpenbooleanrequiredКерує видимістю оверлея; якщо false, оверлей прибирається з DOM
loopbooleanfalseВмикає нескінченний цикл між слайдами
swipeDistanceFactornumber0.12Мінімальна частка відстані свайпу, щоб змінити слайд
timeline'auto' | 'always' | 'never''auto'Правило показу вбудованої смуги таймлайну. 'auto' показує її лише для відео, довших за timelineMinDurationSeconds; 'always' — щойно на активному слайді є відео; 'never' вимикає вбудовану смугу (для повної заміни беріть шаблонний слот rkPlayerTimeline).
timelineMinDurationSecondsnumber30Мінімальна тривалість відео (у секундах), за якої timeline='auto' показує вбудовану смугу. Короткі зациклені кліпи нижче цього порогу не показують її.
transitionDurationnumber300Тривалість анімації слайда в мілісекундах
wheelDebounceMsnumber200Тривалість дебаунсу подій колеса в мілісекундах

Виходи RkReelPlayerOverlayComponent

OutputТипОпис
apiReadyEventEmitter<ReelApi>Видається, коли слайдер готовий, і відкриває імперативний API
closedEventEmitter<void>Видається, коли плеєр закрито
slideChangeEventEmitter<number>Видається, коли змінюється індекс активного слайда
innerSlideChangeEventEmitter<{ outer: number; inner: number }>Видається, коли змінюється індекс внутрішнього медіа активного допису — під час внутрішньої навігації та під час активації зовнішнього, повідомляючи поточний внутрішній індекс активованого допису (0 для допису з одним медіа).

Входи RkReelPlayerUrlOverlayComponent

Приймає всі входи вище, крім isOpen та initialIndex, replaced by a controller position якого обирає слайд. Виходи closed та slideChange.

InputТипТипове значенняОпис
controllerUrlStateControllerrequiredКонтролер із createOverlayUrlState. Its позицією вирішує, чи плеєр відкритий і який слайд показує; оверлей записує через нього назад на зміну слайда та на закриття.

Інтерфейс MediaItem

FieldТипОпис
idstringУнікальний ідентифікатор медіаелемента
type'image' | 'video'Тип медіа
srcstringURL медіафайлу
posterstring?URL мініатюри-постера для елементів-відео
aspectRationumberспіввідношення ширини до висоти. Значення < 1 означають вертикальне (cover), > 1 — горизонтальне (contain)

Типи контексту шаблонних слотів

ТипFields
PlayerControlsContext<T>{ $implicit: onClose, activeIndex, content: T[], soundState: PlayerSoundState }
PlayerNavigationContext{ $implicit: onPrev, onNext, activeIndex, count }
PlayerNestedNavigationContext{ $implicit: onPrev, onNext, activeIndex, count }
PlayerNestedSlideContext{ $implicit: MediaItem, index, size, isActive, isInnerActive, slideKey }
PlayerSlideContext<T>{ $implicit: T, index, size: [number,number], isActive, slideKey, onReady, onWaiting, onError }
PlayerSlideOverlayContext<T>{ $implicit: T, index, isActive }
PlayerTimelineContext<T>{ $implicit: T, activeIndex, timelineState: PlayerTimelineState }
PlayerTimelineState{ duration(), currentTime(), progress(), bufferedRanges(), isScrubbing(), seek(t), bindInteractions(el) }

Класи CSS

Усі класи CSS звичайні (не scoped), тож їх можна перекрити селекторами вищої специфічності в таблиці стилів, підключеній після @reelkit/angular-reel-player/styles.css. Для змін кольору, розміру та z-index краще беріть власні властивості CSS, описані в розділі Theming нижче — вони саме для цього й зроблені.

ClassComponentОпис
.rk-reel-overlayOverlayФіксоване повноекранне тло (фон, z-index)
.rk-reel-containerOverlayКонтейнер плеєра (позиція, переповнення)
.rk-reel-loaderOverlayОверлей хвильової анімації завантаження
.rk-reel-media-errorOverlayОверлей стану помилки (значок і текст по центру)
.rk-reel-media-error-textOverlayТекст повідомлення про помилку
.rk-reel-buttonControlsСпільна кругла кнопка з іконкою (закриття, звук, стрілки)
.rk-reel-close-btnControlsКнопка закриття
.rk-reel-sound-btnControlsКнопка перемикання звуку
.rk-reel-nav-arrowsНавігаціяКонтейнер стрілок лише для десктопа (прихований до 768px)
.rk-reel-nav-btnНавігаціяОкрема стрілка вперед або назад
.rk-reel-slide-wrapperSlideОбгортка навколо медіа та оверлея
.rk-reel-slide-overlaySlideOverlayКонтейнер градієнтного оверлея
.rk-reel-slide-overlay-authorSlideOverlayРядок автора (аватар та ім’я)
.rk-reel-slide-overlay-avatarSlideOverlayЗображення аватара автора
.rk-reel-slide-overlay-nameSlideOverlayТекст імені автора
.rk-reel-slide-overlay-descriptionSlideOverlayТекст опису
.rk-reel-slide-overlay-likesSlideOverlayРядок лайків (сердечко й лічильник)
.rk-reel-video-containerVideoSlideОбгортка відео (фон, переповнення)
.rk-reel-video-elementVideoSlideThe <video> element
.rk-reel-video-posterVideoSlideПостер (зникає з початком відтворення)
.rk-reel-video-loaderVideoSlideХвильова анімація завантаження
.rk-reel-video-poster.rk-visibleVideoSlideМодифікатор стану постера, поки відео на паузі або вантажиться
.rk-reel-nested-indicatorNestedSliderПагінація точками під мультимедійними слайдами (позиція різна на десктопі й на дотикових пристроях)
.rk-reel-nested-navNestedSliderСтрілки горизонтальної каруселі (приховані до 768px)
.rk-reel-nested-nav-nextNestedSliderПозиція вкладеної стрілки вперед
.rk-reel-nested-nav-prevNestedSliderПозиція вкладеної стрілки назад
.rk-reel-nested-slider-innerNestedSliderКорінь вкладеного горизонтального слайдера
.rk-reel-timelineTimelineBarScrub-bar wrapper. Reuse on custom `rkPlayerTimeline` template roots to inherit flush-bottom positioning, safe-area padding, and touch-device slide-overlay clearance.
.rk-reel-timeline-trackTimelineBarДоріжка (невідтворена частина)
.rk-reel-timeline-bufferedTimelineBarШар буферизованих сегментів
.rk-reel-timeline-fillTimelineBarЗаповнення відтвореного прогресу
.rk-reel-timeline-cursorTimelineBarПовзунок перемотування (плаває над доріжкою)

Theming

Кожен колір, розмір, z-index і перехід живе у власній властивості CSS. Перевизначайте одну чи кілька на :root (або на будь-якому предку оверлея), щоб змінити тему, не чіпаючи код компонентів. Токени збігаються з пакетами для React і Vue, тож перевизначення переносяться між прив’язками.

TokenТипове значенняControls
--rk-reel-overlay-bg#000Full-screen backdrop color
--rk-reel-overlay-z1000Overlay z-index
--rk-reel-button-bgrgba(0, 0, 0, 0.5)Default circular button background
--rk-reel-button-bg-hoverrgba(255, 255, 255, 0.1)Nav arrow background (and base hover state)
--rk-reel-button-bg-hover-strongrgba(255, 255, 255, 0.2)Nav arrow hover background
--rk-reel-button-fg#fffButton icon color
--rk-reel-button-size44pxButton width / height
--rk-reel-button-radius50%Button border-radius
--rk-reel-ui-z10Close / sound / nav z-index
--rk-reel-edge-padding16pxEdge inset for close / sound / nav arrows
--rk-reel-nav-gap8pxSpacing between stacked nav arrows
--rk-reel-transition0.2sHover transition duration
--rk-reel-loader-colorrgba(255, 255, 255, 0.12)Wave loader gradient color
--rk-reel-loader-duration1.8sWave loader animation duration
--rk-reel-error-fgrgba(255, 255, 255, 0.4)Error icon and text color
--rk-reel-slide-overlay-bglinear-gradient(transparent, rgba(0, 0, 0, 0.7))Caption scrim gradient
--rk-reel-slide-overlay-padding48px 16px 16pxCaption inner padding
--rk-reel-slide-overlay-name-color#fffAuthor name color
--rk-reel-video-bg#000Letterbox background behind <video>
--rk-reel-video-loader-colorrgba(255, 255, 255, 0.15)Video buffering shimmer color
--rk-reel-nested-button-bgrgba(0, 0, 0, 0.5)Nested arrow background
--rk-reel-nested-button-size36pxNested arrow size
--rk-reel-nested-edge-padding12pxNested arrow edge inset
--rk-reel-timeline-trackrgba(255, 255, 255, 0.22)Track background (unplayed region)
--rk-reel-timeline-bufferedrgba(255, 255, 255, 0.4)Buffered segments color
--rk-reel-timeline-fill#fffPlayed-progress fill color
--rk-reel-timeline-cursor#fffScrub-handle pill color
--rk-reel-timeline-height3pxTrack height at rest
--rk-reel-timeline-height-active6pxTrack height on hover / focus / scrub
--rk-reel-timeline-cursor-width10pxScrub-pill width at rest
--rk-reel-timeline-cursor-width-active14pxScrub-pill width while scrubbing
--rk-reel-timeline-cursor-height24pxScrub-pill height at rest
--rk-reel-timeline-cursor-height-active32pxScrub-pill height while scrubbing
--rk-reel-timeline-transition0.15s ease-outTrack + pill grow/shrink animation

Вставте фрагмент нижче в таблицю стилів, підключену після @reelkit/angular-reel-player/styles.css.

css

Accessibility

Корінь оверлея — модальний діалог (role="dialog", aria-modal="true"). Set the ariaLabel щоб змінити оголошення для екранного читача; типове значення — «Video player». Кожен слайд несе role="group", aria-roledescription="slide", та aria-label="Слайд N з M".

Оверлей захоплює фокус під час відкриття й повертає його на елемент-тригер після закриття. Tab і Shift+Tab циклічно проходять фокусовані елементи всередині; фокус, що вислизнув (клік поза оверлеєм, програмна установка), повертається назад. Реалізовано через captureFocusForReturn та createFocusTrap from @reelkit/core.

Клавіатурні скорочення

KeyAction
ArrowUpPrevious slide
ArrowDownNext slide
ArrowLeftPrevious media (in nested slider)
ArrowRightNext media (in nested slider)
EscapeClose player