Todas as atividades
Vídeo e Áudio

Vídeo 360°

Vídeo imersivo gravado em 360° que permite ao aprendiz controlar o ponto de vista durante a reprodução. Cria senso de presença em ambientes reais inacessíveis ou perigosos.

Compreensão 360 imersivo vídeo realidade virtual

Experimente

Treinamento de astronauta para caminhada espacial (NASA VR/360)

0:00
2:56

Pontos de interesse

Traje espacial de treinamento (EMU) usado na piscina Piscina do Laboratório de Flutuabilidade Neutra (NBL) Equipe de mergulhadores de apoio ao astronauta
Vídeo 360° real, gravado pela NASA no Laboratório de Flutuabilidade Neutra (NBL) em Houston, onde astronautas treinam debaixo d'água para simular a ausência de gravidade antes de missões à Estação Espacial Internacional. Arraste o vídeo (ou mova o celular) para olhar ao redor enquanto ele toca — a exploração é livre, sem pontos obrigatórios.

Código-fonte

PHP

Dados de exemplo

O título exibido no cabeçalho.

1 <?php
2 $titulo = 'Explorando uma Sala de Controle';
3 ?>

Vídeo maior que a janela visível

O <video> não tem o atributo "controls" e é bem mais largo/alto que a janela (overflow: hidden esconde o excesso) — arrastar move a cena por dentro dela, simulando explorar um ambiente maior que a tela.

1 <div class="pan-demo" data-tipo="video-arrastavel">
2
3 <div class="pan-cabecalho">
4 <p><?= htmlspecialchars($titulo) ?></p>
5 </div>
6
7 <div class="pan-janela">
8 <!-- Troque pelo caminho do seu vídeo -->
9 <video class="pan-elemento" src="seu-video.mp4" preload="metadata"></video>
10
11 <!-- Camada transparente por cima do vídeo: é ela que recebe o arrasto. -->
12 <div class="pan-arrastavel" aria-hidden="true"></div>
13
14 <p class="pan-aviso">Arraste para explorar</p>
15 </div>
16
17 <div class="pan-controles">
18 <button type="button" class="pan-play-pause">Reproduzir</button>
19 </div>
20
21 </div>
CSS

Card e cabeçalho

Contêiner geral e a caixa de título centralizada.

1 .pan-demo {
2 max-width: 560px;
3 border: 1px solid #e2e8f0;
4 border-radius: 16px;
5 background: #fff;
6 font-family: sans-serif;
7 overflow: hidden;
8 }
9
10 .pan-cabecalho {
11 padding: 20px 24px;
12 border-bottom: 1px solid #cbd5e1;
13 background: rgba(241, 245, 249, .6);
14 text-align: center;
15 }
16
17 .pan-cabecalho p {
18 margin: 0;
19 font-weight: 700;
20 font-size: 18px;
21 color: #0f172a;
22 }

Janela e vídeo arrastável

overflow: hidden esconde o excesso do vídeo, que é bem maior que a janela — só uma parte da cena fica visível por vez.

1 .pan-janela {
2 position: relative;
3 aspect-ratio: 16 / 9;
4 overflow: hidden;
5 background: #000;
6 }
7
8 .pan-elemento {
9 position: absolute;
10 top: 50%;
11 left: 50%;
12 width: 180%;
13 height: 180%;
14 transform: translate(-50%, -50%);
15 object-fit: cover;
16 pointer-events: none;
17 }
18
19 .pan-arrastavel {
20 position: absolute;
21 inset: 0;
22 cursor: grab;
23 touch-action: none;
24 user-select: none;
25 }
26
27 .pan-arrastavel:active {
28 cursor: grabbing;
29 }
30
31 .pan-aviso {
32 position: absolute;
33 top: 12px;
34 left: 50%;
35 transform: translateX(-50%);
36 margin: 0;
37 padding: 6px 12px;
38 border-radius: 999px;
39 background: rgba(0, 0, 0, .6);
40 color: #fff;
41 font-size: 12px;
42 pointer-events: none;
43 }

