Astroに後付けでmemaid.jsの図をつけて拡大縮小(pan and zoom)できるようにしたメモ

# 概要

少し大きい図を描いたら読めなくなったので [svg-pan-zoom](https://github.com/bumbu/svg-pan-zoom) を使って拡大縮小できるようにした。

[ソースコード](https://github.com/hibohiboo/odyssage/blob/201491c9954eeca19feebfa7de32c1bb4eef7b56/docs/astro/src/layouts/MainLayout.astro)

[ページサンプル](https://hibohiboo.github.io/odyssage/ja/ddd/businessdomain/usecase/)

# コード
```diff:docs/astro/src/layouts/MainLayout.astro
// 省略


    </main>
+    <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@11/dist/mermaid.esm.min.mjs";
-      mermaid.initialize({ startOnLoad: true });
+      mermaid.initialize({ startOnLoad: false });
+      const drawDiagram = async function () {
+        const elements = document.querySelectorAll('.mermaid');
+        elements.forEach(async (element, index) => {
+          const graphDefinition = element.innerText;
+          const { svg } = await mermaid.render(`mySvgId${index}`, graphDefinition);
+          element.innerHTML = svg.replace(/max-width:\s*\d+(\.\d+)?px;/i, 'min-height: 300px');

+          let doPan = false;
+          let eventsHandler;
+          let panZoom;
+          let mousepos;

+          eventsHandler = {
+            haltEventListeners: ['mousedown', 'mousemove', 'mouseup'],
+            mouseDownHandler: function (ev) {
+              if (ev.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${index}`, {
+            zoomEnabled: true,
+            controlIconsEnabled: true,
+            fit: 1,
+            center: 1,
+            customEventsHandler: eventsHandler
+          });
+        });
+      };
+      await drawDiagram();
    </script>
  </body>
</html>
```


# 参考
[How do I pan and zoom on mermaid output?](https://stackoverflow.com/questions/78319916/how-do-i-pan-and-zoom-on-mermaid-output)
[Astro のブログに Mermaid を導入した](https://blog.mono0x.net/2024/02/11/astro-mermaid/)
[Mermaid の図を埋め込むために Shadow DOM を使わなくてもよくなった](https://blog.mono0x.net/2024/06/24/astro-mermaid/)