Tour Virtual 360°
Exploração interativa de ambientes reais ou virtuais por meio de fotografias ou renders 360°. O aprendiz navega entre cenas e interage com hotspots informativos.
Experimente
Science City Kolkata — museu de ciências
Três fotos 360° reais de um museu de ciências (Science City, Kolkata, Índia), ligadas entre si. Clique nas setas para andar entre os ambientes e nos pontos de interesse para saber mais sobre cada exposição.
Fotos de Biswarup Ganguly, Wikimedia Commons (CC BY 3.0 / 4.0)
Código-fonte
Bibliotecas via CDN, sem instalar nada
Photo Sphere Viewer é distribuído como ES module puro — sem build nem npm, um import map no HTML resolve os pacotes direto do CDN.
| 1 |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/core@5.15.1/index.min.css">
|
| 2 |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/virtual-tour-plugin@5.15.1/index.css">
|
| 3 |
|
| 4 |
<script type="importmap">
|
| 5 |
{
|
| 6 |
"imports": {
|
| 7 |
"three": "https://cdn.jsdelivr.net/npm/three@0.185.1/build/three.module.js",
|
| 8 |
"@photo-sphere-viewer/core": "https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/core@5.15.1/index.module.js",
|
| 9 |
"@photo-sphere-viewer/virtual-tour-plugin": "https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/virtual-tour-plugin@5.15.1/index.module.js"
|
| 10 |
}
|
| 11 |
}
|
| 12 |
</script>
|
Contêiner do tour
A biblioteca lê o tamanho deste elemento assim que é criada — por isso ele precisa de altura própria (aqui via aspect-ratio no CSS), não pode depender de conteúdo interno pra ganhar altura.
| 1 |
<div class="tour-demo">
|
| 2 |
<div class="tour-cabecalho">
|
| 3 |
<p>Um Passeio por Dois Ambientes</p>
|
| 4 |
</div>
|
| 5 |
<div class="tour-visor"></div>
|
| 6 |
</div>
|
| 7 |
|
| 8 |
<script type="module" src="virtual-tour-360.js"></script>
|
Card e cabeçalho
Contêiner geral e a caixa de título centralizada.
| 1 |
.tour-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 |
.tour-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 |
.tour-cabecalho p {
|
| 18 |
margin: 0;
|
| 19 |
font-weight: 700;
|
| 20 |
font-size: 18px;
|
| 21 |
color: #0f172a;
|
| 22 |
}
|
Área do tour
A biblioteca cria seu próprio canvas dentro deste elemento — ele só precisa de altura definida (aqui via aspect-ratio), sem isso a foto não aparece.
| 1 |
.tour-visor {
|
| 2 |
position: relative;
|
| 3 |
aspect-ratio: 16 / 9;
|
| 4 |
background: #000;
|
| 5 |
}
|
Corrigir fotos que não cobrem os 180° verticais inteiros
a câmera raramente fotografa até o chão e o teto — sem avisar isso pra biblioteca, ela estica a foto pra preencher a esfera toda e distorce tudo. largura/altura vêm do tamanho real de cada arquivo.
| 1 |
function panoData(largura, altura) {
|
| 2 |
var alturaCheia = largura / 2;
|
| 3 |
return {
|
| 4 |
fullWidth: largura,
|
| 5 |
fullHeight: alturaCheia,
|
| 6 |
croppedWidth: largura,
|
| 7 |
croppedHeight: altura,
|
| 8 |
croppedX: 0,
|
| 9 |
croppedY: (alturaCheia - altura) / 2
|
| 10 |
};
|
| 11 |
}
|
Ligar duas fotos com uma seta de navegação
cada nó aponta pro outro via "links", com a posição da seta em graus (yaw = horizontal, pitch = vertical) — sem precisar de coordenadas GPS reais.
| 1 |
new Viewer({
|
| 2 |
container: container,
|
| 3 |
plugins: [
|
| 4 |
VirtualTourPlugin.withConfig({
|
| 5 |
dataMode: 'client',
|
| 6 |
positionMode: 'manual',
|
| 7 |
startNodeId: 'sala',
|
| 8 |
nodes: [
|
| 9 |
{
|
| 10 |
id: 'sala',
|
| 11 |
panorama: 'sala.jpg',
|
| 12 |
panoData: panoData(4000, 2000),
|
| 13 |
name: 'Sala principal',
|
| 14 |
links: [{ nodeId: 'corredor', position: { yaw: '90deg', pitch: '0deg' } }]
|
| 15 |
},
|
| 16 |
{
|
| 17 |
id: 'corredor',
|
| 18 |
panorama: 'corredor.jpg',
|
| 19 |
panoData: panoData(4000, 1600),
|
| 20 |
name: 'Corredor',
|
| 21 |
links: [{ nodeId: 'sala', position: { yaw: '270deg', pitch: '0deg' } }]
|
| 22 |
}
|
| 23 |
]
|
| 24 |
})
|
| 25 |
]
|
| 26 |
});
|