Da grade ao player

Uma página de perfil ou um resultado de busca mostra os posts como uma grade de miniaturas. Tocar em uma deve abri-la em tela cheia, deixar o usuário seguir deslizando pelo resto e, quando ele fechar o player, deixá-lo no post em que parou, não no post em que começou.

Experimente

Todas as versões importam ./api, que faz o papel do seu backend: posts é a lista que um perfil ou uma busca carregaria, no formato do ContentItem do player, e thumbnailOf escolhe a imagem que uma célula da grade mostra. Troque os dois pelos seus próprios dados.

typescript
import { cdnUrl } from '@reelkit/example-data';

// Stands in for your backend: the posts a profile or a search result would
// load. Each one has the shape of `ContentItem` from the reel player of your
// framework, so the list goes to the player as it is.

export interface Post {
  id: string;
  media: {
    id: string;
    type: 'image' | 'video';
    src: string;
    poster?: string;
    aspectRatio: number;
  }[];
  author: { name: string; avatar: string };
  likes: number;
  description: string;
}

const kAuthors = ['Alex Johnson', 'Sarah Miller', 'Mike Chen', 'Emma Davis'];

const pad = (n: number) => String(n).padStart(2, '0');

export const posts: Post[] = Array.from({ length: 12 }, (_, i) => {
  const isVideo = i % 4 === 0;
  const n = (i % (isVideo ? 3 : 9)) + 1;
  return {
    id: `post-${i + 1}`,
    media: [
      isVideo
        ? {
            id: `video-${i + 1}`,
            type: 'video' as const,
            src: cdnUrl(`samples/videos/video-${pad(n)}.mp4`),
            poster: cdnUrl(`samples/videos/video-poster-${pad(n)}.jpg`),
            aspectRatio: 16 / 9,
          }
        : {
            id: `image-${i + 1}`,
            type: 'image' as const,
            src: cdnUrl(`samples/images/image-${pad(n)}.jpg`),
            aspectRatio: 2 / 3,
          },
    ],
    author: {
      name: kAuthors[i % kAuthors.length],
      avatar: cdnUrl(`samples/avatars/avatar-${pad((i % 8) + 1)}.jpg`),
    },
    likes: 120 + i * 37,
    description: `Post ${i + 1}`,
  };
});

/** The picture a grid cell shows: a video's poster, or the image itself. */
export const thumbnailOf = (post: Post) =>
  post.media[0].poster ?? post.media[0].src;
A mídia de exemplo só funciona em reelkit.dev

cdnUrl monta links para cdn.reelkit.dev, que só entrega seus arquivos aos sites do ReelKit. No seu app essas imagens e vídeos não carregam, então aponte media e avatar para os seus próprios arquivos antes de copiar este arquivo.

GridToPlayer.tsx
GridToPlayer.vue
grid-to-player.component.ts

Abra um post, deslize ou aperte ↓ algumas vezes e feche com Escape: a grade rola até o post em que você parou e a miniatura dele recebe o foco.

Por que funciona

  • O player abre onde o usuário tocou. initialIndex recebe o índice da miniatura e a lista inteira vai para content, então o usuário pode continuar deslizando além do post que escolheu. As duas são props do player em React, Vue e Angular.
  • A grade sabe onde o usuário parou. O callback de troca de slide informa cada post que o usuário alcança, e a receita guarda o último. Ele aparece entre os callbacks, eventos e outputs do player.
  • O foco volta para a miniatura certa. Ao fechar, o player devolve o foco ao elemento que o abriu, como manda a sua acessibilidade. Essa devolução acontece depois que o handler de fechamento rodou, então um foco definido dentro do handler seria desfeito. A receita espera uma tarefa, rola a miniatura do último post até ficar visível e coloca o foco nela.
  • As miniaturas são botões. Cada célula é um <button> com rótulo, então a grade funciona com Tab e Enter, um leitor de tela anuncia o post que ela abre e o player tem um elemento focável para devolver o foco, como descreve a seção de acessibilidade.
  • Uma célula da grade precisa de imagem, não de vídeo. thumbnailOf usa o poster de um vídeo, um campo de MediaItem, e uma imagem como ela é, então a grade nunca carrega um vídeo que não vai tocar.
  • Quer o post aberto na barra de endereço? O player com estado na URL coloca o post visível na URL, para ele poder ser compartilhado e fechado com o botão voltar.

Próximos passos