npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zaxxz/snapkit

v0.2.0

Published

一个面向前端项目的截图、编辑与 WebGL 合成 SDK。

Readme

@zaxxz/snapkit

npm version license types

一个面向前端业务系统的截图 SDK,提供页面截图、截图后裁剪批注、复制保存、WebRTC 截屏、html2canvas DOM 截图,以及 WebGL/Cesium/Three.js 图层合成能力。

安装

npm install @zaxxz/snapkit

也可以按子路径导入:

import { capture } from '@zaxxz/snapkit/capture';
import { captureAndEdit } from '@zaxxz/snapkit/capture-editor';

功能特性

  • 支持 capture() 基础截图。
  • 支持 captureAndEdit() 截图后打开编辑器,进行裁剪、画笔、矩形、箭头、文字批注。
  • 支持 openCaptureEditor() 对已有图片打开编辑器。
  • 支持 dataURLBlobFileHTMLCanvasElement 四种输出类型。
  • 支持 WebRTC getDisplayMedia 截屏,也支持 html2canvas DOM 截图兜底。
  • 支持 WebGL canvas / base64 与 DOM 截图合成,适配 Cesium、Three.js、Mapbox GL、deck.gl 等场景。
  • 提供统一 SnapkitError 和错误码,方便业务方按失败原因做不同提示。
  • 同时发布 ESM、CJS 和 TypeScript 类型声明。

快速开始

import { capture } from '@zaxxz/snapkit';

const image = await capture({
  strategy: 'auto',
  target: '.dashboard',
  mimeType: 'image/png',
  onCaptured(meta) {
    console.log('实际截图策略:', meta.strategy);
  }
});

默认返回 dataURL 字符串,可以直接赋给 img.src、复制到剪贴板,或下载保存。

API

capture(options?)

function capture(options?: CaptureOptions): Promise<string>;

capture() 默认返回 dataURL。你也可以通过 output 指定其他返回类型。

const blob = await capture({
  target: '#report',
  output: 'blob',
  mimeType: 'image/png'
});

const formData = new FormData();
formData.append('file', blob);

完整输出类型:

const dataURL = await capture({ output: 'dataURL' });
const blob = await capture({ output: 'blob' });
const file = await capture({ output: 'file', fileName: 'report.png' });
const canvas = await capture({ output: 'canvas' });

常用配置:

interface CaptureOptions {
  strategy?: 'auto' | 'webrtc' | 'html2canvas';
  output?: 'dataURL' | 'blob' | 'file' | 'canvas';
  fileName?: string;
  target?: HTMLElement | string;
  mimeType?: string;
  quality?: number;
  timeout?: number;
  frameDelay?: number;
  html2canvas?: object;
  webgl?: boolean | WebGLCaptureOptions;
  cesium?: boolean | WebGLCaptureOptions;
  onCaptured?: (meta: CaptureResultMeta) => void;
}

captureAndEdit(options?)

先截图,再打开截图编辑器。用户点击完成时返回编辑后的 dataURL,用户取消时返回 null

import { captureAndEdit } from '@zaxxz/snapkit';

const editedImage = await captureAndEdit({
  target: '.dashboard',
  editor: {
    fileName: 'dashboard.png',
    strokeColor: '#ff4d4f',
    strokeWidth: 3,
    onCopy(dataUrl) {
      console.log('已复制:', dataUrl.length);
    },
    onSave(dataUrl) {
      console.log('已保存:', dataUrl.length);
    }
  }
});

编辑器工具栏包含裁剪、画笔、矩形、箭头、文字、撤销、重置、复制、保存、完成和取消。

openCaptureEditor(imageUrl, options?)

如果业务方已经有图片 dataURL,可以直接打开编辑器:

import { openCaptureEditor } from '@zaxxz/snapkit';

const result = await openCaptureEditor(existingDataUrl, {
  fileName: 'edited.png'
});

auto 策略说明

strategy: 'auto' 是 SDK 推荐策略。当前执行顺序是:

  1. 如果配置了 webglcesium,优先走 html2canvas + WebGL 合成
  2. 如果没有 WebGL 配置,再判断当前环境是否支持 WebRTC。
  3. WebRTC 可用时优先使用 getDisplayMedia,获取真实屏幕画面。
  4. WebRTC 不可用、超时或失败时,自动降级到 html2canvas

