Angular Reel Player

Reproductor vertical de medios a pantalla completa al estilo de Instagram y TikTok para Angular, con @reelkit/angular-reel-player.

Ver la demo en vivo →

Características

Deslizamiento vertical
Táctil, arrastre, teclado, rueda
Reproducción automática
Se reproduce al hacerse visible
Botón de sonido
Sin cortes en iOS
Varios medios
Carruseles horizontales anidados
Recuerda la posición
Continúa donde lo dejaste
Captura de fotogramas
Fundido del póster al vídeo
Virtualizado
Solo 3 slides en el DOM
Relación de aspecto
9:16 en escritorio, completo en móvil
Navegación en escritorio
Botones de flecha
Tipos genéricos
Modelos de datos de contenido propios
Personalizable
Slots de plantilla para todo
Gestión de errores
Detección de medios rotos con caché LRU
Estado en la URL
Enlaces compartibles y cierre con el botón de volver

Instalación

bash
npm install @reelkit/angular-reel-player @reelkit/angular lucide-angular
Iconos

Los controles por defecto usan lucide-angular para los iconos (cerrar, sonido, flechas de navegación). Si prefieres otra librería de iconos, usa los slots de plantilla rkPlayerControls y rkPlayerNavigation para poner los tuyos.

Uso básico

Importa la hoja de estilos y el componente standalone RkReelPlayerOverlayComponent en el array imports de tu componente.

reel-feed.component.ts
import { Component, signal } from '@angular/core';
import {
  RkReelPlayerOverlayComponent,
  type ContentItem,
} from '@reelkit/angular-reel-player';
import '@reelkit/angular-reel-player/styles.css';

const content: ContentItem[] = [
  {
    id: '1',
    media: [{
      id: 'v1',
      type: 'video',
      src: '/cdn/samples/videos/video-01.mp4',
      poster: '/cdn/samples/videos/video-poster-01.jpg',
      aspectRatio: 16 / 9,
    }],
    author: { name: 'Alex Johnson', avatar: '/cdn/samples/avatars/avatar-01.jpg' },
    likes: 1234,
    description: 'Amazing content',
  },
  {
    id: '2',
    media: [{
      id: 'img1',
      type: 'image',
      src: '/cdn/samples/images/image-01.jpg',
      aspectRatio: 2 / 3,
    }],
    author: { name: 'Sarah Miller', avatar: '/cdn/samples/avatars/avatar-02.jpg' },
    likes: 5678,
    description: 'Nature at its finest',
  },
  {
    id: '3',
    media: [{
      id: 'v2',
      type: 'video',
      src: '/cdn/samples/videos/video-02.mp4',
      poster: '/cdn/samples/videos/video-poster-02.jpg',
      aspectRatio: 16 / 9,
    }],
    author: { name: 'Mike Chen', avatar: '/cdn/samples/avatars/avatar-03.jpg' },
    likes: 3456,
    description: 'Adventure awaits',
  },
];

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RkReelPlayerOverlayComponent],
  template: `
    <!-- Grid thumbnail view -->
    <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:6px">
      @for (item of content; track item.id; let i = $index) {
        <button
          (click)="openAt(i)"
          style="aspect-ratio:9/16;cursor:pointer;overflow:hidden"
        >
          <img
            [src]="item.media[0].poster || item.media[0].src"
            style="width:100%;height:100%;object-fit:cover"
          />
        </button>
      }
    </div>

    <rk-reel-player-overlay
      [isOpen]="isOpen()"
      [content]="content"
      [initialIndex]="startIndex()"
      (closed)="isOpen.set(false)"
    />
  `,
})
export class AppComponent {
  readonly content = content;
  readonly isOpen = signal(false);
  readonly startIndex = signal(0);

  openAt(index: number): void {
    this.startIndex.set(index);
    this.isOpen.set(true);
  }
}

Slots de plantilla

Seis directivas de slots de plantilla te permiten personalizar cada aspecto de la interfaz del reproductor. Cada una recibe un objeto de contexto con tipos estrictos. Pasa solo los slots que quieras sobrescribir; para el resto se usan los valores por defecto.

