(function(){
var UID = 'nxt360_6a234765511c5';
var COR = '#093186';
var AUTO = true;
var CENAS = [{"id":1,"nome":"sala","url":"","hs":[]}];
if (!CENAS.length) return;
var wrap = document.getElementById(UID+'-outer');
var inner = document.getElementById(UID);
var canvas = document.getElementById(UID+'-cv');
var ctx = canvas.getContext('2d');
var W = 0, H = 0;
var st = { cena:0, yaw:0.4, pitch:0, fov:80, tYaw:0.4, tPitch:0, tFov:80, auto:AUTO };
var drag = { on:false, lx:0, ly:0 };
var tc = {};
var imgs = {}; // URL -> Image cache
var cur = null; // current loaded Image
var isLandscape = true; // current orientation
function resize(){
W = inner.clientWidth;
H = inner.clientHeight;
canvas.width = W;
canvas.height = H;
}
resize();
new ResizeObserver(resize).observe(inner);
// ── Load image WITHOUT crossOrigin (avoids CORS security error) ──
// Use a proxy via the PHP AJAX handler instead
function loadImg(url, cb) {
if (!url) { cb(null); return; }
if (imgs[url]) { cb(imgs[url]); return; }
var img = new Image();
// NO crossOrigin — draw via drawImage which works without CORS
// WebGL requires CORS; Canvas 2D drawImage does NOT
img.onload = function(){ imgs[url]=img; cb(img); };
img.onerror = function(){ cb(null); };
img.src = url;
}
// ── Equirectangular render via Canvas 2D ──────────────────
// Uses an off-screen canvas to sample pixels
var offCanvas = document.createElement('canvas');
var offCtx = offCanvas.getContext('2d');
var offLoaded = null; // currently drawn image on offCanvas
function draw() {
if (!cur) {
ctx.fillStyle = '#0a0f1a';
ctx.fillRect(0, 0, W, H);
var name = CENAS[st.cena]?.nome || '';
ctx.fillStyle = 'rgba(255,255,255,.3)';
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(name ? name + ' — sem foto 360°' : 'Aguardando foto 360°', W/2, H/2);
return;
}
// Upload to offscreen canvas only when image changes
if (offLoaded !== cur) {
offCanvas.width = cur.naturalWidth || 4096;
offCanvas.height = cur.naturalHeight || 2048;
offCtx.drawImage(cur, 0, 0);
offLoaded = cur;
}
var iW = offCanvas.width;
var iH = offCanvas.height;
var fovR = st.fov * Math.PI / 180;
var tanH = Math.tan(fovR / 2);
var aspect = W / H;
// Sample every 2 pixels for performance, then scale up
var step = Math.max(1, Math.floor(Math.min(W, H) / 400));
var sW = Math.ceil(W / step);
var sH = Math.ceil(H / step);
var id = ctx.createImageData(sW, sH);
var d = id.data;
// Get source pixels once
var src;
try { src = offCtx.getImageData(0, 0, iW, iH).data; }
catch(e) {
// Fallback if same-origin policy blocks even 2d canvas
ctx.fillStyle = '#0a0f1a'; ctx.fillRect(0,0,W,H);
ctx.fillStyle = 'rgba(255,255,255,.4)'; ctx.font='13px sans-serif';
ctx.textAlign='center'; ctx.textBaseline='middle';
ctx.fillText('⚠️ Foto bloqueada por CORS — verifique o servidor', W/2, H/2);
return;
}
for (var y = 0; y < sH; y++) {
for (var x = 0; x Math.PI) dYaw -= Math.PI*2;
while (dYaw Math.PI * 0.44) return null;
var sx = W/2 + dYaw / tanH * (W/2) / (W/H);
var sy = H/2 - (pitchW - st.pitch) / tanH * (H/2);
return (sxW+10||syH+10) ? null : {x:sx,y:sy};
}
// ── Build hotspots DOM ────────────────────────────────────
function buildHS() {
var layer = document.getElementById(UID+'-hs');
layer.innerHTML = '';
var hs = CENAS[st.cena]?.hs || [];
hs.forEach(function(h) {
var el = document.createElement('div');
el.className = 'nhs';
el.dataset.y = h.yaw; el.dataset.p = h.pitch;
var nav = h.tipo === 'navegacao';
el.innerHTML =
'
'
+(nav ? ''
: '')
+'
'+esc(h.label)+'';
el.addEventListener('click', function(e){
e.stopPropagation();
if (nav && h.dest) {
var di = CENAS.findIndex(function(c){return c.id===h.dest;});
if (di>=0) { goRoom(di,true); return; }
}
showPop(h, e.clientX, e.clientY);
});
layer.appendChild(el);
});
}
function updateHS() {
document.querySelectorAll('#'+UID+' .nhs').forEach(function(el){
var pos = w2s(parseFloat(el.dataset.y), parseFloat(el.dataset.p));
el.style.opacity = pos ? '1' : '0';
el.style.pointerEvents = pos ? 'auto' : 'none';
if (pos) { el.style.left=pos.x+'px'; el.style.top=pos.y+'px'; }
});
}
// ── Popup ─────────────────────────────────────────────────
function showPop(h, cx, cy) {
var pop = document.getElementById(UID+'-pop');
var body = document.getElementById(UID+'-pop-body');
var ctas = document.getElementById(UID+'-pop-ctas');
body.innerHTML = '
'+esc(h.label)+'
'
+ (h.desc ? '
'+esc(h.desc)+'
' : '');
ctas.innerHTML = '';
if (h.url) {
ctas.innerHTML += 'Ver →';
}
ctas.innerHTML += '';
var wr = inner.getBoundingClientRect();
pop.style.left = Math.min(cx-wr.left+12, W-225)+'px';
pop.style.top = Math.max(cy-wr.top-140, 10)+'px';
pop.classList.remove('off');
}
window.closePop = function(){ document.getElementById(UID+'-pop').classList.add('off'); };
inner.addEventListener('click',function(e){
if (!e.target.closest('.nhs') && !e.target.closest('.npop')) closePop();
});
// ── Go to room ────────────────────────────────────────────
function goRoom(idx, transition) {
st.cena = idx;
var c = CENAS[idx];
// Update buttons
document.querySelectorAll('#'+UID+' .nrb').forEach(function(b,i){b.classList.toggle('on',i===idx);});
document.querySelectorAll('#'+UID+' .nth').forEach(function(b,i){b.classList.toggle('on',i===idx);});
closePop();
if (transition) {
var fl = document.getElementById(UID+'-flash');
fl.style.transition='opacity .15s'; fl.style.opacity='.7';
setTimeout(function(){fl.style.transition='opacity .5s'; fl.style.opacity='0';},150);
}
if (c.url) {
document.getElementById(UID+'-ld').classList.remove('out');
document.getElementById(UID+'-ldtxt').textContent = 'Carregando ' + c.nome + '…';
loadImg(c.url, function(img){
cur = img;
offLoaded = null; // force re-upload
document.getElementById(UID+'-ld').classList.add('out');
buildHS();
});
} else {
cur = null; offLoaded = null;
document.getElementById(UID+'-ld').classList.add('out');
buildHS();
}
}
// ── Animation loop ────────────────────────────────────────
(function loop(){
requestAnimationFrame(loop);
if (st.auto) st.tYaw += 0.003;
st.yaw += (st.tYaw - st.yaw) * 0.07;
st.pitch += (st.tPitch - st.pitch) * 0.07;
st.fov += (st.tFov - st.fov) * 0.08;
draw();
updateHS();
// Compass
var c = document.getElementById(UID+'-comp');
if (c) c.style.transform = 'rotate('+(-st.yaw*180/Math.PI)+'deg)';
})();
// ── Controls ──────────────────────────────────────────────
inner.addEventListener('mousedown', function(e){drag.on=true;drag.lx=e.clientX;drag.ly=e.clientY;setAuto(false);});
window.addEventListener('mouseup', function(){drag.on=false;});
window.addEventListener('mousemove',function(e){
if(!drag.on) return;
st.tYaw -= (e.clientX-drag.lx) * 0.007;
st.tPitch = Math.max(-0.7, Math.min(0.7, st.tPitch + (e.clientY-drag.ly)*0.004));
drag.lx=e.clientX; drag.ly=e.clientY;
});
inner.addEventListener('wheel',function(e){
e.preventDefault();
st.tFov = Math.max(40, Math.min(110, st.tFov + e.deltaY*0.04));
},{passive:false});
inner.addEventListener('touchstart',function(e){
e.preventDefault(); setAuto(false);
Array.from(e.changedTouches).forEach(function(t){tc[t.identifier]={x:t.clientX,y:t.clientY};});
},{passive:false});
inner.addEventListener('touchmove',function(e){
e.preventDefault();
var ids=Object.keys(tc);
if(ids.length===1){
var t=e.changedTouches[0];
st.tYaw -= (t.clientX-tc[ids[0]].x)*0.009;
st.tPitch = Math.max(-0.7,Math.min(0.7,st.tPitch+(t.clientY-tc[ids[0]].y)*0.005));
tc[ids[0]]={x:t.clientX,y:t.clientY};
} else if(ids.length>=2){
var ta=e.changedTouches[0],tb=e.changedTouches[1];
var prev=Math.hypot((tc[ta.identifier]?.x||0)-(tc[tb.identifier]?.x||0),
(tc[ta.identifier]?.y||0)-(tc[tb.identifier]?.y||0))||1;
var curr=Math.hypot(ta.clientX-tb.clientX,ta.clientY-tb.clientY);
st.tFov=Math.max(40,Math.min(110,st.tFov*(prev/curr)));
Array.from(e.changedTouches).forEach(function(t){tc[t.identifier]={x:t.clientX,y:t.clientY};});
}
},{passive:false});
inner.addEventListener('touchend',function(e){
Array.from(e.changedTouches).forEach(function(t){delete tc[t.identifier];});
});
// ── Build room nav ────────────────────────────────────────
var roomsEl = document.getElementById(UID+'-rooms');
var thumbsEl = document.getElementById(UID+'-thumbs');
CENAS.forEach(function(c,i){
var b=document.createElement('button');
b.className='nrb'+(i===0?' on':'');
b.textContent=c.nome;
b.onclick=function(){goRoom(i,true);};
roomsEl.appendChild(b);
if(c.url && CENAS.length>1){
var th=document.createElement('div');
th.className='nth'+(i===0?' on':'');
th.innerHTML='';
th.onclick=function(){goRoom(i,true);};
thumbsEl.appendChild(th);
}
});
if(CENAS.length>1) thumbsEl.style.display='flex';
setTimeout(function(){var h=document.getElementById(UID+'-hint');if(h)h.style.opacity='0';},5000);
function setAuto(v){
st.auto=v;
var b=document.getElementById(UID+'-auto');
if(b){b.classList.toggle('on',v);b.textContent=v?'⏸ Auto':'▶ Auto';}
}
// ── Public API ────────────────────────────────────────────
window['nxt360Auto'] = function(uid){ if(uid===UID) setAuto(!st.auto); };
window['nxt360Zoom'] = function(uid,d){ if(uid===UID) st.tFov=Math.max(40,Math.min(110,st.tFov-d*10)); };
window['nxt360Fs'] = function(uid){
if(uid!==UID) return;
var w=document.getElementById(uid);
if(!document.fullscreenElement) w.requestFullscreen&&w.requestFullscreen();
else document.exitFullscreen&&document.exitFullscreen();
};
window['nxtToggleOrient'] = function(uid){
if(uid!==UID) return;
isLandscape=!isLandscape;
var o=document.getElementById(uid+'-outer');
if(isLandscape){
o.style.width='100%'; o.style.height='500px';
o.style.aspectRatio=''; o.style.maxWidth='';
} else {
o.style.width='100%'; o.style.height='';
o.style.aspectRatio='9/16'; o.style.maxWidth='420px';
}
setTimeout(resize,50);
};
function esc(s){return String(s||'').replace(/[&"']/g,function(c){return({'&':'&','':'>','"':'"',"'":'''}[c]);});}
// Boot
goRoom(0, false);
})();
Apartamento para Venda, Bombinhas / SC, bairro Bombas, 1 dormitório, 1 banheiro, 1 vaga de garagem, mobiliado, área total 44,00 m²
Venda de imóveis em Bombinhas I Santa Catarina atende nas regiões de Porto Belo Sc, Itajaí Sc, Governador Celso Ramos Sc, Laguna Sc, Balneário Rincão Sc, São Francisco Sul Sc, Imbituba Sc, Penha Sc, Bombinhas Sc, Florianópolis Sc.
Cômodos
cozinhasalavaranda
Localização
📍 Rua Costa Barros 2200, Bombinhas Sc Bombas, Bombinhas, 03210-001