Created
June 5, 2025 19:28
-
-
Save gwpl/4ad330d8a87d72cb65ee2ded981619e5 to your computer and use it in GitHub Desktop.
Mermaid HTML zooming panning template
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Full-Screen Mermaid Diagram with Hovering Controls</title> | |
<link | |
rel="stylesheet" | |
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" | |
crossorigin="anonymous" | |
/> | |
<style> | |
body { | |
margin: 0; | |
padding: 0; | |
font-family: Arial, sans-serif; | |
overflow: hidden; | |
} | |
/* Full viewport container */ | |
#container { | |
position: relative; | |
width: 100vw; | |
height: 100vh; | |
background: #f5f5f5; | |
} | |
/* Diagram takes full space */ | |
#diagramContainer { | |
width: 100%; | |
height: 100%; | |
overflow: hidden; | |
cursor: grab; | |
} | |
#diagramContainer:active { | |
cursor: grabbing; | |
} | |
#diagramContainer svg { | |
width: 100%; | |
height: 100%; | |
} | |
/* Floating control panel */ | |
#controls { | |
position: absolute; | |
bottom: 20px; | |
right: 20px; | |
background: rgba(255, 255, 255, 0.95); | |
border: 1px solid #ddd; | |
border-radius: 8px; | |
padding: 8px; | |
box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
z-index: 1000; | |
display: flex; | |
gap: 4px; | |
} | |
#controls button { | |
width: 36px; | |
height: 36px; | |
border: none; | |
background: white; | |
cursor: pointer; | |
border-radius: 6px; | |
font-size: 16px; | |
color: #333; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
transition: all 0.2s ease; | |
} | |
#controls button:hover { | |
background: #e8e8e8; | |
transform: scale(1.1); | |
} | |
#controls button:active { | |
transform: scale(0.95); | |
} | |
/* Zoom level indicator */ | |
#zoom-level { | |
position: absolute; | |
bottom: 20px; | |
left: 20px; | |
background: rgba(0, 0, 0, 0.7); | |
color: white; | |
padding: 8px 12px; | |
border-radius: 6px; | |
font-size: 14px; | |
font-weight: 500; | |
z-index: 1000; | |
} | |
/* Help text */ | |
#help-text { | |
position: absolute; | |
top: 20px; | |
left: 20px; | |
background: rgba(0, 0, 0, 0.7); | |
color: white; | |
padding: 12px 16px; | |
border-radius: 6px; | |
font-size: 14px; | |
max-width: 300px; | |
z-index: 1000; | |
transition: opacity 0.3s ease; | |
} | |
#help-text h3 { | |
margin: 0 0 8px 0; | |
font-size: 16px; | |
} | |
#help-text p { | |
margin: 4px 0; | |
line-height: 1.4; | |
} | |
/* Loading indicator */ | |
#loading { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 20px; | |
color: #666; | |
z-index: 999; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="container"> | |
<div id="loading">Loading diagram...</div> | |
<div id="help-text"> | |
<h3>Navigation Controls</h3> | |
<p>🖱️ <strong>Mouse wheel:</strong> Zoom in/out</p> | |
<p>🖱️ <strong>Click & drag:</strong> Pan around</p> | |
<p>⌨️ <strong>Double-click:</strong> Zoom to point</p> | |
</div> | |
<div id="zoom-level">Zoom: 100%</div> | |
<div id="controls"> | |
<button onclick="zoomIn()" title="Zoom In"> | |
<i class="fas fa-search-plus"></i> | |
</button> | |
<button onclick="zoomOut()" title="Zoom Out"> | |
<i class="fas fa-search-minus"></i> | |
</button> | |
<button onclick="resetZoom()" title="Reset View"> | |
<i class="fas fa-undo"></i> | |
</button> | |
<button onclick="fitToScreen()" title="Fit to Screen"> | |
<i class="fas fa-expand"></i> | |
</button> | |
</div> | |
<!-- Mermaid container takes full viewport --> | |
<div id="diagramContainer" class="mermaid"> | |
graph LR | |
Start[fa:fa-play Start Process] --> Init[fa:fa-cog Initialize System] | |
Init --> Config[fa:fa-wrench Load Configuration] | |
Config --> DB1[fa:fa-database Connect to Database 1] | |
Config --> DB2[fa:fa-database Connect to Database 2] | |
Config --> Cache[fa:fa-memory Connect to Redis Cache] | |
DB1 --> DataLoad1[fa:fa-download Load User Data] | |
DB2 --> DataLoad2[fa:fa-download Load Product Data] | |
Cache --> CacheWarm[fa:fa-fire Warm Up Cache] | |
DataLoad1 --> ProcessUser[fa:fa-users Process User Records] | |
DataLoad2 --> ProcessProd[fa:fa-box Process Product Records] | |
CacheWarm --> CacheReady[fa:fa-check Cache Ready] | |
ProcessUser --> UserValid[fa:fa-check-circle Validate Users] | |
ProcessProd --> ProdValid[fa:fa-check-circle Validate Products] | |
UserValid --> Transform1[fa:fa-exchange Transform User Data] | |
ProdValid --> Transform2[fa:fa-exchange Transform Product Data] | |
Transform1 --> Aggregate[fa:fa-layer-group Aggregate All Data] | |
Transform2 --> Aggregate | |
CacheReady --> Aggregate | |
Aggregate --> Analysis[fa:fa-chart-line Run Analytics] | |
Analysis --> Report1[fa:fa-file-alt Generate Sales Report] | |
Analysis --> Report2[fa:fa-file-alt Generate User Report] | |
Analysis --> Report3[fa:fa-file-alt Generate Inventory Report] | |
Report1 --> Format1[fa:fa-paint-brush Format Sales Data] | |
Report2 --> Format2[fa:fa-paint-brush Format User Data] | |
Report3 --> Format3[fa:fa-paint-brush Format Inventory Data] | |
Format1 --> Export1[fa:fa-file-export Export to Excel] | |
Format2 --> Export2[fa:fa-file-export Export to PDF] | |
Format3 --> Export3[fa:fa-file-export Export to CSV] | |
Export1 --> Store[fa:fa-save Store in File System] | |
Export2 --> Store | |
Export3 --> Store | |
Store --> Email[fa:fa-envelope Send Email Notifications] | |
Store --> Slack[fa:fa-slack Send Slack Notifications] | |
Store --> Dashboard[fa:fa-tachometer-alt Update Dashboard] | |
Email --> Complete[fa:fa-flag-checkered Process Complete] | |
Slack --> Complete | |
Dashboard --> Complete | |
Complete --> Cleanup[fa:fa-broom Cleanup Temporary Files] | |
Cleanup --> Log[fa:fa-history Log Process Results] | |
Log --> End[fa:fa-stop End Process] | |
%% Additional branches for error handling | |
UserValid --> UserError[fa:fa-exclamation-triangle User Validation Error] | |
ProdValid --> ProdError[fa:fa-exclamation-triangle Product Validation Error] | |
UserError --> ErrorHandler[fa:fa-tools Error Handler] | |
ProdError --> ErrorHandler | |
ErrorHandler --> Retry[fa:fa-redo Retry Process] | |
Retry --> ProcessUser | |
Retry --> ProcessProd | |
%% Monitoring branch | |
Analysis --> Monitor[fa:fa-desktop System Monitor] | |
Monitor --> CPU[fa:fa-microchip CPU Usage Check] | |
Monitor --> Memory[fa:fa-memory Memory Usage Check] | |
Monitor --> Disk[fa:fa-hdd Disk Usage Check] | |
CPU --> Alert[fa:fa-bell Alert System] | |
Memory --> Alert | |
Disk --> Alert | |
Alert --> Complete | |
</div> | |
</div> | |
<!-- Load Mermaid ESM module --> | |
<script type="module"> | |
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'; | |
mermaid.initialize({ startOnLoad: false }); | |
</script> | |
<!-- Load svg-pan-zoom --> | |
<script src="https://unpkg.com/[email protected]/dist/svg-pan-zoom.min.js"></script> | |
<!-- Initialize diagram with pan/zoom --> | |
<script type="module"> | |
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'; | |
let panZoomInstance; | |
// Get the DSL content | |
const dsl = document.querySelector('#diagramContainer').textContent.trim(); | |
// Render the diagram | |
mermaid.render('zoomableSvg', dsl).then(result => { | |
// Replace container content with rendered SVG | |
document.querySelector('#diagramContainer').innerHTML = result.svg; | |
// Hide loading indicator | |
document.getElementById('loading').style.display = 'none'; | |
// Initialize svg-pan-zoom | |
panZoomInstance = svgPanZoom('#zoomableSvg', { | |
zoomEnabled: true, | |
controlIconsEnabled: false, | |
fit: true, | |
center: true, | |
minZoom: 0.1, | |
maxZoom: 10, | |
zoomScaleSensitivity: 0.3, | |
dblClickZoomEnabled: true, | |
mouseWheelZoomEnabled: true, | |
preventMouseEventsDefault: true, | |
onZoom: function(newScale) { | |
updateZoomLevel(newScale); | |
} | |
}); | |
// Store instance globally for button access | |
window.mermaidPanZoom = panZoomInstance; | |
// Initial zoom level display | |
updateZoomLevel(panZoomInstance.getZoom()); | |
// Hide help text after 5 seconds | |
setTimeout(() => { | |
const helpText = document.getElementById('help-text'); | |
helpText.style.opacity = '0'; | |
setTimeout(() => { | |
helpText.style.display = 'none'; | |
}, 300); | |
}, 5000); | |
}).catch(error => { | |
console.error('Error rendering diagram:', error); | |
document.getElementById('loading').textContent = 'Error loading diagram'; | |
}); | |
// Update zoom level display | |
function updateZoomLevel(scale) { | |
const percentage = Math.round(scale * 100); | |
document.getElementById('zoom-level').textContent = `Zoom: ${percentage}%`; | |
} | |
// Control functions | |
window.zoomIn = function() { | |
if (window.mermaidPanZoom) { | |
window.mermaidPanZoom.zoomIn(); | |
} | |
}; | |
window.zoomOut = function() { | |
if (window.mermaidPanZoom) { | |
window.mermaidPanZoom.zoomOut(); | |
} | |
}; | |
window.resetZoom = function() { | |
if (window.mermaidPanZoom) { | |
window.mermaidPanZoom.reset(); | |
} | |
}; | |
window.fitToScreen = function() { | |
if (window.mermaidPanZoom) { | |
window.mermaidPanZoom.fit(); | |
window.mermaidPanZoom.center(); | |
} | |
}; | |
// Keyboard shortcuts | |
document.addEventListener('keydown', function(e) { | |
if (!window.mermaidPanZoom) return; | |
switch(e.key) { | |
case '+': | |
case '=': | |
e.preventDefault(); | |
zoomIn(); | |
break; | |
case '-': | |
case '_': | |
e.preventDefault(); | |
zoomOut(); | |
break; | |
case '0': | |
e.preventDefault(); | |
resetZoom(); | |
break; | |
case 'f': | |
case 'F': | |
e.preventDefault(); | |
fitToScreen(); | |
break; | |
} | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment