SnapDOM

repository·main·Indexed 27 days ago

https://github.com/zumerlab/snapdom

A high-performance DOM capture engine and modern alternative to html2canvas and dom-to-image. It converts DOM subtrees into PNG, JPG, SVG, WebP, or Canvas formats using standard Web APIs. The library includes a plugin system (@zumer/snapdom-plugins) for extending functionality with features like CSS filters, timestamp overlays, text replacement, ASCII export, PDF generation, and video/GIF recording.

Tokens
20.7K
Snippets
44
Records
127
Agent score
89%

What's inside @zumer/snapdom

  1. Build SnapDOM from source

    main

    To build the project from the development branch, use the following commands. This will generate build outputs in the dist/ directory, including snapdom.js, snapdom.mjs, preCache.mjs, and plugins.mjs.

    git clone https://github.com/zumerlab/snapdom.git
    cd snapdom
    git checkout dev
    npm install
    npm run compile
  2. Handle images, backgrounds, and CORS

    main

    SnapDOM inlines images and background layers to ensure they are self-contained in the export.

    • Inlining: Resolves src, currentSrc, and url() layers in background-image, mask, and border-image. Supports background-clip: text.
    • Lazy Loading: Automatically resolves <picture> sources and common lazy attributes (e.g., data-src, data-lazy-src).
    • CORS & Proxies: Uses a non-throwing fetch layer. You can configure a proxy using the useProxy option with templates like {url}, {urlRaw}, or ?url= suffix.
    • Compression: The compress option (default true) performs perceptual downsampling to the visible resolution to keep file sizes small.
    • Fallbacks: If an image fails, SnapDOM uses a fallbackURL (string or callback), then a placeholder box, then a hidden spacer.
  3. Create a SnapDOM plugin using the Factory Pattern

    main

    A SnapDOM plugin is a JavaScript object with a unique name and lifecycle hooks. It is highly recommended to use the Plugin Factory Pattern, which wraps the plugin in a function to allow for configuration via an options object.

    You can register plugins either per-capture (passed to the snapdom function) or globally (using snapdom.plugins()).

    import { snapdom } from '@zumer/snapdom';
    import { myPlugin } from 'snapdom-plugin-my-plugin';
    
    // Per-capture registration with options
    const result = await snapdom(element, {
      plugins: [myPlugin({ color: 'blue' })]
    });
    
    // Global registration
    snapdom.plugins(myPlugin());
  4. Install official SnapDOM plugins

    main

    Official plugins are distributed as a separate package to keep the core library lightweight. You can install the entire suite or import individual tree-shakeable plugins.

    Install command:

    npm i @zumer/snapdom-plugins
    npm i @zumer/snapdom-plugins
  5. Develop and publish a community plugin

    main

    To publish a community plugin to npm, follow these requirements:

    1. Package Name: Must follow the convention snapdom-plugin-[name].
    2. Plugin name field: Must be lowercase kebab-case (e.g., 'watermark').
    3. Peer Dependency: Include @zumer/snapdom as a peer dependency.

    Example package.json structure:

    {
      "name": "snapdom-plugin-yourname",
      "version": "1.0.0",
      "type": "module",
      "main": "index.js",
      "exports": { ".": "./index.js" },
      "peerDependencies": { "@zumer/snapdom": ">=0.9.0" }
    }
  6. Capture and clone DOM subtrees with SnapDOM

    main

    SnapDOM serializes a DOM subtree into a self-contained SVG using <foreignObject> and then rasterizes it. It supports several complex web elements:

    • Shadow DOM: Traverses shadowRoot, extracts/scopes CSS, and resolves <slot> content.
    • Iframes: Same-origin iframes are rasterized inline. Cross-origin iframes render as placeholders (or hidden spacers if placeholders: false).
    • Media: <canvas> is snapshotted to a PNG; <video> captures the current frame (falling back to poster); <audio controls> is replaced with a drawn player mock.
    • Form Controls: Preserves value, checked, indeterminate, and selection states, along with state attributes like disabled, required, and readonly to ensure correct CSS rendering.
    • Images: Preserves srcset, object-fit, and object-position.
    • SVG: Inlines paint properties and resolves external <defs>/<symbol> references via <use>.
    • Scroll Position: Reproduces scrolled containers via translation and clipping; handles fixed and sticky elements by freezing them in place.

    Note: meta, script, noscript, title, link, template, and nested <foreignObject> elements are skipped by design.

  7. Validate Simplified Chinese documentation structure

    main

    Use the following shell commands to verify that the Chinese documentation (README_CN.md and FEATURES_CN.md) maintains structural alignment with the English source by checking heading, code fence, and table row counts.

    for file in README.md README_CN.md FEATURES.md FEATURES_CN.md; do
      awk '
        /^#{1,6} / { headings++ }
        /^```/ { fences++ }
        /^\|/ { table_rows++ }
        END {
          printf "%s: headings=%d fences=%d table_rows=%d\n",
            FILENAME, headings, fences, table_rows
        }
      ' "$file"
    done
  8. Quick Start: Capture DOM to PNG

    main

    To capture a single DOM element and export it directly to a PNG image in one line, use the snapdom.toPng() shortcut method.

    import { snapdom } from '@zumer/snapdom';
    
    const img = await snapdom.toPng(document.querySelector('#card'));
    document.body.appendChild(img);
  9. Capture and clone DOM elements with SnapDOM

    main

    SnapDOM performs a deep clone of the DOM and records computed styles to replicate the browser's actual rendering. It handles several complex web components:

    • Shadow DOM: Traverses shadowRoot, extracts scoped CSS, and resolves <slot> content using assignedNodes({ flatten: true }).
    • Same-origin iframes: Converts the iframe to a bitmap and embeds it.
    • <canvas>: Converts to a PNG <img> (with Safari retry mechanisms).
    • <video>: Captures the current frame as an image or uses the poster attribute.
    • Form Controls: Preserves values, checked states, and attributes like disabled, required, and readonly. It uses inline SVG graphics for checkboxes/radio buttons to ensure compatibility in Firefox.
    • <img>: Resolves srcset, fixes dimensions to pixel values (when using % or auto), and preserves object-fit and object-position.
    • SVG: Inlines drawing attributes (fill, stroke, etc.) and resolves <use> references by inlining definitions.
    • Scrolling: Replicates the current scroll position by moving internal content and clipping overflow, while adjusting fixed, absolute, and sticky elements.
  10. Configure CSS styles and layout in SnapDOM

    main

    SnapDOM handles complex CSS through computed-style inlining. Every node's computed style is snapshotted and deduplicated into CSS classes.

    • Preserved Details: Supports text-decoration longhands, -webkit-text-stroke, and advanced font settings (kerning, optical sizing, etc.).
    • CSS Counters: Full resolver for counter() and counters() in pseudo-element content.
    • Text Truncation: -webkit-line-clamp and text-overflow: ellipsis are baked into real text to ensure compatibility in <foreignObject>.
    • Transforms: Reads translate, rotate, and scale into a total matrix.
    • Shadows & Blurs: Use the outerShadows: true option to expand the viewBox to include box-shadow, filter: blur, and outline. If false, root shadows are stripped.
    • Custom Scrollbars: Injects ::-webkit-scrollbar rules to preserve styling.
    • Style Filtering: Use excludeStyleProps (RegExp or predicate) to skip specific properties from the snapshot.
  11. Use @zumer/snapdom-plugins with SnapDOM

    main

    To use plugins, import them and pass them into the plugins array within the options object of the snapdom function call. It is recommended to import plugins individually from their specific subpaths to support tree-shaking.

    import { snapdom } from '@zumer/snapdom';
    import { filter } from '@zumer/snapdom-plugins/filter';
    import { timestampOverlay } from '@zumer/snapdom-plugins/timestamp-overlay';
    
    const result = await snapdom(element, {
      plugins: [filter({ preset: 'grayscale' }), timestampOverlay()]
    });
    const png = await result.toPng();