@aphrody/canvas

中文文档
1.4.7发布于 2026/10/23MITTypeScriptES Module零依赖近 30 天下载 2222

Aphrody native Skia canvas facade over the pinned upstream @napi-rs/canvas runtime

原始使用文档
文档目录7 节
  1. Entry points
  2. Native Skia binary
  3. M3 scenes
  4. Layout and upstream
  5. Updating upstream
  6. Validation
  7. Publishing

@aphrody/canvas

Aphrody's canvas package: a stable @aphrody/* namespace over the pinned upstream Skia runtime @napi-rs/canvas (1.0.9, commit 8411071), plus the M3 surface contract used by the M3 packages, SpaceUI, Tauri and the render crates. Consumers import @aphrody/canvas and never depend on @napi-rs/canvas directly.

bun add @aphrody/canvas   # Bun >= 1.4.2

Entry points

ImportNative addonContents
@aphrody/canvasloaded on importThe upstream API (createCanvas, loadImage, Image, GlobalFonts, Path2D, SKRSContext2D, ...), loadNativeCanvas(), createNativeCanvas(), loadNativeImage(), drawM3Scene(), drawM3Scene3d() and the site contract.
@aphrody/canvas/sitenevercreateM3CanvasSurface(), createBrowserCanvas(), configureBrowserCanvas(), selectCanvasBackend().
@aphrody/canvas/browserneverSame module as /site.
import { createCanvas, GlobalFonts, type SKRSContext2D } from "@aphrody/canvas";

const canvas = createCanvas(320, 120);
const ctx: SKRSContext2D = canvas.getContext("2d");
ctx.fillRect(0, 0, 320, 120);
const png = await canvas.encode("png"); // also "webp", "avif", "jpeg"

aphrody-site and browser bundles must use the native-free entry point, so the Skia addon never reaches a site bundle:

import {
  createBrowserCanvas,
  createM3CanvasSurface,
} from "@aphrody/canvas/site";

The package ships TypeScript sources for Bun ("default" conditions) and hand-written declarations ("types" conditions); a Next.js consumer of /site lists the package in transpilePackages.

Native Skia binary

The addon is resolved by the upstream loader (vendor/js-binding.js), first match wins:

  1. NAPI_RS_NATIVE_LIBRARY_PATH: absolute path to a skia.<platform>.node, for example one built by Aphrody from third_party/canvas. When set, it is the only candidate.
  2. vendor/skia.<platform>.node next to the loader (ignored by Git, never committed).
  3. The upstream prebuilt package @napi-rs/canvas-<platform>@1.0.9, declared in optionalDependencies. Package managers skip the packages whose os/cpu do not match the host (on Linux the loader then picks gnu or musl at runtime): win32-x64-msvc, win32-arm64-msvc, linux-x64-gnu, linux-x64-musl, linux-arm64-gnu, linux-arm64-musl, linux-arm-gnueabihf, linux-riscv64-gnu, darwin-x64, darwin-arm64, android-arm64.

optionalDependencies are generated from third_party/canvas/npm/* at the vendored version, so the JavaScript loader and the binary always come from the same upstream release.

M3 scenes

drawM3Scene() and drawM3Scene3d() rasterize an M3 scene in Rust (aphrody_render, through @aphrody/bun) and blit the PNG with the native addon. @aphrody/bun is an optional peer dependency: it is imported only when one of these two functions runs, and a missing install throws @aphrody/canvas: drawM3Scene needs the optional peer dependency @aphrody/bun. The scene types (M3Scene, M3Scene3d, M3Render3dPolicy) come from scene-types.d.ts, a copy of packages/bun/src/scene-types.ts, so type-checking never needs @aphrody/bun.

Layout and upstream

third_party/canvas is a Git submodule on the non-fork upstream and is never patched in place; Aphrody behavior goes in this facade or in the Rust render crates. The files the package needs from other trees are copies:

PathSource
vendor/index.js, js-binding.js, geometry.js, load-image.js, index.d.ts, LICENSEthird_party/canvas (MIT, upstream copyright notice kept)
vendor/package.json{"type": "commonjs"}: the upstream loader is CommonJS, this package is ESM
vendor/UPSTREAM.mdProvenance: commit, version, license, SHA-256 of every copy
scene-types.d.tspackages/bun/src/scene-types.ts

The copies are committed rather than generated at pack time:

  • bun test, the type-check and every workspace consumer work from a plain checkout, with no build step and without initializing the submodule;
  • the published tarball is exactly the reviewed tree (files lists every shipped path);
  • drift is still impossible to publish: scripts/vendor-upstream.ts --check runs as prepack (it fails if the submodule is missing), and vendor.test.ts compares every copy with its source when the submodule is checked out and with the SHA-256 in vendor/UPSTREAM.md otherwise.

Copies are byte-identical after line-ending normalization to LF.

Updating upstream

git -C third_party/canvas fetch && git -C third_party/canvas checkout <reviewed-sha>
cd packages/canvas
bun run vendor            # rewrites vendor/, scene-types.d.ts and optionalDependencies
# keep aphrodyCanvasVersion in site.ts, site.d.ts and index.d.ts equal to the
# package version (the release-aligned Aphrody version), then run the gates below

Validation

cd packages/canvas
bun test                  # contract, native encode (PNG/WebP/AVIF), vendor drift, manifest
bun run typecheck         # sources, then published declarations with skipLibCheck: false
bun run lint && bun run format:check
bun run vendor:check
bun pm pack --destination <dir>

Lint and format checks use Bun's native bun lint and bun fmt commands directly, without bunx, standalone Oxc tools, or subprocess backends.

Publishing

.github/workflows/release-m3-packages.yml publishes this package before @aphrody/material-web (which depends on it) when an m3-v* tag is pushed. The manifest holds no workspace: or catalog: specifier, so the packed package.json is the registry package.json. Manual equivalent, run by the owner:

cd packages/canvas && bun publish --access public --tag latest --registry https://registry.npmjs.org

版本与发布记录

@aphrody/canvas 共发布 7 个版本,首次发布于 2026/06/04;过去 12 个月发布了 7 个版本。

版本发布日期距今
1.4.7latest2026/10/2315 天前
1.0.9-aphrody.62026/10/1029 天前
1.0.9-aphrody.52026/10/0929 天前
1.0.9-aphrody.42026/10/071 个月前
1.0.9-aphrody.32026/10/051 个月前
1.0.9-aphrody.22026/09/241 个月前

关于 @aphrody/canvas 的常见问题

@aphrody/canvas 是什么?

Aphrody native Skia canvas facade over the pinned upstream @napi-rs/canvas runtime

如何安装 @aphrody/canvas?

在项目目录运行 npm install @aphrody/canvas,或使用 pnpm add / yarn add / bun add。

@aphrody/canvas 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@aphrody/canvas 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@aphrody/canvas 可以商用吗?

@aphrody/canvas 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@aphrody/canvas 的下载量有多少?

根据 npm 官方下载统计,@aphrody/canvas 在 2026/10/06 – 2026/11/04 这 30 天内被下载 2,222 次,日均约 74 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@aphrody/canvas 还在维护吗?

最新版本 1.4.7 发布于 2026/10/23(15 天前);至今共发布 7 个版本,其中过去 12 个月发布了 7 个;首次发布于 2026/06/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。