@effing/canvas 0.23.1 → 0.24.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 +46 -39
- package/dist/index.d.ts +29 -2
- package/dist/index.js +543 -437
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
-
|
|
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.
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
@@ -57,21 +57,7 @@ const png = await canvas.encode("png");
|
|
|
57
57
|
await fs.writeFile("output.png", png);
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
##
|
|
61
|
-
|
|
62
|
-
### Rendering Pipeline
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
JSX → Yoga layout → Skia canvas → PNG
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
1. **Yoga** calculates flex layout from your JSX tree (flexbox, positioning, text measurement)
|
|
69
|
-
2. **Skia** draws each node to a canvas context (backgrounds, borders, text, images, SVG, gradients)
|
|
70
|
-
3. **Encode** the canvas to PNG or JPEG via `canvas.encode()` or `canvas.encodeSync()`
|
|
71
|
-
|
|
72
|
-
Canvas dimensions default to `ctx.canvas.width` / `ctx.canvas.height`, but you can override them with the `width` and `height` options. This is useful for HiDPI rendering where the canvas is larger than the logical layout size.
|
|
73
|
-
|
|
74
|
-
### Font Loading
|
|
60
|
+
## Font Loading
|
|
75
61
|
|
|
76
62
|
Fonts must be registered before rendering text. You can pass them via the `fonts` option (registered automatically), or register them manually:
|
|
77
63
|
|
|
@@ -92,7 +78,7 @@ registerFontFromPath("./fonts/Inter-Bold.ttf", "Inter");
|
|
|
92
78
|
console.log(registeredFamilies()); // ["Inter", ...]
|
|
93
79
|
```
|
|
94
80
|
|
|
95
|
-
|
|
81
|
+
## Emoji Support
|
|
96
82
|
|
|
97
83
|
Emoji characters are automatically rendered as images from CDNs. Supported styles:
|
|
98
84
|
|
|
@@ -107,7 +93,24 @@ Emoji characters are automatically rendered as images from CDNs. Supported style
|
|
|
107
93
|
|
|
108
94
|
Pass `emoji: "none"` to disable emoji image rendering.
|
|
109
95
|
|
|
110
|
-
|
|
96
|
+
## Fit Text
|
|
97
|
+
|
|
98
|
+
Find the largest font size that keeps text within a bounding box, using binary search over the built-in text layout engine:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { findLargestUsableFontSize } from "@effing/canvas";
|
|
102
|
+
|
|
103
|
+
const fontSize = findLargestUsableFontSize({
|
|
104
|
+
text: "Hello World",
|
|
105
|
+
font,
|
|
106
|
+
maxWidth: 500,
|
|
107
|
+
maxHeight: 100,
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Supports optional `lineHeight` (`"normal"` or a numeric multiplier), `minFontSize` (default 1), and `maxFontSize` (default 1000).
|
|
112
|
+
|
|
113
|
+
## Lottie Animations
|
|
111
114
|
|
|
112
115
|
Render individual frames of Lottie animations to a canvas:
|
|
113
116
|
|
|
@@ -249,7 +252,7 @@ function createCanvas(width: number, height: number): Canvas;
|
|
|
249
252
|
Render a React element tree to a canvas context.
|
|
250
253
|
|
|
251
254
|
```typescript
|
|
252
|
-
function renderReactElement(
|
|
255
|
+
async function renderReactElement(
|
|
253
256
|
ctx: SKRSContext2D,
|
|
254
257
|
element: ReactNode,
|
|
255
258
|
options: RenderReactElementOptions,
|
|
@@ -279,6 +282,30 @@ function renderLottieFrame(
|
|
|
279
282
|
): void;
|
|
280
283
|
```
|
|
281
284
|
|
|
285
|
+
### `findLargestUsableFontSize(options)`
|
|
286
|
+
|
|
287
|
+
Find the largest integer font size that keeps text within the given bounds. Uses binary search with the built-in text layout engine — no external dependencies needed.
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
function findLargestUsableFontSize(
|
|
291
|
+
options: FindLargestUsableFontSizeOptions,
|
|
292
|
+
): number;
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { findLargestUsableFontSize } from "@effing/canvas";
|
|
297
|
+
|
|
298
|
+
const fontSize = findLargestUsableFontSize({
|
|
299
|
+
text: "Hello World",
|
|
300
|
+
font,
|
|
301
|
+
maxWidth: 500,
|
|
302
|
+
maxHeight: 100,
|
|
303
|
+
lineHeight: "normal", // optional (default: "normal")
|
|
304
|
+
minFontSize: 1, // optional (default: 1)
|
|
305
|
+
maxFontSize: 200, // optional (default: 1000)
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
282
309
|
### Font Helpers
|
|
283
310
|
|
|
284
311
|
- `registerFont(font)` — Register a font from a `FontData` buffer (idempotent)
|
|
@@ -389,27 +416,7 @@ await renderReactElement(ctx, <MyComponent />, {
|
|
|
389
416
|
});
|
|
390
417
|
```
|
|
391
418
|
|
|
392
|
-
### HiDPI Rendering
|
|
393
|
-
|
|
394
|
-
For sharp output on high-DPI displays, you can create a larger canvas and use `width`/`height` overrides to keep layout at the logical size:
|
|
395
|
-
|
|
396
|
-
```typescript
|
|
397
|
-
const dpr = 2;
|
|
398
|
-
const canvas = createCanvas(1080 * dpr, 1080 * dpr);
|
|
399
|
-
const ctx = canvas.getContext("2d");
|
|
400
|
-
ctx.scale(dpr, dpr);
|
|
401
|
-
|
|
402
|
-
await renderReactElement(ctx, <MyComponent />, {
|
|
403
|
-
fonts,
|
|
404
|
-
width: 1080,
|
|
405
|
-
height: 1080,
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
const png = await canvas.encode("png");
|
|
409
|
-
```
|
|
410
|
-
|
|
411
419
|
## Related Packages
|
|
412
420
|
|
|
413
421
|
- [`@effing/tween`](../tween) — Step iteration and easing for frame generation
|
|
414
422
|
- [`@effing/annie`](../annie) — Package rendered frames into animations
|
|
415
|
-
- [`@effing/satori`](../satori) — Alternative JSX-to-PNG renderer (Satori + Resvg)
|
package/dist/index.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ type EmojiStyle = "twemoji" | "openmoji" | "blobmoji" | "noto" | "fluent" | "flu
|
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Font data for text rendering.
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
*/
|
|
54
54
|
type FontData = {
|
|
55
55
|
name: string;
|
|
@@ -133,6 +133,33 @@ declare function registerFontFromPath(path: string, nameAlias?: string): void;
|
|
|
133
133
|
*/
|
|
134
134
|
declare function registeredFamilies(): string[];
|
|
135
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Options for {@link findLargestUsableFontSize}.
|
|
138
|
+
*/
|
|
139
|
+
type FindLargestUsableFontSizeOptions = {
|
|
140
|
+
/** The text to fit */
|
|
141
|
+
text: string;
|
|
142
|
+
/** Font data to use for measurement */
|
|
143
|
+
font: FontData;
|
|
144
|
+
/** Maximum width in pixels */
|
|
145
|
+
maxWidth: number;
|
|
146
|
+
/** Maximum height in pixels */
|
|
147
|
+
maxHeight: number;
|
|
148
|
+
/** Line height — `"normal"` uses font metrics, numeric values are CSS multipliers */
|
|
149
|
+
lineHeight?: number | "normal";
|
|
150
|
+
/** Minimum font size to consider (default: 1) */
|
|
151
|
+
minFontSize?: number;
|
|
152
|
+
/** Maximum font size to consider (default: 1000) */
|
|
153
|
+
maxFontSize?: number;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Find the largest integer font size that keeps text within the given bounds.
|
|
157
|
+
*
|
|
158
|
+
* Uses binary search over integer font sizes, measuring with {@link layoutText}
|
|
159
|
+
* at each step. Returns `minFontSize` if even the smallest size overflows.
|
|
160
|
+
*/
|
|
161
|
+
declare function findLargestUsableFontSize(options: FindLargestUsableFontSizeOptions): number;
|
|
162
|
+
|
|
136
163
|
declare function createCanvas(width: number, height: number): _napi_rs_canvas.Canvas;
|
|
137
164
|
|
|
138
|
-
export { type EmojiStyle, type FontData, type RenderReactElementOptions, createCanvas, loadLottie, registerFont, registerFontFromPath, registeredFamilies, renderLottieFrame, renderReactElement };
|
|
165
|
+
export { type EmojiStyle, type FindLargestUsableFontSizeOptions, type FontData, type RenderReactElementOptions, createCanvas, findLargestUsableFontSize, loadLottie, registerFont, registerFontFromPath, registeredFamilies, renderLottieFrame, renderReactElement };
|