DirectivaTipo del contextoDescripción
[rkPlayerControls]PlayerControlsContext<T>Barra de controles globales propia (cerrar, botón de sonido, etc.)
[rkPlayerError]{ $implicit: activeIndex, item, innerActiveIndex }Slot de plantilla para un indicador de error propio
[rkPlayerLoading]{ $implicit: activeIndex, item, innerActiveIndex }Slot de plantilla para un indicador de carga propio
[rkPlayerNavigation]PlayerNavigationContextFlechas de navegación de anterior y siguiente propias
[rkPlayerNestedNavigation]PlayerNestedNavigationContextFlechas de navegación propias para el slider horizontal interior
[rkPlayerNestedSlide]PlayerNestedSlideContextContenido propio de cada slide dentro del slider horizontal interior
[rkPlayerSlide]PlayerSlideContext<T>Contenido del slide totalmente propio que sustituye el slide de medios por defecto
[rkPlayerSlideOverlay]PlayerSlideOverlayContext<T>Overlay por slide (información del autor, me gusta, descripción, etc.)
[rkPlayerTimeline]PlayerTimelineContext<T>Barra de reproducción propia. Solo se renderiza cuando la regla (modo de timeline y duración mínima) renderizaría la barra por defecto (la misma lógica de auto/always/never).
typescript
import {
  RkReelPlayerOverlayComponent,
  RkPlayerSlideOverlayDirective,
  RkPlayerControlsDirective,
  RkPlayerNavigationDirective,
  RkCloseButtonComponent,
  RkSoundButtonComponent,
  type ContentItem,
  type PlayerSlideOverlayContext,
  type PlayerControlsContext,
  type PlayerNavigationContext,
} from '@reelkit/angular-reel-player';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    RkReelPlayerOverlayComponent,
    RkPlayerSlideOverlayDirective,
    RkPlayerControlsDirective,
    RkPlayerNavigationDirective,
    RkCloseButtonComponent,
    RkSoundButtonComponent,
  ],
  template: `
    <rk-reel-player-overlay
      [isOpen]="isOpen"
      [content]="content"
      (closed)="isOpen = false"
    >
      <!-- Custom per-slide overlay: author + likes -->
      <ng-template rkPlayerSlideOverlay let-item let-isActive="isActive">
        @if (isActive) {
          <div style="position:absolute;bottom:80px;left:16px;color:#fff">
            <div style="display:flex;align-items:center;gap:8px">
              <img [src]="item.author.avatar" style="width:40px;height:40px;border-radius:50%" />
              <span style="font-weight:600">{{ item.author.name }}</span>
            </div>
            <p style="margin-top:8px">{{ item.description }}</p>
          </div>
        }
      </ng-template>

      <!-- Custom global controls -->
      <ng-template rkPlayerControls
                   let-onClose
                   let-soundState="soundState">
        <div style="position:absolute;top:16px;right:16px;display:flex;gap:8px">
          <rk-sound-button [soundState]="soundState" />
          <rk-close-button (click)="onClose()" />
        </div>
      </ng-template>

      <!-- Custom navigation -->
      <ng-template rkPlayerNavigation
                   let-onPrev
                   let-onNext="onNext"
                   let-activeIndex="activeIndex"
                   let-count="count">
        <div style="position:absolute;right:16px;top:50%;transform:translateY(-50%)">
          <button (click)="onPrev()" [disabled]="activeIndex === 0">&#9650;</button>
          <button (click)="onNext()" [disabled]="activeIndex === count - 1">&#9660;</button>
        </div>
      </ng-template>

      <!-- Custom playback timeline -->
      <ng-template rkPlayerTimeline let-state="timelineState">
        <div class="rk-reel-timeline" style="padding:0 16px"
             (pointerdown)="bindTrack(track, state); track.focus()">
          <div #track
               role="slider"
               [attr.aria-valuenow]="state.currentTime()"
               style="height:6px;background:rgba(255,255,255,0.2);border-radius:999px">
            <div [style.width.%]="state.progress() * 100"
                 style="height:100%;background:linear-gradient(90deg,#6366f1,#ec4899);border-radius:999px"></div>
          </div>
        </div>
      </ng-template>
    </rk-reel-player-overlay>
  `,
})
export class AppComponent {
  isOpen = false;
  content: ContentItem[] = [];

  private _trackDispose: (() => void) | null = null;
  /** Wire pointer + keyboard scrub onto your custom track element. */
  bindTrack(el: HTMLElement, state: PlayerTimelineState) {
    this._trackDispose?.();
    this._trackDispose = state.bindInteractions(el);
  }
}

Línea de tiempo propia

El slot de plantilla rkPlayerTimeline solo se usa cuando las reglas del overlay renderizarían la barra por defecto (el mismo modo timeline y timelineMinDurationSeconds), así que no tienes que reimplementarlas. Reutiliza la clase .rk-reel-timeline en tu raíz para heredar la posición pegada al borde inferior, el relleno de la zona segura y el espacio que deja en dispositivos táctiles. Llama a state.bindInteractions(el) en tu pista de arrastre para conectar el arrastre con puntero y teclado.