Controles

Um botão só alterna reproduzir/pausar.

1 .pan-controles {
2 padding: 12px 16px 16px;
3 text-align: center;
4 }
5
6 .pan-play-pause {
7 padding: 10px 24px;
8 border: none;
9 border-radius: 999px;
10 background: #6366f1;
11 color: #fff;
12 font: inherit;
13 font-weight: 600;
14 cursor: pointer;
15 }
JavaScript

Arrastar sem confundir com um clique

A mesma camada transparente recebe os dois gestos — um clique sem deslocamento alterna reproduzir/pausar; só um arrasto de verdade move a cena. Sem essa distinção, todo clique seria interpretado como os dois ao mesmo tempo.

1 document.addEventListener('DOMContentLoaded', function () {
2 var demo = document.querySelector('[data-tipo="video-arrastavel"]');
3 var video = demo.querySelector('.pan-elemento');
4 var janela = demo.querySelector('.pan-janela');
5 var arrastavel = demo.querySelector('.pan-arrastavel');
6 var playPauseBtn = demo.querySelector('.pan-play-pause');
7
8 function alternarPlayPause() {
9 if (video.paused) { video.play(); playPauseBtn.textContent = 'Pausar'; }
10 else { video.pause(); playPauseBtn.textContent = 'Reproduzir'; }
11 }
12
13 playPauseBtn.addEventListener('click', alternarPlayPause);

Mover a cena na mesma direção do arrasto

O conteúdo acompanha o mouse, como arrastar uma foto — arrastar pra direita revela o que estava escondido à esquerda. deslocX/deslocY guardam a posição entre um arrasto e o próximo.

1 var arrastando = false;
2 var moveu = false;
3 var inicioX = 0, inicioY = 0;
4 var deslocX = 0, deslocY = 0;
5 var LIMIAR = 4; // pixels — abaixo disso ainda conta como clique, não arrasto
6
7 function limites() {
8 return {
9 x: Math.max(0, (video.offsetWidth - janela.clientWidth) / 2),
10 y: Math.max(0, (video.offsetHeight - janela.clientHeight) / 2)
11 };
12 }
13
14 function aplicarPosicao(x, y) {
15 video.style.transform = 'translate(calc(-50% + ' + x + 'px), calc(-50% + ' + y + 'px))';
16 }
17
18 arrastavel.addEventListener('pointerdown', function (evento) {
19 arrastando = true;
20 moveu = false;
21 inicioX = evento.clientX;
22 inicioY = evento.clientY;
23 arrastavel.setPointerCapture(evento.pointerId);
24 });
25
26 arrastavel.addEventListener('pointermove', function (evento) {
27 if (!arrastando) return;
28 var dx = evento.clientX - inicioX;
29 var dy = evento.clientY - inicioY;
30 if (!moveu && Math.abs(dx) + Math.abs(dy) > LIMIAR) moveu = true;
31 if (!moveu) return;
32
33 var lim = limites();
34 aplicarPosicao(
35 Math.max(-lim.x, Math.min(lim.x, deslocX + dx)),
36 Math.max(-lim.y, Math.min(lim.y, deslocY + dy))
37 );
38 });
39
40 function soltar(evento) {
41 if (!arrastando) return;
42 arrastando = false;
43 if (arrastavel.hasPointerCapture && arrastavel.hasPointerCapture(evento.pointerId)) {
44 arrastavel.releasePointerCapture(evento.pointerId);
45 }
46 if (moveu) {
47 var lim = limites();
48 deslocX = Math.max(-lim.x, Math.min(lim.x, deslocX + (evento.clientX - inicioX)));
49 deslocY = Math.max(-lim.y, Math.min(lim.y, deslocY + (evento.clientY - inicioY)));
50 } else {
51 alternarPlayPause(); // foi um clique de verdade, não um arrasto
52 }
53 }
54 arrastavel.addEventListener('pointerup', soltar);
55 arrastavel.addEventListener('pointercancel', function () { arrastando = false; });