这样设计的原因是:业务按钮截图通常更希望“点击即截图”,不希望弹出浏览器授权框;而 WebGL 场景通过 DOM 截图加 WebGL 合成,更适合 Cesium、Three.js 这类页面内部截图。

如果你明确想触发浏览器屏幕选择器,可以强制指定:

await capture({
  strategy: 'webrtc'
});

如果你只想截某个 DOM 区域,可以强制指定:

await capture({
  strategy: 'html2canvas',
  target: '#panel'
});

错误处理

SDK 提供统一错误类型 SnapkitError 和错误码 SnapkitErrorCode。业务方可以根据 error.code 展示不同提示。

import { capture, SnapkitError, SNAPKIT_ERROR_CODES } from '@zaxxz/snapkit';

try {
  await capture({
    strategy: 'webrtc'
  });
} catch (error) {
  if (error instanceof SnapkitError) {
    if (error.code === SNAPKIT_ERROR_CODES.WEBRTC_NEED_HTTPS) {
      console.warn('请在 HTTPS 或 localhost 环境使用 WebRTC 截屏');
    }

    if (error.code === SNAPKIT_ERROR_CODES.WEBRTC_PERMISSION_DENIED) {
      console.warn('用户取消了屏幕共享授权');
    }
  }
}

常见错误码:

| 错误码 | 含义 | | --- | --- | | SNAPKIT_TARGET_NOT_FOUND | 传入的 target 选择器没有匹配到元素 | | SNAPKIT_WEBRTC_NOT_SUPPORTED | 当前浏览器不支持 getDisplayMedia | | SNAPKIT_WEBRTC_NEED_HTTPS | WebRTC 截屏需要 HTTPS 或 localhost | | SNAPKIT_WEBRTC_PERMISSION_DENIED | 用户拒绝或取消屏幕共享授权 | | SNAPKIT_HTML2CANVAS_FAILED | html2canvas 渲染 DOM 失败 | | SNAPKIT_WEBGL_CANVAS_NOT_FOUND | 开启 WebGL 合成但没有找到 canvas 或 image | | SNAPKIT_WEBGL_EXPORT_FAILED | WebGL canvas 导出失败 | | SNAPKIT_CANVAS_TAINTED | canvas 被跨域资源污染,无法导出 | | SNAPKIT_WEBGL_IMAGE_LOAD_FAILED | 传入的 WebGL 图片加载失败 | | SNAPKIT_WEBGL_COMPOSITE_FAILED | WebGL 图层与 DOM 图层合成失败 |

WebGL 图层合成

为什么需要 webgl

html2canvas 主要面向 DOM 和 CSS 渲染,并不真正理解 WebGL 场景。Cesium、Three.js、Mapbox GL、deck.gl、Babylon.js 或自研 WebGL 渲染器的画面,在 DOM 截图时可能出现黑屏、透明、空白或错位。

Snapkit 的 webgl 方案不是解析三维场景,而是进行图层合成:

  1. 业务方提供 WebGL 图层,可以是 canvas,也可以是已经生成好的 dataURL/base64。
  2. SDK 截 DOM 时临时隐藏 WebGL 元素,避免 html2canvas 抓到黑块。
  3. SDK 让 DOM 截图中对应区域保持透明。
  4. SDK 先绘制 WebGL 底图,再绘制 DOM 截图。
  5. 最终得到“WebGL 画面 + DOM 控件/弹窗/标注”的完整截图。

推荐:传入 canvas

const image = await capture({
  strategy: 'auto',
  target: '#map-page',
  webgl: {
    canvas: () => viewer.scene.canvas,
    beforeCapture() {
      viewer.scene.requestRender();
      viewer.scene.render();
    },
    renderDelay: 80
  }
});

这种方式适合:

  • Cesium 的 viewer.scene.canvas
  • Three.js 的 renderer.domElement
  • Mapbox GL 的地图 canvas
  • 自研 WebGL renderer 的 canvas

更稳定:传入 dataURL/base64

如果业务方已经知道如何稳定生成 WebGL 快照,可以直接传 image。此时 SDK 只负责合成。

const image = await capture({
  target: '#map-page',
  webgl: {
    image: () => renderer.domElement.toDataURL('image/png'),
    element: () => renderer.domElement,
    rect: () => renderer.domElement.getBoundingClientRect()
  }
});

只传 image 时,建议同时传 elementrect

webgl: {
  image: () => webglImage,
  element: () => webglCanvas,
  rect: () => webglCanvas.getBoundingClientRect()
}

image 说明“画什么”,element 说明 DOM 截图时隐藏哪个元素,rect 说明这张图片应该画到最终截图的哪个位置。

webgl 配置项

interface WebGLCaptureOptions {
  enabled?: boolean;
  canvas?: HTMLCanvasElement | string | (() => HTMLCanvasElement | undefined);
  image?: string | (() => string | Promise<string>);
  element?: HTMLElement | string | (() => HTMLElement | undefined);
  rect?: DOMRect | { left: number; top: number; width: number; height: number } | (() => DOMRect | undefined);
  hideCanvasDuringDomCapture?: boolean;
  renderDelay?: number;
  beforeCapture?: (canvas: HTMLCanvasElement) => void | Promise<void>;
  afterCapture?: (canvas: HTMLCanvasElement) => void | Promise<void>;
}

常用写法:

webgl: {
  canvas: '.map-container canvas'
}
webgl: {
  image: async () => {
    await waitTilesReady();
    return canvas.toDataURL('image/png');
  },
  element: () => canvas,
  rect: () => canvas.getBoundingClientRect()
}
webgl: {
  canvas: () => viewer.scene.canvas,
  hideCanvasDuringDomCapture: true,
  renderDelay: 100,
  beforeCapture(canvas) {
    console.log(canvas.width, canvas.height);
  }
}

Cesium 示例

推荐使用通用 webgl 配置:

const image = await capture({
  strategy: 'auto',
  target: '#cesium-page',
  webgl: {
    canvas: () => viewer.scene.canvas,
    beforeCapture() {
      viewer.scene.requestRender();
      viewer.scene.render();
    },
    renderDelay: 80
  }
});

Cesium 初始化时建议开启:

const viewer = new Viewer(container, {
  contextOptions: {
    webgl: {
      preserveDrawingBuffer: true
    }
  }
});

preserveDrawingBuffer: true 可以提高 canvas 导出稳定性,但可能带来渲染性能开销。建议只在确实需要截图的页面或业务中开启。

兼容写法:

await capture({
  target: '#cesium-page',
  cesium: {
    canvas: () => viewer.scene.canvas,
    beforeCapture() {
      viewer.scene.render();
    }
  }
});

cesium 仍然可用,但它只是 webgl 的别名,新项目建议统一使用 webgl

Three.js 示例

const renderer = new THREE.WebGLRenderer({
  antialias: true,
  preserveDrawingBuffer: true
});

const image = await capture({
  target: '#three-page',
  webgl: {
    canvas: () => renderer.domElement,
    beforeCapture() {
      renderer.render(scene, camera);
    }
  }
});

也可以让业务方自己生成图片,再交给 SDK 合成:

const image = await capture({
  target: '#three-page',
  webgl: {
    image: () => renderer.domElement.toDataURL('image/png'),
    element: () => renderer.domElement,
    rect: () => renderer.domElement.getBoundingClientRect()
  }
});

Vue3 使用示例

<template>
  <section>
    <div ref="captureRef" class="report-panel">
      <h2>销售看板</h2>
      <p>这里是需要截图的业务内容。</p>
    </div>

    <button @click="handleCapture">截图</button>
    <button @click="handleCaptureAndEdit">截图并编辑</button>

    <img v-if="preview" :src="preview" alt="截图预览" />
  </section>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { capture, captureAndEdit, SnapkitError } from '@zaxxz/snapkit';

const captureRef = ref<HTMLElement | null>(null);
const preview = ref('');