Slider anidado (elementos con varios medios)

Cuando un ContentItem tiene varias entradas en media, el reproductor las muestra en un slider horizontal anidado (al estilo del carrusel de Instagram). Usa el slot rkPlayerNestedSlide para personalizar el contenido de los slides interiores.

typescript
const carouselItem: ContentItem = {
  id: '3',
  media: [
    { id: 'img-a', type: 'image', src: '/photo-a.jpg', aspectRatio: 2 / 3 },
    { id: 'img-b', type: 'image', src: '/photo-b.jpg', aspectRatio: 3 / 4 },
    { id: 'img-c', type: 'image', src: '/photo-c.jpg', aspectRatio: 1 },
  ],
  author: { name: 'Emma Davis', avatar: '/avatar3.jpg' },
  likes: 8901,
  description: 'Travel moments',
};

Carga de contenido y gestión de errores

El reproductor sigue el estado de carga y de error de cada slide. Mientras el contenido se carga se muestra una animación de onda; con un medio roto aparece un icono de error. Las URLs con error se guardan en caché, así que al volver se muestra el error al instante sin reintentar.

Callbacks del ciclo de vida

Si usas el slot de plantilla rkPlayerSlide, usa los callbacks del contexto para controlar el indicador de carga:

CallbackCuándo llamarlo
onReadyLa imagen se ha cargado o el vídeo ha empezado a reproducirse. Limpia los estados de carga y de error.
onWaitingEl vídeo está cargando a mitad de la reproducción. Muestra el indicador de carga.
onErrorEl contenido no se ha podido cargar. Muestra el overlay de error y guarda la URL en caché como rota.
html
<!-- Wire lifecycle callbacks in a custom slide template -->
<rk-reel-player-overlay [isOpen]="isOpen" [content]="items" (closed)="isOpen = false">
  <ng-template rkPlayerSlide
    let-item
    let-size="size"
    let-isActive="isActive"
    let-onReady="onReady"
    let-onWaiting="onWaiting"
    let-onError="onError"
  >
    @if (item.media[0].type === 'image') {
      <img
        [src]="item.media[0].src"
        (load)="onReady()"
        (error)="onError()"
        [style.width.px]="size[0]"
        [style.height.px]="size[1]"
        style="object-fit: cover"
      />
    } @else {
      <video
        [src]="item.media[0].src"
        [autoplay]="isActive"
        (canplay)="onReady()"
        (waiting)="onWaiting()"
        (error)="onError()"
        [style.width.px]="size[0]"
        [style.height.px]="size[1]"
        style="object-fit: cover"
      />
    }
  </ng-template>
</rk-reel-player-overlay>

Interfaz de carga y de error propia

Sustituye la animación de onda y el icono de error por defecto por plantillas propias:

html
<rk-reel-player-overlay [isOpen]="isOpen" [content]="items" (closed)="isOpen = false">
  <ng-template rkPlayerLoading let-index let-item="item">
    <div style="
      position: absolute; inset: 0; z-index: 10;
      display: flex; align-items: center; justify-content: center;
      color: #fff; font-size: 14px;
    ">
      Loading slide {{ index + 1 }}...
    </div>
  </ng-template>

  <ng-template rkPlayerError let-index let-item="item">
    <div style="
      position: absolute; inset: 0; z-index: 10;
      display: flex; flex-direction: column;
      align-items: center; justify-content: center;
      gap: 12px; color: rgba(255,255,255,0.5);
    ">
      <span style="font-size: 48px">!</span>
      <span>Failed to load media</span>
    </div>
  </ng-template>
</rk-reel-player-overlay>

Línea de tiempo

El overlay renderiza una barra de reproducción incluida sobre el vídeo activo. Controla cuándo con el input timeline: 'auto' (por defecto) la renderiza siempre que el medio activo sea un vídeo más largo que timelineMinDurationSeconds (30 por defecto), 'always' siempre que haya un vídeo activo y 'never' la desactiva. Para una barra de arrastre totalmente propia, usa la directiva de plantilla rkPlayerTimeline; su contexto expone un timelineState respaldado por el TimelineController que hay debajo.

html
<rk-reel-player-overlay
  [isOpen]="isOpen()"
  [content]="items"
  timeline="auto"
  [timelineMinDurationSeconds]="30"
  (closed)="isOpen.set(false)"
/>

Cambia su tema con las propiedades personalizadas de CSS --rk-reel-timeline-*. Para controlarla directamente desde componentes propios, inyecta TimelineStateService.

RkTimelineBarComponent

