snapDOM 是一款快速精准的 DOM 转图像捕获工具,专为基于缩放的视图过渡框架 Zumly 打造。
它可以将任何 HTML 元素捕获为可缩放的 SVG 图像,并保留样式、字体、背景图像、伪元素甚至阴影 DOM。它还支持导出为光栅图像格式和画布。
canvas
或 Blobnpm i @zumer/snapdom
yarn add @zumer/snapdom
Then import it in your code:
import { snapdom } from '@zumer/snapdom';
<script src="https://cdn.jsdelivr.net/npm/@zumer/snapdom/dist/snapdom.min.js"></script>
<script src="snapdom.js"></script>
import { snapdom } from './snapdom.mjs';
<script type="module">
import { snapdom } from 'https://cdn.jsdelivr.net/npm/@zumer/snapdom/dist/snapdom.mjs';
</script>
const el = document.querySelector('#target');
const result = await snapdom(el);
const img = await result.toPng();
document.body.appendChild(img);
await result.download({ format: 'jpg', filename: 'my-capture' });
const el = document.querySelector('#target');
const png = await snapdom.toPng(el);
document.body.appendChild(png);
const blob = await snapdom.toBlob(el);
snapdom(el, options?)
返回具有可重用导出方法的对象:
{
url: string;
toRaw(): string;
toImg(): Promise<HTMLImageElement>;
toCanvas(): Promise<HTMLCanvasElement>;
toBlob(options?): Promise<Blob>;
toPng(options?): Promise<HTMLImageElement>;
toJpg(options?): Promise<HTMLImageElement>;
toWebp(options?): Promise<HTMLImageElement>;
download(options?): Promise<void>;
}
方法 | 说明 |
---|---|
snapdom.toImg(el, options?) |
返回 HTMLImageElement |
snapdom.toCanvas(el, options?) |
返回 Canvas |
snapdom.toBlob(el, options?) |
返回 SVG 或光栅 Blob |
snapdom.toPng(el, options?) |
返回 PNG 图像 |
snapdom.toJpg(el, options?) |
返回 JPG 图像 |
snapdom.toWebp(el, options?) |
返回 WebP 图像 |
snapdom.download(el, options?) |
触发下载 |
✅ 注意: 样式压缩现在始终在内部启用。
compress
选项已被移除。
所有捕获方法都接受 options
对象:
选项 | 类型 | 默认值 | 描述 |
---|---|---|---|
fast |
布尔值 | true |
跳过短暂的空闲延迟以获得更快的结果 |
embedFonts |
布尔值 | false |
内联非图标字体(图标字体始终启用) |
localFonts |
数组 | [] |
本地字体 { family, src, weight?, style? } |
iconFonts |
string|RegExp|Array | [] |
额外的图标字体匹配器 |
excludeFonts |
对象 | {} |
嵌入期间排除字体系列/域/子集 |
scale |
数字 | 1 |
输出比例乘数 |
dpr |
数字 | devicePixelRatio |
设备像素比 |
width |
数字 | - | 输出宽度 |
height |
数字 | - | 输出高度 |
backgroundColor |
字符串 | "#fff" |
JPG/WebP 的备用颜色 |
quality |
数字 | 1 |
JPG/WebP 的质量 (0 到 1) |
useProxy |
字符串 | '' |
CORS 备用的代理库 |
type |
字符串 | svg |
默认 Blob 类型 (svg |png |jpg |webp ) |
exclude |
字符串[] | - | 要排除的 CSS 选择器 |
filter |
函数 | - | 自定义谓词 (el) => boolean |
cache |
字符串 | "soft" |
控制内部缓存:disabled 、soft 、auto 、full |
defaultImageUrl |
string | function | - | <img> 加载失败时的后备图片。如果提供了函数,它将接收 { width?, height?, src?, element } 参数,并且必须返回一个 URL(字符串或 Promisehttps://placehold.co/{width}x{height} )。 |
<img>
加载失败时的后备图片为 <img>
加载失败提供默认图片。您可以传递固定 URL 或回调函数,该回调函数接收测量尺寸并返回 URL(方便生成动态占位符)。
// 1) Fixed URL fallback
await snapdom.toImg(element, {
defaultImageUrl: '/images/fallback.png'
});
// 2) Dynamic placeholder via callback
await snapdom.toImg(element, {
defaultImageUrl: ({ width = 300, height = 150 }) =>
`https://placehold.co/${Math.round(width)}x${Math.round(height)}`
});
// 3) With proxy (if your fallback host has no CORS)
await snapdom.toImg(element, {
defaultImageUrl: ({ width = 300, height = 150 }) =>
`https://dummyimage.com/${Math.round(width)}x${Math.round(height)}/cccccc/666.png&text=img`,
useProxy: 'https://corsproxy.io/?url='
});
注意:
<img>
替换为一个保留宽度/高度的占位符块。scale
、width
、height
)scale
,则其**优先于width
/height
。width
,则高度将按比例缩放(反之亦然)。width
和 height
会强制使用精确尺寸(可能会失真)。useProxy
)默认情况下,snapDOM 会尝试 crossOrigin="anonymous"
(对于同域,则使用 use-credentials
)。如果资产被 CORS 阻止,您可以将 useProxy
设置为转发实际 src
的前缀 URL:
await snapdom.toPng(el, {
useProxy: 'your-proxy' // Example 'https://api.allorigins.win/raw?url='
});
提示
embedFonts
设置为 true
时,snapDOM 会嵌入在捕获子树中检测到的非图标 @font-face
规则。图标字体(Font Awesome、Material Icons 等)始终嵌入。
localFonts
如果您自行提供字体或拥有数据 URL,可以在此处声明它们以避免额外的 CSS 检测:
await snapdom.toPng(el, {
embedFonts: true,
localFonts: [
{ family: 'Inter', src: '/fonts/Inter-Variable.woff2', weight: 400, style: 'normal' },
{ family: 'Inter', src: '/fonts/Inter-Italic.woff2', style: 'italic' }
]
});
iconFonts
添加自定义图标系列(名称或正则表达式匹配器)。适用于私有图标集:
await snapdom.toPng(el, {
iconFonts: ['MyIcons', /^(Remix|Feather) Icons?$/i]
});
excludeFonts
跳过特定的非图标字体以加快捕获速度或避免不必要的下载。
await snapdom.toPng(el, {
embedFonts: true,
excludeFonts: {
families: ['Noto Serif', 'SomeHeavyFont'], // skip by family name
domains: ['fonts.gstatic.com', 'cdn.example'], // skip by source host
subsets: ['cyrillic-ext'] // skip by unicode-range subset tag
}
});
注释
excludeFonts
仅适用于非图标字体。图标字体始终嵌入。families
的匹配不区分大小写。Hosts 根据解析后的 URL 通过子字符串进行匹配。exclude
与 filter
exclude
:通过选择器移除。filter
:每个元素的高级谓词(返回 false
表示删除)。示例:使用 display:none
过滤元素:
/**
* Example filter: skip elements with display:none
* @param {Element} el
* @returns {boolean} true = keep, false = exclude
*/
function filterHidden(el) {
const cs = window.getComputedStyle(el);
if (cs.display === 'none') return false;
return true;
}
await snapdom.toPng(document.body, { filter: filterHidden });
Example with exclude
: remove banners or tooltips by selector
await snapdom.toPng(el, {
exclude: ['.cookie-banner', '.tooltip', '[data-test="debug"]']
});
preCache()
– Optional helper预加载外部资源以避免首次捕获停顿(对大型/复杂的树有帮助)。
import { preCache } from '@zumer/snapdom';
await preCache({
root: document.body,
embedFonts: true,
localFonts: [{ family: 'Inter', src: '/fonts/Inter.woff2', weight: 400 }],
useProxy: 'your-proxy'
});
SnapDOM 维护图像、背景、资源、样式和字体的内部缓存。
您可以使用 cache
选项控制在两次捕获之间如何清除这些缓存:
模式 | 描述 |
---|---|
"disabled" |
无缓存 |
"soft" |
清除会话缓存(styleMap 、nodeMap 、styleCache )(默认) |
"auto" |
最小清理:仅清除临时地图 |
"full" |
保留所有缓存(不清除任何缓存,性能最佳) |
示例:
// Use minimal but fast cache
await snapdom.toPng(el, { cache: 'auto' });
// Keep everything in memory between captures
await snapdom.toPng(el, { cache: 'full' });
// Force a full cleanup on every capture
await snapdom.toPng(el, { cache: 'disabled' });
useProxy
选项处理 CORS 拒绝)@font-face
CSS 规则已得到良好支持,但如果需要使用 JS FontFace()
,请参阅此解决方法 #43
设置。 Vitest 在 Chromium 上的基准测试,代码库测试。硬件可能会影响结果。
值为平均捕获时间(毫秒) → 越低越好。
Scenario | SnapDOM current | SnapDOM v1.9.9 | html2canvas | html-to-image |
---|---|---|---|---|
Small (200×100) | 0.5 ms | 0.8 ms | 67.7 ms | 3.1 ms |
Modal (400×300) | 0.5 ms | 0.8 ms | 75.5 ms | 3.6 ms |
Page View (1200×800) | 0.5 ms | 0.8 ms | 114.2 ms | 3.3 ms |
Large Scroll (2000×1500) | 0.5 ms | 0.8 ms | 186.3 ms | 3.2 ms |
Very Large (4000×2000) | 0.5 ms | 0.9 ms | 425.9 ms | 3.3 ms |
Scenario | SnapDOM current | SnapDOM v1.9.9 | html2canvas | html-to-image |
---|---|---|---|---|
Small (200×100) | 1.6 ms | 3.3 ms | 68.0 ms | 14.3 ms |
Modal (400×300) | 2.9 ms | 6.8 ms | 87.5 ms | 34.8 ms |
Page View (1200×800) | 17.5 ms | 50.2 ms | 178.0 ms | 429.0 ms |
Large Scroll (2000×1500) | 54.0 ms | 201.8 ms | 735.2 ms | 984.2 ms |
Very Large (4000×2000) | 171.4 ms | 453.7 ms | 1,800.4 ms | 2,611.9 ms |
git clone https://github.com/zumerlab/snapdom.git
cd snapdom
npm install
npm run test:benchmark
SnapDOM 未来版本的改进计划:
实现插件系统
SnapDOM 将支持外部插件来扩展或覆盖内部行为(例如自定义节点转换器、导出器或过滤器)。
重构模块化架构
内部逻辑将被拆分为更小、更集中的模块,以提高可维护性和代码重用性。
将内部逻辑与全局选项分离
函数将被重新设计,以避免直接依赖“选项”。集中式捕获上下文将提高清晰度、自主性和可测试性。请参阅next
分支
公开缓存控制
用户将能够手动清除图像和字体缓存,或配置自己的缓存策略。
自动字体预加载
所需字体将在捕获前自动检测并预加载,从而减少手动调用 preCache()
的需要。
文档插件开发
将提供创建和注册自定义 SnapDOM 插件的完整指南。
使导出实用程序可进行树优化
诸如 toPng
、toJpg
、toBlob
等导出函数将被重组为独立模块,以支持树优化和最小化构建。
有任何想法或功能请求?
欢迎在 GitHub 讨论区 分享建议或反馈。
贡献或本地构建 snapDOM:
# Clone the repository
git clone https://github.com/zumerlab/snapdom.git
cd snapdom
# Switch to dev branch
git checkout dev
# Install dependencies
npm install
# Compile the library (ESM, CJS, and minified versions)
npm run compile
# Install playwright browsers (necessary for running tests)
npx playwright install
# Run tests
npm test
# Run Benchmarks
npm run test:benchmark
主入口位于 src/
,输出包在 dist/
文件夹中生成。
有关详细的贡献指南,请参阅 CONTRIBUTING。