Mermaid离线使用,导出png及svg

1. 下载 mermaid.min.js

https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js下载。

放置到一个本地位置,比如我这里的 “D:/mermaid.min.js”

2. 下面代码保存xx.html,与上述下载的文件在同一位置,用浏览器打开。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Mermaid 离线生产力工具</title>
    <script src="./mermaid.min.js"></script>
    <style>
        body { display: flex; height: 100vh; margin: 0; font-family: -apple-system, "Segoe UI", sans-serif; overflow: hidden; background: #282c34; }
        #input { width: 40%; height: 100%; box-sizing: border-box; padding: 20px; font-family: 'Cascadia Code', 'Consolas', monospace; background: #282c34; color: #abb2bf; border: none; outline: none; font-size: 14px; resize: none; line-height: 1.6; }
        #right-pane { width: 60%; display: flex; flex-direction: column; background: #fff; }
        #toolbar { padding: 12px 20px; background: #f8f9fa; border-bottom: 1px solid #ddd; display: flex; gap: 12px; align-items: center; }
        #output-container { flex: 1; padding: 40px; overflow: auto; display: flex; justify-content: center; align-items: flex-start; background: #fff; }
        button { padding: 8px 16px; cursor: pointer; border: 1px solid #ccc; border-radius: 4px; font-weight: 600; font-size: 13px; transition: all 0.2s; }
        button:hover { background: #eee; border-color: #999; }
        .btn-png { background: #107c10; color: white; border: none; }
        .btn-png:hover { background: #0b5a0b; }
        .btn-clear { margin-left: auto; background: #fff; color: #d9534f; border-color: #d9534f; }
        .btn-clear:hover { background: #d9534f; color: #fff; }
        .hint { font-size: 11px; color: #888; }
    </style>
</head>
<body>
    <textarea id="input" placeholder="在此输入 Mermaid 代码..." spellcheck="false">
graph LR
    A[输入代码] --> B{渲染图表}
    B --> C(导出 PNG)
    B --> D(导出 SVG)
    style C fill:#e1f5fe,stroke:#01579b
    style D fill:#fff3e0,stroke:#e65100
    </textarea>
    
    <div id="right-pane">
        <div id="toolbar">
            <button class="btn-png" onclick="downloadPNG()">保存为 PNG (推荐)</button>
            <button onclick="downloadSVG()">保存为 SVG</button>
            <button class="btn-clear" onclick="clearAll()">清空</button>
            <span class="hint">※ SVG 建议用 Chrome 打开查看</span>
        </div>
        <div id="output-container">
            <div id="output" class="mermaid"></div>
        </div>
    </div>

    <script>
        const input = document.getElementById('input');
        const output = document.getElementById('output');

        mermaid.initialize({ 
            startOnLoad: false, 
            theme: 'default',
            flowchart: { useMaxWidth: false, htmlLabels: false } // 禁用 HTML 标签以提高 SVG 兼容性
        });

        const render = async () => {
            const code = input.value.trim();
            if (!code) { output.innerHTML = ""; return; }
            try {
                output.removeAttribute('data-processed');
                output.innerHTML = code;
                await mermaid.run({ nodes: [output] });
            } catch (e) { }
        };

        function processSVG() {
            const svgEl = output.querySelector('svg');
            if (!svgEl) return null;

            const bBox = svgEl.getBBox();
            const padding = 25;
            const w = bBox.width + padding * 2;
            const h = bBox.height + padding * 2;

            const cloned = svgEl.cloneNode(true);
            cloned.setAttribute('viewBox', `${bBox.x - padding} ${bBox.y - padding} ${w} ${h}`);
            cloned.setAttribute('width', w);
            cloned.setAttribute('height', h);
            cloned.style.backgroundColor = "white";

            // 增强版样式补丁:强制对齐文字
            const style = document.createElementNS("http://www.w3.org/2000/svg", "style");
            style.textContent = `
                text { 
                    font-family: 'Segoe UI', Arial, sans-serif !important; 
                    font-size: 14px !important; 
                    dominant-baseline: central !important;
                    text-anchor: middle !important;
                }
                .node rect, .node circle, .node polygon { fill: #ffffff !important; stroke: #333333 !important; }
                .edgePath path { stroke: #333333 !important; }
            `;
            cloned.insertBefore(style, cloned.firstChild);
            return { cloned, w, h };
        }

        async function downloadPNG() {
            const data = processSVG();
            if (!data) return;
            const zoom = 2;
            const canvas = document.createElement('canvas');
            canvas.width = data.w * zoom;
            canvas.height = data.h * zoom;
            const ctx = canvas.getContext('2d');
            const img = new Image();
            const xml = new XMLSerializer().serializeToString(data.cloned);
            const url = URL.createObjectURL(new Blob([xml], {type: 'image/svg+xml;charset=utf-8'}));

            img.onload = () => {
                ctx.fillStyle = "white";
                ctx.fillRect(0, 0, canvas.width, canvas.height);
                ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
                const a = document.createElement('a');
                a.download = `chart-${Date.now()}.png`;
                a.href = canvas.toDataURL('image/png', 1.0);
                a.click();
                URL.revokeObjectURL(url);
            };
            img.src = url;
        }

        function downloadSVG() {
            const data = processSVG();
            if (!data) return;
            const xml = new XMLSerializer().serializeToString(data.cloned);
            const blob = new Blob([xml], {type: 'image/svg+xml;charset=utf-8'});
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.download = `chart-${Date.now()}.svg`;
            a.href = url;
            a.click();
            setTimeout(() => URL.revokeObjectURL(url), 100);
        }

        function clearAll() {
            if(confirm("确定要清空所有代码吗?")) {
                input.value = "";
                render();
            }
        }

        let timer;
        input.addEventListener('input', () => {
            clearTimeout(timer);
            timer = setTimeout(render, 500);
        });

        window.onload = render;
    </script>
</body>
</html>

3.在chrome内打开,效果图如下:

不满意:使用chrome查看导入的svg不错位,使用bindview等图片查看工具文字会错位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值