Componente de la barra de reproducción por defecto. Usa TimelineStateService (lo proporciona RkReelPlayerOverlayComponent) y renderiza la pista, los rangos cargados, el relleno del progreso y la píldora de arrastre. Selector: rk-timeline-bar. Inputs: class?: string, style?: Record<string, string>. Úsalo dentro de una plantilla rkPlayerTimeline para envolver o ampliar la barra por defecto; por separado, úsalo solo dentro de un componente que proporcione el servicio.

typescript
import { RkTimelineBarComponent } from '@reelkit/angular-reel-player';

@Component({
  standalone: true,
  imports: [RkReelPlayerOverlayComponent, RkTimelineBarComponent],
  template: `
    <rk-reel-player-overlay [isOpen]="isOpen()" [content]="items">
      <!-- Wrap or augment the default bar: -->
      <ng-template rkPlayerTimeline>
        <my-timecode />
        <rk-timeline-bar />
      </ng-template>
    </rk-reel-player-overlay>
  `,
})
export class AppComponent {}

SoundStateService

Se proporciona en el nivel de RkReelPlayerOverlayComponent. Lo inyecta el botón de sonido por defecto y se expone en el contexto del slot de plantilla de los controles. Puedes inyectarlo en controles propios que sean hijos del overlay para acceder a él directamente.

typescript
import { inject } from '@angular/core';
import { SoundStateService } from '@reelkit/angular-reel-player';

@Component({ ... })
export class AppComponent {
  readonly soundState = inject(SoundStateService);

  // Use in template:
  // [class.muted]="soundState.muted()"
  // [disabled]="soundState.disabled()"
  // (click)="soundState.toggle()"
}
MiembroTipoDescripción
muted()Signal<boolean>Indica si el reproductor está en silencio
disabled()Signal<boolean>Es true cuando el slide activo no tiene vídeo o está en transición
toggle()() => voidAlterna el estado de silencio

Estado en la URL

Ver la demo en vivo →

RkReelPlayerUrlOverlayComponent es un componente aparte cuyo estado abierto vive en la barra de direcciones. Crea un controlador con createOverlayUrlState en un contexto de inyección y pásalo como [controller]: el reproductor se abre cuando el parámetro nombra un slide y se cierra cuando desaparece. Los enlaces se pueden compartir y el botón de volver cierra el reproductor. RkReelPlayerOverlayComponent sigue controlándose con [isOpen], así que cada componente tiene una única forma de controlar el estado abierto.

Claves incluidas

Puedes apuntar a los slides con una clave incluida: pasa con spread urlIndexKey (por posición) o urlStableIdKey (por un id estable) al controlador; las dos se reexportan desde @reelkit/angular. Consulta la guía del estado en la URL y la API del core.

Una aplicación con router pasa un adaptador basado en el Router, para que este siga siendo la única fuente de verdad de la navegación: escribir en el historial a sus espaldas deja su ubicación desfasada y pierde el parámetro en la siguiente navegación. createRouterUrlAdapter de @reelkit/angular/ng-router-url-adapter es el adaptador listo para usar.

typescript
import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';
import {
  RkReelPlayerUrlOverlayComponent,
  type ContentItem,
} from '@reelkit/angular-reel-player';
import { createOverlayUrlState, urlIndexKey, urlStableIdKey } from '@reelkit/angular';
import { createRouterUrlAdapter } from '@reelkit/angular/ng-router-url-adapter';
import '@reelkit/angular-reel-player/styles.css';

@Component({
  standalone: true,
  imports: [RkReelPlayerUrlOverlayComponent, RouterLink],
  template: `
    @for (post of content; track post.id; let i = $index) {
      <a [routerLink]="[]" [queryParams]="{ reel: i }">{{ post.id }}</a>
    }
    <rk-reel-player-url-overlay [controller]="reel" [content]="content" />
  `,
})
export class FeedComponent {
  content: ContentItem[] = [/* ... */];
  protected readonly reel = createOverlayUrlState({
    param: 'reel',
    adapter: createRouterUrlAdapter(),
    ...urlIndexKey(() => this.content.length),
  });
}
  • Abrir añade una entrada al historial. Deslizar por el feed la sustituye, así que N deslizamientos no añaden entradas y volver una vez siempre sale del reproductor. Volver cierra; no cambia de slide.
  • Volver solo cierra cuando el reproductor se abrió desde dentro de la aplicación, es decir, cuando el enlace añadió una entrada. Un enlace compartido abierto directamente en una pestaña nueva no tiene historial detrás, así que el botón de volver del navegador sale del sitio; el botón ✕ o Escape quitan el parámetro en el sitio y se quedan.
  • Un enlace directo ?reel=3 abre el reproductor en ese slide al cargar.
  • Un parámetro que no nombra ningún slide (un marcador desfasado, un valor editado a mano) se quita de la URL en lugar de dejar la barra de direcciones apuntando a un slide que no se puede abrir.
  • La profundidad de la URL depende de la clave del controlador: un eje solo para la publicación, o dos ejes (urlIndexTwoAxisKey) para llevar también el índice de la imagen interior de una publicación con varios medios. Elige una clave por aplicación; los formatos no se decodifican entre sí.

