Pular para conteúdo

Especificação do Componente: Renderizador de Cenário

Esta seção detalha a arquitetura lógica do componente de visualização (src/ui/cenario.ts). Diferente de image maps estáticos, este componente opera como uma Máquina de Estado, gerenciando geometria complexa (polígonos em perspectiva), transições animadas (Zoom/Modais) e a integração de segurança com o Moodle.

1. Ciclo de Vida da Renderização (Pipeline)

O componente segue um fluxo estrito de "Busca Paralela, Renderização Unificada" para evitar layout shifts (mudanças bruscas na tela) e garantir que o usuário só veja links que realmente funcionam.

flowchart TD
    A[Gatilho Detectado] --> B{Inicia Carregamento}
    B -->|Fetch 1| C["Download JSON Visual (CDN)"]
    B -->|Fetch 2| D["Consulta API Moodle (AJAX)"]
    C --> E[Validação de Schema]
    D --> F["Parse de Permissões (cmid)"]
    E & F --> G{Merge de Dados}
    G --> H[Cálculo de Geometria]
    H --> I[Renderização DOM Final]
    I --> J[Anexar Eventos e Tooltips]

1.1 Fases do Processo

  1. Mounting: O script injeta um loader (spinner) dentro do container alvo e prepara a estrutura HTML básica.
  2. Fetching:
    • Baixa o JSON de configuração (ex: cenario-01.json) para obter o layout visual.
    • Chama core_courseformat_get_state para resolver URLs dinâmicas e checar permissões.
  3. Merging (Fail Open):
    • O sistema cruza os dados do JSON com a resposta do Moodle pelo ID.
    • Se a API do Moodle falhar: O sistema assume que o acesso está Liberado (Fail Open) e tenta gerar links genéricos para não bloquear o aluno indevidamente.
    • Se a API responder:
      • uservisible === false: Hotspot marcado como Bloqueado (Visual + Comportamento).
      • uservisible === true: Hotspot recebe a URL final validada pelo LMS.
  4. Painting: O HTML é inserido na página. Se estiver em modo DEV, os auxiliares visuais (bordas piscantes) são ativados.

2. Lógica de Geometria e Responsividade

Para suportar objetos em perspectiva (como portas laterais e corredores) e garantir a adaptação a qualquer tela, o sistema utiliza dois modelos geométricos baseados em porcentagem.

2.1 Modelo Retangular (Box Model)

Usado para botões simples, TVs frontais e cartazes.

  • Input: x, y, width, height (Pixels originais).
  • Output: top: %; left: %; width: %; height: %;.
  • Fórmula: $$ \text{left} = (\frac{x}{W_{ref}}) \times 100\% $$ $$ \text{top} = (\frac{y}{H_{ref}}) \times 100\% $$

2.2 Modelo Poligonal (Clip-Path)

Usado para formas irregulares (trapézios, losangos) que simulam perspectiva.

  • Input: points: [[x1,y1], [x2,y2], [x3,y3], ...].
  • Cálculo: Cada par de coordenadas é convertido para porcentagem relativa ao tamanho total da imagem.
  • CSS Resultante:
    /* Exemplo para uma porta lateral em perspectiva */
    clip-path: polygon(10% 20%, 25% 15%, 25% 85%, 10% 90%);
    /* Área clicável precisa de fill (mesmo que transparente) para capturar eventos */
    background-color: transparent; 
    

3. Estados do Hotspot (Comportamento)

O componente visual reflete o estado funcional da atividade no Moodle.

3.1 Habilitado (Enabled)

  • Critério: uservisible: true ou link externo.
  • Comportamento: Cursor pointer, escala (zoom) ao passar o mouse, clique executa a ação.
  • CSS: .middag-app-cn-hotspot

3.2 Bloqueado (Locked)

  • Critério: uservisible: false (retornado explicitamente pelo Moodle).
  • Comportamento:
    • Cursor not-allowed.
    • Ícone de cadeado visível (opcional via CSS).
    • Clique prevenido (e.preventDefault()).
    • Tooltip exibe: "Atividade restrita".
  • CSS: .middag-app-cn-hotspot--locked

3.3 Carregando (Loading)

  • Critério: Enquanto as promessas do fetch estão pendentes.
  • Comportamento: Exibe skeleton ou spinner. Interação desabilitada.

4. Estilização Visual e Efeitos

O JSON define a semântica visual através da propriedade variant, que é mapeada para classes CSS prefixadas para isolamento total.

4.1 Variantes Pré-definidas

Variante JSON Classe CSS Aplicação Típica
glass-door .middag-app-cn-hotspot--glass-door Portas de vidro. Aplica opacidade leve e brilho no hover.
poster .middag-app-cn-hotspot--poster Cartazes na parede. Aplica box-shadow leve.
tv-screen .middag-app-cn-hotspot--tv-screen Telas de TV. Pode ter brilho interno ou scanlines.
btn-exit .middag-app-cn-hotspot--btn-exit Botão de sair/voltar para home.

4.2 Efeito de Hover em Polígonos

Como a propriedade border não acompanha o recorte do clip-path, utilizamos filtros CSS para criar o efeito de destaque ("glow") ao redor da forma irregular.

.middag-app-cn-hotspot:hover {
    /* Cria uma "luz" externa ao redor do recorte exato do polígono */
    filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.8)) brightness(1.1);
    cursor: pointer;
}

