Reel Player
Повноекранний компонент відеоплеєра у стилі Instagram Reels чи TikTok на основі @reelkit/react-reel-player.
Features
Встановлення
Не забудьте імпортувати стилі:
Icons
lucide-react для іконок. Якщо ви віддаєте перевагу іншій бібліотеці іконок, скористайтеся renderControls та renderNavigation , щоб передати власні.Швидкий старт
The ReelPlayerOverlay рендерить повноекранний оверлей плеєра. Передайте масив ContentItem і керуйте видимістю через isOpen.
Демо наживо
Натисніть мініатюру, щоб відкрити повноекранний плеєр. Escape або кнопка закриття повертають назад.
Customization
Узагальнений тип вмісту
Використовуйте власні типи даних, розширивши BaseContentItem:
Власний оверлей слайда
Замініть вбудований оверлей слайда власним вмістом для кожного слайда:
Слайди без медіа
Використовуйте renderSlide щоб вставити власний вміст (наприклад, картки із закликом до дії). Поверніть null щоб лишити стандартний:
Власні елементи керування
Складайте готові підкомпоненти разом із власними доповненнями:
Власний таймлайн
Замініть вбудовану смугу відтворення власним інтерфейсом перемотування через renderTimeline. Колбек спрацьовує лише тоді, коли за правилами показу оверлей вивів би стандартну смугу (та сама логіка timeline плюс timelineMinDurationSeconds ), тож переписувати її не доведеться. Використайте клас .rk-reel-timeline на своєму корені, щоб успадкувати притиснення до низу, відступи безпечної зони та проміжок для оверлея слайда на дотикових пристроях.
Власна навігація
Власна вкладена навігація
Замініть стрілки ліворуч і праворуч усередині мультимедійних слайдів (горизонтальна карусель) власною навігацією:
Власні вкладені слайди
Налаштуйте окремі слайди всередині мультимедійних каруселей через renderNestedSlide. Use props.defaultContent щоб загорнути стандартний ImageSlide чи VideoSlide або замінити його повністю:
Стан в URL
ReelPlayerUrlOverlay — окремий компонент, стан відкриття якого живе в адресному рядку. Побудуйте контролер через useOverlayUrlState from @reelkit/react і передайте його як controller: плеєр відкривається сам, коли параметр називає слайд, і закривається, коли параметр зникає. Посиланнями можна ділитися, а кнопка «назад» закриває плеєр, а не виводить зі сторінки.
Вбудовані клавіші
urlIndexKey (за позицією) або urlStableIdKey (за стабільним id) into the controller — both re-exported from @reelkit/react. See the посібник зі стану в URL та API ядра.Full useOverlayUrlState опції — param, adapter, codec, locator — у довіднику API для React. Покроковий розбір — у посібнику для React.
- Відкриття додає один запис в історію. Свайп стрічки замінює його, тож N свайпів не додають записів, і один крок назад завжди виходить із плеєра. «Назад» закриває, а не гортає слайди.
- «Назад» закриває лише тоді, коли плеєр відкрили всередині застосунку — посилання додало запис. За надісланим посиланням у новій вкладці історії позаду немає, тож кнопка «назад» виведе із сайту; кнопка ✕ або Escape прибирає параметр на місці й лишає вас на сторінці.
- Пряме посилання
?reel=3відкриває плеєр одразу на цьому слайді. - Параметр, який не називає жодного слайда — застаріла закладка, змінене вручну значення, — прибирається з URL, а не лишає в адресному рядку слайд, який не відкриється.
- Типово параметр адресує лише vertical допис (
?reel=3). Opt into a two-axis key to also carry the inner media index of a multi-media carousel — see below.
Один ключ чи два — оберіть глибину URL
Той самий ReelPlayerUrlOverlay працює з обома формами; він розрізняє їх під час виконання за position контролера, тож жодного пропса режиму немає. Ключ обирайте, коли будуєте контролер:
| Key | Wire | Carries |
|---|---|---|
| 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.
Застосунок із роутером — передайте адаптер. Writing history.pushState повз роутер лишає його місцеположення застарілим, і наступна навігація втрачає параметр:
Стабільні посилання. Індекс адресує за позицією, тож збережений у закладках ?reel=3 відкриє інший допис, щойно стрічку перевпорядкують — а для стрічки це радше правило, ніж виняток. urlStableIdKey адресує за стабільним id, scanning the live feed — one call covers the common case.
Pass hashCodec: base64UrlCodec щоб закодувати id в URL у base64url — оборотне маскування, а не криптографічний хеш.
Адресуєте за іншим полем ( slug), or page an infinite feed with locateAsync, and build the codec/locator самі. Дві окремі задачі: codec записує ідентичність в URL, locator знаходить, де ця ідентичність лежить.
Нескінченні стрічки. locate синхронний, тож відповідає лише за вже завантажені дописи — надіслане посилання на допис 400 у стрічці, де завантажено 20, нічого не знайде. locateAsync — запасний варіант, що викликається лише коли locate misses.
Shortcut
id? Не пишіть кодек і локатор вручну — передайте locateAsync просто в urlStableIdKey({ items, locateAsync }) (він вантажить дані, якщо не знайшов, і повертає індекс). Розгорнутий варіант нижче — для адресації за іншим полем або для повного контролю.- While
locateAsyncу процесі, плеєр лишається закритим, а параметр — недоторканим, тож пряме посилання переживає запит.nullабо відмова прибирає параметр. - Відповідь, що приходить після зміни URL, після закриття або після демонтажу, відкидається — повільний запит не відкриє слайд, якого ніхто не просив.
- Поки триває очікування, нічого не рендериться: цей стан завантаження вже належить сторінці, тож малюйте власний скелетон.
- Тайм-ауту немає — плеєр не може знати, яка стрічка завдовжки. Завершуйте значенням
nullколи сторінки скінчилися, інакше оверлей лишиться закритим назавжди.
Довідник API
Пропси ReelPlayerOverlayProps
ReelPlayerOverlayProps<T>
| Prop | Тип | Типове значення | Опис |
|---|---|---|---|
| apiRef | MutableRefObject<ReelApi> | - | Ref для доступу до API Reel |
| ariaLabel | string | 'Video player' | Доступна назва області діалогу; екранні читачі оголошують її, коли оверлей відкривається |
| aspectRatio | number | 9/16 (0.5625) | Співвідношення ширини до висоти контейнера плеєра на десктопі. На мобільних плеєр завжди займає всю область перегляду. |
| content | T[] | required | Масив елементів вмісту (узагальнений тип, типово ContentItem) |
| initialIndex | number | 0 | Початковий індекс слайда |
| initialInnerIndex | number | 0 | Індекс внутрішнього медіа, з якого відкриватися, лише для початково видимого допису — дає змогу двовісному URL вести просто до потрібного зображення мультимедійного допису. Ігнорується, щойно плеєр відкрито й користувач починає гортати. |
| isOpen | boolean | required | Керує видимістю оверлея. Для стану відкриття, керованого URL, беріть окремий ReelPlayerUrlOverlay — дивіться розділ про стан в URL нижче. |
| timeline | 'auto' | 'always' | 'never' | 'auto' | Правило показу вбудованої смуги таймлайну. 'auto' показує її лише для відео, довших за timelineMinDurationSeconds; 'always' — щойно на активному слайді є відео; 'never' вимикає вбудовану смугу (для повної заміни беріть renderTimeline). |
| timelineMinDurationSeconds | number | 30 | Мінімальна тривалість відео (у секундах), за якої timeline='auto' показує вбудовану смугу. Короткі зациклені кліпи нижче цього порогу не показують її. |
| renderControls | (props: ControlsRenderProps) => ReactNode | - | Власні елементи керування, замінюють стандартні кнопки закриття та звуку |
| renderError | (props: { item: T; activeIndex: number }) => ReactNode | - | Власний індикатор помилки, замінює стандартний значок |
| renderLoading | (props: { item: T; activeIndex: number }) => ReactNode | - | Вл асний індикатор завантаження, замінює стандартну хвилю |
| renderNavigation | (props: NavigationRenderProps) => ReactNode | - | Власна навігація, замінює стандартні вертикальні стрілки |
| renderNestedNavigation | (props: NavigationRenderProps) => ReactNode | - | Власна навігація для вкладеного горизонтального слайдера (мультимедійні дописи), замінює стандартні стрілки ліворуч і праворуч |
| renderNestedSlide | (props: NestedSlideRenderProps) => ReactNode | - | Власний рендерер слайдів вкладеного горизонтального слайдера. Через props.defaultContent можна загорнути або вбудувати стандартний ImageSlide чи VideoSlide. На відміну від renderSlide, null тут не означає повернення до стандартного. |
| renderSlide | (props: SlideRenderProps) => ReactNode | null | - | Власний рендеринг слайда. Поверніть null, щоб лишити стандартний. Через props.defaultContent можна загорнути або вбудувати стандартний слайд. |
| renderSlideOverlay | (item, index, isActive) => ReactNode | - | Власний оверлей для кожного слайда, замінює стандартний SlideOverlay. Поверніть null, щоб сховати. |
| renderTimeline | (props: TimelineRenderProps) => ReactNode | - | Власна смуга таймлайну відтворення. Викликається лише тоді, коли за правилами показу вивелася б стандартна смуга (та сама логіка auto/always/never плюс timelineMinDurationSeconds). Через props.defaultContent можна загорнути вбудований <TimelineBar />; поверніть null, щоб сховати. |
Пропси ReelPlayerUrlOverlay
ReelPlayerUrlOverlayProps<T>
Приймає всі візуальні та поведінкові пропси вище, крім isOpen, replaced by a controller. initialIndex ігнорується — слайд обирає position контролера, тож передане поруч значення перезаписується на кожному відкритті.
| Prop | Тип | Типове значення | Опис |
|---|---|---|---|
| controller | UrlStateController | required | Контролер із useOverlayUrlState. Його position вирішує, чи оверлей відкритий і який слайд показує; оверлей записує через нього назад на зміну слайда та на закриття. |
Колбеки
| Prop | Тип | Опис |
|---|---|---|
| onClose | () => void | Викликається, коли плеєр закривається. Обов’язковий у ReelPlayerOverlay (стан відкриття ваш, тож закриття обробляєте ви); необов’язковий у ReelPlayerUrlOverlay, де закриттям керує URL — передавайте лише щоб зреагувати після закриття. |
| onSlideChange | (index: number) => void | Викликається після зміни слайда |
| onInnerSlideChange | (outerIndex: number, innerIndex: number) => void | Викликається, коли змінюється індекс внутрішнього медіа активного допису — під час навігації всередині мультимедійного допису й під час активації зовнішнього, повідомляючи поточний внутрішній індекс активованого допису (0 для допису з одним медіа). |
Пропси Reel (передані далі)
Ці пропси передаються далі в Reel component.
| Prop | Тип | Типове значення | Опис |
|---|---|---|---|
| enableNavKeys | boolean | true | Вмикає навігацію з клавіатури |
| enableWheel | boolean | true | Вмикає навігацію колесом миші |
| loop | boolean | false | Вмикає н ескінченний цикл |
| swipeDistanceFactor | number | 0.12 | Поріг свайпу (0–1) |
| transitionDuration | number | 300 | Тривалість анімації переходу (мс) |
| wheelDebounceMs | number | 200 | Тривалість дебаунсу колеса (мс) |
Types
BaseContentItem
Тип-обмеження для узагальнення. Розширюйте його, щоб використовувати власні типи даних із ReelPlayerOverlay.
ContentItem
MediaItem
MediaType
ControlsRenderProps<T>
NavigationRenderProps
SlideRenderProps<T>
NestedSlideRenderProps
SlideOverlayProps
ImageSlideProps
VideoSlideProps
CloseButtonProps
SoundButtonProps
TimelineBarProps
TimelineRenderProps<T>
Sub-Components
Повторно використовувані блоки, експортовані для складання власних render props:
CloseButton
Окрема кнопка закриття зі стандартними стилями плеєра. Використовуйте в середині renderControls.
SoundButton
Окремий перемикач звуку. Має бути всередині SoundProvider (ReelPlayerOverlay додає його автоматично).
TimelineBar
Стандартна смуга перемотування. Читає з найближчого TimelineProvider (автоматично монтується всередині ReelPlayerOverlay) and renders the track, buffered ranges, progress fill, and scrub pill. Theme via the --rk-reel-timeline-* власними властивостями або замініть через renderTimeline.
SlideOverlay
Стандартний градієнтний оверлей з автором, описом і лайками. Показується автоматично, коли у вмісті є потрібні поля. Через renderSlideOverlay його можна замінити або сховати.
ImageSlide
Слайд-зображення з лінивим завантаженням і object-fit: cover за замовчуванням. Використовуйте всередині renderSlide щоб складати власні слайди-зображення зі своїми стилями.
VideoSlide
Слайд-відео зі спільним елементом <video> заради безперервності звуку на iOS, з постерами, пам’яттю позиції та індикатором завантаження. Має бути всередині SoundProvider (ReelPlayerOverlay додає його автоматично).
Складання власних слайдів
renderSlide з ImageSlide / VideoSlide щоб змінити рендеринг медіа, зберігши всю вбудовану поведінку (автовідтворення, знімок постера, синхронізацію звуку).Завантаження вмісту та обробка помилок
Плеєр стежить за станом завантаження й помилок кожного слайда. Поки вміст вантажиться, показується хвильовий індикатор; для зіпсованого медіа — значок помилки. URL з помилками кешуються, тож повторний перехід одразу показує помилку без нової спроби.
Колбеки життєвого циклу
Якщо ви використовуєте renderSlide, call these callbacks to control the loading indicator:
| Колбек | Коли викликати |
|---|---|
| onReady | Зображення завантажилося або відео почало грати. Скидає стани завантаження й помилки. |
| onWaiting | Відео буферизується посеред відтворення. Показує індикатор завантаження. |
| onError | Вміст не завантажився. Показує оверлей помилки й кешує URL як зіпсований. |
Власний інтерфейс завантаження та помилок
Замініть стандартний хвильовий індикатор і значок помилки власними компонентами:
Таймлайн
Оверлей показує вбудовану смугу таймлайну відтворення над активним відео. Пропс timeline визначає, коли її показувати:
'auto'(типово): показується, коли активне медіа — відео, тривалість якого перевищуєtimelineMinDurationSeconds(типово 30). Працює і для слайдів з одним відео, і для мультимедійних каруселей; смуга слідує за активним вкладеним елементом і ховається на зображеннях.'always': показується, щойно на активному слайді є відео.'never': не показується ніколи. Власну смугу будуйте черезrenderTimeline.
Тему задавайте через власні властивості CSS --rk-reel-timeline-* (висота, кольори, розмір повзунка). Для повністю власної смуги перемотування, таймкоду чи індикатора прогресу беріть renderTimeline; the callback receives a timelineState на основі TimelineController.
Контекст звуку
Для власних реалізацій доступ до стану звуку відкритий:
Класи CSS
Усі класи CSS звичайні (не CSS-модулі), тож їх можна перекрити селекторами вищої специфічності в таблиці стилів, підключеній після @reelkit/react-reel-player/styles.css. Для змін кольору, розміру та z-index краще беріть власні властивості CSS, описані в розділі Theming нижче — вони саме для цього й зроблені.
| Class | Component | Опис |
|---|---|---|
| .rk-reel-overlay | Overlay | Фіксоване повноекранне тло (фон, z-index) |
| .rk-reel-container | Overlay | Контейнер плеєра (позиція, переповнення) |
| .rk-reel-loader | Overlay | Оверлей хвильової анімації завантаження |
| .rk-reel-media-error | Overlay | Оверлей стану помилки (значок і текст по центру) |
| .rk-reel-media-error-text | Overlay | Текст повідомлення про помилку |
| .rk-reel-button | Controls | Спільна кругла кнопка з іконкою (закриття, звук, стрілки) |
| .rk-reel-close-btn | Controls | Кнопка закриття |
| .rk-reel-sound-btn | Controls | Кнопка перемикання звуку |
| .rk-reel-nav-arrows | Навігація | Контейнер стрілок лише для десктопа (прихований до 768px) |
| .rk-reel-nav-button | Навігація | Окрема стрілка вперед або назад |
| .rk-reel-slide-wrapper | Slide | Обгортка навколо медіа та оверлея |
| .rk-reel-slide-overlay | SlideOverlay | Контейнер градієнтного оверлея |
| .rk-reel-slide-overlay-author | SlideOverlay | Рядок автора (аватар та ім’я) |
| .rk-reel-slide-overlay-avatar | SlideOverlay | Зображення аватара автора |
| .rk-reel-slide-overlay-name | SlideOverlay | Текст імені автора |
| .rk-reel-slide-overlay-description | SlideOverlay | Текст опису |
| .rk-reel-slide-overlay-likes | SlideOverlay | Рядок лайків (сердечко й лічильник) |
| .rk-reel-video-container | VideoSlide | Обгортка відео (фон, переповнення) |
| .rk-reel-video-element | VideoSlide | The <video> element |
| .rk-reel-video-poster | VideoSlide | Постер (зникає з початком відтворення) |
| .rk-reel-video-poster.rk-visible | VideoSlide | Модифікатор стану постера, поки відео на паузі або вантажиться |
| .rk-reel-nested-indicator | NestedSlider | Пагінація точками під мультимедійними слайдами (позиція різна на десктопі й на дотикових пристроях) |
| .rk-reel-nested-nav | NestedSlider | Стрілки горизонтальної каруселі (приховані до 768px) |
| .rk-reel-nested-nav-next | NestedSlider | Позиція вкладеної стрілки вперед |
| .rk-reel-nested-nav-prev | NestedSlider | Позиція вкладеної стрілки назад |
| .rk-reel-timeline | TimelineBar | Обгортка смуги перемотування. Використовуйте на власних коренях `renderTimeline`, щоб успадкувати притиснення до низу, відступи безпечної зони та проміжок для оверлея слайда на дотикових пристроях. |
| .rk-reel-timeline-track | TimelineBar | Доріжка (невідтворена частина) |
| .rk-reel-timeline-buffered | TimelineBar | Шар буферизованих сегментів |
| .rk-reel-timeline-fill | TimelineBar | Заповнення відтвореного прогресу |
| .rk-reel-timeline-cursor | TimelineBar | Повзунок перемотування (плаває над доріжкою) |
Theming
Кожен колір, розмір, z-index і перехід живе у власній властивості CSS. Перевизначайте одну чи кілька на :root (або на будь-якому предку оверлея), щоб змінити тему, не чіпаючи код компонентів.
| Token | Типове значення | Controls |
|---|---|---|
| --rk-reel-overlay-bg | #000 | Full-screen backdrop color |
| --rk-reel-overlay-z | 1000 | Overlay z-index |
| --rk-reel-button-bg | rgba(0, 0, 0, 0.5) | Default circular button background |
| --rk-reel-button-bg-hover | rgba(255, 255, 255, 0.1) | Nav arrow background (and base hover state) |
| --rk-reel-button-bg-hover-strong | rgba(255, 255, 255, 0.2) | Nav arrow hover background |
| --rk-reel-button-fg | #fff | Button icon color |
| --rk-reel-button-size | 44px | Button width / height |
| --rk-reel-button-radius | 50% | Button border-radius |
| --rk-reel-ui-z | 10 | Close / sound / nav z-index |
| --rk-reel-edge-padding | 16px | Edge inset for close / sound / nav arrows |
| --rk-reel-nav-gap | 8px | Spacing between stacked nav arrows |
| --rk-reel-transition | 0.2s | Hover transition duration |
| --rk-reel-loader-color | rgba(255, 255, 255, 0.12) | Wave loader gradient color |
| --rk-reel-loader-duration | 1.8s | Wave loader animation duration |
| --rk-reel-error-fg | rgba(255, 255, 255, 0.4) | Error icon and text color |
| --rk-reel-error-text-size | 13px | Error message font size |
| --rk-reel-slide-overlay-bg | linear-gradient(transparent, rgba(0, 0, 0, 0.7)) | Caption scrim gradient |
| --rk-reel-slide-overlay-padding | 48px 16px 16px | Caption inner padding |
| --rk-reel-slide-overlay-name-color | #fff | Author name color |
| --rk-reel-slide-overlay-description-color | rgba(255, 255, 255, 0.9) | Description text color |
| --rk-reel-slide-overlay-likes-color | rgba(255, 255, 255, 0.8) | Likes row text color |
| --rk-reel-video-bg | #000 | Letterbox background behind <video> |
| --rk-reel-nested-button-bg | rgba(0, 0, 0, 0.5) | Nested arrow background |
| --rk-reel-nested-button-bg-hover | rgba(255, 255, 255, 0.2) | Nested arrow hover background |
| --rk-reel-nested-button-size | 36px | Nested arrow size |
| --rk-reel-nested-edge-padding | 12px | Nested arrow edge inset |
| --rk-reel-timeline-track | rgba(255, 255, 255, 0.22) | Track background (unplayed region) |
| --rk-reel-timeline-buffered | rgba(255, 255, 255, 0.4) | Buffered segments color |
| --rk-reel-timeline-fill | #fff | Played-progress fill color |
| --rk-reel-timeline-cursor | #fff | Scrub-handle pill color |
| --rk-reel-timeline-height | 3px | Track height at rest |
| --rk-reel-timeline-height-active | 6px | Track height on hover / focus / scrub |
| --rk-reel-timeline-cursor-width | 10px | Scrub-pill width at rest |
| --rk-reel-timeline-cursor-width-active | 14px | Scrub-pill width while scrubbing |
| --rk-reel-timeline-cursor-height | 24px | Scrub-pill height at rest |
| --rk-reel-timeline-cursor-height-active | 32px | Scrub-pill height while scrubbing |
| --rk-reel-timeline-hitbox | 16px | Extra pointer hit-area above the track |
| --rk-reel-timeline-transition | 0.15s ease-out | Track + pill grow/shrink animation |
| --rk-reel-timeline-z | 11 | Timeline z-index (above the default UI layer) |
Вставте фрагмент нижче в таблицю стилів, підключену після @reelkit/react-reel-player/styles.css.
Accessibility
Корінь оверлея — модальний діалог (role="dialog", aria-modal="true"). Set ariaLabel щоб змінити оголошення для екранного читача; типове значення — «Video player». Кожен слайд несе role="group", aria-roledescription="slide", and an aria-label="Слайд N з M", so swiping announces position in the sequence.
Оверлей захоплює фокус під час відкриття й повертає його на елемент-тригер після закриття. Tab і Shift+Tab циклічно проходять фокусовані елементи всередині; фокус, що вислизнув (клік поза оверлеєм, програмна установка), повертається назад. Реалізовано ч ерез captureFocusForReturn та createFocusTrap from @reelkit/core.
Клавіатурні скорочення
| Key | Action |
|---|---|
| ArrowUp | Previous slide |
| ArrowDown | Next slide |
| ArrowLeft | Previous media (in nested slider) |
| ArrowRight | Next media (in nested slider) |
| Escape | Close player |