Las opciones completas de createOverlayUrlState están en la referencia de la API de Angular.

Una clave o dos: elige la profundidad de la URL

El mismo RkReelPlayerUrlOverlayComponent admite las dos formas; las distingue en tiempo de ejecución por la posición del controlador, así que no hay ningún input de modo. Elige la clave al crear el controlador:

ClaveFormatoLleva
urlIndexKey(…)?reel=3Solo la publicación vertical.
urlIndexTwoAxisKey(…)?reel=3.2La publicación y el índice del medio interior de un carrusel.

Los dos formatos son distintos a propósito: una clave de dos ejes siempre lleva punto (3.0, nunca un 3 suelto), así que un enlace de un eje no se decodifica como de dos. Por eso cambiar una aplicación de una clave a otra invalida los enlaces compartidos antes. Elige una forma y mantenla.

typescript
import { createOverlayUrlState, urlIndexTwoAxisKey } from '@reelkit/angular';

protected readonly reel = createOverlayUrlState({
  param: 'reel',
  ...urlIndexTwoAxisKey({
    outerCount: () => this.content.length,
    innerCounts: () => this.content.map((post) => post.media.length),
  }),
});

// A link now names both axes: post 3, inner media 2 — ?reel=3.2

Enlaces estables. El índice es posicional, así que un ?reel=3 guardado abre otra publicación en cuanto el feed se reordena, y en un feed eso es lo normal. urlStableIdKey usa como clave el id estable de cada publicación y recorre el feed actual; una sola llamada cubre el caso habitual.

typescript
protected readonly reel = createOverlayUrlState({
  param: 'reel',
  ...urlStableIdKey({ items: () => this.loaded() }),
});

Pasa hashCodec: base64UrlCodec para codificar el id en base64url en la URL: es una ofuscación reversible, no un hash criptográfico.

Si usas otro campo como clave (un slug) o paginas un feed infinito con locateAsync, crea tú el codec y el locator. Son dos tareas distintas: codec escribe la identidad en la URL y locator encuentra dónde está esa identidad.

typescript
protected readonly reel = createOverlayUrlState({
  param: 'reel',
  codec: { decode: (raw) => raw, encode: (id) => id },
  locator: {
    locate: (id) => this.loaded().findIndex((x) => x.id === id),
    identify: (index) => this.loaded()[index].id,
  },
});

Feeds infinitos. locate es síncrono, así que solo puede responder por las publicaciones ya cargadas: un enlace compartido a la publicación 400 de un feed que ha cargado 20 no encuentra nada. locateAsync es la alternativa y solo se llama cuando locate falla: carga las páginas que necesites y devuelve el índice que resulte tener la identidad.

Atajo

¿Usas el id del elemento como clave? Sáltate el codec y el locator hechos a mano y pasa locateAsync directamente a urlStableIdKey({ items, locateAsync }) (carga cuando no lo encuentra y después devuelve el índice). La versión más completa de abajo es para usar otro campo como clave o para tener el control total.

typescript
protected readonly reel = createOverlayUrlState({
  param: 'reel',
  adapter: createRouterUrlAdapter(),
  codec: { decode: (raw) => raw, encode: (id) => id },
  locator: {
    locate: (id) => this.loaded().findIndex((x) => x.id === id),
    identify: (index) => this.loaded()[index].id,
    locateAsync: async (id) => {
      const page = await this.loadById(id); // or loadUntil(id) — fetch just that one, or page up to it
      if (!page) return null; // exhausted — link names no post
      this.loaded.set(page); // commit — the overlay renders from this state
      return page.findIndex((x) => x.id === id);
    },
  },
});
  • Mientras locateAsync está pendiente, el reproductor sigue cerrado y el parámetro no se toca, así que el enlace directo sobrevive a la carga. null o un rechazo quitan el parámetro.
  • Una respuesta que llega después de que la URL haya cambiado, después de cerrar o después de desmontar se descarta: una carga lenta no puede abrir un slide que nadie ha pedido.
  • Mientras está pendiente no se renderiza nada; la página ya es dueña de ese estado de carga, así que renderiza tu propio esqueleto.
  • No hay tiempo límite: el reproductor no puede saber lo largo que es el feed. Resuelve con null cuando se agote la paginación, o el overlay seguirá cerrado indefinidamente.

