Angular Reel Player
Player vertical de mídia em tela cheia no estilo Instagram/TikTok para Angular, construído sobre @reelkit/angular-reel-player.
Recursos
Instalação
Ícones
Os controles padrão usam lucide-angular para os ícones (fechar, som, setas de navegação). Se você prefere outra biblioteca de ícones, use os slots de template rkPlayerControls e rkPlayerNavigation para fornecer os seus.
Uso básico
Importe a folha de estilo e o componente standalone RkReelPlayerOverlayComponent no array imports do seu componente.
Slots de template
Seis diretivas de slot de template permitem personalizar cada parte da interface do player. Cada uma recebe um objeto de contexto fortemente tipado. Informe apenas os slots que quiser trocar — nos demais valem os padrões.
| Diretiva | Tipo do contexto | Descrição |
|---|---|---|
| [rkPlayerControls] | PlayerControlsContext<T> | Barra de controles global própria (fechar, botão de som e afins) |
| [rkPlayerError] | { $implicit: activeIndex, item, innerActiveIndex } | Slot de template do indicador de erro próprio |
| [rkPlayerLoading] | { $implicit: activeIndex, item, innerActiveIndex } | Slot de template do indicador de carregamento próprio |
| [rkPlayerNavigation] | PlayerNavigationContext | Setas próprias de avançar e voltar |
| [rkPlayerNestedNavigation] | PlayerNestedNavigationContext | Setas próprias do slider horizontal interno |
| [rkPlayerNestedSlide] | PlayerNestedSlideContext | Conteúdo próprio de cada slide dentro do slider horizontal interno |
| [rkPlayerSlide] | PlayerSlideContext<T> | Conteúdo de slide totalmente seu, no lugar do slide de mídia padrão |
| [rkPlayerSlideOverlay] | PlayerSlideOverlayContext<T> | Camada de cada slide (dados do autor, curtidas, descrição e afins) |
| [rkPlayerTimeline] | PlayerTimelineContext<T> | Barra de reprodução própria. Só é renderizada quando a regra (modo da linha do tempo e duração mínima) renderizaria a barra padrão (a mesma lógica de auto/always/never). |
Linha do tempo própria
O slot de template rkPlayerTimeline só é chamado quando as regras do overlay renderizariam a barra padrão (o mesmo modo timeline e o mesmo timelineMinDurationSeconds), então você não precisa reimplementá-las. Reaproveite a classe .rk-reel-timeline na sua raiz para herdar o posicionamento rente à base, o respiro da área segura e o espaço reservado em dispositivos de toque. Chame state.bindInteractions(el) na sua trilha de arrasto para ligar o arrasto por ponteiro e por teclado.
Slider aninhado (itens com várias mídias)
Quando um ContentItem traz várias entradas em media, o player as exibe em um slider horizontal aninhado (no estilo carrossel do Instagram). Use o slot rkPlayerNestedSlide para personalizar o conteúdo dos slides internos.
Carregamento de conteúdo e tratamento de erros
O player acompanha o carregamento e os erros de cada slide. Uma animação de onda aparece enquanto o conteúdo carrega; um ícone de erro aparece para mídias quebradas. As URLs com erro ficam em cache, então voltar a elas mostra o erro na hora, sem nova tentativa.
Callbacks de ciclo de vida
Ao usar o slot de template rkPlayerSlide, use os callbacks do contexto para comandar o indicador de carregamento:
| Callback | Quando chamar |
|---|---|
onReady | A imagem carregou ou o vídeo começou a tocar. Limpa os estados de carregamento e erro. |
onWaiting | O vídeo está carregando no meio da reprodução. Mostra o indicador de carregamento. |
onError | O conteúdo não carregou. Mostra a camada de erro e registra a URL como quebrada. |
Interface própria de carregamento e erro
Troque a animação de onda e o ícone de erro padrão por templates seus:
Linha do tempo
O overlay desenha uma barra de reprodução embutida sobre o vídeo ativo. Libere-a pelo input timeline: 'auto' (padrão) aparece sempre que a mídia ativa for um vídeo mais longo que timelineMinDurationSeconds (30 por padrão), 'always' sempre que houver um vídeo ativo, 'never' para desligar. Para uma barra totalmente sua, use a diretiva de template rkPlayerTimeline; o contexto dela expõe um timelineState apoiado no TimelineController por baixo.
Personalize pelas propriedades CSS --rk-reel-timeline-*. Para controle direto em componentes seus, injete o TimelineStateService.
RkTimelineBarComponent
Componente da barra de reprodução padrão. Consome o TimelineStateService (fornecido pelo RkReelPlayerOverlayComponent) e desenha a trilha, os trechos em buffer, o preenchimento do progresso e a pílula de arrasto. Seletor: rk-timeline-bar. Inputs: class?: string, style?: Record<string, string>. Use dentro de um template rkPlayerTimeline para envolver ou complementar a barra padrão; use isolado apenas dentro de um componente que forneça o serviço.
SoundStateService
Fornecido no nível do RkReelPlayerOverlayComponent. Injetado pelo botão de som padrão e exposto no contexto do slot de controles. Pode ser injetado em controles próprios que sejam filhos do overlay, para acesso direto.
| Membro | Tipo | Descrição |
|---|---|---|
muted() | Signal<boolean> | Se o player está sem som no momento |
disabled() | Signal<boolean> | True quando o slide ativo não tem vídeo ou está em transição |
toggle() | () => void | Alterna o estado de mudo |
Estado na URL
Ver demonstração ao vivo →RkReelPlayerUrlOverlayComponent é um componente à parte cujo estado aberto vive na barra de endereços. Monte um controlador com createOverlayUrlState em um contexto de injeção e passe-o como [controller]: o player abre quando o parâmetro nomeia um slide e fecha quando ele some. Os links dão para compartilhar, e o botão voltar fecha o player. O RkReelPlayerOverlayComponent continua comandado por [isOpen], então cada componente carrega exatamente um comando do estado aberto.
Chaves prontas
Você pode endereçar os slides com uma chave pronta — espalhe urlIndexKey (por posição) ou urlStableIdKey (por um id estável) no controlador — as duas reexportadas por @reelkit/angular. Veja o guia de estado na URL e a API do core.
Um app com rotas passa um adaptador ligado ao Router, para que ele siga sendo a única fonte de verdade da navegação — escrever no histórico por trás dele deixa sua localização desatualizada e derruba o parâmetro na navegação seguinte. createRouterUrlAdapter, de @reelkit/angular/ng-router-url-adapter, é o adaptador pronto.
- Abrir empilha uma entrada no histórico. Percorrer o feed substitui essa entrada, então N deslizes não acrescentam nenhuma e um único passo para trás sempre sai do player. Voltar fecha; não troca de slide.
- Voltar só fecha quando o player foi aberto de dentro do app — foi o link que empilhou a entrada. Um link compartilhado aberto direto em uma aba nova não tem histórico atrás de si, então o voltar do navegador sai do site; o botão ✕ ou o Escape removem o parâmetro ali mesmo e você continua na página.
- Um link direto
?reel=3abre o player naquele slide já no carregamento. - Um parâmetro que não nomeia slide nenhum — um favorito velho, um valor editado à mão — é retirado da URL, em vez de deixar a barra de endereços afirmando um slide que não abre.
- A profundidade da URL acompanha a chave do controlador: eixo único para o post apenas, ou dois eixos (
urlIndexTwoAxisKey) para carregar também o índice da imagem interna de um post com várias mídias. Escolha uma chave por app; os formatos não são decodificados um pelo outro.
As opções completas de createOverlayUrlState estão na referência da API para Angular.
Uma chave ou duas — escolha a profundidade da URL
O mesmo RkReelPlayerUrlOverlayComponent conduz os dois formatos; ele distingue em tempo de execução pela posição do controlador, então não existe input de modo. Escolha a chave ao montar o controlador:
| Chave | Fio | O que carrega |
|---|---|---|
urlIndexKey(…) | ?reel=3 | Apenas o post vertical. |
urlIndexTwoAxisKey(…) | ?reel=3.2 | O post e o índice da mídia interna do carrossel. |
Os dois fios são deliberadamente distintos — uma chave de dois eixos é estritamente pontuada (3.0, nunca um 3 solto), então um link de um eixo não é decodificado pelo outro. Trocar a chave de um app, portanto, invalida qualquer link já compartilhado. Escolha um formato e fique com ele.
Links estáveis. O índice é posicional, então um ?reel=3 favoritado abre outro post assim que o feed muda de ordem — e num feed isso é o normal. urlStableIdKey usa o id estável de cada post, varrendo o feed atual — uma chamada resolve o caso comum.
Passe hashCodec: base64UrlCodec para escrever o id em base64url na URL — é um disfarce reversível, não um hash criptográfico.
Quer endereçar por outro campo (um slug), ou paginar um feed infinito com locateAsync? Monte o codec e o locator você mesmo. São dois papéis distintos: o codec escreve a identidade na URL, o locator descobre onde ela está.
Feeds infinitos. locate é síncrono, então só responde pelos posts já carregados — um link compartilhado para o post 400 de um feed que carregou 20 volta vazio. locateAsync é o plano B, chamado somente quando locate falha: carregue as páginas necessárias e devolva o índice que a identidade acabou tendo.
Atalho
Vai endereçar pelo id do item? Dispense o codec e o locator escritos à mão — passe locateAsync direto para urlStableIdKey({ items, locateAsync }) (ele busca quando falha e então devolve o índice). A versão mais completa abaixo serve para endereçar por outro campo, ou para ter controle total.
- Enquanto
locateAsyncestá pendente, o player fica fechado e o parâmetro é deixado em paz, então o link direto sobrevive à busca. Umnullou uma rejeição derrubam o parâmetro. - Uma resposta que chega depois de a URL ter mudado, depois de um fechamento ou depois da desmontagem é descartada — uma busca lenta não pode abrir um slide que ninguém pediu.
- Nada é renderizado enquanto a busca corre; esse estado de carregamento já pertence à página, então desenhe o seu próprio esqueleto.
- Não existe tempo limite — o player não tem como saber o tamanho do feed. Termine com
nullquando a paginação se esgotar, ou o overlay fica fechado para sempre.
Tipos de dados próprios
Estenda BaseContentItem para usar o seu próprio modelo de domínio. O componente é genérico: RkReelPlayerOverlayComponent<T extends BaseContentItem>.
Inputs do RkReelPlayerOverlayComponent
| Input | Tipo | Padrão | Descrição |
|---|---|---|---|
ariaLabel | string | 'Video player' | Rótulo acessível da região do diálogo |
aspectRatio | number | undefined | undefined | Proporção entre largura e altura do contêiner no desktop. O padrão é 9/16. No celular o player ocupa a tela inteira. |
content | T[] (extends BaseContentItem) | obrigatório | Array de itens de conteúdo a exibir no player |
enableNavKeys | boolean | true | Liga a navegação pelas setas do teclado |
enableWheel | boolean | true | Liga a navegação pela roda do mouse |
initialIndex | number | 0 | Índice, começando em zero, do item visível no início |
initialInnerIndex | number | 0 | Índice da mídia interna em que abrir, apenas para o post visível no início — permite que uma URL de dois eixos aponte direto para uma imagem específica de um post com várias mídias. Ignorado assim que o leitor navega. |
isOpen | boolean | obrigatório | Controla a visibilidade do overlay; com false, o overlay sai do DOM |
loop | boolean | false | Liga o ciclo infinito entre os slides |
swipeDistanceFactor | number | 0.12 | Fração mínima da distância de deslize para trocar de slide |
timeline | 'auto' | 'always' | 'never' | 'auto' | Estratégia que libera a barra de reprodução embutida. 'auto' só renderiza para vídeos mais longos que timelineMinDurationSeconds; 'always' renderiza sempre que o slide ativo tiver vídeo; 'never' desliga a barra embutida (use o slot de template rkPlayerTimeline para substituí-la inteiramente). |
timelineMinDurationSeconds | number | 30 | Duração mínima do vídeo, em segundos, para que timeline='auto' renderize a barra embutida. Clipes curtos em repetição abaixo desse limite ficam sem barra. |
transitionDuration | number | 300 | Duração da animação do slide em ms |
wheelDebounceMs | number | 200 | Duração do debounce dos eventos de roda em ms |
Outputs do RkReelPlayerOverlayComponent
| Output | Tipo | Descrição |
|---|---|---|
apiReady | EventEmitter<ReelApi> | Emitido assim que o slider fica pronto, expondo a API imperativa |
closed | EventEmitter<void> | Emitido quando o player fecha |
slideChange | EventEmitter<number> | Emitido quando o índice do slide ativo muda |
innerSlideChange | EventEmitter<{ outer: number; inner: number }> | Emitido quando o índice da mídia interna do post ativo muda — na navegação interna e na ativação externa, informando o índice interno atual do post ativado (0 para um post de mídia única). |
Inputs do RkReelPlayerUrlOverlayComponent
Aceita todos os inputs acima, menos isOpen e initialIndex, substituídos por um controller cuja posição escolhe o slide. Emite closed e slideChange.
| Input | Tipo | Padrão | Descrição |
|---|---|---|---|
controller | UrlStateController | obrigatório | Controlador vindo de createOverlayUrlState. A position dele decide se o player está aberto e qual slide aparece; o overlay escreve de volta por ele na troca de slide e no fechamento. |
Interface MediaItem
| Campo | Tipo | Descrição |
|---|---|---|
id | string | Identificador único do item de mídia |
type | 'image' | 'video' | Tipo da mídia |
src | string | URL do arquivo de mídia |
poster | string? | URL da miniatura de pôster, para itens de vídeo |
aspectRatio | number | Proporção entre largura e altura. Valores < 1 indicam vertical (cover), > 1 horizontal (contain) |
Tipos de contexto dos slots de template
| Tipo | Campos |
|---|---|
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) } |
Classes CSS
Todas as classes CSS são simples (sem escopo), então dá para alcançá-las com seletores de especificidade maior em uma folha de estilo carregada depois de @reelkit/angular-reel-player/styles.css. Para mudar cor, tamanho e z-index, prefira as propriedades CSS documentadas na seção Temas, abaixo — elas existem exatamente para isso.
| Classe | Componente | Descrição |
|---|---|---|
.rk-reel-overlay | Overlay | Fundo fixo em tela cheia (cor de fundo, z-index) |
.rk-reel-container | Overlay | Contêiner do player (posição, transbordo) |
.rk-reel-loader | Overlay | Camada da animação de onda do carregamento |
.rk-reel-media-error | Overlay | Camada do estado de erro (ícone e texto centralizados) |
.rk-reel-media-error-text | Overlay | Texto da mensagem de erro |
.rk-reel-button | Controls | Botão circular de ícone compartilhado (fechar, som, setas) |
.rk-reel-close-btn | Controls | Botão de fechar |
.rk-reel-sound-btn | Controls | Botão de alternar o som |
.rk-reel-nav-arrows | Navigation | Contêiner das setas, só no desktop (escondido abaixo de 768px) |
.rk-reel-nav-btn | Navigation | Cada seta de avançar e voltar |
.rk-reel-slide-wrapper | Slide | Invólucro em volta da mídia e da camada sobreposta |
.rk-reel-slide-overlay | SlideOverlay | Contêiner da camada em gradiente |
.rk-reel-slide-overlay-author | SlideOverlay | Linha do autor (avatar e nome) |
.rk-reel-slide-overlay-avatar | SlideOverlay | Imagem do avatar do autor |
.rk-reel-slide-overlay-name | SlideOverlay | Texto do nome do autor |
.rk-reel-slide-overlay-description | SlideOverlay | Texto da descrição |
.rk-reel-slide-overlay-likes | SlideOverlay | Linha das curtidas (coração e contagem) |
.rk-reel-video-container | VideoSlide | Invólucro do vídeo (cor de fundo, transbordo) |
.rk-reel-video-element | VideoSlide | O elemento <video> |
.rk-reel-video-poster | VideoSlide | Imagem de pôster (some ao dar play) |
.rk-reel-video-loader | VideoSlide | Animação de onda do carregamento |
.rk-reel-video-poster.rk-visible | VideoSlide | Modificador de estado aplicado ao pôster enquanto o vídeo está pausado ou carregando |
.rk-reel-nested-indicator | NestedSlider | Pontinhos de paginação sob os slides com várias mídias (a posição muda entre desktop e toque) |
.rk-reel-nested-nav | NestedSlider | Setas do carrossel horizontal (escondidas abaixo de 768px) |
.rk-reel-nested-nav-next | NestedSlider | Posição da seta de avançar aninhada |
.rk-reel-nested-nav-prev | NestedSlider | Posição da seta de voltar aninhada |
.rk-reel-nested-slider-inner | NestedSlider | Raiz do slider horizontal aninhado |
.rk-reel-timeline | TimelineBar | Invólucro da barra de arrasto. Reaproveite nas raízes do seu template `rkPlayerTimeline` para herdar o posicionamento rente à base, o respiro da área segura e o espaço reservado para a camada do slide em dispositivos de toque. |
.rk-reel-timeline-track | TimelineBar | Trilha (trecho ainda não reproduzido) |
.rk-reel-timeline-buffered | TimelineBar | Camada dos trechos em buffer |
.rk-reel-timeline-fill | TimelineBar | Preenchimento do que já foi reproduzido |
.rk-reel-timeline-cursor | TimelineBar | Pílula da alça de arrasto (flutua acima da trilha) |
Temas
Cada cor, tamanho, z-index e transição vive em uma propriedade CSS. Sobrescreva uma ou várias em :root (ou em qualquer ancestral do overlay) para trocar o tema sem mexer no código dos componentes. Os tokens são os mesmos dos pacotes de React e de Vue, então as sobrescritas valem para todos os bindings.
| Token | Padrão | O que controla |
|---|---|---|
--rk-reel-overlay-bg | #000 | Cor do fundo em tela cheia |
--rk-reel-overlay-z | 1000 | z-index do overlay |
--rk-reel-button-bg | rgba(0, 0, 0, 0.5) | Fundo padrão dos botões circulares |
--rk-reel-button-bg-hover | rgba(255, 255, 255, 0.1) | Fundo das setas de navegação (e estado básico ao passar o mouse) |
--rk-reel-button-bg-hover-strong | rgba(255, 255, 255, 0.2) | Fundo das setas ao passar o mouse |
--rk-reel-button-fg | #fff | Cor do ícone do botão |
--rk-reel-button-size | 44px | Largura e altura do botão |
--rk-reel-button-radius | 50% | Arredondamento do botão |
--rk-reel-ui-z | 10 | z-index de fechar, som e navegação |
--rk-reel-edge-padding | 16px | Recuo da borda para fechar, som e setas |
--rk-reel-nav-gap | 8px | Espaço entre as setas empilhadas |
--rk-reel-transition | 0.2s | Duração da transição ao passar o mouse |
--rk-reel-loader-color | rgba(255, 255, 255, 0.12) | Cor do gradiente da animação de onda |
--rk-reel-loader-duration | 1.8s | Duração da animação de onda |
--rk-reel-error-fg | rgba(255, 255, 255, 0.4) | Cor do ícone e do texto de erro |
--rk-reel-slide-overlay-bg | linear-gradient(transparent, rgba(0, 0, 0, 0.7)) | Gradiente que escurece o fundo da legenda |
--rk-reel-slide-overlay-padding | 48px 16px 16px | Respiro interno da legenda |
--rk-reel-slide-overlay-name-color | #fff | Cor do nome do autor |
--rk-reel-video-bg | #000 | Cor das faixas em volta do <video> |
--rk-reel-video-loader-color | rgba(255, 255, 255, 0.15) | Cor do brilho que passa enquanto o vídeo carrega |
--rk-reel-nested-button-bg | rgba(0, 0, 0, 0.5) | Fundo das setas aninhadas |
--rk-reel-nested-button-size | 36px | Tamanho das setas aninhadas |
--rk-reel-nested-edge-padding | 12px | Recuo das setas aninhadas em relação à borda |
--rk-reel-timeline-track | rgba(255, 255, 255, 0.22) | Fundo da trilha (trecho ainda não reproduzido) |
--rk-reel-timeline-buffered | rgba(255, 255, 255, 0.4) | Cor dos trechos em buffer |
--rk-reel-timeline-fill | #fff | Cor do preenchimento já reproduzido |
--rk-reel-timeline-cursor | #fff | Cor da pílula da alça de arrasto |
--rk-reel-timeline-height | 3px | Altura da trilha em repouso |
--rk-reel-timeline-height-active | 6px | Altura da trilha ao passar o mouse, no foco ou durante o arrasto |
--rk-reel-timeline-cursor-width | 10px | Largura da pílula em repouso |
--rk-reel-timeline-cursor-width-active | 14px | Largura da pílula durante o arrasto |
--rk-reel-timeline-cursor-height | 24px | Altura da pílula em repouso |
--rk-reel-timeline-cursor-height-active | 32px | Altura da pílula durante o arrasto |
--rk-reel-timeline-transition | 0.15s ease-out | Animação de crescer e encolher da trilha e da pílula |
Cole o trecho abaixo em uma folha de estilo carregada depois de @reelkit/angular-reel-player/styles.css.
Acessibilidade
A raiz do overlay é um diálogo modal (role="dialog", aria-modal="true"). Defina o input ariaLabel para mudar o que o leitor de tela anuncia; o padrão é "Video player". Cada slide carrega role="group", aria-roledescription="slide" e aria-label="Slide N of M".
O overlay captura o foco ao abrir e o devolve ao gatilho ao fechar. Tab e Shift+Tab percorrem os elementos focáveis lá dentro; um foco que escapa (clique fora, foco por código) é trazido de volta. Implementado com captureFocusForReturn e createFocusTrap, de @reelkit/core.
Atalhos de teclado
| Tecla | Ação |
|---|---|
ArrowUp | Slide anterior |
ArrowDown | Próximo slide |
ArrowLeft | Mídia anterior (no slider aninhado) |
ArrowRight | Próxima mídia (no slider aninhado) |
Escape | Fecha o player |