Mudanças entre as edições de "Widget:MapViewer.js"
Ir para navegação
Ir para pesquisar
| Linha 1: | Linha 1: | ||
<includeonly><div id="mapa-viewer-{{{id|mapa1}}} | <includeonly>{{#tag:pre|{{{json}}}|style=display:none|id=mapa-json-{{{id}}}}} | ||
<div id="mapa-viewer-{{{id|mapa1}}}" style="width:{{{largura|100%}}}; height:{{{altura|500px}}}; background:#0f172a; border-radius:12px; overflow:hidden; position:relative;"> | |||
<div style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:#64748b; text-align:center;"> | <div style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:#64748b; text-align:center;"> | ||
<div>🗺️ Carregando mapa...</div> | <div>🗺️ Carregando mapa...</div> | ||
| Linha 11: | Linha 12: | ||
var containerId = 'mapa-viewer-{{{id|mapa1}}}'; | var containerId = 'mapa-viewer-{{{id|mapa1}}}'; | ||
var container = document.getElementById(containerId); | var container = document.getElementById(containerId); | ||
var jsonId = 'mapa-json-{{{id|mapa1}}}'; | |||
var preElement = document.getElementById(jsonId); | |||
if (!container) return; | if (!container) return; | ||
if (!preElement) { | |||
container.innerHTML = '<div style="padding:20px; text-align:center; color:#ef4444;">❌ Dados do mapa não encontrados</div>'; | |||
return; | |||
} | |||
// Pegar o JSON do pre escondido | |||
var jsonString = preElement.textContent || preElement.innerText; | |||
if (!jsonString || jsonString.trim() === '') { | |||
container.innerHTML = '<div style="padding:20px; text-align:center; color:#ef4444;">❌ Configuração vazia</div>'; | |||
return; | |||
} | |||
var mapConfig; | var mapConfig; | ||
try { | try { | ||
mapConfig = JSON.parse( | mapConfig = JSON.parse(jsonString); | ||
} catch(e) { | } catch(e) { | ||
container.innerHTML = '<div style="padding:20px; text-align:center; color:#ef4444;">❌ Erro | container.innerHTML = '<div style="padding:20px; text-align:center; color:#ef4444;">❌ Erro no JSON: ' + e.message + '</div>'; | ||
console.error(' | console.error('JSON inválido:', jsonString); | ||
return; | |||
} | |||
if (!mapConfig.layers || mapConfig.layers.length === 0) { | |||
container.innerHTML = '<div style="padding:20px; text-align:center; color:#f59e0b;">⚠️ Nenhuma camada configurada</div>'; | |||
return; | return; | ||
} | } | ||
// | // Mapa carregado com sucesso! | ||
iniciarMapaViewer(containerId, mapConfig); | iniciarMapaViewer(containerId, mapConfig); | ||
function iniciarMapaViewer(containerId, config) { | function iniciarMapaViewer(containerId, config) { | ||
var container = document.getElementById(containerId); | var container = document.getElementById(containerId); | ||
if (!container) return; | if (!container) return; | ||
container.innerHTML = ''; | container.innerHTML = ''; | ||
container.style.position = 'relative'; | container.style.position = 'relative'; | ||
container.style.overflow = 'hidden'; | container.style.overflow = 'hidden'; | ||
// | // Toolbar superior | ||
var toolbar = document.createElement('div'); | |||
toolbar.style.cssText = 'position:absolute; top:10px; left:10px; right:10px; z-index:100; display:flex; justify-content:space-between; gap:8px; flex-wrap:wrap;'; | |||
toolbar.innerHTML = ` | |||
<div style="display:flex; gap:5px; background:rgba(0,0,0,0.7); padding:5px 10px; border-radius:30px;"> | |||
<button class="mapa-zoom-in" style="background:#334155; border:none; color:white; width:32px; height:32px; border-radius:50%; cursor:pointer;">+</button> | |||
<button class="mapa-zoom-out" style="background:#334155; border:none; color:white; width:32px; height:32px; border-radius:50%; cursor:pointer;">-</button> | |||
<button class="mapa-reset" style="background:#334155; border:none; color:white; width:32px; height:32px; border-radius:50%; cursor:pointer;">⟳</button> | |||
</div> | </div> | ||
<div style="background:rgba(0,0,0,0.7); padding:5px 15px; border-radius:30px; color:white; font-size:12px;"> | |||
<span class="mapa-floor-name">${config.layers[0]?.name || 'Mapa'}</span> | |||
<div style=" | |||
< | |||
</div> | </div> | ||
<div style="background:rgba(0,0,0,0.7); padding:5px 12px; border-radius:30px; color:#a5b4fc; font-size:12px;" class="mapa-zoom-level">100%</div> | |||
`; | |||
// Viewport | |||
var viewport = document.createElement('div'); | |||
viewport.className = 'mapa-viewport'; | |||
viewport.style.cssText = 'width:100%; height:100%; overflow:auto; cursor:grab; background:#0f172a;'; | |||
var layersContainer = document.createElement('div'); | |||
layersContainer.className = 'mapa-layers'; | |||
layersContainer.style.cssText = 'position:relative; min-width:100%; min-height:100%;'; | |||
viewport.appendChild(layersContainer); | |||
// Coordenadas | |||
var coordsDiv = document.createElement('div'); | |||
coordsDiv.style.cssText = 'position:absolute; bottom:10px; right:10px; background:rgba(0,0,0,0.6); padding:4px 10px; border-radius:20px; color:#a5b4fc; font-size:10px; font-family:monospace;'; | |||
coordsDiv.textContent = '📍 0, 0'; | |||
// Navegação | |||
var navDiv = document.createElement('div'); | |||
navDiv.style.cssText = 'position:absolute; bottom:20px; left:20px; z-index:100; display:flex; gap:8px; background:rgba(0,0,0,0.7); padding:8px; border-radius:40px;'; | |||
navDiv.innerHTML = ` | |||
<button class="mapa-prev-floor" style="background:#334155; border:none; color:white; width:36px; height:36px; border-radius:50%; cursor:pointer;">▲</button> | |||
<button class="mapa-next-floor" style="background:#334155; border:none; color:white; width:36px; height:36px; border-radius:50%; cursor:pointer;">▼</button> | |||
`; | `; | ||
container.appendChild(toolbar); | |||
container.appendChild(viewport); | |||
container.appendChild(coordsDiv); | |||
container.appendChild(navDiv); | |||
// Variáveis | // Variáveis | ||
var currentZoom = config.mapConfig.defaultZoom || 1; | var currentZoom = config.mapConfig.defaultZoom || 1; | ||
var currentFloor = config.mapConfig.initialFloor || 0; | var currentFloor = config.mapConfig.initialFloor || 0; | ||
| Linha 78: | Linha 107: | ||
var isPanning = false; | var isPanning = false; | ||
var panStart = { x: 0, y: 0, scrollLeft: 0, scrollTop: 0 }; | var panStart = { x: 0, y: 0, scrollLeft: 0, scrollTop: 0 }; | ||
var floorNameSpan = toolbar.querySelector('.mapa-floor-name'); | |||
var zoomLevelSpan = toolbar.querySelector('.mapa-zoom-level'); | |||
// Renderizar camadas | // Renderizar camadas | ||
function renderizarCamadas() { | function renderizarCamadas() { | ||
layersContainer.innerHTML = ''; | layersContainer.innerHTML = ''; | ||
layers = []; | layers = []; | ||
markers = []; | markers = []; | ||
// Verificar se o andar atual existe | |||
var floorExists = false; | |||
for (var i = 0; i < config.layers.length; i++) { | |||
if (config.layers[i].id === currentFloor) floorExists = true; | |||
} | |||
if (!floorExists && config.layers.length > 0) { | |||
currentFloor = config.layers[0].id; | |||
} | } | ||
var floorAtual = null; | |||
for (var i = 0; i < config.layers.length; i++) { | |||
if (config.layers[i].id === currentFloor) floorAtual = config.layers[i]; | |||
} | } | ||
if (floorAtual && floorNameSpan) { | if (floorAtual && floorNameSpan) { | ||
floorNameSpan.textContent = floorAtual.name; | floorNameSpan.textContent = floorAtual.name; | ||
} | } | ||
for (var i = 0; i < config.layers.length; i++) { | for (var i = 0; i < config.layers.length; i++) { | ||
var layer = config.layers[i]; | var layer = config.layers[i]; | ||
| Linha 115: | Linha 147: | ||
var img = document.createElement('img'); | var img = document.createElement('img'); | ||
img.style.display = 'block'; | img.style.display = 'block'; | ||
img.style.transform = 'scale(' + currentZoom + ')'; | img.style.transform = 'scale(' + currentZoom + ')'; | ||
| Linha 121: | Linha 152: | ||
img.src = layer.imagePath || ''; | img.src = layer.imagePath || ''; | ||
img.onload = | img.onload = function() { | ||
layersContainer.style.width = this.width + 'px'; | |||
layersContainer.style.height = this.height + 'px'; | |||
}; | |||
} | |||
img.onerror = | img.onerror = function() { | ||
var canvas = document.createElement('canvas'); | |||
canvas.width = 800; | |||
canvas.height = 600; | |||
var ctx = canvas.getContext('2d'); | |||
ctx.fillStyle = '#334155'; | |||
ctx.fillRect(0, 0, canvas.width, canvas.height); | |||
ctx.fillStyle = 'white'; | |||
ctx.font = '20px Arial'; | |||
ctx.fillText(layer.name || 'Andar', 50, 100); | |||
this.src = canvas.toDataURL(); | |||
}; | |||
} | |||
divLayer.appendChild(img); | divLayer.appendChild(img); | ||
| Linha 157: | Linha 180: | ||
markersDiv.style.pointerEvents = 'none'; | markersDiv.style.pointerEvents = 'none'; | ||
if (layer.markers | if (layer.markers) { | ||
for (var j = 0; j < layer.markers.length; j++) { | for (var j = 0; j < layer.markers.length; j++) { | ||
var marker = layer.markers[j]; | var marker = layer.markers[j]; | ||
| Linha 172: | Linha 195: | ||
} | } | ||
function criarMarcador(marker, floorId) { | function criarMarcador(marker, floorId) { | ||
var div = document.createElement('div'); | var div = document.createElement('div'); | ||
| Linha 186: | Linha 208: | ||
div.style.zIndex = '100'; | div.style.zIndex = '100'; | ||
div.style.transform = 'translate(-50%, -50%)'; | div.style.transform = 'translate(-50%, -50%)'; | ||
div.style.cursor = (marker.action && marker.action !== 'none') ? 'pointer' : 'default'; | |||
var tamanhoIcone = Math.max(20, Math.floor(20 * currentZoom)); | var tamanhoIcone = Math.max(20, Math.floor(20 * currentZoom)); | ||
var htmlIcone | var htmlIcone = marker.iconBase64 ? | ||
'<img src="' + marker.iconBase64 + '" style="width:' + tamanhoIcone + 'px; height:' + tamanhoIcone + 'px;">' : | |||
'<span style="font-size:' + tamanhoIcone + 'px;">📍</span>'; | |||
var textoAcao = ''; | var textoAcao = ''; | ||
| Linha 212: | Linha 227: | ||
div.innerHTML = ` | div.innerHTML = ` | ||
<div style="display:flex; align-items:center; justify-content:center;">${htmlIcone}</div> | <div style="display:flex; align-items:center; justify-content:center;">${htmlIcone}</div> | ||
<div style="position:absolute; bottom:100%; left:50%; transform:translateX(-50%); background:#1e293b; color:white; padding:4px 8px; border-radius:6px; font-size:10px; white-space:nowrap; opacity:0; visibility:hidden; transition:0.2s; pointer-events:none | <div style="position:absolute; bottom:100%; left:50%; transform:translateX(-50%); background:#1e293b; color:white; padding:4px 8px; border-radius:6px; font-size:10px; white-space:nowrap; opacity:0; visibility:hidden; transition:0.2s; pointer-events:none;"> | ||
<strong>${marker.name || 'Marcador'}</strong><br> | <strong>${marker.name || 'Marcador'}</strong><br> | ||
<small>${textoAcao}</small> | <small>${textoAcao}</small> | ||
| Linha 219: | Linha 234: | ||
`; | `; | ||
div.addEventListener('mouseenter', function() { | div.addEventListener('mouseenter', function() { | ||
this.style.transform = 'translate(-50%, -50%) scale(1.15)'; | this.style.transform = 'translate(-50%, -50%) scale(1.15)'; | ||
| Linha 238: | Linha 252: | ||
}); | }); | ||
if (marker.action && marker.action !== 'none') { | |||
if ( | |||
div.addEventListener('click', function(e) { | div.addEventListener('click', function(e) { | ||
e.stopPropagation(); | e.stopPropagation(); | ||
| Linha 249: | Linha 262: | ||
} | } | ||
function executarAcao(marker) { | function executarAcao(marker) { | ||
switch(marker.action) { | switch(marker.action) { | ||
case 'popup': | case 'popup': | ||
alert(marker.name + '\n\n' + (marker.actionData?.text || 'Sem informações')); | |||
alert(marker.name + '\n\n' + | |||
break; | break; | ||
case 'nextFloor': | case 'nextFloor': | ||
| Linha 275: | Linha 286: | ||
} | } | ||
function proximoAndar() { | function proximoAndar() { | ||
var idx = -1; | var idx = -1; | ||
for (var i = 0; i < config.layers.length; i++) { | for (var i = 0; i < config.layers.length; i++) { | ||
if (config.layers[i].id === currentFloor) | if (config.layers[i].id === currentFloor) idx = i; | ||
} | } | ||
if (idx < config.layers.length - 1) { | if (idx < config.layers.length - 1) { | ||
| Linha 292: | Linha 299: | ||
var idx = -1; | var idx = -1; | ||
for (var i = 0; i < config.layers.length; i++) { | for (var i = 0; i < config.layers.length; i++) { | ||
if (config.layers[i].id === currentFloor) | if (config.layers[i].id === currentFloor) idx = i; | ||
} | } | ||
if (idx > 0) { | if (idx > 0) { | ||
| Linha 304: | Linha 308: | ||
function irParaAndar(floorId) { | function irParaAndar(floorId) { | ||
currentFloor = floorId; | currentFloor = floorId; | ||
for (var i = 0; i < layers.length; i++) { | for (var i = 0; i < layers.length; i++) { | ||
var | var layerFloor = parseInt(layers[i].getAttribute('data-floor')); | ||
layers[i].style.display = layerFloor === floorId ? 'block' : 'none'; | |||
} | } | ||
var floorData = null; | var floorData = null; | ||
for (var i = 0; i < config.layers.length; i++) { | for (var i = 0; i < config.layers.length; i++) { | ||
if (config.layers[i].id === floorId) | if (config.layers[i].id === floorId) floorData = config.layers[i]; | ||
} | } | ||
if (floorData && floorNameSpan) floorNameSpan.textContent = floorData.name; | |||
atualizarPosicaoMarkers(); | atualizarPosicaoMarkers(); | ||
} | } | ||
function atualizarPosicaoMarkers() { | function atualizarPosicaoMarkers() { | ||
for (var i = 0; i < markers.length; i++) { | for (var i = 0; i < markers.length; i++) { | ||
| Linha 332: | Linha 326: | ||
item.el.style.left = (item.data.x * currentZoom) + 'px'; | item.el.style.left = (item.data.x * currentZoom) + 'px'; | ||
item.el.style.top = (item.data.y * currentZoom) + 'px'; | item.el.style.top = (item.data.y * currentZoom) + 'px'; | ||
var tamanhoIcone = Math.max(20, Math.floor(20 * currentZoom)); | var tamanhoIcone = Math.max(20, Math.floor(20 * currentZoom)); | ||
var iconDiv = item.el.children[0]; | var iconDiv = item.el.children[0]; | ||
| Linha 346: | Linha 339: | ||
} | } | ||
function zoomIn() { | function zoomIn() { | ||
if (currentZoom < maxZoom) { | if (currentZoom < maxZoom) { | ||
| Linha 368: | Linha 360: | ||
images[i].style.transform = 'scale(' + currentZoom + ')'; | images[i].style.transform = 'scale(' + currentZoom + ')'; | ||
} | } | ||
var rect = viewport.getBoundingClientRect(); | var rect = viewport.getBoundingClientRect(); | ||
var cx = rect.width / 2 | var cx = rect.width / 2, cy = rect.height / 2; | ||
var sx = (viewport.scrollLeft + cx) / oldZoom; | var sx = (viewport.scrollLeft + cx) / oldZoom; | ||
var sy = (viewport.scrollTop + cy) / oldZoom; | var sy = (viewport.scrollTop + cy) / oldZoom; | ||
viewport.scrollLeft = sx * currentZoom - cx; | viewport.scrollLeft = sx * currentZoom - cx; | ||
viewport.scrollTop = sy * currentZoom - cy; | viewport.scrollTop = sy * currentZoom - cy; | ||
atualizarPosicaoMarkers(); | atualizarPosicaoMarkers(); | ||
if (zoomLevelSpan) | if (zoomLevelSpan) zoomLevelSpan.textContent = Math.round(currentZoom * 100) + '%'; | ||
} | } | ||
| Linha 393: | Linha 379: | ||
viewport.scrollTop = 0; | viewport.scrollTop = 0; | ||
atualizarPosicaoMarkers(); | atualizarPosicaoMarkers(); | ||
if (zoomLevelSpan) | if (zoomLevelSpan) zoomLevelSpan.textContent = Math.round(currentZoom * 100) + '%'; | ||
} | } | ||
// | // Eventos dos botões | ||
toolbar.querySelector('.mapa-zoom-in')?.addEventListener('click', zoomIn); | |||
toolbar.querySelector('.mapa-zoom-out')?.addEventListener('click', zoomOut); | |||
toolbar.querySelector('.mapa-reset')?.addEventListener('click', resetarView); | |||
navDiv.querySelector('.mapa-prev-floor')?.addEventListener('click', andarAnterior); | |||
navDiv.querySelector('.mapa-next-floor')?.addEventListener('click', proximoAndar); | |||
// | // Pan | ||
viewport.addEventListener('mousedown', function(e) { | viewport.addEventListener('mousedown', function(e) { | ||
if (e.target.closest('.mapa-marker')) return; | if (e.target.closest('.mapa-marker')) return; | ||
isPanning = true; | isPanning = true; | ||
panStart = { | panStart = { x: e.clientX, y: e.clientY, scrollLeft: viewport.scrollLeft, scrollTop: viewport.scrollTop }; | ||
viewport.style.cursor = 'grabbing'; | viewport.style.cursor = 'grabbing'; | ||
e.preventDefault(); | e.preventDefault(); | ||
| Linha 440: | Linha 413: | ||
if (e.ctrlKey) { | if (e.ctrlKey) { | ||
e.preventDefault(); | e.preventDefault(); | ||
e.deltaY < 0 ? zoomIn() : zoomOut(); | |||
} | } | ||
}); | }); | ||
// Tracking | // Tracking coordenadas | ||
viewport.addEventListener('mousemove', function(e) { | viewport.addEventListener('mousemove', function(e) { | ||
var rect = viewport.getBoundingClientRect(); | var rect = viewport.getBoundingClientRect(); | ||
var x = (e.clientX - rect.left + viewport.scrollLeft) / currentZoom; | var x = (e.clientX - rect.left + viewport.scrollLeft) / currentZoom; | ||
var y = (e.clientY - rect.top + viewport.scrollTop) / currentZoom; | var y = (e.clientY - rect.top + viewport.scrollTop) / currentZoom; | ||
coordsDiv.textContent = '📍 ' + Math.round(x) + ', ' + Math.round(y) + ' | ' + Math.round(currentZoom * 100) + '%'; | |||
}); | }); | ||
renderizarCamadas(); | renderizarCamadas(); | ||
} | } | ||
})(); | })(); | ||
</script></includeonly> | </script></includeonly> | ||