Tipos de datos personalizados

Extiende BaseContentItem para usar tu propio modelo de dominio. El componente es genérico: RkReelPlayerOverlayComponent<T extends BaseContentItem>.

typescript
import type { BaseContentItem } from '@reelkit/angular-reel-player';

interface MyPost extends BaseContentItem {
  // id: string  — from BaseContentItem
  // media: MediaItem[]  — from BaseContentItem
  title: string;
  tags: string[];
  publishedAt: Date;
}

@Component({
  imports: [RkReelPlayerOverlayComponent],
  template: `
    <rk-reel-player-overlay [isOpen]="isOpen" [content]="posts" (closed)="isOpen = false">
      <ng-template rkPlayerSlideOverlay let-post let-isActive="isActive">
        @if (isActive) {
          <div style="position:absolute;bottom:80px;left:16px;color:#fff">
            <h3>{{ post.title }}</h3>
            @for (tag of post.tags; track tag) {
              <span>#{{ tag }} </span>
            }
          </div>
        }
      </ng-template>
    </rk-reel-player-overlay>
  `,
})
export class AppComponent {
  isOpen = false;
  posts: MyPost[] = [];
}

Inputs de RkReelPlayerOverlayComponent

InputTipoPor defectoDescripción
ariaLabelstring'Video player'Etiqueta accesible de la región del diálogo
aspectRationumber | undefinedundefinedRelación entre ancho y alto del contenedor en escritorio. Por defecto es 9/16. En móvil el reproductor ocupa todo el viewport.
contentT[] (extends BaseContentItem)obligatorioArray de elementos de contenido que mostrar en el reproductor
enableNavKeysbooleantrueActiva la navegación con las teclas de flecha
enableWheelbooleantrueActiva la navegación con la rueda del ratón
initialIndexnumber0Índice, empezando en cero, del elemento visible al principio
initialInnerIndexnumber0Índice del medio interior con el que abrir, solo para la publicación visible al principio: permite que una URL de dos ejes enlace a una imagen concreta de una publicación con varios medios. Se ignora cuando el usuario navega.
isOpenbooleanobligatorioControla la visibilidad del overlay; con false el overlay se quita del DOM
loopbooleanfalseActiva el bucle infinito entre slides
swipeDistanceFactornumber0.12Fracción mínima de la distancia de deslizamiento para cambiar de slide
timeline'auto' | 'always' | 'never''auto'Cuándo se muestra la barra de reproducción incluida. 'auto' solo la renderiza con vídeos más largos que timelineMinDurationSeconds; 'always' la renderiza siempre que el slide activo tenga un vídeo; 'never' desactiva la barra incluida (usa el slot de plantilla rkPlayerTimeline para sustituirla por completo).
timelineMinDurationSecondsnumber30Duración mínima del vídeo (en segundos) para que timeline='auto' renderice la barra incluida. Los clips cortos en bucle por debajo de este umbral no la muestran.
transitionDurationnumber300Duración de la animación de los slides en ms
wheelDebounceMsnumber200Duración del debounce de los eventos de la rueda en ms

Outputs de RkReelPlayerOverlayComponent

OutputTipoDescripción
apiReadyEventEmitter<ReelApi>Se emite cuando el slider está listo y expone la API imperativa
closedEventEmitter<void>Se emite al cerrar el reproductor
slideChangeEventEmitter<number>Se emite cuando cambia el índice del slide activo
innerSlideChangeEventEmitter<{ outer: number; inner: number }>Se emite cuando cambia el índice del medio interior de la publicación activa: al navegar por dentro y al activar una publicación, con su índice interior actual (0 en una publicación con un solo medio).

Inputs de RkReelPlayerUrlOverlayComponent

Acepta todos los inputs de arriba salvo isOpen e initialIndex, que se sustituyen por un controller cuya posición elige el slide. Tiene los outputs closed y slideChange.

InputTipoPor defectoDescripción
controllerUrlStateControllerobligatorioControlador de createOverlayUrlState. Su position decide si el reproductor está abierto y qué slide muestra; el overlay escribe a través de él al cambiar de slide y al cerrar.

Interfaz MediaItem

