@napi-rs/canvas
🚀 Help me to become a full-time open-source developer by sponsoring me on Github
Google Skia binding to Node.js via N-API
.
⚠️ This project is in very early stage.
For details on planned features and future direction please refer to the Roadmap.
中文文档
Support matrix
| node10 | node12 | node14 | node15 |
---|
Windows x64 | ✓ | ✓ | ✓ | ✓ |
macOS x64 | ✓ | ✓ | ✓ | ✓ |
macOS aarch64 | ✓ | ✓ | ✓ | ✓ |
Linux x64 gnu | ✓ | ✓ | ✓ | ✓ |
Linux x64 musl | ✓ | ✓ | ✓ | ✓ |
Linux aarch64 gnu | ✓ | ✓ | ✓ | ✓ |
Linux arm gnueabihf | ✓ | ✓ | ✓ | ✓ |
Linux aarch64 android | ✓ | ✓ | ✓ | ✓ |
Usage
const { promises } = require('fs')
const { join } = require('path')
const { createCanvas } = require('@napi-rs/canvas')
const canvas = createCanvas(1024, 768)
const ctx = canvas.getContext('2d')
ctx.lineWidth = 10
ctx.strokeStyle = '#03a9f4'
ctx.fillStyle = '#03a9f4'
ctx.strokeRect(75, 140, 150, 110)
ctx.fillRect(130, 190, 40, 60)
ctx.beginPath()
ctx.moveTo(50, 140)
ctx.lineTo(150, 60)
ctx.lineTo(250, 140)
ctx.closePath()
ctx.stroke()
async function main() {
const pngData = await canvas.png()
await promises.writeFile(join(__dirname, 'simple.png'), pngData)
}
main()
Features
Path2D
new Path2D()
new Path2D(path: Path2D)
new Path2D(path: string)
export interface DOMMatrix2DInit {
a: number
b: number
c: number
d: number
e: number
f: number
}
export class Path2D {
constructor(path?: Path2D | string)
addPath(path: Path2D, transform?: DOMMatrix2DInit): void
arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): void
arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void
bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void
closePath(): void
ellipse(
x: number,
y: number,
radiusX: number,
radiusY: number,
rotation: number,
startAngle: number,
endAngle: number,
anticlockwise?: boolean,
): void
lineTo(x: number, y: number): void
moveTo(x: number, y: number): void
quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void
rect(x: number, y: number, w: number, h: number): void
op(path: Path2D, operation: PathOp): Path2D
toSVGString(): string
getFillType(): FillType
setFillType(type: FillType): void
simplify(): Path2D
asWinding(): Path2D
stroke(stroke?: StrokeOptions): Path2D
transform(transform: DOMMatrix2DInit): Path2D
getBounds(): [left: number, top: number, right: number, bottom: number]
computeTightBounds(): [left: number, top: number, right: number, bottom: number]
trim(start: number, end: number, isComplement?: boolean): Path2D
equals(path: Path2D): boolean
}
PathKit
PathKit
is a toolset for manipulating Path in Skia
, supporting quadratic beziers, cubic beziers and conics.
The main features are.
Path Operation
.op(path, PathOp)
const pathOne = new Path2D(
'M8 50H92C96.4183 50 100 53.5817 100 58V142C100 146.418 96.4183 150 92 150H8C3.58172 150 0 146.418 0 142V58C0 53.5817 3.58172 50 8 50Z',
)
const pathTwo = new Path2D(
'"M58 0H142C146.418 0 150 3.58172 150 8V92C150 96.4183 146.418 100 142 100H58C53.5817 100 50 96.4183 50 92V8C50 3.58172 53.5817 0 58 0Z',
)
pathOne.op(pathTwo, PathOp.Intersect).toSVGString()
- Union, subtract the op path from the first path
- Difference, intersect the two paths
- ReverseDifference, union (inclusive-or) the two paths
- Intersect, exclusive-or the two paths
- XOR, subtract the first path from the op path
Covert FillType
in Path
.asWinding()
You can convert fill-rule="evenodd"
to fill-rule="nonzero"
in SVG.
This is useful for OpenType font-related tools, as fill-rule="nonzero"
is only supported in OpenType fonts.
const pathCircle = new Path2D(
'M50 87.5776C70.7536 87.5776 87.5776 70.7536 87.5776 50C87.5776 29.2464 70.7536 12.4224 50 12.4224C29.2464 12.4224 12.4224 29.2464 12.4224 50C12.4224 70.7536 29.2464 87.5776 50 87.5776ZM50 100C77.6142 100 100 77.6142 100 50C100 22.3858 77.6142 0 50 0C22.3858 0 0 22.3858 0 50C0 77.6142 22.3858 100 50 100Z',
)
pathCircle.setFillType(FillType.EvenOdd)
pathCircle.asWinding().toSVGString()
Simplify Path
.simplify()
Set the path to the same non-overlapping contour as the original path area, which means that it can also remove overlapping paths.
SVG with overlapping paths (Left)
const path =
'M2.933,89.89 L89.005,3.818 Q90.412,2.411 92.249,1.65 Q94.087,0.889 96.076,0.889 Q98.065,0.889 99.903,1.65 Q101.741,2.411 103.147,3.818 L189.22,89.89 Q190.626,91.296 191.387,93.134 Q192.148,94.972 192.148,96.961 Q192.148,98.95 191.387,100.788 Q190.626,102.625 189.219,104.032 Q187.813,105.439 185.975,106.2 Q184.138,106.961 182.148,106.961 Q180.159,106.961 178.322,106.2 Q176.484,105.439 175.077,104.032 L89.005,17.96 L96.076,10.889 L103.147,17.96 L17.075,104.032 Q15.668,105.439 13.831,106.2 Q11.993,106.961 10.004,106.961 Q8.015,106.961 6.177,106.2 Q4.339,105.439 2.933,104.032 Q1.526,102.625 0.765,100.788 Q0.004,98.95 0.004,96.961 Q0.004,94.972 0.765,93.134 Q1.526,91.296 2.933,89.89 Z'
path.simplify().toSVGString()
The tiger.json was serialized from gojs/samples/tiger
Building
Build skia from source
You can build this project from source, with no OS-specific package installing commands required:
$ git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git
$ cd canvas
$ node scripts/build-skia.js
$ yarn install --ignore-scripts
$ yarn build
$ yarn test
$ node example/tiger.js
Pull pre-build skia binary from GitHub
You can pull skia pre-build binaries if you just care the Rust
part:
$ git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git
$ cd canvas
$ node scripts/release-skia-binary.js --download
$ yarn install --ignore-scripts
$ yarn build
$ yarn test
$ node example/tiger.js