Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I pan and zoom on mermaid output?

Tags:

mermaid

I have a really large mermaid flowchart and I can't read the text unless I zoom in. I've tried some VS Code extensions, but they weren't that great.

What are some ways to view/pan a large mermaid diagram?

like image 283
Ambrose Leung Avatar asked Oct 15 '25 00:10

Ambrose Leung


2 Answers

  1. If you don't mind pasting the data onto the mermaid live editor - they have a Pan & Zoom toggle which makes use of svg-pan-zoom

  2. You can also rip the Javascript code out, which someone has already done. I went ahead and modified it so that you can highlight the text in the Node labels as well.

Here is what I cobbled together after reading a suggestion from the author of svg-pan-zoom.

Here's hoping someone can take this and make pan and zoom easier for end users of mermaid diagrams.

<html>

<head>
  <style type="text/css">
    #mySvgId {
      height: 90%;
      width: 100%;
    }
  </style>
</head>

<body>
  <div id="graphDiv"></div>
  <script src="https://bumbu.me/svg-pan-zoom/dist/svg-pan-zoom.min.js"></script>
  <script type="module">
    import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
    mermaid.initialize({ startOnLoad: false });

    const drawDiagram = async function () {
      const element = document.querySelector('#graphDiv');
      const graphDefinition = `
flowchart LR
a(mermaid) --> b(flowchart) --> c(flowchart) --> d(flowchart) --> e(flowchart) --> f(flowchart)
        `;
      const { svg } = await mermaid.render('mySvgId', graphDefinition);
      element.innerHTML = svg.replace(/( )*max-width:( 0-9\.)*px;/i, '');

      var doPan = false;
      var eventsHandler;
      var panZoom;
      var mousepos;

      eventsHandler = {
        haltEventListeners: ['mousedown', 'mousemove', 'mouseup']

        , mouseDownHandler: function (ev) {
          if (event.target.className == "[object SVGAnimatedString]") {
            doPan = true;
            mousepos = { x: ev.clientX, y: ev.clientY }
          };
        }

        , mouseMoveHandler: function (ev) {
          if (doPan) {
            panZoom.panBy({ x: ev.clientX - mousepos.x, y: ev.clientY - mousepos.y });
            mousepos = { x: ev.clientX, y: ev.clientY };
            window.getSelection().removeAllRanges();
          }
        }

        , mouseUpHandler: function (ev) {
          doPan = false;
        }

        , init: function (options) {
          options.svgElement.addEventListener('mousedown', this.mouseDownHandler, false);
          options.svgElement.addEventListener('mousemove', this.mouseMoveHandler, false);
          options.svgElement.addEventListener('mouseup', this.mouseUpHandler, false);
        }

        , destroy: function (options) {
          options.svgElement.removeEventListener('mousedown', this.mouseDownHandler, false);
          options.svgElement.removeEventListener('mousemove', this.mouseMoveHandler, false);
          options.svgElement.removeEventListener('mouseup', this.mouseUpHandler, false);
        }
      }
      panZoom = svgPanZoom('#mySvgId', {
        zoomEnabled: true
        , controlIconsEnabled: true
        , fit: 1
        , center: 1
        , customEventsHandler: eventsHandler
      })
    };
    await drawDiagram();
  </script>
</body>

</html>
like image 161
Ambrose Leung Avatar answered Oct 19 '25 15:10

Ambrose Leung


A simple solution using https://github.com/timmywil/panzoom

Add a div wrapper around the mermaid syntax

<div class="diagram-container" id="diagram-container">
    <pre class="mermaid">
        flowchart TD
            A[Christmas] -->|Get money| B(Go shopping)
            B --> C{Let me think}
            C -->|One| D[Laptop]
            C -->|Two| E[iPhone]
            C -->|Three| F[fa:fa-car Car]
    </pre>
</div>

Set up a callback after the SVG is rendered to initialize panzoom and zoom using the mouse wheel.

mermaid.initialize({ startOnLoad: false });
await mermaid.run({
    querySelector: '.mermaid',
    postRenderCallback: (id) => {
        const container = document.getElementById("diagram-container");
        const svgElement = container.querySelector("svg");

        // Initialize Panzoom
        const panzoomInstance = Panzoom(svgElement, {
            maxScale: 5,
            minScale: 0.5,
            step: 0.1,
        });

        // Add mouse wheel zoom
        container.addEventListener("wheel", (event) => {
            panzoomInstance.zoomWithWheel(event);
        });
    }
});

Basic CSS to style the container

.diagram-container {
    width: 100%;
    height: 100%;
    overflow: hidden;
    border: 1px solid #ccc;
    position: relative;
    margin-bottom: 10px;
}
svg {
    cursor: grab;
}
like image 43
grappler Avatar answered Oct 19 '25 15:10

grappler