CampoTipoDescripción
idstringIdentificador único del medio
type'image' | 'video'Tipo de medio
srcstringURL del medio
posterstring?URL de la miniatura del póster en los vídeos
aspectRationumberRelación entre ancho y alto. Los valores < 1 indican vertical (cover) y > 1, horizontal (contain)

Tipos de contexto de los slots de plantilla

TipoCampos
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) }

Clases CSS

Todas las clases CSS son normales (no scoped), así que puedes apuntar a ellas con selectores más específicos en una hoja de estilos cargada después de @reelkit/angular-reel-player/styles.css. Para cambiar colores, tamaños y z-index, es mejor usar las propiedades personalizadas de CSS de la sección Temas de más abajo: están pensadas justo para eso.

ClaseComponenteDescripción
.rk-reel-overlayOverlayFondo fijo a pantalla completa (fondo, z-index)
.rk-reel-containerOverlayContenedor del reproductor (posición, desbordamiento)
.rk-reel-loaderOverlayOverlay con la animación de carga en onda
.rk-reel-media-errorOverlayOverlay del estado de error (icono y texto centrados)
.rk-reel-media-error-textOverlayTexto del mensaje de error
.rk-reel-buttonControlsBotón circular de icono compartido (cerrar, sonido, flechas de navegación)
.rk-reel-close-btnControlsBotón de cerrar
.rk-reel-sound-btnControlsBotón de sonido
.rk-reel-nav-arrowsNavigationContenedor de flechas solo para escritorio (oculto por debajo de 768px)
.rk-reel-nav-btnNavigationCada flecha de navegación de anterior o siguiente
.rk-reel-slide-wrapperSlideEnvoltorio alrededor del medio y el overlay
.rk-reel-slide-overlaySlideOverlayContenedor del overlay con degradado
.rk-reel-slide-overlay-authorSlideOverlayFila del autor (avatar y nombre)
.rk-reel-slide-overlay-avatarSlideOverlayImagen del avatar del autor
.rk-reel-slide-overlay-nameSlideOverlayTexto con el nombre del autor
.rk-reel-slide-overlay-descriptionSlideOverlayTexto de la descripción
.rk-reel-slide-overlay-likesSlideOverlayFila de me gusta (corazón y recuento)
.rk-reel-video-containerVideoSlideEnvoltorio del vídeo (fondo, desbordamiento)
.rk-reel-video-elementVideoSlideEl elemento <video>
.rk-reel-video-posterVideoSlideImagen del póster (se desvanece al reproducir)
.rk-reel-video-loaderVideoSlideAnimación de carga en onda
.rk-reel-video-poster.rk-visibleVideoSlideModificador de estado que se aplica al póster mientras el vídeo está en pausa o cargando
.rk-reel-nested-indicatorNestedSliderPaginación con puntos bajo los slides con varios medios (su posición cambia entre escritorio y táctil)
.rk-reel-nested-navNestedSliderFlechas del carrusel horizontal (ocultas por debajo de 768px)
.rk-reel-nested-nav-nextNestedSliderPosición de la flecha anidada de siguiente
.rk-reel-nested-nav-prevNestedSliderPosición de la flecha anidada de anterior
.rk-reel-nested-slider-innerNestedSliderRaíz del slider horizontal anidado
.rk-reel-timelineTimelineBarEnvoltorio de la barra de arrastre. Reutilízalo en las raíces de una plantilla `rkPlayerTimeline` propia para heredar la posición pegada al borde inferior, el relleno de la zona segura y el espacio que deja al overlay del slide en dispositivos táctiles.
.rk-reel-timeline-trackTimelineBarPista (la zona sin reproducir)
.rk-reel-timeline-bufferedTimelineBarCapa de segmentos cargados
.rk-reel-timeline-fillTimelineBarRelleno del progreso reproducido
.rk-reel-timeline-cursorTimelineBarPíldora de arrastre (flota sobre la pista)

Temas

Cada color, tamaño, z-index y transición vive en una propiedad personalizada de CSS. Sobrescribe una o varias en :root (o en cualquier ancestro del overlay) para cambiar el tema sin tocar el código de los componentes. Los tokens coinciden con los de los paquetes de React y Vue, así que los cambios sirven para todos los bindings.

