├── madopic.png ├── favicon.svg ├── manifest.json ├── README.md ├── index.html ├── style.css └── script.js /madopic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolinbaba/Madopic/HEAD/madopic.png -------------------------------------------------------------------------------- /favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Madopic - Markdown to Picture", 3 | "short_name": "Madopic", 4 | "description": "精美的 Markdown 转图片工具,轻松制作社交媒体图片", 5 | "start_url": "/", 6 | "display": "standalone", 7 | "background_color": "#f8fafc", 8 | "theme_color": "#6366f1", 9 | "orientation": "portrait-primary", 10 | "icons": [ 11 | { 12 | "src": "favicon.svg", 13 | "sizes": "any", 14 | "type": "image/svg+xml", 15 | "purpose": "any maskable" 16 | } 17 | ], 18 | "categories": ["productivity", "utilities", "graphics"], 19 | "lang": "zh-CN" 20 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Madopic 2 | 3 | > Madopic (Markdown to Picture) 精美的 Markdown 转图片工具 ✨ 4 | 5 |  6 | 7 | 一个现代化的在线工具,可以将 Markdown 文本转换为精美的图片海报,特别适合社交媒体分享。 8 | 9 | ## 项目简介 10 | 11 | Madopic 是一个功能丰富的 Markdown 可视化工具,集成了数学公式渲染、图表绘制、多格式导出等强大功能。无论是制作学术海报、技术文档,还是社交媒体内容,都能轻松胜任。 12 | 13 | 14 | 15 | ## 主要功能 16 | 17 | ### 基础功能 18 | 1. **Markdown 渲染**:将 Markdown 内容转化为精美图文海报 19 | 2. **自定义外观**:可调整背景、字体大小、边距和模式(自由/小红书/朋友圈) 20 | 3. **多格式导出**:支持 PNG、PDF 和HTML格式导出 21 | 4. **所见即所得**:实时预览,快速导出 22 | 5. **图片支持**:本地选择、剪贴板粘贴,Base64 智能管理 23 | 6. **信息卡片**:支持好看的信息卡片 24 | 25 | ### 数学公式渲染 26 | 6. **KaTeX 集成**:支持行内公式 `$E=mc^{2}$` 和块级公式 `$$\int_a^b f(x)dx$$` 27 | 7. **公式模板库**:内置数学、物理、化学公式模板 28 | 8. **符号支持**:希腊字母、积分微分、矩阵等复杂数学符号 29 | 30 | ### 图表可视化 31 | 9. **Mermaid 图表**:流程图、序列图、甘特图、饼图等 32 | 10. **ECharts 数据图表**:柱状图、折线图、饼图等数据可视化 33 | 11. **一键插入**:丰富的图表模板,快速创建专业图表 34 | 35 | ### 用户体验 36 | 12. **代码块优化**:自动换行,支持长代码行完整显示 37 | 13. **预览缩放**:50%~150% 灵活查看 38 | 14. **键盘快捷键**:提升编辑效率 39 | 15. **智能通知**:操作反馈与错误提示 40 | 16. **完全开源**:MIT 协议,免费使用 41 | 42 | ## 使用场景 43 | 44 | ### 学术科研 45 | - **学术海报制作**:支持复杂数学公式和专业图表 46 | - **论文图表生成**:流程图、数据可视化 47 | - **教学课件制作**:数学、物理、化学公式展示 48 | - **研究报告可视化**:数据分析结果展示 49 | 50 | ### 商业应用 51 | - **技术文档制作**:API文档、架构图、流程说明 52 | - **产品功能介绍**:功能流程图、数据展示 53 | - **项目报告**:进度甘特图、统计图表 54 | - **培训材料**:操作流程图、知识图谱 55 | 56 | ### 社交媒体 57 | - **知识分享**:公式推导、概念解释 58 | - **学习笔记**:思维导图、重点总结 59 | - **技术博客**:代码示例、架构说明 60 | - **创意海报**:精美排版、视觉呈现 61 | 62 | ## 使用方法 63 | 64 | ### 基础操作 65 | 1. **编写内容**:在左侧编辑器中输入 Markdown 文本 66 | 2. **实时预览**:右侧会实时显示渲染效果,包括数学公式和图表 67 | 3. **自定义外观**:点击"自定义"按钮调整: 68 | - 背景渐变色彩 69 | - 字体大小(12-32px) 70 | - 内容边距(20-80px) 71 | - 显示模式(自由/小红书3:4/朋友圈长图) 72 | 73 | ### 数学公式 74 | 4. **插入公式**:点击工具栏 🧮 按钮插入数学公式模板 75 | 5. **物理公式**:点击 🔬 按钮获取物理公式(牛顿定律、电磁学等) 76 | 6. **化学公式**:点击 🧪 按钮插入化学反应方程式 77 | 7. **质能守恒**:点击 ⚛️ 按钮快速插入 E=mc² 78 | 79 | ### 图表绘制 80 | 8. **流程图**:点击 🔄 按钮创建 Mermaid 流程图 81 | 9. **序列图**:点击 ⏭️ 按钮绘制时序图 82 | 10. **甘特图**:点击 📅 按钮制作项目进度图 83 | 11. **数据图表**:点击 📊 按钮创建 ECharts 数据可视化 84 | 85 | ### 导出保存 86 | 12. **PNG 导出**:点击"导出PNG"按钮生成高清图片 87 | 13. **PDF 导出**:点击"导出PDF"按钮生成矢量文档 88 | 14. **HTML 导出**:点击"导出HTML"按钮生成HTML页面 89 | 15. **预览缩放**:使用 +/- 按钮调整预览大小(50%-150%) 90 | 91 | ## 技术栈 92 | 93 | ### 核心框架 94 | - **前端框架**:原生 HTML/CSS/JavaScript 95 | - **Markdown 解析**:marked.js 96 | - **图片生成**:html2canvas 97 | - **PDF 生成**:jsPDF 98 | 99 | ### 数学与图表 100 | - **数学公式渲染**:KaTeX + mhchem(化学公式扩展) 101 | - **流程图渲染**:Mermaid.js 102 | - **数据可视化**:Apache ECharts 103 | 104 | ### UI与交互 105 | - **图标字体**:Font Awesome 106 | - **样式设计**:现代化 CSS Grid/Flexbox/变量 107 | - **响应式设计**:移动端适配 108 | - **PWA支持**:manifest.json 安装元数据 109 | 110 | ## 技术实现与核心逻辑亮点 111 | 112 | ### 渲染优化 113 | - **独立导出DOM**:导出时创建隔离节点避免影响预览,确保样式一致性 114 | - **异步渲染管线**:数学公式→图表→ECharts 串行渲染,确保导出完整性 115 | - **ID冲突解决**:为导出节点的Mermaid图表生成唯一ID,避免预览区干扰 116 | - **代码块换行**:移除水平滚动,使用CSS自动换行确保长代码完整显示 117 | 118 | ### 公式与图表 119 | - **KaTeX集成**:支持mhchem化学扩展,正确处理上标语法 `^{2}` 120 | - **Mermaid渲染**:异步图表生成,支持主题配置和错误处理 121 | - **ECharts可视化**:动态数据图表,响应式尺寸适配 122 | - **模板系统**:内置数学、物理、化学公式和图表模板 123 | 124 | ### 储与导出 125 | - **Base64管理**:Markdown中短码展示,导出时替换完整数据 126 | - **多格式支持**:PNG位图和PDF矢量双格式导出 127 | - **样式变量化**:CSS变量统一管理,实时预览无重排 128 | - **错误降级**:html2canvas多尺度重试,确保导出成功率 129 | 130 | ### 用户体验 131 | - **智能预处理**:自动识别公式语法,预处理特殊字符 132 | - **键盘快捷键**:提升编辑效率,支持常用操作 133 | - **响应式设计**:适配多种屏幕尺寸和导出比例 134 | - **通知反馈**:操作状态实时提示,错误信息友好展示 135 | 136 | 137 | ## 本地部署 138 | 139 | 1. 克隆项目到本地 140 | 2. 使用任意HTTP服务器启动 141 | 142 | ## 更新日志 143 | 144 | ### 20250906 145 | - 增加导出HTML 146 | - 已知bug修复 147 | 148 | ### 20250824 149 | - UI调整 150 | - 增加卡片信息 151 | 152 | ### 20250814 153 | 154 | 重大功能更新 感谢贡献者 @[southbaird](https://github.com/southbaird) 155 | - **数学公式渲染**:集成KaTeX引擎,支持LaTeX数学公式 156 | - **化学公式支持**:添加mhchem扩展,支持化学反应方程式 157 | - **图表绘制系统**:集成Mermaid,支持流程图、序列图、甘特图等 158 | - **数据可视化**:集成ECharts,支持柱状图、折线图、饼图等 159 | - **PDF导出功能**:新增矢量格式导出,支持高质量打印 160 | - **丰富模板库**:内置数学、物理、化学公式和图表模板 161 | - **扩展工具栏**:新增12个专业功能按钮,快速插入内容 162 | - **功能优化**:UI界面、代码块、表格样式优化及已知BUG修复 163 | 164 | ### 20250801 165 | - **渲染性能**:优化异步渲染管线,减少阻塞时间 166 | - **错误处理**:完善错误降级机制,提升用户体验 167 | - **代码结构**:模块化重构,提高代码可维护性 168 | 169 | ## 📄 开源协议 170 | 171 | 本项目采用 MIT 协议开源。 172 | 173 | --- 174 | 175 | **Madopic 2.0** - 让你的知识更有画面感 🎨✨ -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |卡片内容解析失败
'; 402 | } 403 | 404 | // 创建卡片HTML结构 405 | const cardHtml = ` 406 |卡片渲染失败:${error.message}
421 |\n\n`; 741 | cursorPos = start + insertText.length; 742 | break; 743 | case 'clear': 744 | textarea.value = ''; 745 | updatePreview(); 746 | textarea.focus(); 747 | return; 748 | } 749 | 750 | textarea.value = beforeText + insertText + afterText; 751 | textarea.setSelectionRange(cursorPos, cursorPos); 752 | textarea.focus(); 753 | updatePreview(); 754 | } 755 | 756 | // 更新预览 757 | async function updatePreview() { 758 | const markdownText = markdownInput.value.trim(); 759 | // 同步行号(在去抖预览之外也保证立即更新) 760 | updateLineNumbers(); 761 | 762 | // 检查是否为空内容 763 | if (!markdownText) { 764 | showEmptyPreview(); 765 | return; 766 | } 767 | 768 | // 预处理数学公式 769 | let processedMarkdown = mathRenderer.preprocessMath(markdownText); 770 | 771 | // 预处理图表 772 | processedMarkdown = diagramRenderer.preprocessDiagram(processedMarkdown); 773 | 774 | // 预处理 ECharts 图表 775 | processedMarkdown = echartsRenderer.preprocessECharts(processedMarkdown); 776 | 777 | // 预处理卡片 778 | processedMarkdown = cardRenderer.preprocessCards(processedMarkdown); 779 | 780 | // 替换简化的base64为完整版本进行预览 781 | processedMarkdown = replaceImageDataForPreview(processedMarkdown); 782 | 783 | // 仅在已完成至少一次渲染后,且内容确实未变化时跳过 784 | if (hasInitialPreviewRendered && processedMarkdown === lastRenderedMarkdown) { 785 | return; 786 | } 787 | lastRenderedMarkdown = processedMarkdown; 788 | 789 | let htmlContent = ''; 790 | try { 791 | htmlContent = marked.parse(processedMarkdown); 792 | } catch (err) { 793 | console.error('Markdown 渲染失败: ', err); 794 | htmlContent = '
渲染失败,请检查 Markdown 内容。
'; 795 | } 796 | posterContent.innerHTML = htmlContent; 797 | 798 | // 渲染数学公式 799 | mathRenderer.renderMath(posterContent); 800 | 801 | // 渲染图表 802 | await diagramRenderer.renderDiagrams(posterContent); 803 | 804 | // 渲染 ECharts 图表 805 | await echartsRenderer.renderECharts(posterContent); 806 | 807 | // 渲染卡片 808 | await cardRenderer.renderCards(posterContent); 809 | 810 | // 确保内容容器可见 811 | posterContent.style.display = 'block'; 812 | 813 | // 重新应用当前的字体大小设置 814 | applyFontSize(currentFontSize); 815 | 816 | // 仅首次渲染使用淡入动画,后续输入不再触发,避免屏闪 817 | if (!hasInitialPreviewRendered) { 818 | posterContent.style.animation = 'fadeIn 0.3s ease'; 819 | hasInitialPreviewRendered = true; 820 | } else { 821 | posterContent.style.animation = ''; 822 | } 823 | } 824 | 825 | // ===== 行号逻辑 ===== 826 | function updateLineNumbers() { 827 | if (!lineNumbersEl) return; 828 | const value = markdownInput.value || ''; 829 | const lines = value.split('\n').length; 830 | // 构造包含行号的内容(使用换行分隔) 831 | let content = ''; 832 | for (let i = 1; i <= lines; i++) { 833 | content += (i === 1 ? '' : '\n') + i; 834 | } 835 | lineNumbersEl.textContent = content || '1'; 836 | // 高度同步 837 | lineNumbersEl.style.height = markdownInput.scrollHeight + 'px'; 838 | syncLineNumbersScroll(); 839 | } 840 | 841 | function syncLineNumbersScroll() { 842 | if (!lineNumbersEl) return; 843 | lineNumbersEl.scrollTop = markdownInput.scrollTop; 844 | } 845 | 846 | // 显示空内容提示 847 | function showEmptyPreview() { 848 | posterContent.innerHTML = ` 849 |在左侧编辑器中输入 Markdown 内容
855 |