Mudanças entre as edições de "Widget:MapViewer.js"
Ir para navegação
Ir para pesquisar
| Linha 1: | Linha 1: | ||
<script> | <includeonly> | ||
<script type="text/javascript"> | |||
/** | |||
// | * MapViewer Widget para MediaWiki - Versão Simplificada | ||
* Uso: {{#widget:MapViewer|json=JSON_do_mapa|width=800|height=600}} | |||
*/ | |||
(function() { | |||
// Evitar múltiplas execuções | |||
if (window.MWMapViewerLoaded) return; | if (window.MWMapViewerLoaded) return; | ||
window.MWMapViewerLoaded = true; | window.MWMapViewerLoaded = true; | ||
// | // ==================== ESTILOS ==================== | ||
const styles = ` | const styles = ` | ||
.mw-map-container { | |||
position: relative; | |||
background: #1e1e2e; | |||
border-radius: 8px; | |||
overflow: hidden; | |||
box-shadow: 0 2px 8px rgba(0,0,0,0.2); | |||
} | |||
.mw-map-container .mw-viewport { | |||
width: 100%; | |||
height: 100%; | |||
overflow: auto; | |||
cursor: grab; | |||
background: #0f0f1a; | |||
} | |||
.mw-map-container .mw-viewport:active { | |||
cursor: grabbing; | |||
} | |||
.mw-map-container .mw-layers { | |||
position: relative; | |||
min-width: 100%; | |||
min-height: 100%; | |||
} | |||
.mw-map-container .mw-layer { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
} | |||
.mw-map-container .mw-image { | |||
display: block; | |||
pointer-events: none; | |||
} | |||
.mw-map-container .mw-marker { | |||
position: absolute; | |||
cursor: pointer; | |||
transform: translate(-50%, -50%); | |||
transition: transform 0.1s; | |||
z-index: 100; | |||
} | |||
.mw-map-container .mw-marker:hover { | |||
transform: translate(-50%, -50%) scale(1.2); | |||
z-index: 101; | |||
} | |||
.mw-map-container .mw-marker-icon { | |||
font-size: 20px; | |||
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3)); | |||
} | |||
.mw-map-container .mw-marker-tooltip { | |||
position: absolute; | |||
bottom: 100%; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
background: #1e1e2e; | |||
color: white; | |||
padding: 4px 8px; | |||
border-radius: 6px; | |||
font-size: 11px; | |||
white-space: nowrap; | |||
opacity: 0; | |||
visibility: hidden; | |||
transition: 0.2s; | |||
pointer-events: none; | |||
margin-bottom: 5px; | |||
} | |||
.mw-map-container .mw-marker:hover .mw-marker-tooltip { | |||
opacity: 1; | |||
visibility: visible; | |||
} | |||
.mw-map-container .mw-badge { | |||
position: absolute; | |||
top: -8px; | |||
right: -8px; | |||
background: #ef4444; | |||
color: white; | |||
font-size: 9px; | |||
min-width: 16px; | |||
height: 16px; | |||
border-radius: 10px; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
padding: 0 4px; | |||
} | |||
.mw-map-container .mw-badge.hidden { | |||
display: none; | |||
} | |||
.mw-map-container .mw-toolbar { | |||
position: absolute; | |||
top: 10px; | |||
left: 10px; | |||
right: 10px; | |||
display: flex; | |||
justify-content: space-between; | |||
z-index: 100; | |||
gap: 10px; | |||
} | |||
.mw-map-container .mw-controls { | |||
display: flex; | |||
gap: 5px; | |||
background: rgba(0,0,0,0.6); | |||
padding: 5px 10px; | |||
border-radius: 30px; | |||
} | |||
.mw-map-container .mw-controls button { | |||
background: #334155; | |||
border: none; | |||
color: white; | |||
width: 32px; | |||
height: 32px; | |||
border-radius: 50%; | |||
cursor: pointer; | |||
font-size: 16px; | |||
} | |||
.mw-map-container .mw-controls button:hover { | |||
background: #6366f1; | |||
} | |||
.mw-map-container .mw-floor-info { | |||
background: rgba(0,0,0,0.6); | |||
padding: 5px 15px; | |||
border-radius: 30px; | |||
color: white; | |||
font-size: 12px; | |||
} | |||
.mw-map-container .mw-zoom-level { | |||
background: rgba(0,0,0,0.6); | |||
padding: 5px 12px; | |||
border-radius: 30px; | |||
color: #a5b4fc; | |||
font-size: 12px; | |||
} | |||
.mw-map-container .mw-nav { | |||
position: absolute; | |||
bottom: 20px; | |||
left: 20px; | |||
display: flex; | |||
gap: 8px; | |||
z-index: 100; | |||
} | |||
.mw-map-container .mw-nav button { | |||
background: rgba(0,0,0,0.7); | |||
border: none; | |||
color: white; | |||
width: 40px; | |||
height: 40px; | |||
border-radius: 50%; | |||
cursor: pointer; | |||
font-size: 18px; | |||
} | |||
.mw-map-container .mw-nav button:hover { | |||
background: #6366f1; | |||
} | |||
.mw-map-container .mw-coords { | |||
position: absolute; | |||
bottom: 10px; | |||
right: 10px; | |||
background: rgba(0,0,0,0.5); | |||
padding: 4px 10px; | |||
border-radius: 20px; | |||
color: #a5b4fc; | |||
font-size: 10px; | |||
font-family: monospace; | |||
z-index: 100; | |||
} | |||
.mw-map-popup { | |||
display: none; | |||
position: fixed; | |||
z-index: 10000; | |||
left: 0; | |||
top: 0; | |||
width: 100%; | |||
height: 100%; | |||
background: rgba(0,0,0,0.7); | |||
justify-content: center; | |||
align-items: center; | |||
} | |||
.mw-map-popup .mw-popup-content { | |||
background: #1e1e2e; | |||
border-radius: 12px; | |||
max-width: 350px; | |||
width: 90%; | |||
padding: 20px; | |||
position: relative; | |||
} | border: 1px solid #334155; | ||
} | |||
.mw-map-popup .mw-popup-close { | |||
position: absolute; | |||
right: 15px; | |||
top: 10px; | |||
font-size: 24px; | |||
cursor: pointer; | |||
color: #94a3b8; | |||
} | |||
.mw-map-popup .mw-popup-close:hover { | |||
color: #ef4444; | |||
} | |||
.mw-map-popup h3 { | |||
color: #a5b4fc; | |||
margin: 0 0 10px 0; | |||
} | |||
.mw-map-popup p { | |||
color: #cbd5e1; | |||
margin: 0; | |||
line-height: 1.5; | |||
} | |||
} | |||
`; | `; | ||
class | // ==================== CLASSE DO MAPA ==================== | ||
constructor(container, config | class MWMap { | ||
constructor(container, config) { | |||
this.container = container; | this.container = container; | ||
this.config = | this.config = config; | ||
this. | // Configurações do mapa | ||
this.currentFloor = | this.zoom = config.mapConfig?.defaultZoom || 1; | ||
this.minZoom = | this.currentFloor = config.mapConfig?.initialFloor || 0; | ||
this.maxZoom = | this.minZoom = config.mapConfig?.minZoom || 0.5; | ||
this.zoomStep = | this.maxZoom = config.mapConfig?.maxZoom || 3; | ||
this.zoomStep = config.mapConfig?.zoomStep || 0.1; | |||
this.layers = []; | this.layers = []; | ||
this.markers = []; | this.markers = []; | ||
this.isPanning = false; | this.isPanning = false; | ||
this.panStart = { x: 0, y: 0, | this.panStart = { x: 0, y: 0, left: 0, top: 0 }; | ||
this.init(); | this.init(); | ||
} | } | ||
init() { | init() { | ||
this.createDOM(); | this.createDOM(); | ||
this. | this.render(); | ||
this.setupEvents(); | this.setupEvents(); | ||
this.updateMarkersPosition(); | this.updateMarkersPosition(); | ||
this. | this.createPopup(); | ||
} | } | ||
createDOM() { | createDOM() { | ||
// Limpar container | |||
this.container.innerHTML = ''; | this.container.innerHTML = ''; | ||
this.container.className = 'mw-map- | this.container.className = 'mw-map-container'; | ||
// Criar estrutura | |||
this.container.innerHTML = ` | this.container.innerHTML = ` | ||
<div class=" | <div class="mw-toolbar"> | ||
<div class=" | <div class="mw-controls"> | ||
<button class="mw-zoom-in" title="Zoom In | <button class="mw-zoom-in" title="Zoom In">+</button> | ||
<button class="mw-zoom-out" title="Zoom Out | <button class="mw-zoom-out" title="Zoom Out">-</button> | ||
<button class="mw-reset" title=" | <button class="mw-reset" title="Reset">⟳</button> | ||
</div> | </div> | ||
<div class="floor-info"> | <div class="mw-floor-info"> | ||
<span class="mw-floor-name">Carregando...</span> | <span class="mw-floor-name">Carregando...</span> | ||
</div> | </div> | ||
<div class="zoom-level"> | <div class="mw-zoom-level"> | ||
<span class="mw-zoom- | <span class="mw-zoom-text">100%</span> | ||
</div> | </div> | ||
</div> | </div> | ||
<div class=" | <div class="mw-viewport"> | ||
<div class=" | <div class="mw-layers"></div> | ||
</div> | </div> | ||
<div class=" | <div class="mw-coords"> | ||
<span class="mw-coords">📍 0, 0</span> | <span class="mw-coords-text">📍 0, 0</span> | ||
</div> | </div> | ||
<div class="nav | <div class="mw-nav"> | ||
<button class="mw-prev-floor" title="Andar Anterior | <button class="mw-prev-floor" title="Andar Anterior">▲</button> | ||
<button class="mw-next-floor" title="Próximo Andar | <button class="mw-next-floor" title="Próximo Andar">▼</button> | ||
</div> | </div> | ||
`; | `; | ||
this.viewport = this.container.querySelector('. | this.viewport = this.container.querySelector('.mw-viewport'); | ||
this.layersContainer = this.container.querySelector('. | this.layersContainer = this.container.querySelector('.mw-layers'); | ||
this.floorNameSpan = this.container.querySelector('.mw-floor-name'); | this.floorNameSpan = this.container.querySelector('.mw-floor-name'); | ||
this. | this.zoomTextSpan = this.container.querySelector('.mw-zoom-text'); | ||
this.coordsSpan = this.container.querySelector('.mw-coords'); | this.coordsSpan = this.container.querySelector('.mw-coords-text'); | ||
// | // Botões | ||
this.container.querySelector('.mw-zoom-in')?.addEventListener('click', () => this.zoomIn()); | this.container.querySelector('.mw-zoom-in')?.addEventListener('click', () => this.zoomIn()); | ||
this.container.querySelector('.mw-zoom-out')?.addEventListener('click', () => this.zoomOut()); | this.container.querySelector('.mw-zoom-out')?.addEventListener('click', () => this.zoomOut()); | ||
this.container.querySelector('.mw-reset')?.addEventListener('click', () => this. | this.container.querySelector('.mw-reset')?.addEventListener('click', () => this.reset()); | ||
this.container.querySelector('.mw-prev-floor')?.addEventListener('click', () => this.prevFloor()); | this.container.querySelector('.mw-prev-floor')?.addEventListener('click', () => this.prevFloor()); | ||
this.container.querySelector('.mw-next-floor')?.addEventListener('click', () => this.nextFloor()); | this.container.querySelector('.mw-next-floor')?.addEventListener('click', () => this.nextFloor()); | ||
} | } | ||
setupEvents() { | |||
// Pan | // Pan | ||
this.viewport.addEventListener('mousedown', (e) => { | this.viewport.addEventListener('mousedown', (e) => { | ||
if (e.target.closest('. | if (e.target.closest('.mw-marker')) return; | ||
this.isPanning = true; | this.isPanning = true; | ||
this.panStart = { | this.panStart = { | ||
x: e.clientX, | x: e.clientX, | ||
y: e.clientY, | y: e.clientY, | ||
left: this.viewport.scrollLeft, | |||
top: this.viewport.scrollTop | |||
}; | }; | ||
this.viewport.style.cursor = 'grabbing'; | this.viewport.style.cursor = 'grabbing'; | ||
| Linha 381: | Linha 310: | ||
window.addEventListener('mousemove', (e) => { | window.addEventListener('mousemove', (e) => { | ||
if (!this.isPanning) return; | if (!this.isPanning) return; | ||
const dx = e.clientX - this.panStart.x; | |||
this.viewport. | const dy = e.clientY - this.panStart.y; | ||
this.viewport.scrollLeft = this.panStart.left - dx; | |||
this.viewport.scrollTop = this.panStart.top - dy; | |||
}); | }); | ||
| Linha 401: | Linha 332: | ||
this.viewport.addEventListener('mousemove', (e) => { | this.viewport.addEventListener('mousemove', (e) => { | ||
const rect = this.viewport.getBoundingClientRect(); | const rect = this.viewport.getBoundingClientRect(); | ||
const x = (e.clientX - rect.left + this.viewport.scrollLeft) / this. | const x = (e.clientX - rect.left + this.viewport.scrollLeft) / this.zoom; | ||
const y = (e.clientY - rect.top + this.viewport.scrollTop) / this. | const y = (e.clientY - rect.top + this.viewport.scrollTop) / this.zoom; | ||
if (this.coordsSpan) { | if (this.coordsSpan) { | ||
this.coordsSpan.textContent = `📍 ${Math.round(x)}, ${Math.round(y)} | ${Math.round(this. | this.coordsSpan.textContent = `📍 ${Math.round(x)}, ${Math.round(y)} | ${Math.round(this.zoom * 100)}%`; | ||
} | } | ||
}); | }); | ||
// Teclado | |||
document.addEventListener('keydown', (e) => { | |||
if (e.target.tagName === 'INPUT') return; | if (e.target.tagName === 'INPUT') return; | ||
switch(e.key) { | switch(e.key) { | ||
case '+': case '=': e.preventDefault(); this.zoomIn(); break; | case '+': case '=': e.preventDefault(); this.zoomIn(); break; | ||
case '-': case '_': e.preventDefault(); this.zoomOut(); break; | case '-': case '_': e.preventDefault(); this.zoomOut(); break; | ||
case 'r': case 'R': e.preventDefault(); this. | case 'r': case 'R': e.preventDefault(); this.reset(); break; | ||
case 'ArrowUp': case 'PageUp': e.preventDefault(); this.prevFloor(); break; | case 'ArrowUp': case 'PageUp': e.preventDefault(); this.prevFloor(); break; | ||
case 'ArrowDown': case 'PageDown': e.preventDefault(); this.nextFloor(); break; | case 'ArrowDown': case 'PageDown': e.preventDefault(); this.nextFloor(); break; | ||
} | } | ||
} | }); | ||
} | } | ||
render() { | |||
if (!this.layersContainer) return; | if (!this.layersContainer) return; | ||
this.layersContainer.innerHTML = ''; | this.layersContainer.innerHTML = ''; | ||
| Linha 430: | Linha 359: | ||
if (!this.config.layers || this.config.layers.length === 0) { | if (!this.config.layers || this.config.layers.length === 0) { | ||
this.layersContainer.innerHTML = '<div style="text-align:center | this.layersContainer.innerHTML = '<div style="padding:50px; text-align:center; color:#64748b;">Nenhum andar configurado</div>'; | ||
return; | return; | ||
} | } | ||
// Verificar se o andar atual existe | |||
if (!this.config.layers.find(l => l.id === this.currentFloor)) { | if (!this.config.layers.find(l => l.id === this.currentFloor)) { | ||
this.currentFloor = this.config.layers[0]?.id || 0; | this.currentFloor = this.config.layers[0]?.id || 0; | ||
| Linha 443: | Linha 373: | ||
} | } | ||
this.config.layers.forEach | this.config.layers.forEach(layer => { | ||
const $layer = document.createElement('div'); | const $layer = document.createElement('div'); | ||
$layer.className = ' | $layer.className = 'mw-layer'; | ||
$layer.setAttribute('data-floor', layer.id); | $layer.setAttribute('data-floor', layer.id); | ||
$layer.style.display = layer.id === this.currentFloor ? 'block' : 'none'; | $layer.style.display = layer.id === this.currentFloor ? 'block' : 'none'; | ||
$layer.style.transform = `translate(${layer.alignment?.offsetX || 0}px, ${layer.alignment?.offsetY || 0}px)`; | $layer.style.transform = `translate(${layer.alignment?.offsetX || 0}px, ${layer.alignment?.offsetY || 0}px)`; | ||
const $img = document.createElement('img'); | const $img = document.createElement('img'); | ||
$img.className = ' | $img.className = 'mw-image'; | ||
$img.style.transform = `scale(${this. | $img.style.transform = `scale(${this.zoom})`; | ||
$img.style.transformOrigin = '0 0'; | $img.style.transformOrigin = '0 0'; | ||
| Linha 459: | Linha 388: | ||
$img.src = layer.imagePath; | $img.src = layer.imagePath; | ||
} else { | } else { | ||
// Imagem placeholder | |||
const canvas = document.createElement('canvas'); | const canvas = document.createElement('canvas'); | ||
canvas.width = 800; | canvas.width = 800; | ||
| Linha 478: | Linha 408: | ||
$layer.appendChild($img); | $layer.appendChild($img); | ||
const $ | // Adicionar marcadores | ||
$ | const $markersDiv = document.createElement('div'); | ||
$ | $markersDiv.style.position = 'absolute'; | ||
$ | $markersDiv.style.top = '0'; | ||
$ | $markersDiv.style.left = '0'; | ||
$ | $markersDiv.style.width = '100%'; | ||
$ | $markersDiv.style.height = '100%'; | ||
$markersDiv.style.pointerEvents = 'none'; | |||
if (layer.markers | if (layer.markers) { | ||
layer.markers.forEach(marker => { | layer.markers.forEach(marker => { | ||
const $marker = this.createMarker(marker, layer.id); | const $marker = this.createMarker(marker, layer.id); | ||
$ | $markersDiv.appendChild($marker); | ||
this.markers.push( | this.markers.push($marker); | ||
}); | }); | ||
} | } | ||
$layer.appendChild($ | $layer.appendChild($markersDiv); | ||
this.layersContainer.appendChild($layer); | this.layersContainer.appendChild($layer); | ||
this.layers.push($layer); | this.layers.push($layer); | ||
| Linha 502: | Linha 433: | ||
createMarker(marker, floorId) { | createMarker(marker, floorId) { | ||
const $marker = document.createElement('div'); | const $marker = document.createElement('div'); | ||
$marker.className = ' | $marker.className = 'mw-marker'; | ||
$marker.setAttribute('data-marker-id', marker.id); | $marker.setAttribute('data-marker-id', marker.id); | ||
$marker.setAttribute('data-floor', floorId); | $marker.setAttribute('data-floor', floorId); | ||
| Linha 508: | Linha 439: | ||
$marker.setAttribute('data-y', marker.y); | $marker.setAttribute('data-y', marker.y); | ||
$marker.style.left = `${marker.x * this.zoom}px`; | |||
$marker.style.left = `${marker.x * this. | $marker.style.top = `${marker.y * this.zoom}px`; | ||
$marker.style.top = `${marker.y * this. | |||
// Ícone | |||
const iconSize = Math.max(20, Math.floor(20 * this.zoom)); | |||
let iconHtml = marker.iconBase64 ? | |||
const iconSize = Math.max(20, Math.floor(20 * this. | |||
let iconHtml = marker.iconBase64 ? | |||
`<img src="${marker.iconBase64}" style="width:${iconSize}px; height:${iconSize}px;">` : | `<img src="${marker.iconBase64}" style="width:${iconSize}px; height:${iconSize}px;">` : | ||
`<span style="font-size:${iconSize}px;">📍</span>`; | `<span style="font-size:${iconSize}px;">📍</span>`; | ||
let | // Tooltip | ||
let actionLabel = ''; | |||
switch(marker.action) { | switch(marker.action) { | ||
case 'popup': | case 'popup': actionLabel = '📋 Informações'; break; | ||
case 'nextFloor': | case 'nextFloor': actionLabel = '⬆️ Próximo andar'; break; | ||
case 'prevFloor': | case 'prevFloor': actionLabel = '⬇️ Andar anterior'; break; | ||
case 'gotoFloor': | case 'gotoFloor': actionLabel = '🎯 Ir para andar'; break; | ||
case 'link': | case 'link': actionLabel = '🔗 Link'; break; | ||
default: | default: actionLabel = 'Clique'; | ||
} | } | ||
$marker.innerHTML = ` | $marker.innerHTML = ` | ||
<div class="marker-icon">${iconHtml}</div> | <div class="mw-marker-icon">${iconHtml}</div> | ||
<div class="marker-tooltip"> | <div class="mw-marker-tooltip"> | ||
<strong>${marker.name || 'Marcador'}</strong><br> | <strong>${marker.name || 'Marcador'}</strong><br> | ||
<small>${ | <small>${actionLabel}</small> | ||
</div> | </div> | ||
<div class=" | <div class="mw-badge ${!marker.hasBadge ? 'hidden' : ''}">${marker.hasBadge ? (marker.badgeNumber || marker.number || '') : ''}</div> | ||
`; | `; | ||
// | // Evento de clique | ||
if (marker.action !== 'none') { | |||
$marker.addEventListener('click', (e) => { | $marker.addEventListener('click', (e) => { | ||
e.stopPropagation(); | e.stopPropagation(); | ||
| Linha 561: | Linha 480: | ||
executeAction(marker) { | executeAction(marker) { | ||
console.log('Ação:', marker.action, marker.name); | |||
switch(marker.action) { | switch(marker.action) { | ||
case 'popup': | case 'popup': | ||
| Linha 584: | Linha 505: | ||
} | } | ||
createPopup() { | |||
if (document.querySelector('.mw-map | if (document.querySelector('.mw-map-popup')) return; | ||
const popupHtml = ` | const popupHtml = ` | ||
<div class="mw-map | <div class="mw-map-popup"> | ||
<div class="popup-content"> | <div class="mw-popup-content"> | ||
<span class="popup-close">×</span> | <span class="mw-popup-close">×</span> | ||
<div class="popup-body"></div> | <div class="mw-popup-body"></div> | ||
</div> | </div> | ||
</div> | </div> | ||
| Linha 597: | Linha 518: | ||
document.body.insertAdjacentHTML('beforeend', popupHtml); | document.body.insertAdjacentHTML('beforeend', popupHtml); | ||
this.popup = document.querySelector('.mw-map | this.popup = document.querySelector('.mw-map-popup'); | ||
this.popupBody = this.popup?.querySelector('.popup-body'); | this.popupBody = this.popup?.querySelector('.mw-popup-body'); | ||
this.popup?.querySelector('.popup-close')?.addEventListener('click', () => this.closePopup()); | this.popup?.querySelector('.mw-popup-close')?.addEventListener('click', () => this.closePopup()); | ||
this.popup?.addEventListener('click', (e) => { | this.popup?.addEventListener('click', (e) => { | ||
if (e.target === this.popup) this.closePopup(); | if (e.target === this.popup) this.closePopup(); | ||
| Linha 607: | Linha 528: | ||
showPopup(marker) { | showPopup(marker) { | ||
if (!this.popup) this. | if (!this.popup) this.createPopup(); | ||
const iconHtml = marker.iconBase64 ? | const iconHtml = marker.iconBase64 ? | ||
`<img src="${marker.iconBase64}" style="width: | `<img src="${marker.iconBase64}" style="width:24px; height:24px; vertical-align:middle; margin-right:8px;">` : | ||
`<span style="font-size: | `<span style="font-size:20px; margin-right:8px;">📍</span>`; | ||
this.popupBody.innerHTML = ` | this.popupBody.innerHTML = ` | ||
${marker.actionData?.image ? `<img src="${marker.actionData.image}" | ${marker.actionData?.image ? `<img src="${marker.actionData.image}" style="max-width:100%; border-radius:8px; margin-bottom:15px;">` : ''} | ||
<div style="display:flex; align-items:center | <div style="display:flex; align-items:center;"> | ||
${iconHtml} | ${iconHtml} | ||
<h3 style="margin:0;">${marker.name}</h3> | <h3 style="margin:0;">${marker.name}</h3> | ||
</div> | </div> | ||
<p>${marker.actionData?.text || 'Sem informações'}</p> | <p style="margin-top:10px;">${marker.actionData?.text || 'Sem informações'}</p> | ||
`; | `; | ||
| Linha 630: | Linha 551: | ||
updateMarkersPosition() { | updateMarkersPosition() { | ||
this.markers.forEach( | this.markers.forEach($marker => { | ||
const floorId = parseInt($marker.getAttribute('data-floor')); | |||
if (floorId === this.currentFloor) { | |||
const x = parseFloat($marker.getAttribute('data-x')); | |||
const y = parseFloat($marker.getAttribute('data-y')); | |||
$marker.style.left = `${x * this.zoom}px`; | |||
$marker.style.top = `${y * this.zoom}px`; | |||
const iconSize = Math.max(20, Math.floor(20 * this. | const iconSize = Math.max(20, Math.floor(20 * this.zoom)); | ||
const iconDiv = | const iconDiv = $marker.querySelector('.mw-marker-icon'); | ||
if (iconDiv) { | if (iconDiv) { | ||
if ( | const hasImage = $marker.getAttribute('data-has-image'); | ||
if (hasImage) { | |||
// manter imagem | |||
} else { | } else { | ||
iconDiv.innerHTML = `<span style="font-size:${iconSize}px;">📍</span>`; | iconDiv.innerHTML = `<span style="font-size:${iconSize}px;">📍</span>`; | ||
| Linha 649: | Linha 574: | ||
zoomIn() { | zoomIn() { | ||
if (this. | if (this.zoom < this.maxZoom) { | ||
const oldZoom = this. | const oldZoom = this.zoom; | ||
this. | this.zoom = Math.min(this.zoom + this.zoomStep, this.maxZoom); | ||
this.applyZoom(oldZoom); | this.applyZoom(oldZoom); | ||
} | } | ||
| Linha 657: | Linha 582: | ||
zoomOut() { | zoomOut() { | ||
if (this. | if (this.zoom > this.minZoom) { | ||
const oldZoom = this. | const oldZoom = this.zoom; | ||
this. | this.zoom = Math.max(this.zoom - this.zoomStep, this.minZoom); | ||
this.applyZoom(oldZoom); | this.applyZoom(oldZoom); | ||
} | } | ||
| Linha 665: | Linha 590: | ||
applyZoom(oldZoom) { | applyZoom(oldZoom) { | ||
this.container.querySelectorAll('. | // Aplicar zoom nas imagens | ||
img.style.transform = `scale(${this. | this.container.querySelectorAll('.mw-image').forEach(img => { | ||
img.style.transform = `scale(${this.zoom})`; | |||
}); | }); | ||
// Ajustar scroll para manter centro | |||
const rect = this.viewport.getBoundingClientRect(); | const rect = this.viewport.getBoundingClientRect(); | ||
const cx = rect.width / 2 | const cx = rect.width / 2; | ||
const cy = rect.height / 2; | |||
const sx = (this.viewport.scrollLeft + cx) / oldZoom; | const sx = (this.viewport.scrollLeft + cx) / oldZoom; | ||
const sy = (this.viewport.scrollTop + cy) / oldZoom; | const sy = (this.viewport.scrollTop + cy) / oldZoom; | ||
this.viewport.scrollLeft = sx * this. | this.viewport.scrollLeft = sx * this.zoom - cx; | ||
this.viewport.scrollTop = sy * this. | this.viewport.scrollTop = sy * this.zoom - cy; | ||
this.updateMarkersPosition(); | this.updateMarkersPosition(); | ||
if (this. | if (this.zoomTextSpan) { | ||
this. | this.zoomTextSpan.textContent = `${Math.round(this.zoom * 100)}%`; | ||
} | } | ||
} | } | ||
reset() { | |||
this. | this.zoom = this.config.mapConfig?.defaultZoom || 1; | ||
this.container.querySelectorAll('. | this.container.querySelectorAll('.mw-image').forEach(img => { | ||
img.style.transform = `scale(${this. | img.style.transform = `scale(${this.zoom})`; | ||
}); | }); | ||
this.viewport.scrollLeft = 0; | this.viewport.scrollLeft = 0; | ||
this.viewport.scrollTop = 0; | this.viewport.scrollTop = 0; | ||
this.updateMarkersPosition(); | this.updateMarkersPosition(); | ||
if (this. | if (this.zoomTextSpan) { | ||
this. | this.zoomTextSpan.textContent = `${Math.round(this.zoom * 100)}%`; | ||
} | } | ||
} | } | ||
| Linha 725: | Linha 653: | ||
setTimeout(() => this.updateMarkersPosition(), 50); | setTimeout(() => this.updateMarkersPosition(), 50); | ||
} | } | ||
} | |||
// ==================== INICIALIZAÇÃO DO WIDGET ==================== | |||
function initWidget() { | |||
// Procurar todos os containers do widget | |||
const containers = document.querySelectorAll('[data-mw-map]'); | |||
containers.forEach(container => { | |||
const jsonStr = container.getAttribute('data-mw-config'); | |||
if (!jsonStr) return; | |||
try { | |||
} | const config = JSON.parse(jsonStr); | ||
new MWMap(container, config); | |||
} catch(e) { | |||
console.error('Erro ao criar mapa:', e); | |||
container.innerHTML = '<div style="padding:20px; text-align:center; color:#ef4444;">Erro ao carregar mapa</div>'; | |||
} | |||
}); | |||
} | |||
// Injetar estilos | |||
if (!document.getElementById('mw-map-styles')) { | |||
const styleTag = document.createElement('style'); | |||
styleTag.id = 'mw-map-styles'; | |||
styleTag.textContent = styles; | |||
document.head.appendChild(styleTag); | |||
} | |||
// Aguardar DOM e inicializar | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', initWidget); | |||
} else { | |||
initWidget(); | |||
} | } | ||
// | // Expor para uso global | ||
window. | window.MWMap = MWMap; | ||
})(); | })(); | ||
</script> | </script> | ||
</includeonly> | |||