TokenPor defectoControla
--rk-reel-overlay-bg#000Color del fondo a pantalla completa
--rk-reel-overlay-z1000z-index del overlay
--rk-reel-button-bgrgba(0, 0, 0, 0.5)Fondo por defecto de los botones circulares
--rk-reel-button-bg-hoverrgba(255, 255, 255, 0.1)Fondo de las flechas de navegación (y estado hover base)
--rk-reel-button-bg-hover-strongrgba(255, 255, 255, 0.2)Fondo de las flechas de navegación al pasar el ratón
--rk-reel-button-fg#fffColor del icono de los botones
--rk-reel-button-size44pxAncho / alto de los botones
--rk-reel-button-radius50%border-radius de los botones
--rk-reel-ui-z10z-index de cerrar / sonido / navegación
--rk-reel-edge-padding16pxSeparación del borde de cerrar / sonido / flechas
--rk-reel-nav-gap8pxEspacio entre las flechas de navegación apiladas
--rk-reel-transition0.2sDuración de la transición al pasar el ratón
--rk-reel-loader-colorrgba(255, 255, 255, 0.12)Color del degradado de la animación de onda
--rk-reel-loader-duration1.8sDuración de la animación de onda
--rk-reel-error-fgrgba(255, 255, 255, 0.4)Color del icono y el texto de error
--rk-reel-slide-overlay-bglinear-gradient(transparent, rgba(0, 0, 0, 0.7))Degradado del fondo del pie
--rk-reel-slide-overlay-padding48px 16px 16pxRelleno interior del pie
--rk-reel-slide-overlay-name-color#fffColor del nombre del autor
--rk-reel-video-bg#000Fondo de las bandas detrás del <video>
--rk-reel-video-loader-colorrgba(255, 255, 255, 0.15)Color del brillo mientras el vídeo carga
--rk-reel-nested-button-bgrgba(0, 0, 0, 0.5)Fondo de las flechas anidadas
--rk-reel-nested-button-size36pxTamaño de las flechas anidadas
--rk-reel-nested-edge-padding12pxSeparación del borde de las flechas anidadas
--rk-reel-timeline-trackrgba(255, 255, 255, 0.22)Fondo de la pista (la zona sin reproducir)
--rk-reel-timeline-bufferedrgba(255, 255, 255, 0.4)Color de los segmentos cargados
--rk-reel-timeline-fill#fffColor del relleno del progreso reproducido
--rk-reel-timeline-cursor#fffColor de la píldora de arrastre
--rk-reel-timeline-height3pxAltura de la pista en reposo
--rk-reel-timeline-height-active6pxAltura de la pista al pasar el ratón, con foco o al arrastrar
--rk-reel-timeline-cursor-width10pxAncho de la píldora en reposo
--rk-reel-timeline-cursor-width-active14pxAncho de la píldora al arrastrar
--rk-reel-timeline-cursor-height24pxAltura de la píldora en reposo
--rk-reel-timeline-cursor-height-active32pxAltura de la píldora al arrastrar
--rk-reel-timeline-transition0.15s ease-outAnimación al crecer y encoger la pista y la píldora

Pega el fragmento de abajo en una hoja de estilos cargada después de @reelkit/angular-reel-player/styles.css.

css
/* Brand the reel-player overlay */
:root {
  --rk-reel-overlay-bg: #0f172a;
  --rk-reel-button-bg: rgba(99, 102, 241, 0.65);
  --rk-reel-button-bg-hover-strong: rgba(168, 85, 247, 0.85);
  --rk-reel-edge-padding: 24px;
  --rk-reel-button-size: 52px;

  /* Timeline bar: brand-matched, beefier on desktop */
  --rk-reel-timeline-track: rgba(99, 102, 241, 0.25);
  --rk-reel-timeline-buffered: rgba(168, 85, 247, 0.45);
  --rk-reel-timeline-fill: #a855f7;
  --rk-reel-timeline-cursor: #a855f7;
  --rk-reel-timeline-height: 4px;
  --rk-reel-timeline-height-active: 8px;
  --rk-reel-timeline-cursor-width-active: 18px;
  --rk-reel-timeline-transition: 0.2s ease-out;
}

Accesibilidad

La raíz del overlay es un diálogo modal (role="dialog", aria-modal="true"). Usa el input ariaLabel para cambiar lo que anuncia el lector de pantalla; por defecto es "Video player". Cada slide lleva role="group", aria-roledescription="slide" y aria-label="Slide N of M".

El overlay captura el foco al abrirse y lo devuelve al disparador al cerrarse. Tab y Mayús+Tab recorren los elementos enfocables de dentro; el foco que sale (un clic fuera, un foco por código) vuelve dentro. Está implementado con captureFocusForReturn y createFocusTrap de @reelkit/core.

Atajos de teclado

TeclaAcción
ArrowUpSlide anterior
ArrowDownSlide siguiente
ArrowLeftMedio anterior (en el slider anidado)
ArrowRightMedio siguiente (en el slider anidado)
EscapeCierra el reproductor