Skia Canvas 是 HTML Canvas 绘图的节点.js 执行 API 密码 用于屏幕和屏幕外渲染。 因为它使用谷歌 斯基亚 图形引擎,其输出与Chrome非常相似 <canvas> 元素——虽然它也能够实现浏览器的Canva仍然无法实现的东西.
特别是Skia Canvas:
- 以向量( PDF & SVG) 和位图( JPEG, PNG, & WEBP) 格式生成图像
- 能够绘制到交互式图形界面 窗口 并提供了类似浏览器的 事件 框架
- 可以将图像保存到 文件编码为 数据URL 字符串,然后返回 缓冲 或者说 锐利 对象
- 在 a 中使用本地线程 用户配置 同步渲染和文件 I/ 欧
- 能够创建 多页 在给定的画布上,然后 产出 它们作为单页多页 PDF 或图像序列保存到多个文件
- 能够 简化, (中文(简体) ), 钝子, (中文(简体) ), 组合, (中文(简体) ), 节选,以及 原子化 使用贝塞尔路径 高效 布尔操作或点数 插值
- 提供 三维视角 转换之外 缩放, (中文(简体) ), 旋转,以及 翻译
- 可以以向量填充形状 纹理 除以位图为基础的 模式 并使用自定义支持行绘制 标记
- 支持完整的一组 CSS 过滤器 图像处理操作员
- 提供丰富的打印控制,包括:
- 可用于在标准 Linux 主机和Vercel 和 AWS Lambda 等 " 无服务器 " 平台上的服务器侧图像渲染
安装
如果您运行在一个支持的平台上,安装应该像:
npm install skia-canvas
这将下载项目最新版本的预编译库 释放。 。 。 .
pnpm
如果你使用 pnpm 软件包管理器, 它不会下载 skia-canvas平台 - 本地二进制, 除非你明确允许 。 您可以通过“ 核准构建” 命令进行交互操作( 注意需要按 <space> 切换选择,然后 <enter> 继续:
pnpm install skia-canvas
pnpm approve-builds
在非交互情况下(如通过CI构建),您可以在添加时批准构建步骤 skia-canvas 对您的项目:
pnpm install skia-canvas --allow-build=skia-canvas
或者,您可以添加一个 pnpm.onlyBuiltDependencies 条目 package.json 用于标记构建步骤的文件 :
{
"pnpm": {
"onlyBuiltDependencies": ["skia-canvas"]
}
}
平台支持
Skia Canvas运行于Linux,macOS,或Windows以及Vercel和AWS Lambda等无服务器平台上. 预编版的图书馆本土代码将在适当的架构中自动下载(arm64 或者说 x64当您通过 npm 安装它.
Rust 图书馆的基本用途 N - API (英语) v8 允许它全部运行 当前支持 节点.js发布后,它与可回V12.22+,v14.17+,v15.12+,和v16+的版本相后相兼容.
链接
库与 Linux 系统兼容 glibc 图像 2.28 或以后的阿尔卑斯-利努斯和 马斯尔 喜欢的C图书馆 它将利用该系统 fontconfig 设置在 /etc/fonts 如果它们存在,否则将倒退到使用 占位符配置,在常用的 Linux 路径中寻找已安装的字体.
夹克
如果你正在设置一个 粘贴文件 用于 node 作为它的基础,最简单的方法是确定你的 FROM 图像到( Debian- 衍生) 默认之一 node:lts, (中文(简体) ), node:22, (中文(简体) ), node:24-bookworm,或者简单地:
FROM node
如果你想使用阿尔卑斯山作为基础分布,可以从以下几个方面开始:
FROM node:alpine
AWS 兰布达
Skia Canvas 依赖于标准 Lambda 中没有的图书馆 运行时间。您可以通过上传一个“层' (包含所需库和 node_modules 目录)和配置您的功能以使用它.
详细 AWS 指令
将 Skia Canvas 层添加到您的 AWS 账户
- 看那边 资产 skia Canvas的章节 当前发布 并下载
aws-lambda-x64.zip或者说aws-lambda-arm64.zip文件( 取决于您的架构) 但不要解压缩 - 去AWS蓝巴达 层控制台 并单击 创建图层 按钮,然后填入字段:
- 名称数字 :
skia-canvas(或随想 - 说明: 您也许需要注意这里的 Skia Canvas 版本
- 兼容结构选择 x86 64 (韩语) 或者说 军火64 根据你选择的拉链
- 兼容的运行时间选择 节点.js 22.x (和(或)20.x)
- 点击 选择文件 按钮,然后单击 创建
或者,你可以使用 aws 命令行工具 来创建层。 这部闪闪发光的脚本将获取您所选择的 skia-canvas 版本,并提供给您的 Lambda 功能 .
#!/usr/bin/env bash
VERSION=3.0.8 # the skia-canvas version to include
PLATFORM=arm64 # arm64 or x64
curl -sLO https://github.com/samizdatco/skia-canvas/releases/download/v${VERSION}/aws-lambda-${PLATFORM}.zip
aws lambda publish-layer-version \
--layer-name "skia-canvas" \
--description "Skia Canvas ${VERSION} layer" \
--zip-file "fileb://aws-lambda-${PLATFORM}.zip" \
--compatible-runtimes "nodejs20.x" "nodejs22.x" \
--compatible-architectures "${X/#x/x86_}"
在 Lambda 函数中使用层
您现在可以在您创建的任何函数中使用此层 函数控制台,单击 添加图层 按钮后,您可以选择新建的 Skia Canvas 层 自定义图层 层源.
请注意, 层只包含 Skia Canvas 及其依赖性 —— 您想要使用的任何其他 npm 模块都需要被捆绑到您的函数中 。 防止 skia-canvas 模块,请确保您添加到 devDependencies 节次(没有 经常预算 dependencies 您的软件包。 json 文件.
下个.js / Webpack 软件包
如果您使用像Next.js这样的框架,将您的服务器侧代码与Webpack捆绑在一起,则需要标记 skia-canvas 作为"外部",否则其平台本土二进制文件将被排除在最终构建之外. 尝试将这些选项添加到您的 next.config.ts 文件 :
const nextConfig: NextConfig = {
serverExternalPackages: ['skia-canvas'],
webpack: (config, options) => {
if (options.isServer){
config.externals = [
...config.externals,
{'skia-canvas': 'commonjs skia-canvas'},
]
}
return config
}
};
从来源汇编
如果您的系统无法使用预建的二进制, 您需要编译此库中直接与 Skia 接口的部分 .
安装开始 :
- 最新版本
git(旧版本对斯基亚的子模块有困难) - 这个 Rust 编译器 和货物包管理器
rustup - C 编译工具链( LLVM/ Clang 或 MSVC)
- Python 3 (由 Skia 使用) 构建进程(中文(简体) )
- 这个 忍者会 构建系统
- 在 Linux 上: 字体配置和 OpenSSL
详细说明 在Rust Skia文档的 " 建筑 " 部分可以找到用于建立对不同操作系统的依赖。 内有多克文件 集装箱 名录也可能有助于确定所需的依赖性。 一旦所有必要的编译器和图书馆都到齐,即可运行 npm run build 这将为您提供一个有用的库(经过相当长的编译过程).
全局设置
有一些设置只能在启动时配置,并将应用于您在脚本中创建的所有画布. 以下各节介绍不同的情况: 环境变量 您可以设定进行全球变化。 你可以把它们设定为命令行引用的一部分,或者放在一个
.env在您的工程目录中的文件并使用节点 20's--env-file参数 把它们全部装上.
多脚踏车
在背景中渲染画布时( 例如, 使用同步) 移动 或者说 缓冲 将任务产入由 线条 图书馆。 默认它会创造出与你的CPU一样多的线程. 您可以通过检查任何默认值来查看此值 帆布 对象的 engine.threads 属性。 如果您想要推翻此默认, 您可以设置 SKIA_CANVAS_THREADS 环境变量与您的首选值.
例如,您可以通过运行脚本将同步处理限制在两个同时进行的任务:
SKIA_CANVAS_THREADS=2 node my-canvas-script.js
参数验证
有些情况下浏览器API会通过默默地忽略方法调用而不是出错来回应无效的参数. 例如,这些线条根本就没有效果:
ctx.fillRect(0, 0, 100, "october")
ctx.lineTo(NaN, 0)
Skia Canvas尽了最大努力去模仿这些怪事,但允许您选择更严格的模式,在这种情况下它会扔出TypeErrors(这可用于调试).
设定 SKIA_CANVAS_STRICT 环境变量改为 1 或者说 true 以启用此模式.
示例使用
生成图像文件
import {Canvas} from 'skia-canvas'
let canvas = new Canvas(400, 400),
ctx = canvas.getContext("2d"),
{width, height} = canvas;
let sweep = ctx.createConicGradient(Math.PI * 1.2, width/2, height/2)
sweep.addColorStop(0, "red")
sweep.addColorStop(0.25, "orange")
sweep.addColorStop(0.5, "yellow")
sweep.addColorStop(0.75, "green")
sweep.addColorStop(1, "red")
ctx.strokeStyle = sweep
ctx.lineWidth = 100
ctx.strokeRect(100,100, 200,200)
// render to multiple destinations using a background thread
async function render(){
// save a ‘retina’ image...
await canvas.saveAs("rainbox.png", {density:2})
// ...or use a shorthand for canvas.toBuffer("png")
let pngData = await canvas.png
// ...or embed it in a string
let pngEmbed = `<img src="${await canvas.toDataURL("png")}">`
}
render()
// ...or save the file synchronously from the main thread
canvas.saveAsSync("rainbox.pdf")
多页序列
import {Canvas} from 'skia-canvas'
let canvas = new Canvas(400, 400),
ctx = canvas.getContext("2d"),
{width, height} = canvas
for (const color of ['orange', 'yellow', 'green', 'skyblue', 'purple']){
ctx = canvas.newPage()
ctx.fillStyle = color
ctx.fillRect(0,0, width, height)
ctx.fillStyle = 'white'
ctx.arc(width/2, height/2, 40, 0, 2 * Math.PI)
ctx.fill()
}
async function render(){
// save to a multi-page PDF file
await canvas.saveAs("all-pages.pdf")
// save to files named `page-01.png`, `page-02.png`, etc.
await canvas.saveAs("page-{2}.png")
}
render()
向窗口渲染
import {Window} from 'skia-canvas'
let win = new Window(300, 300)
win.title = "Canvas Window"
win.on("draw", e => {
let ctx = e.target.canvas.getContext("2d")
ctx.lineWidth = 25 + 25 * Math.cos(e.frame / 10)
ctx.beginPath()
ctx.arc(150, 150, 50, 0, 2 * Math.PI)
ctx.stroke()
ctx.beginPath()
ctx.arc(150, 150, 10, 0, 2 * Math.PI)
ctx.stroke()
ctx.fill()
})
与 锐利.js
import sharp from 'sharp'
import {Canvas, loadImage} from 'skia-canvas'
let canvas = new Canvas(400, 400),
ctx = canvas.getContext("2d"),
{width, height} = canvas,
[x, y] = [width/2, height/2]
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, x, y)
ctx.fillStyle = 'orange'
ctx.fillRect(x, y, x, y)
// Render the canvas to a Sharp object on a background thread then desaturate
await canvas.toSharp().modulate({saturation:.25}).jpeg().toFile("faded.jpg")
// Convert an ImageData to a Sharp object and save a grayscale version
let imgData = ctx.getImageData(0, 0, width, height, {matte:'white', density:2})
await imgData.toSharp().grayscale().png().toFile("black-and-white.png")
// Create an image using Sharp then draw it to the canvas as an Image object
let sharpImage = sharp({create:{ width:x, height:y, channels:4, background:"skyblue" }})
let canvasImage = await loadImage(sharpImage)
ctx.drawImage(canvasImage, x, 0)
await canvas.saveAs('mosaic.png')
基准
在这些基准中,Skia Canvas通过两种模式进行测试:串行和Aync. 连续运行时,每次渲染操作都要等待到继续到下一次测试迭代. 在同步运行时,所有测试迭代会立即启动,并使用库的多行支持并行执行.
启动延迟
| 图书馆 | 每个运行 | 总时间(100次重复) |
|---|---|---|
| 画布基特 - wasm | 25 ms | 2.47 s |
| 画布 | 88 ms | 8.77 s |
| @napi-rs/canvas (英语) | 69 ms | 6.87 s |
| 扫描片 | <1 ms | 33 ms |
贝塞尔曲线
| 图书馆 | 每个运行 | 总时间(20次重复) |
|---|---|---|
| 画布基特 - wasm 👁️ | 790 ms | 15.81 s |
| 画布 👁️ | 486 ms | 9.72 s |
| @napi-rs/canvas (英语) 👁️ | 230 ms | 4.60 s |
| 轮廓( 连续) 👁️ | 137 ms | 2.74 s |
| 扫描( 同步) 👁️ | 28 ms | 558 ms |
SVG至巴布亚新几内亚
| 图书馆 | 每个运行 | 总时间(100次重复) |
|---|---|---|
| 画布基特 - wasm | ————— | ————— 不支持 |
| 画布 👁️ | 122 ms | 12.16 s |
| @napi-rs/canvas (英语) 👁️ | 84 ms | 8.42 s |
| 轮廓( 连续) 👁️ | 58 ms | 5.83 s |
| 扫描( 同步) 👁️ | 11 ms | 1.08 s |
缩放/旋转图像
| 图书馆 | 每个运行 | 总时间(50次重复) |
|---|---|---|
| 画布基特 - wasm 👁️ | 274 ms | 13.72 s |
| 画布 👁️ | 283 ms | 14.13 s |
| @napi-rs/canvas (英语) 👁️ | 112 ms | 5.60 s |
| 轮廓( 连续) 👁️ | 100 ms | 5.00 s |
| 扫描( 同步) 👁️ | 19 ms | 935 ms |
基本案文
| 图书馆 | 每个运行 | 总时间(200次重复) |
|---|---|---|
| 画布基特 - wasm 👁️ | 24 ms | 4.75 s |
| 画布 👁️ | 24 ms | 4.88 s |
| @napi-rs/canvas (英语) 👁️ | 19 ms | 3.83 s |
| 轮廓( 连续) 👁️ | 21 ms | 4.26 s |
| 扫描( 同步) 👁️ | 4 ms | 819 ms |
鸣谢
这个项目深深地感激联合国 Rust Skia项目 它的Skia绑定提供了安全和平庸的接口 来应对C++的混乱 多亏了开发商 节点扫描 他们的一套出色的单位测试。 在无 酸性测试 对于帆布,这些例行公事是宝贵的.
著名捐助者
- @mpaparno (英语) 向 SVG 渲染、原始图像缓存处理、 WEBP 导入/导出和众多错误修正提供了支持
- @ 沙门德克思 开发初始 Raw 图像加载和渲染常规
- @lucasmerlin (英语) 帮助GPU在Vulkan上工作
- @cprecioso (英语) 打开 @saantonandre (英语) 在 TypeScript 类型定义上更正和扩展
- @meihuanyu (英语) 贡献过滤器和路径渲染修正
版权
2020–2025 (中文(简体) ) 萨米兹达特起草公司.