@effing/canvas 0.41.1 โ 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -37
- package/dist/index.d.ts +15 -13
- package/dist/index.js +707 -1271
- package/dist/index.js.map +1 -1
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
> Part of the [**Effing**](../../README.md) family โ programmatic video creation with TypeScript.
|
|
6
6
|
|
|
7
|
-
A canvas that can render JSX elements and Lottie animations, powered by Skia (via [@napi-rs/canvas](https://github.com/Brooooooklyn/canvas)) and Yoga flexbox layout. Supports emoji, font management, and CSS properties including transforms, gradients, and text effects.
|
|
7
|
+
A canvas that can render JSX elements and Lottie animations, powered by Skia (via [@effing/skia](https://github.com/builtbyfew/effing-skia), Effing's fork of [@napi-rs/canvas](https://github.com/Brooooooklyn/canvas)) and Yoga flexbox layout. Supports emoji, font management, and CSS properties including transforms, gradients, and text effects.
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
@@ -12,11 +12,7 @@ A canvas that can render JSX elements and Lottie animations, powered by Skia (vi
|
|
|
12
12
|
npm install @effing/canvas
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm install @napi-rs/canvas
|
|
19
|
-
```
|
|
15
|
+
The Skia backend, `@effing/skia`, is installed along with it. It ships prebuilt binaries for Linux x64 and arm64 (glibc and musl), macOS x64 and arm64, and Windows x64.
|
|
20
16
|
|
|
21
17
|
## Quick Start
|
|
22
18
|
|
|
@@ -91,7 +87,7 @@ Emoji characters are automatically rendered as images from CDNs. Supported style
|
|
|
91
87
|
| `fluent` | Microsoft Fluent Emoji (color) |
|
|
92
88
|
| `fluentFlat` | Microsoft Fluent Emoji (flat) |
|
|
93
89
|
|
|
94
|
-
Pass `emoji: "none"` to disable emoji image rendering.
|
|
90
|
+
An emoji is an emoji character ([UTS #51](https://www.unicode.org/reports/tr51/)), with or without U+FE0F (`๐`, `โค`, `โค๏ธ`, `๐ต`), a keycap (`1๏ธโฃ`), a flag (`๐ง๐ช`, `๐ด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ`), a skin-tone sequence (`๐๐ฝ`) or a ZWJ sequence of emoji (`๐จโ๐ฉโ๐ง`). Emoji characters are drawn as images even when Unicode presents them as text by default, except `ยฉ`, `ยฎ` and `โข`, which stay text unless U+FE0F follows them, and digits, `#` and `*`, which stay text unless a keycap follows them, as browsers draw them; U+FE0E keeps any emoji character text. For text-default emoji in the Basic Multilingual Plane, such as `โค` or `โ`, this differs from browsers, which draw them as monochrome text through the system's fallback fonts: canvas has no such fallback, so an image beats a missing glyph. Text-default emoji outside it, such as `๐ต` or `๐ณ`, are colour emoji in browsers too. Characters with no emoji form, such as `โ` or `โ`, are text. Each emoji takes an inline box of 1em on its line, with its bottom 0.1em below the baseline, about where browsers draw an emoji glyph. Lines break around it as browsers break them around an emoji, so it stays with the punctuation next to it (`๐!`, `(๐)`). Pass `emoji: "none"` to disable emoji image rendering. An emoji drawn without an image โ with `emoji: "none"`, one the style has no image for (the Fluent styles have no flags), or one whose image failed to load โ is drawn as text, so it needs a font that covers it among the text's font families (every font given to `renderReactElement` is one), as do text symbols.
|
|
95
91
|
|
|
96
92
|
## Fit Text
|
|
97
93
|
|
|
@@ -108,7 +104,7 @@ const fontSize = findLargestUsableFontSize({
|
|
|
108
104
|
});
|
|
109
105
|
```
|
|
110
106
|
|
|
111
|
-
By default the text wraps to `maxWidth` and is fit into the `maxWidth` ร `maxHeight` box. Pass `whiteSpace: "nowrap"` to fit the text on one line (per newline-separated paragraph)
|
|
107
|
+
By default the text wraps to `maxWidth` and is fit into the `maxWidth` ร `maxHeight` box. Pass `whiteSpace: "nowrap"` to fit the text on one line instead (or `"pre"` for one line per newline-separated paragraph), where `maxWidth` constrains the full line width:
|
|
112
108
|
|
|
113
109
|
```typescript
|
|
114
110
|
const fontSize = findLargestUsableFontSize({
|
|
@@ -120,7 +116,7 @@ const fontSize = findLargestUsableFontSize({
|
|
|
120
116
|
});
|
|
121
117
|
```
|
|
122
118
|
|
|
123
|
-
Supports optional `lineHeight` (`"normal"` or a numeric multiplier), `whiteSpace` (`"normal"` by default; `"nowrap"
|
|
119
|
+
Supports optional `lineHeight` (`"normal"` or a numeric multiplier), `whiteSpace` (`"normal"` by default; `"nowrap"` for one line, `"pre"` for one line per paragraph), `minFontSize` (default 1), and `maxFontSize` (default 1000).
|
|
124
120
|
|
|
125
121
|
## Inline SVG
|
|
126
122
|
|
|
@@ -249,27 +245,34 @@ const png = canvas.encodeSync("png");
|
|
|
249
245
|
|
|
250
246
|
### Typography
|
|
251
247
|
|
|
252
|
-
| Property | Values / Notes
|
|
253
|
-
| ----------------------- |
|
|
254
|
-
| `fontFamily` | Font name (inherited)
|
|
255
|
-
| `fontSize` | Number or CSS length (inherited)
|
|
256
|
-
| `fontWeight` | Numeric weight (inherited)
|
|
257
|
-
| `fontStyle` | `normal`, `italic` (inherited)
|
|
258
|
-
| `textAlign` | `left`, `center`, `right`, `justify` (inherited)
|
|
259
|
-
| `textDecoration` | String (inherited)
|
|
260
|
-
| `textTransform` | `none`, `uppercase`, `lowercase`, `capitalize` (inherited)
|
|
261
|
-
| `lineHeight` | Number or string (inherited)
|
|
262
|
-
| `letterSpacing` | Number or CSS length (inherited)
|
|
263
|
-
| `whiteSpace` | `normal`, `nowrap`, `pre`, `pre-wrap`, `pre-line` (inherited) |
|
|
264
|
-
| `wordBreak` | `normal`, `break-all`, `
|
|
265
|
-
| `
|
|
266
|
-
| `
|
|
267
|
-
| `
|
|
268
|
-
| `
|
|
269
|
-
| `
|
|
270
|
-
| `
|
|
271
|
-
| `
|
|
272
|
-
| `
|
|
248
|
+
| Property | Values / Notes |
|
|
249
|
+
| ----------------------- | -------------------------------------------------------------- |
|
|
250
|
+
| `fontFamily` | Font name (inherited) |
|
|
251
|
+
| `fontSize` | Number or CSS length (inherited) |
|
|
252
|
+
| `fontWeight` | Numeric weight (inherited) |
|
|
253
|
+
| `fontStyle` | `normal`, `italic` (inherited) |
|
|
254
|
+
| `textAlign` | `left`, `center`, `right`, `justify` (inherited) |
|
|
255
|
+
| `textDecoration` | String (inherited) |
|
|
256
|
+
| `textTransform` | `none`, `uppercase`, `lowercase`, `capitalize` (inherited) |
|
|
257
|
+
| `lineHeight` | Number or string (inherited) |
|
|
258
|
+
| `letterSpacing` | Number or CSS length (inherited) |
|
|
259
|
+
| `whiteSpace` | `normal`, `nowrap`, `pre`, `pre-wrap`, `pre-line` (inherited)ยน |
|
|
260
|
+
| `wordBreak` | `normal`, `break-all`, `keep-all`, `break-word`ยฒ (inherited) |
|
|
261
|
+
| `overflowWrap` | `normal`, `break-word`, `anywhere`ยฒ (inherited) |
|
|
262
|
+
| `textOverflow` | `clip`, `ellipsis` (inherited) |
|
|
263
|
+
| `lineClamp` | Number โ max visible lines (adds ellipsis)ยณ |
|
|
264
|
+
| `textBox` | Shorthand for `textBoxTrim` and `textBoxEdge` |
|
|
265
|
+
| `textBoxTrim` | `none`, `trim-start`, `trim-end`, `trim-both` (inherited) |
|
|
266
|
+
| `textBoxEdge` | e.g. `"cap alphabetic"` (inherited) |
|
|
267
|
+
| `WebkitTextStroke` | Shorthand, e.g. `"2px red"` (inherited) |
|
|
268
|
+
| `WebkitTextStrokeWidth` | CSS length (inherited) |
|
|
269
|
+
| `WebkitTextStrokeColor` | Any CSS color (inherited) |
|
|
270
|
+
|
|
271
|
+
ยน White space is processed as in CSS: under `normal`, `nowrap` and `pre-line`, a run of spaces and tabs collapses to one space, the spaces at the start and end of the text and around a newline are removed, and a newline becomes a space (`pre-line` keeps it as a line break). Adjacent strings, as `Hello {name}` gives, are one run of text, and each run of text between elements is laid out as a flex item of its own, as browsers lay out text in a flex container: a run of nothing but white space, such as `{" "}` between two elements, isn't rendered, and an element whose only text is empty or white space has no line box. Fragments, arrays and function components are unwrapped first, as the DOM has no trace of them. A `<br />` is an item of its own, not a forced break inside a run of text: in a column it puts the text after it on the next line, but in a row (the default `flexDirection`) the text either side of it sits side by side, where browsers stack it. A line or paragraph separator (U+2028, U+2029) is a space with a break opportunity after it, as in browsers, but with `letterSpacing` it gets the spacing twice where browsers add it once. `pre` and `pre-wrap` keep the spaces before a line break in the line's width and alignment, as browsers do; a tab is one space wide. A CRLF counts as one newline, and `pre` and `pre-wrap` drop a lone CR, which browsers draw as nothing.
|
|
272
|
+
|
|
273
|
+
ยฒ A word wider than its line overflows it, as in CSS, unless `overflowWrap` is `break-word` or `anywhere` (or `wordBreak` the deprecated `break-word`): the word then starts a line of its own and is broken between grapheme clusters where that line is full. `anywhere` breaks as `break-word` does.
|
|
274
|
+
|
|
275
|
+
ยณ The last line shown is its own text with an ellipsis after it, cut to fit, as in browsers. Under `whiteSpace: "pre"`, a clamped line that ends at a newline gets no ellipsis, where browsers add one. Canvas aligns the clamped line with its ellipsis, where browsers align its text alone and let the ellipsis overflow (hidden by `overflow: hidden`): right-aligned, `"ab\ncd"` clamped to one line is "abโฆ" at the right edge, where browsers show "ab" there.
|
|
273
276
|
|
|
274
277
|
### Effects
|
|
275
278
|
|
|
@@ -289,6 +292,17 @@ const png = canvas.encodeSync("png");
|
|
|
289
292
|
| ----------- | ------------------------------------------------ |
|
|
290
293
|
| `objectFit` | `contain`, `cover`, `fill`, `none`, `scale-down` |
|
|
291
294
|
|
|
295
|
+
### Opacity and filters
|
|
296
|
+
|
|
297
|
+
`opacity` and `filter` apply to an element and its descendants as one group,
|
|
298
|
+
as in CSS: overlapping children fade together instead of showing through each
|
|
299
|
+
other, and a `drop-shadow()` is cast once by the whole group.
|
|
300
|
+
|
|
301
|
+
A filter covers the element as painted, so `blur()` softens its edge too,
|
|
302
|
+
rounded corners included. To keep the edge sharp, clip after the filter: give
|
|
303
|
+
the element a `clipPath` (for example `inset(0px round 8px)`), or wrap it in an
|
|
304
|
+
element with `overflow: hidden`.
|
|
305
|
+
|
|
292
306
|
### Clip paths
|
|
293
307
|
|
|
294
308
|
`clipPath` clips the element's entire rendering โ background, borders,
|
|
@@ -347,10 +361,9 @@ pixels of the element and follow its `transform`, including non-uniform
|
|
|
347
361
|
scales and skews. At the canvas edge the backdrop is extended outward, like a
|
|
348
362
|
browser clamps it at the viewport edge, so a blur stays uniform there.
|
|
349
363
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
unfiltered backdrop instead of the ancestor forming its own backdrop root.
|
|
364
|
+
As in browsers, an ancestor with `opacity` below 1 or a `filter` is a backdrop
|
|
365
|
+
root: an element inside it sees only what that ancestor has painted so far,
|
|
366
|
+
not what lies behind the ancestor.
|
|
354
367
|
|
|
355
368
|
### Units
|
|
356
369
|
|
|
@@ -360,7 +373,7 @@ unfiltered backdrop instead of the ancestor forming its own backdrop root.
|
|
|
360
373
|
|
|
361
374
|
### `createCanvas(width, height)`
|
|
362
375
|
|
|
363
|
-
Create a new canvas. Re-exported from `@
|
|
376
|
+
Create a new canvas. Re-exported from `@effing/skia`.
|
|
364
377
|
|
|
365
378
|
```typescript
|
|
366
379
|
function createCanvas(width: number, height: number): Canvas;
|
|
@@ -402,7 +415,7 @@ for (let frame = 0; frame < frameCount; frame++) {
|
|
|
402
415
|
|
|
403
416
|
### `loadImage(source, options?)`
|
|
404
417
|
|
|
405
|
-
Load an image from a path, Buffer, data URI, or remote URL. Remote `http`/`https` URLs are fetched via the global `fetch()` โ the same path `<img>` sources take in `renderReactElement` โ so a global dispatcher / proxy (e.g. undici's `setGlobalDispatcher`) and the `userAgent` option are honored. All other sources delegate to `@
|
|
418
|
+
Load an image from a path, Buffer, data URI, or remote URL. Remote `http`/`https` URLs are fetched via the global `fetch()` โ the same path `<img>` sources take in `renderReactElement` โ so a global dispatcher / proxy (e.g. undici's `setGlobalDispatcher`) and the `userAgent` option are honored. All other sources delegate to `@effing/skia`'s native loader.
|
|
406
419
|
|
|
407
420
|
```typescript
|
|
408
421
|
function loadImage(
|
|
@@ -504,7 +517,7 @@ type EmojiStyle =
|
|
|
504
517
|
|
|
505
518
|
### Canvas Primitives
|
|
506
519
|
|
|
507
|
-
The building blocks you need alongside a context are re-exported from `@
|
|
520
|
+
The building blocks you need alongside a context are re-exported from `@effing/skia`, so you never need to import (or declare a dependency on) `@effing/skia` directly: `Canvas`, `SKRSContext2D`, `GlobalFonts`, `Image`, `ImageData`, `Path2D`, `DOMMatrix`, `DOMPoint`, `DOMRect`, `PathOp`, `FillType`, `StrokeJoin`, `StrokeCap`, and `LottieAnimation`. (`loadImage` is wrapped โ see above โ not re-exported directly.)
|
|
508
521
|
|
|
509
522
|
```typescript
|
|
510
523
|
import { createCanvas, Path2D } from "@effing/canvas";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { Image, loadImage as loadImage$1, LottieAnimation, SKRSContext2D } from '@
|
|
3
|
-
export { Canvas, DOMMatrix, DOMPoint, DOMRect, FillType, GlobalFonts, Image, ImageData, LottieAnimation, Path2D, PathOp, SKRSContext2D, StrokeCap, StrokeJoin } from '@
|
|
1
|
+
import * as _effing_skia from '@effing/skia';
|
|
2
|
+
import { Image, loadImage as loadImage$1, LottieAnimation, SKRSContext2D } from '@effing/skia';
|
|
3
|
+
export { Canvas, DOMMatrix, DOMPoint, DOMRect, FillType, GlobalFonts, Image, ImageData, LottieAnimation, Path2D, PathOp, SKRSContext2D, StrokeCap, StrokeJoin } from '@effing/skia';
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
5
|
|
|
6
|
-
/** Source types accepted by {@link loadImage}, mirroring @
|
|
6
|
+
/** Source types accepted by {@link loadImage}, mirroring @effing/skia. */
|
|
7
7
|
type LoadImageSource = Parameters<typeof loadImage$1>[0];
|
|
8
8
|
/** Options for {@link loadImage}. */
|
|
9
9
|
interface LoadImageOptions {
|
|
@@ -23,11 +23,11 @@ interface LoadImageOptions {
|
|
|
23
23
|
* native loader as bytes โ the same path `<img>` sources take in
|
|
24
24
|
* `renderReactElement`. This keeps `loadImage(url)` and `<img src={url}>`
|
|
25
25
|
* consistent: both honor a global dispatcher / proxy (undici's
|
|
26
|
-
* setGlobalDispatcher) and the `userAgent` option. @
|
|
26
|
+
* setGlobalDispatcher) and the `userAgent` option. @effing/skia's own URL
|
|
27
27
|
* loader uses Node's raw http modules, which bypass any dispatcher, so it is
|
|
28
28
|
* only used for non-remote sources here.
|
|
29
29
|
*
|
|
30
|
-
* Note: @
|
|
30
|
+
* Note: @effing/skia's `maxRedirects` / `requestOptions` load options are
|
|
31
31
|
* intentionally not exposed โ they only configure its built-in URL loader,
|
|
32
32
|
* which this wrapper bypasses. Control remote fetches via fetch/undici instead.
|
|
33
33
|
*/
|
|
@@ -277,6 +277,7 @@ type ComputedStyle = {
|
|
|
277
277
|
letterSpacing?: number | string;
|
|
278
278
|
whiteSpace?: "normal" | "nowrap" | "pre" | "pre-wrap" | "pre-line";
|
|
279
279
|
wordBreak?: "normal" | "break-all" | "break-word" | "keep-all";
|
|
280
|
+
overflowWrap?: "normal" | "break-word" | "anywhere";
|
|
280
281
|
textOverflow?: "clip" | "ellipsis";
|
|
281
282
|
lineClamp?: number;
|
|
282
283
|
boxShadow?: string;
|
|
@@ -308,9 +309,10 @@ type FindLargestUsableFontSizeOptions = {
|
|
|
308
309
|
/** Line height โ `"normal"` uses font metrics, numeric values are CSS multipliers */
|
|
309
310
|
lineHeight?: number | "normal";
|
|
310
311
|
/**
|
|
311
|
-
* Whitespace handling,
|
|
312
|
-
* on one line
|
|
313
|
-
*
|
|
312
|
+
* Whitespace handling, as in CSS (default: `"normal"`). Use `"nowrap"` to
|
|
313
|
+
* fit the text on one line instead of wrapping to `maxWidth`, its newlines
|
|
314
|
+
* collapsing to spaces, or `"pre"` to fit each newline-separated paragraph
|
|
315
|
+
* on a line of its own.
|
|
314
316
|
*/
|
|
315
317
|
whiteSpace?: ComputedStyle["whiteSpace"];
|
|
316
318
|
/** Minimum font size to consider (default: 1) */
|
|
@@ -325,12 +327,12 @@ type FindLargestUsableFontSizeOptions = {
|
|
|
325
327
|
* at each step. Returns `minFontSize` if even the smallest size overflows.
|
|
326
328
|
*
|
|
327
329
|
* By default text wraps to `maxWidth` and is fit into the `maxWidth` ร `maxHeight`
|
|
328
|
-
* box. Set `whiteSpace: "nowrap"` to fit the text on one line (
|
|
329
|
-
* newline-separated paragraph)
|
|
330
|
-
* full line width.
|
|
330
|
+
* box. Set `whiteSpace: "nowrap"` to fit the text on one line instead (or
|
|
331
|
+
* `"pre"` for one line per newline-separated paragraph), in which case
|
|
332
|
+
* `maxWidth` constrains the full line width.
|
|
331
333
|
*/
|
|
332
334
|
declare function findLargestUsableFontSize(options: FindLargestUsableFontSizeOptions): number;
|
|
333
335
|
|
|
334
|
-
declare function createCanvas(width: number, height: number):
|
|
336
|
+
declare function createCanvas(width: number, height: number): _effing_skia.Canvas;
|
|
335
337
|
|
|
336
338
|
export { type EmojiStyle, type FindLargestUsableFontSizeOptions, type FontData, type ImageCache, type LoadImageOptions, type LoadImageSource, type RenderReactElementOptions, createCanvas, findLargestUsableFontSize, loadImage, loadLottie, registerFont, registerFontFromPath, registeredFamilies, renderLottieFrame, renderReactElement };
|