async function handleCapture() {
  try {
    preview.value = await capture({
      target: captureRef.value!,
      strategy: 'auto',
      output: 'dataURL'
    });
  } catch (error) {
    if (error instanceof SnapkitError) {
      console.warn(error.code, error.message);
    }
  }
}

async function handleCaptureAndEdit() {
  const result = await captureAndEdit({
    target: captureRef.value!,
    strategy: 'auto',
    editor: {
      fileName: 'report.png'
    }
  });

  if (result) {
    preview.value = result;
  }
}
</script>

上传到后端时可以直接输出 Blob:

const blob = await capture({
  target: captureRef.value!,
  output: 'blob'
});

const formData = new FormData();
formData.append('file', blob, 'report.png');

Vue2 使用示例

<template>
  <section>
    <div ref="captureRef" class="report-panel">
      <h2>销售看板</h2>
      <p>这里是需要截图的业务内容。</p>
    </div>

    <button @click="handleCapture">截图</button>
    <button @click="handleCaptureAndEdit">截图并编辑</button>

    <img v-if="preview" :src="preview" alt="截图预览" />
  </section>
</template>

<script>
import { capture, captureAndEdit, SnapkitError } from '@zaxxz/snapkit';

export default {
  data() {
    return {
      preview: ''
    };
  },
  methods: {
    async handleCapture() {
      try {
        this.preview = await capture({
          target: this.$refs.captureRef,
          strategy: 'auto',
          output: 'dataURL'
        });
      } catch (error) {
        if (error instanceof SnapkitError) {
          console.warn(error.code, error.message);
        }
      }
    },
    async handleCaptureAndEdit() {
      const result = await captureAndEdit({
        target: this.$refs.captureRef,
        strategy: 'auto',
        editor: {
          fileName: 'report.png'
        }
      });

      if (result) {
        this.preview = result;
      }
    }
  }
};
</script>

使用限制 / 注意事项

  • WebRTC 截屏依赖 navigator.mediaDevices.getDisplayMedia,只能在 HTTPS 或 localhost 环境使用,并且一定会触发用户授权。
  • html2canvas 不是真实截图,它是对 DOM 和 CSS 的模拟渲染,对 WebGL、视频、iframe、跨域图片支持有限。
  • 不同源 iframe 的内部内容不能被父页面直接读取。可以由子页面通过 postMessage 把自己的截图 dataURL 传给父页面,再由父页面使用 webgl.image 或业务 canvas 合成。
  • WebGL canvas 如果绘制了跨域资源,可能导致 canvas tainted,浏览器会禁止 toDataURL / toBlob 导出。
  • Cesium、Three.js 等 WebGL 场景建议开启 preserveDrawingBuffer,但它可能增加 GPU 内存和渲染性能开销。
  • 大尺寸页面或高倍屏截图会占用较多内存,必要时可以降低 html2canvas.scale 或缩小截图区域。
  • 页面滚动、CSS transform、fixed 弹窗、缩放容器等复杂布局可能导致合成错位,建议显式传 webgl.rect 修正位置。
  • 如果页面中有跨域图片,建议图片服务开启 CORS,并设置 html2canvas: { useCORS: true }

常见问题

为什么 WebGL 区域会黑屏?

WebGL 画面通常在 GPU 缓冲区中,html2canvas 并不真正理解 WebGL 场景。某些浏览器或渲染器配置下,DOM 截图只能得到黑色、透明或空白区域。建议使用 webgl.canvaswebgl.image 让 SDK 做图层合成。

canvas 还是传 image

如果 WebGL canvas 可以稳定读取,传 canvas 更简单。如果业务库有自己的截图 API,或者读取时机比较复杂,传 image 更稳定。

合成位置错位怎么办?

优先显式传 elementrect

webgl: {
  image: () => snapshot,
  element: () => canvas,
  rect: () => canvas.getBoundingClientRect()
}

如果页面存在滚动、缩放、弹窗或 CSS transform,显式传 rect 会更稳定。

本地开发

npm install
npm run typecheck
npm run build

发布

首次发布前请确认已经登录 npm,并且当前账号拥有 @zaxxz scope 的发布权限。

npm login
npm run typecheck
npm run build
npm pack --dry-run
npm publish --access public