<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Mermaid Flowchart Custom Pan Zoom Demo</title> <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"> <style> #svg-container { width: 100%; height: 600px; border: 1px solid #dbdbdb; overflow: hidden; position: relative; border-radius: 4px; } #mermaid-diagram { width: 100%; height: 100%; position: absolute; top: 0; left: 0; } #mermaid-diagram svg { width: 100%; height: 100%; } .mermaid .node rect, .mermaid .node circle, .mermaid .node ellipse, .mermaid .node polygon, .mermaid .node path { stroke: #1a5f94 !important; stroke-width: 2px !important; fill: #3298dc !important; filter: url(#rough); } .mermaid .edgePath .path { stroke: #1a5f94 !important; stroke-width: 3px !important; filter: url(#rough); } .mermaid .label { font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif; color: #ffffff !important; font-weight: bold; } } .button-container { margin-top: 1rem; } </style> </head> <body> <section class="section"> <div class="container"> <h1 class="title">Mermaid Flowchart Demo</h1> <svg width="0" height="0"> <defs> <filter id="rough"> <feTurbulence type="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="0"/> <feDisplacementMap in="SourceGraphic" in2="noise" scale="2" /> </filter> </defs> </svg> <div id="svg-container"> <div id="mermaid-diagram" class="mermaid"> flowchart TD A["Cloud User"] -- CHF/EUR/... --> B("CLOUD MARKET PLACE<br>Discount based on position<br>in TF Liquidity Pool.") & B2(("ThreeFold<br>Liquidity Pool")) B2 -- TFT or INCA --> B B -- TFT or INCA --> C{"Proof Of Utilization"} G["FARMING GRANTS<br>40m Tokens / Month"] --> I{"Proof Of Capacity<br>uptime, location, ..."} I --> D["ThreeFold Farmers"] C -- 80% --> D C -- 10% --> E["ThreeFold Cooperative"] & F["Validators<br>Commercial Partners"] </div> </div> <div class="button-container"> <button id="zoom-in" class="button is-small is-primary"> <span class="icon is-small"> <i class="fas fa-search-plus"></i> </span> <span>Zoom In</span> </button> <button id="zoom-out" class="button is-small is-info"> <span class="icon is-small"> <i class="fas fa-search-minus"></i> </span> <span>Zoom Out</span> </button> <button id="reset" class="button is-small is-warning"> <span class="icon is-small"> <i class="fas fa-undo"></i> </span> <span>Reset</span> </button> </div> </div> </section> <script> mermaid.initialize({ startOnLoad: true, theme: 'base', themeVariables: { primaryColor: '#3298dc', primaryTextColor: '#ffffff', // primaryBorderColor: '#3298dc', lineColor: '#3298dc', secondaryColor: '#4FC3F7', // tertiaryColor: '#3298dc' }, flowchart: { curve: 'basis', useMaxWidth: true, htmlLabels: true, diagramPadding: 8, nodeSpacing: 60, rankSpacing: 60, edgeThickness: 3, curve: 'basis' } }); mermaid.init(undefined, "#mermaid-diagram").then(function() { const container = document.getElementById('svg-container'); const diagram = document.getElementById('mermaid-diagram'); let scale = 1; let panning = false; let start = { x: 0, y: 0 }; let translate = { x: 0, y: 0 }; function setTransform() { diagram.style.transform = `translate(${translate.x}px, ${translate.y}px) scale(${scale})`; } container.addEventListener('mousedown', (e) => { panning = true; start = { x: e.clientX - translate.x, y: e.clientY - translate.y }; }); container.addEventListener('mousemove', (e) => { if (!panning) return; translate = { x: e.clientX - start.x, y: e.clientY - start.y }; setTransform(); }); container.addEventListener('mouseup', () => { panning = false; }); container.addEventListener('mouseleave', () => { panning = false; }); container.addEventListener('touchstart', (e) => { if (e.touches.length === 1) { panning = true; start = { x: e.touches[0].clientX - translate.x, y: e.touches[0].clientY - translate.y }; } }); container.addEventListener('touchmove', (e) => { if (!panning || e.touches.length !== 1) return; translate = { x: e.touches[0].clientX - start.x, y: e.touches[0].clientY - start.y }; setTransform(); }); container.addEventListener('touchend', () => { panning = false; }); container.addEventListener('wheel', (e) => { e.preventDefault(); const xs = (e.clientX - translate.x) / scale; const ys = (e.clientY - translate.y) / scale; const delta = -e.deltaY; const newScale = scale * Math.pow(1.001, delta); scale = Math.min(Math.max(0.1, newScale), 10); translate = { x: e.clientX - xs * scale, y: e.clientY - ys * scale }; setTransform(); }, { passive: false }); document.getElementById('zoom-in').addEventListener('click', () => { scale *= 1.2; setTransform(); }); document.getElementById('zoom-out').addEventListener('click', () => { scale /= 1.2; setTransform(); }); document.getElementById('reset').addEventListener('click', () => { scale = 1; translate = { x: 0, y: 0 }; setTransform(); }); }); </script> </body> </html>