DOM截图离线存储:snapDOM 三种落地方式与常见坑
【免费下载链接】snapdomHigh-performance engine for capturing, modifying, and converting DOM elements into any format.项目地址: https://gitcode.com/GitHub_Trending/sn/snapdom
先说场景:你在做一个离线笔记工具,用户断网时想把当前笔记页面存成一张图片,本地保存、随时回看。这时候你需要的是两件事——把 DOM 元素转成图片,再把图片写进浏览器本地存储。snapDOM 是一个高性能的 DOM 截图引擎,能在浏览器里把任意元素转成 SVG、PNG、JPG、WebP 等格式,整个流程在本地执行,不产生网络请求,天然适合离线截图存储。
第一步:从元素拿到可存储的 Blob
snapDOM 的调用入口就一行:传入元素,得到一个带导出方法的对象。下面这段代码演示"截图 + 转 WebP Blob"的最小链路:
import { snapdom } from '@zumer/snapdom' const blob = await snapdom(noteEl, { cache: 'full' }) .toBlob({ format: 'webp', quality: 0.85 }) console.log(blob.size) // 文件大小(字节),可直接判断是否超存储配额这里有两个值得展开的细节:
toBlob内部会先把 SVG 结果画到 canvas,再调canvas.toBlob编码(见 src/exporters/toBlob.js)。format支持png、jpeg、webp,quality只在有损格式下生效。- 选项里的
cache: 'full'是四级缓存策略之一(disabled/full/soft/auto),策略归一化和淘汰逻辑在 src/core/cache.js。重复截同一个元素时,full能明显减少重复计算。
如果你的页面图片、背景、字体都来自远端,可以在截图前先预热资源,避免截图时卡在网络请求上:
import { preCache } from '@zumer/snapdom/preCache' await preCache(noteEl, { cache: 'full' }) // 预取 <img>、背景图并内嵌字体preCache会扫描元素子树,把img和带url(...)的背景提前转成 dataURL 存进缓存(实现见 src/api/preCache.js)。对离线场景尤其有用:有网时把关键资源缓存下来,断网后截图就不会丢图。
三种本地存储落点
拿到 Blob 后,往哪儿存取决于图片大小和使用频率。
方案一:IndexedDB 存大量截图(推荐)
IndexedDB 配额大(通常为可用磁盘空间的 50%~60%),原生支持存 Blob,是离线截图库的主力方案:
function openDB() { return new Promise((resolve, reject) => { const req = indexedDB.open('snapdom-screens', 1) req.onupgradeneeded = () => req.result.createObjectStore('shots', { keyPath: 'id', autoIncrement: true }) req.onsuccess = () => resolve(req.result) req.onerror = () => reject(req.error) }) } const db = await openDB() const tx = db.transaction('shots', 'readwrite') tx.objectStore('shots').add({ ts: Date.now(), blob }) await new Promise((r, j) => { tx.oncomplete = r; tx.onerror = () => j(tx.error) })方案二:File System Access API 存到磁盘
Chrome/Edge 支持showSaveFilePicker,让用户像"另存为"一样把截图写到本地文件系统,适合导出单张大图:
const handle = await window.showSaveFilePicker({ suggestedName: `note-${Date.now()}.webp`, types: [{ description: 'WebP 图片', accept: { 'image/webp': ['.webp'] } }] }) const writable = await handle.createWritable() await writable.write(blob) await writable.close()注意它必须触发在用户手势里(点击按钮),不能放异步回调深处。
方案三:localStorage 存小图
只适合缩略图级别(几十 KB)。转成 base64 后体积约膨胀 33%,而 localStorage 一般只有 5MB,存 3~4 张 1MP 的 WebP 就接近上限:
const b64 = await new Promise(r => { const fr = new FileReader() fr.onload = () => r(fr.result) fr.readAsDataURL(blob) }) localStorage.setItem('last-shot', b64)选型速查:
| 方案 | 容量 | 适合 |
|---|---|---|
| IndexedDB | GB 级 | 截图历史库 |
| File System API | 磁盘 | 单次导出 |
| localStorage | ~5MB | 最近一次快照/缩略图 |
离线场景的三个常见坑
- 透明背景变黑。JPG 和 WebP 不支持透明通道,snapDOM 在生成有损格式时若没指定
backgroundColor,会自动填#ffffff(归一化逻辑在 src/core/context.js)。如果你的页面是深色主题,截图前显式设置backgroundColor更稳妥。 - 字体没加载完就截图。离线包里如果用了
@font-face,先await document.fonts.ready或依赖preCache的内嵌字体流程,否则截图会退回系统字体。 - 批量截图内存飙高。逐张处理、拿到结果后再截下一张;不需要留存的中间 blob 及时用
blob.close()(File 对象)或丢弃引用,别把几百张塞进同一个数组再一起存。
for (const el of els) { const blob = await snapdom(el).toBlob({ format: 'jpeg', quality: 0.7 }) await store.add({ ts: Date.now(), blob }) // 存一张、释一张 }落地建议
- 格式按内容选:文字为主的卡片用 WebP(压缩比高),需要透明背景用 PNG,纯照片类用 JPG。
- 给历史库加淘汰策略:按时间倒序遍历 IndexedDB 记录,删掉 30 天前的,或控制总数上限。
- 断网提示做前置:
navigator.onLine为 false 时直接走本地存储分支,别等写库失败才兜底。
把preCache放在应用启动时跑一次,截图逻辑只依赖本地缓存——这是离线截图最省心的组合。
【免费下载链接】snapdomHigh-performance engine for capturing, modifying, and converting DOM elements into any format.项目地址: https://gitcode.com/GitHub_Trending/sn/snapdom
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考