5. Sistema de Modais (TV Expandida)

O modal permite navegação aprofundada (ex: ver a tela da TV em tela cheia) sem sair do contexto da página.

5.1 Transição de Entrada (Zoom In)

Para criar continuidade visual:

  1. Captura: O JS captura o getBoundingClientRect() do hotspot clicado.
  2. Animação: O modal inicia com as dimensões e posição da origem e transiciona para top: 0; left: 0; width: 100% usando CSS Transitions.
  3. Backdrop: Um fundo escuro (opacity: 0.8) aparece suavemente atrás do conteúdo.

5.2 Estratégia de Fechamento

Sistema híbrido de segurança para evitar que o usuário fique preso:

  1. Botão "X" Nativo: Renderizado automaticamente no canto superior direito (z-index máximo).
  2. Hotspot Interno: O conteudista pode desenhar um botão "Voltar" dentro da imagem da TV.
  3. Tecla ESC: Fecha o modal e devolve o foco para o elemento de origem.

6. Developer Experience (DX)

Para facilitar a configuração das coordenadas JSON sem "chutar" valores, o ambiente de desenvolvimento possui auxiliares visuais.

6.1 Modo de Debug

O script detecta automaticamente localhost ou import.meta.env.DEV. Quando ativo, adiciona a classe .middag-app-cn-debug-mode ao container.

/* Apenas em Dev/Localhost: Pisca os hotspots para facilitar a localização visual */
.middag-app-cn-debug-mode .middag-app-cn-hotspot {
    background-color: rgba(255, 0, 0, 0.2) !important; /* Vermelho translúcido */
    border: 1px dashed red;
    animation: middag-app-cn-debug-pulse 2s infinite;
}

@keyframes middag-app-cn-debug-pulse {
    0% {
        opacity: 0.3;
    }
    50% {
        opacity: 0.7;
    }
    100% {
        opacity: 0.3;
    }
}

Isso permite que o desenvolvedor veja exatamente onde as áreas clicáveis estão, mesmo que sejam transparentes em produção.


7. Estrutura DOM Renderizada

Abaixo, a hierarquia final injetada no DOM. O gerenciamento de z-index é crítico para que o Modal cubra o cenário, mas não o menu do Moodle (se desejado).

<div class="middag-app-cn-container middag-app-cn-debug-mode">

    <div class="middag-app-cn-scene-layer">
        <img src="..." class="middag-app-cn-image-base" alt="" aria-hidden="true"/>

        <button
                class="middag-app-cn-hotspot middag-app-cn-hotspot--glass-door"
                style="clip-path: polygon(10% 20%, ...);"
                aria-label="Acessar Triagem"
        ></button>

        <button
                class="middag-app-cn-hotspot middag-app-cn-hotspot--tv-screen"
                data-action="modal"
                style="top: 40%; left: 40%; ..."
        ></button>
    </div>

    <div class="middag-app-cn-modal-layer" aria-hidden="true" style="display: none;">
        <div class="middag-app-cn-modal-backdrop"></div>

        <div class="middag-app-cn-modal-content">
            <img src=".../tv-expandida.jpg" class="middag-app-cn-image-modal"/>

            <a href="..." class="middag-app-cn-hotspot ...">Link Videoaula</a>

            <button class="middag-app-cn-modal-close-btn" aria-label="Fechar"></button>
        </div>
    </div>
</div>

8. Acessibilidade (a11y)

O componente adere às diretrizes WCAG 2.1 AA.

8.1 Navegação e Foco

  • Ordem de Tabulação: Segue a ordem do array hotspots no JSON. Elementos bloqueados usam <button disabled>, que nativamente são removidos da ordem de tabulação em alguns navegadores, ou mantidos com aria-disabled="true" se quisermos que o usuário saiba que eles existem (configurável).
  • Indicador de Foco: O CSS implementa :focus-visible com outline de alto contraste para navegação via teclado.

8.2 Leitores de Tela

  • Rótulos: Usa aria-label concatenado com o estado (ex: "Avisos (Bloqueado)").
  • Imagens: Imagens de fundo possuem aria-hidden="true" para reduzir ruído.

8.3 Gestão de Modal (Focus Trap)

Quando o modal abre:

  1. O foco é movido para o botão "Fechar" ou primeiro elemento interativo.
  2. Um Focus Trap impede que o Tab saia do modal e vá para o fundo da página.
  3. A tecla Esc fecha o modal imediatamente.

9. CSS Isolado (Scoped)

Para evitar conflitos com temas do Moodle (Boost, Classic), todas as classes usam o prefixo middag-app-cn-. Não utilizamos IDs para estilização.

/* Exemplo de CSS injetado */
.middag-app-cn-container {
    position: relative;
    width: 100%;
    line-height: 0;
}

.middag-app-cn-hotspot {
    position: absolute;
    z-index: 10;
    display: block;
    /* Reset de estilos do botão do tema */
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    transition: transform 0.2s ease;
}

.middag-app-cn-hotspot:hover, .middag-app-cn-hotspot:focus {
    transform: scale(1.05);
    box-shadow: 0 0 15px rgba(255, 255, 0, 0.5);
}

.middag-app-cn-hotspot--locked {
    cursor: not-allowed;
    background-color: rgba(0, 0, 0, 0.3);
    border: 1px dashed #999;
}

Próximo passo na leitura: Verifique as regras de integridade do JSON na seção Validação de Campos.