English | 简体中文
# SnapDOM SnapDOM 是一个高性能 DOM 引擎:在浏览器中捕获已渲染的元素,并将其导出为 PNG、SVG 或 Canvas。插件可将同一次捕获转换为自包含的 HTML、PDF、可编辑矢量图、GIF、视频或供智能体使用的上下文。整个过程在页面内运行,核心引擎零依赖。 [文档与演示](https://snapdom.dev/) · [技术功能](FEATURES_CN.md) · [官方插件](packages/plugins/README.md) · [English](README.md) ## 为什么选择 SnapDOM - **速度。** 在浏览器中[运行实时基准测试](https://snapdom.dev/compare/live/):首次捕获与重复捕获,与 html2canvas 等库按实测时间排序。未变化的元素会复用上一次捕获。 - **保真度。** 输出与浏览器实际绘制的结果一致。每个版本发布前都会在真实演示页面上运行像素测试套件。 - **灵活性。** 一次捕获,导出多种格式。官方插件提供更多输出,同样的[插件钩子](PLUGIN_SPEC.md)也可用来编写你自己的插件。 - **持续维护。** 过去一年大约每周发布一个版本,采用 MIT 许可证,[更新日志](CHANGELOG.md)公开。 ## 快速开始 ```sh npm i @zumer/snapdom ``` ```js import { snapdom } from '@zumer/snapdom'; const card = document.querySelector('#card'); const image = await snapdom.toPng(card); document.body.appendChild(image); ``` 需要多种输出时,捕获一次即可: ```js const result = await snapdom(card); const image = await result.toPng(); const canvas = await result.toCanvas(); const blob = await result.toBlob({ format: 'png' }); await result.download({ format: 'jpg', filename: 'card' }); ``` 即使源元素随后发生变化,结果对象仍保留这次捕获。再次调用 `snapdom(card)` 才会捕获新状态。 ## 可以用它做什么 | 用途 | 输出 | 提供方 | | --- | --- | --- | | 分享卡片、图表、发票或仪表盘 | SVG、PNG、JPG、WebP、Canvas 或 Blob | 核心引擎 | | 将捕获结果用于纹理、覆盖层或过渡效果 | Canvas 与捕获几何信息 | 核心引擎 | | 保存页面片段,供以后展示 | 包含捕获样式和字体的 HTML | [`html-export`](packages/plugins/README.md#html-export) 插件 | | 为智能体或日志提供页面内容 | 文本/JSON 上下文,或附带元素映射的图片 | [`context-export`](packages/plugins/README.md#context-export) / [`agent-map`](packages/plugins/README.md#agent-map) 插件 | | 下载文档或录制变化中的内容 | 可搜索、可分页的 PDF、动画 GIF 或浏览器编码的视频 | [`pdf`](packages/plugins/pdf/README.md) / [`gif-export`](packages/plugins/README.md#gif-export) / [`video-export`](packages/plugins/README.md#video-export) 插件 | | 导出可编辑图形或复制到 Figma | 原生 SVG 形状与文本,或 Figma 剪贴板数据 | [`vector`](packages/plugins/vector/README.md) 插件 | 图片、HTML 和上下文导出使用捕获时保存的状态。GIF 和视频插件则在一段时间内持续捕获当前元素。 ## 安装 安装核心,并按需安装官方插件: ```sh npm i @zumer/snapdom@latest @zumer/snapdom-plugins@latest ``` 插件 4.x 要求 SnapDOM 3.3.0 或更高的 3.x 版本。其中包含 PDF 和 Vector 导出器,与核心一样采用 MIT 许可证。源码位于 `packages/plugins/`。 也可以在浏览器中加载: ```html ``` 以 ES Module 形式从 CDN 加载: ```js import { snapdom } from 'https://esm.sh/@zumer/snapdom@latest'; import { htmlExport } from 'https://esm.sh/@zumer/snapdom-plugins@latest/html-export'; ``` `https://unpkg.com/@zumer/snapdom@latest/dist/snapdom.mjs` 提供同一份模块。这些示例加载核心和插件的最新发布版本。 ### 构建产物 | 文件 | 用途 | | --- | --- | | `dist/snapdom.mjs` | 用于导入和打包工具的 ES Module | | `dist/snapdom.js` | 通过 script 标签加载,提供 `window.snapdom` | | `types/snapdom.d.ts` | TypeScript 类型声明 | 没有 CommonJS 构建产物。`@zumer/snapdom/plugins` 与包的主入口共用同一份运行时和插件注册表。 ## 基本用法 ### 选择输出格式 | 结果对象的方法 | 返回 | | --- | --- | | `toPng()`、`toJpg()`、`toWebp()` | `HTMLImageElement` | | `toSvg()` | 以 SVG 为数据源的 `HTMLImageElement` | | `toCanvas()` | `HTMLCanvasElement` | | `toBlob()` | 若捕获或导出时未显式指定格式,则返回 SVG `Blob` | | `toRaw()` / `url` | 捕获结果的 SVG Data URL | | `download()` | 下载所选格式 | | `to(name, options?)` | 按名称调用核心或插件导出器 | `snapdom.toPng(element, options)` 等快捷方法在一次调用中完成捕获和导出。结果对象还提供 `toJpeg()`,作为 `toJpg()` 的别名。`toImg()` 仍然可用;导出 SVG 图片时建议使用 `toSvg()`。 ### 设置尺寸与排除内容 ```js const result = await snapdom(card, { width: 800, dpr: 1, backgroundColor: '#ffffff', exclude: '.capture-ignore', excludeMode: 'remove' }); ``` `width` 和 `height` 指定输出尺寸;只设置一个时保持宽高比。只有两者都未设置时才应用 `scale`,最终像素尺寸还会乘以 `dpr`。`download()` 始终使用 DPR 1,文件尺寸与元素的 CSS 尺寸一致;如需与屏幕同样清晰的文件,请传入 `scale: devicePixelRatio`。 | 常用选项 | 默认值 | 用途 | | --- | --- | --- | | `scale` / `dpr` | `1` / 设备像素比 | 输出分辨率 | | `width` / `height` | 未设置 | 输出尺寸 | | `embedFonts` | `'auto'` | 嵌入捕获内容实际使用的网页字体 | | `backgroundColor` | 透明;JPG/WebP 为白色 | 输出背景 | | `exclude` | 无 | 选择器或判断函数;`true` 表示排除 | | `excludeMode` | `'hide'` | 保留不可见的占位空间,或设为 `'remove'` | | `filter` | 无 | 判断函数;`true` 保留节点,`false` 过滤节点 | | `filterMode` | `'hide'` | 独立控制被 `filter` 过滤节点的布局方式 | | `clip` | 未设置 | 捕获视口或页面坐标下的矩形区域 | | `captureSelection` | `false` | 包含用户的文字选区 | | `canvas` | 未设置 | 复用现有 Canvas | | `invalidate` | `false` | 在编程修改 CSSOM 等变化后刷新捕获 | | `fast` | `true` | 设为 `false` 时,长时间捕获期间页面仍可响应 | | `debug` | `false` | 在控制台输出降级和错误信息;关闭时 SnapDOM 不输出日志,可通过 `result.warnings` 查看 | [完整选项](https://snapdom.dev/docs/options/)还包括阴影、变换、字体、CORS、回退方案和布局校正。 ### 导出超长页面 使用默认的 SVG 引擎时,PNG 和 JPEG 文件导出会自动处理超出浏览器 Canvas 限制的捕获。捕获一次,即可下载一个完整分辨率的文件,或获取它的 Blob: ```js const capture = await snapdom(article); await capture.download({ format: 'png', scale: 2 }); // 或自行上传、保存文件: const blob = await capture.toBlob({ format: 'png', scale: 2, dpr: 1 }); // JPEG 用法相同,每边最多 65,535px: await capture.download({ format: 'jpg', scale: 2, quality: 0.9 }); ``` SnapDOM 将保存的捕获结果分块渲染,再编码为一个 PNG 或 JPEG 文件。它保留请求的尺寸,包括阴影和留白,无需为整张图片分配一个 Canvas。`download()` 始终使用 DPR 1;`toBlob()` 使用捕获时的 DPR,除非导出时另行指定。未超出限制的导出仍使用浏览器原生编码器。 PNG 需要 `CompressionStream`;不支持它的旧浏览器仍会缩小输出并发出警告。JPEG 超过每边 65,535px(该格式的上限)时同样如此。WebP 每边不能超过 16,383px。超大文件的编码仍需要时间和内存,打开这些文件的应用也有自己的图片限制。 ### 分段导出超长页面 `toCanvas()`、`toPng()`、`toJpg()` 和 WebP 导出仍使用单个 Canvas,超出 SnapDOM 的尺寸保护阈值时会整体缩小(Safari 每边 16,384px,Chrome/Firefox 每边 32,767px,另有总像素面积限制)。需要分别获取多个 Canvas 时,可以使用 `crop`: ```js const capture = await snapdom(article); const { contentX, contentY, w0, h0 } = capture.meta; const pieceHeight = 4000; // CSS px for (let y = 0; y < h0; y += pieceHeight) { const canvas = await capture.toCanvas({ scale: 2, dpr: 1, crop: { x: contentX, y: contentY + y, width: w0, height: Math.min(pieceHeight, h0 - y) } }); // 保存或上传这一段,然后继续 } ``` `crop` 是一个矩形,不是开关。它使用 `capture.meta` 的坐标,每次调用返回一个 canvas。捕获只执行一次,重复的只是栅格化。请让 `pieceHeight × scale × dpr` 保持在上限以内,并显式设置 `dpr`,因为它默认等于设备像素比。[分块原理](https://snapdom.dev/blog/huge-page-mosaic/)。 ### 导出 HTML 或结构化上下文 ```js import { htmlExport, contextExport } from '@zumer/snapdom-plugins'; const result = await snapdom(card, { plugins: [htmlExport(), contextExport({ format: 'json' })] }); const html = await result.toHtml(); const context = await result.toContext(); ``` 同一套插件系统也支持覆盖层、内容遮蔽和自定义导出器。同名的局部插件优先于全局插件。参见[官方插件参考](packages/plugins/README.md)和[插件规范](PLUGIN_SPEC.md)。 ### 导出 PDF 和可编辑矢量图 PDF 返回 `Blob`,支持可搜索、可选择的文本、链接、分页和可选的表单字段。Vector 返回不含 `衷心感谢 Syntax 对 SnapDOM 的赞助。
特别感谢 [@megaphonecolin](https://github.com/megaphonecolin)、[@sdraper69](https://github.com/sdraper69)、[@reynaldichernando](https://github.com/reynaldichernando)、[@gamma-app](https://github.com/gamma-app)、[@jrjohnson](https://github.com/jrjohnson) 和 [@ryanander](https://github.com/ryanander) 对本项目的支持! 如果你也愿意支持这个项目,可以[成为赞助者](https://github.com/sponsors/tinchox5)。 ## 支持我们 如果 SnapDOM 帮你节省了时间,欢迎在 GitHub 上点一个星标,让更多开发者发现它。 用 SnapDOM 构建了项目?欢迎把这个徽章添加到你的 README: [](https://snapdom.dev) ```md [](https://snapdom.dev) ``` ### 使用 SnapDOM 的项目 以下项目正在使用 SnapDOM: - [LobeHub](https://github.com/lobehub/lobehub) — AI 智能体平台 - [Hugging Face Chat UI](https://github.com/huggingface/chat-ui) — HuggingChat 界面,使用 SnapDOM 截取作品预览 - [Sealos](https://github.com/labring/sealos) — AI 原生云操作系统 - [Tencent tmagic-editor](https://github.com/Tencent/tmagic-editor) — 低代码页面编辑器 - [Playroom](https://github.com/seek-oss/playroom) — SEEK 推出的 JSX 设计工具 - [GPT-Vis](https://github.com/antvis/GPT-Vis) — 蚂蚁集团 AntV 推出的、面向 AI 的数据可视化工具 - [Rabby Wallet](https://github.com/RabbyHub/Rabby) — 面向 EVM 链的浏览器钱包 - [uMap](https://github.com/umap-project/umap) — OpenStreetMap 地图制作工具 - [ListenBrainz](https://github.com/metabrainz/listenbrainz-server) — MetaBrainz 推出的音乐收听记录服务 - [Mind Elixir](https://github.com/SSShooter/mind-elixir-core) — 思维导图核心库,推荐使用 SnapDOM 导出图片 - [Kong UI Components](https://github.com/Kong/public-ui-components) — Kong 仪表盘渲染器使用 SnapDOM 导出 PDF - [SnapDIFF](https://zumerlab.com/snapdiff/) — 浏览器内的视觉回归测试工具 *(由 Zumerlab 开发)* 完整案例见 **[snapdom.dev/made-with](https://snapdom.dev/made-with/)**。如果你的项目也在使用 SnapDOM,欢迎[提交 PR](https://github.com/zumerlab/snapdom/pulls)添加到列表中,仅收录真实、可验证的项目。 ## 许可证 MIT © Zumerlab