@nmmty/lazycanvas 0.6.4 → 1.0.0-dev.3
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 +1 -1
- package/biome.json +41 -0
- package/dist/core/Interpolation.d.ts +30 -0
- package/dist/core/Interpolation.js +200 -0
- package/dist/core/Scene.d.ts +96 -0
- package/dist/core/Scene.js +172 -0
- package/dist/core/Signal.d.ts +133 -0
- package/dist/core/Signal.js +255 -0
- package/dist/core/SignalUtils.d.ts +133 -0
- package/dist/core/SignalUtils.js +333 -0
- package/dist/core/ThreadScheduler.d.ts +38 -0
- package/dist/core/ThreadScheduler.js +74 -0
- package/dist/helpers/Filters.js +1 -1
- package/dist/helpers/FontsList.js +18 -18
- package/dist/helpers/Utlis.d.ts +3 -3
- package/dist/helpers/Utlis.js +15 -18
- package/dist/helpers/index.d.ts +3 -3
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/jsx-runtime.d.ts +17 -0
- package/dist/jsx-runtime.js +111 -0
- package/dist/structures/LazyCanvas.d.ts +3 -45
- package/dist/structures/LazyCanvas.js +11 -74
- package/dist/structures/components/BaseLayer.d.ts +34 -12
- package/dist/structures/components/BaseLayer.js +68 -35
- package/dist/structures/components/BezierLayer.d.ts +18 -39
- package/dist/structures/components/BezierLayer.js +85 -48
- package/dist/structures/components/{Group.d.ts → Div.d.ts} +26 -20
- package/dist/structures/components/{Group.js → Div.js} +39 -40
- package/dist/structures/components/ImageLayer.d.ts +2 -2
- package/dist/structures/components/ImageLayer.js +25 -26
- package/dist/structures/components/LineLayer.d.ts +13 -39
- package/dist/structures/components/LineLayer.js +44 -44
- package/dist/structures/components/MorphLayer.d.ts +4 -33
- package/dist/structures/components/MorphLayer.js +33 -47
- package/dist/structures/components/Path2DLayer.d.ts +4 -32
- package/dist/structures/components/Path2DLayer.js +28 -33
- package/dist/structures/components/PolygonLayer.d.ts +95 -0
- package/dist/structures/components/PolygonLayer.js +205 -0
- package/dist/structures/components/QuadraticLayer.d.ts +27 -44
- package/dist/structures/components/QuadraticLayer.js +87 -49
- package/dist/structures/components/TextLayer.d.ts +16 -45
- package/dist/structures/components/TextLayer.js +66 -68
- package/dist/structures/components/index.d.ts +10 -10
- package/dist/structures/components/index.js +2 -2
- package/dist/structures/helpers/Exporter.d.ts +13 -4
- package/dist/structures/helpers/Exporter.js +79 -42
- package/dist/structures/helpers/Font.js +1 -17
- package/dist/structures/helpers/Gradient.js +32 -45
- package/dist/structures/helpers/Link.js +2 -14
- package/dist/structures/helpers/Pattern.js +9 -17
- package/dist/structures/helpers/index.d.ts +7 -7
- package/dist/structures/helpers/readers/JSONReader.d.ts +4 -4
- package/dist/structures/helpers/readers/JSONReader.js +34 -40
- package/dist/structures/helpers/readers/YAMLReader.js +5 -5
- package/dist/structures/managers/FontsManager.js +9 -18
- package/dist/structures/managers/LayersManager.d.ts +18 -28
- package/dist/structures/managers/LayersManager.js +14 -36
- package/dist/structures/managers/RenderManager.d.ts +1 -15
- package/dist/structures/managers/RenderManager.js +17 -110
- package/dist/structures/managers/index.d.ts +3 -5
- package/dist/structures/managers/index.js +0 -2
- package/dist/types/enum.d.ts +4 -3
- package/dist/types/enum.js +3 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/APNGEncoder.d.ts +67 -0
- package/dist/utils/APNGEncoder.js +205 -0
- package/dist/utils/DrawUtils.d.ts +9 -0
- package/dist/utils/DrawUtils.js +42 -0
- package/dist/utils/LazyUtil.js +1 -2
- package/dist/utils/utils.d.ts +5 -9
- package/dist/utils/utils.js +148 -77
- package/package.json +62 -59
- package/dist/structures/components/ClearLayer.d.ts +0 -147
- package/dist/structures/components/ClearLayer.js +0 -158
- package/dist/structures/managers/AnimationManager.d.ts +0 -120
- package/dist/structures/managers/AnimationManager.js +0 -99
- package/dist/structures/managers/PluginManager.d.ts +0 -230
- package/dist/structures/managers/PluginManager.js +0 -182
- package/dist/types/types.d.ts +0 -105
package/ReadMe.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@nmmty/lazycanvas)
|
|
4
4
|
[](https://www.npmjs.com/package/@nmmty/lazycanvas)
|
package/biome.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": false,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": false
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"ignoreUnknown": false,
|
|
10
|
+
"ignore": [
|
|
11
|
+
"node_modules/",
|
|
12
|
+
"dist/",
|
|
13
|
+
"biome.json",
|
|
14
|
+
".biome/",
|
|
15
|
+
".git/",
|
|
16
|
+
".hg/",
|
|
17
|
+
".svn/",
|
|
18
|
+
"public/",
|
|
19
|
+
"docs/",
|
|
20
|
+
"resources/"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"formatter": {
|
|
24
|
+
"enabled": true,
|
|
25
|
+
"indentStyle": "space",
|
|
26
|
+
"indentWidth": 2,
|
|
27
|
+
"lineWidth": 100
|
|
28
|
+
},
|
|
29
|
+
"organizeImports": {
|
|
30
|
+
"enabled": true
|
|
31
|
+
},
|
|
32
|
+
"linter": {
|
|
33
|
+
"enabled": true,
|
|
34
|
+
"rules": { "recommended": true }
|
|
35
|
+
},
|
|
36
|
+
"javascript": {
|
|
37
|
+
"formatter": {
|
|
38
|
+
"quoteStyle": "double"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type Interpolator<T> = (from: T, to: T, progress: number) => T;
|
|
2
|
+
/**
|
|
3
|
+
* Linear interpolation for numbers
|
|
4
|
+
*/
|
|
5
|
+
export declare function lerpNumber(from: number, to: number, progress: number): number;
|
|
6
|
+
/**
|
|
7
|
+
* Interpolate colors in RGB space
|
|
8
|
+
*/
|
|
9
|
+
export declare function lerpColorRGB(from: string, to: string, progress: number): string;
|
|
10
|
+
/**
|
|
11
|
+
* Interpolate colors in HSL space (smoother for hue transitions)
|
|
12
|
+
*/
|
|
13
|
+
export declare function lerpColorHSL(from: string, to: string, progress: number): string;
|
|
14
|
+
/**
|
|
15
|
+
* Interpolate 2D vectors/points
|
|
16
|
+
*/
|
|
17
|
+
export declare function lerpVector(from: {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
}, to: {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
}, progress: number): {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Auto-detect interpolator based on value type
|
|
29
|
+
*/
|
|
30
|
+
export declare function getInterpolator<T>(from: T, to: T): Interpolator<T>;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lerpNumber = lerpNumber;
|
|
4
|
+
exports.lerpColorRGB = lerpColorRGB;
|
|
5
|
+
exports.lerpColorHSL = lerpColorHSL;
|
|
6
|
+
exports.lerpVector = lerpVector;
|
|
7
|
+
exports.getInterpolator = getInterpolator;
|
|
8
|
+
/**
|
|
9
|
+
* Linear interpolation for numbers
|
|
10
|
+
*/
|
|
11
|
+
function lerpNumber(from, to, progress) {
|
|
12
|
+
return from + (to - from) * progress;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse color string to RGBA components
|
|
16
|
+
*/
|
|
17
|
+
function parseColor(color) {
|
|
18
|
+
// Handle hex colors
|
|
19
|
+
if (color.startsWith("#")) {
|
|
20
|
+
const hex = color.slice(1);
|
|
21
|
+
let r, g, b, a = 1;
|
|
22
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
23
|
+
r = parseInt(hex[0] + hex[0], 16);
|
|
24
|
+
g = parseInt(hex[1] + hex[1], 16);
|
|
25
|
+
b = parseInt(hex[2] + hex[2], 16);
|
|
26
|
+
a = hex.length === 4 ? parseInt(hex[3] + hex[3], 16) / 255 : 1;
|
|
27
|
+
}
|
|
28
|
+
else if (hex.length === 6 || hex.length === 8) {
|
|
29
|
+
r = parseInt(hex.slice(0, 2), 16);
|
|
30
|
+
g = parseInt(hex.slice(2, 4), 16);
|
|
31
|
+
b = parseInt(hex.slice(4, 6), 16);
|
|
32
|
+
a = hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return { r, g, b, a };
|
|
38
|
+
}
|
|
39
|
+
// Handle rgb/rgba
|
|
40
|
+
const rgbaMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
41
|
+
if (rgbaMatch) {
|
|
42
|
+
return {
|
|
43
|
+
r: parseInt(rgbaMatch[1]),
|
|
44
|
+
g: parseInt(rgbaMatch[2]),
|
|
45
|
+
b: parseInt(rgbaMatch[3]),
|
|
46
|
+
a: rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Handle hsl/hsla
|
|
50
|
+
const hslaMatch = color.match(/hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*([\d.]+))?\)/);
|
|
51
|
+
if (hslaMatch) {
|
|
52
|
+
const h = parseInt(hslaMatch[1]) / 360;
|
|
53
|
+
const s = parseInt(hslaMatch[2]) / 100;
|
|
54
|
+
const l = parseInt(hslaMatch[3]) / 100;
|
|
55
|
+
const a = hslaMatch[4] ? parseFloat(hslaMatch[4]) : 1;
|
|
56
|
+
// Convert HSL to RGB
|
|
57
|
+
const { r, g, b } = hslToRgb(h, s, l);
|
|
58
|
+
return { r, g, b, a };
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Convert HSL to RGB
|
|
64
|
+
*/
|
|
65
|
+
function hslToRgb(h, s, l) {
|
|
66
|
+
let r, g, b;
|
|
67
|
+
if (s === 0) {
|
|
68
|
+
r = g = b = l;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const hue2rgb = (p, q, t) => {
|
|
72
|
+
if (t < 0)
|
|
73
|
+
t += 1;
|
|
74
|
+
if (t > 1)
|
|
75
|
+
t -= 1;
|
|
76
|
+
if (t < 1 / 6)
|
|
77
|
+
return p + (q - p) * 6 * t;
|
|
78
|
+
if (t < 1 / 2)
|
|
79
|
+
return q;
|
|
80
|
+
if (t < 2 / 3)
|
|
81
|
+
return p + (q - p) * (2 / 3 - t) * 6;
|
|
82
|
+
return p;
|
|
83
|
+
};
|
|
84
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
85
|
+
const p = 2 * l - q;
|
|
86
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
87
|
+
g = hue2rgb(p, q, h);
|
|
88
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
r: Math.round(r * 255),
|
|
92
|
+
g: Math.round(g * 255),
|
|
93
|
+
b: Math.round(b * 255),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Convert RGB to HSL
|
|
98
|
+
*/
|
|
99
|
+
function rgbToHsl(r, g, b) {
|
|
100
|
+
r /= 255;
|
|
101
|
+
g /= 255;
|
|
102
|
+
b /= 255;
|
|
103
|
+
const max = Math.max(r, g, b);
|
|
104
|
+
const min = Math.min(r, g, b);
|
|
105
|
+
let h = 0, s = 0;
|
|
106
|
+
const l = (max + min) / 2;
|
|
107
|
+
if (max !== min) {
|
|
108
|
+
const d = max - min;
|
|
109
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
110
|
+
switch (max) {
|
|
111
|
+
case r:
|
|
112
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
113
|
+
break;
|
|
114
|
+
case g:
|
|
115
|
+
h = ((b - r) / d + 2) / 6;
|
|
116
|
+
break;
|
|
117
|
+
case b:
|
|
118
|
+
h = ((r - g) / d + 4) / 6;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { h, s, l };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Interpolate colors in RGB space
|
|
126
|
+
*/
|
|
127
|
+
function lerpColorRGB(from, to, progress) {
|
|
128
|
+
const fromColor = parseColor(from);
|
|
129
|
+
const toColor = parseColor(to);
|
|
130
|
+
if (!fromColor || !toColor) {
|
|
131
|
+
return progress < 0.5 ? from : to;
|
|
132
|
+
}
|
|
133
|
+
const r = Math.round(lerpNumber(fromColor.r, toColor.r, progress));
|
|
134
|
+
const g = Math.round(lerpNumber(fromColor.g, toColor.g, progress));
|
|
135
|
+
const b = Math.round(lerpNumber(fromColor.b, toColor.b, progress));
|
|
136
|
+
const a = lerpNumber(fromColor.a, toColor.a, progress);
|
|
137
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Interpolate colors in HSL space (smoother for hue transitions)
|
|
141
|
+
*/
|
|
142
|
+
function lerpColorHSL(from, to, progress) {
|
|
143
|
+
const fromColor = parseColor(from);
|
|
144
|
+
const toColor = parseColor(to);
|
|
145
|
+
if (!fromColor || !toColor) {
|
|
146
|
+
return progress < 0.5 ? from : to;
|
|
147
|
+
}
|
|
148
|
+
const fromHSL = rgbToHsl(fromColor.r, fromColor.g, fromColor.b);
|
|
149
|
+
const toHSL = rgbToHsl(toColor.r, toColor.g, toColor.b);
|
|
150
|
+
// Interpolate hue with shortest path
|
|
151
|
+
let deltaH = toHSL.h - fromHSL.h;
|
|
152
|
+
if (deltaH > 0.5)
|
|
153
|
+
deltaH -= 1;
|
|
154
|
+
if (deltaH < -0.5)
|
|
155
|
+
deltaH += 1;
|
|
156
|
+
const h = fromHSL.h + deltaH * progress;
|
|
157
|
+
const s = lerpNumber(fromHSL.s, toHSL.s, progress);
|
|
158
|
+
const l = lerpNumber(fromHSL.l, toHSL.l, progress);
|
|
159
|
+
const a = lerpNumber(fromColor.a, toColor.a, progress);
|
|
160
|
+
const { r, g, b } = hslToRgb(h, s, l);
|
|
161
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Interpolate 2D vectors/points
|
|
165
|
+
*/
|
|
166
|
+
function lerpVector(from, to, progress) {
|
|
167
|
+
return {
|
|
168
|
+
x: lerpNumber(from.x, to.x, progress),
|
|
169
|
+
y: lerpNumber(from.y, to.y, progress),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Auto-detect interpolator based on value type
|
|
174
|
+
*/
|
|
175
|
+
function getInterpolator(from, to) {
|
|
176
|
+
// Number
|
|
177
|
+
if (typeof from === "number" && typeof to === "number") {
|
|
178
|
+
return lerpNumber;
|
|
179
|
+
}
|
|
180
|
+
// String (assume color)
|
|
181
|
+
if (typeof from === "string" && typeof to === "string") {
|
|
182
|
+
// Try to detect if it's a color
|
|
183
|
+
if (from.startsWith("#") || from.startsWith("rgb") || from.startsWith("hsl")) {
|
|
184
|
+
return lerpColorRGB;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Vector/Point
|
|
188
|
+
if (typeof from === "object" &&
|
|
189
|
+
from !== null &&
|
|
190
|
+
typeof to === "object" &&
|
|
191
|
+
to !== null &&
|
|
192
|
+
"x" in from &&
|
|
193
|
+
"y" in from &&
|
|
194
|
+
"x" in to &&
|
|
195
|
+
"y" in to) {
|
|
196
|
+
return lerpVector;
|
|
197
|
+
}
|
|
198
|
+
// Default: snap to end value
|
|
199
|
+
return ((from, to, progress) => (progress < 1 ? from : to));
|
|
200
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Div } from "../structures/components";
|
|
2
|
+
import { AnyLayer } from "../types";
|
|
3
|
+
import { ThreadGenerator, Signal } from "./Signal";
|
|
4
|
+
import { LazyCanvas } from "../structures/LazyCanvas";
|
|
5
|
+
import { Canvas } from "@napi-rs/canvas";
|
|
6
|
+
/**
|
|
7
|
+
* Scene class - manages canvas, context, layers, and animation timeline
|
|
8
|
+
*/
|
|
9
|
+
export declare class Scene {
|
|
10
|
+
readonly lazyCanvas: LazyCanvas;
|
|
11
|
+
private allLayers;
|
|
12
|
+
private scheduler;
|
|
13
|
+
private lastFrameTime;
|
|
14
|
+
/**
|
|
15
|
+
* Create a new Scene
|
|
16
|
+
* @param width - Canvas width in pixels
|
|
17
|
+
* @param height - Canvas height in pixels
|
|
18
|
+
*/
|
|
19
|
+
constructor(width: number, height: number);
|
|
20
|
+
/**
|
|
21
|
+
* Load a layer tree created via JSX
|
|
22
|
+
* Registers all layers with IDs into the manager
|
|
23
|
+
* @param tree - Root layer or group
|
|
24
|
+
*/
|
|
25
|
+
load(tree: AnyLayer | Div): void;
|
|
26
|
+
/**
|
|
27
|
+
* Render a single frame at the given time
|
|
28
|
+
* @param time - Time in seconds
|
|
29
|
+
*/
|
|
30
|
+
renderFrame(time: number): Promise<void>;
|
|
31
|
+
renderFirstFrame(): Promise<Canvas>;
|
|
32
|
+
/**
|
|
33
|
+
* Get current frame as ImageData (Uint8ClampedArray)
|
|
34
|
+
* Much faster than encoding to PNG
|
|
35
|
+
*/
|
|
36
|
+
getImageData(): Uint8ClampedArray;
|
|
37
|
+
/**
|
|
38
|
+
* Get canvas width
|
|
39
|
+
*/
|
|
40
|
+
get width(): number;
|
|
41
|
+
/**
|
|
42
|
+
* Get canvas height
|
|
43
|
+
*/
|
|
44
|
+
get height(): number;
|
|
45
|
+
/**
|
|
46
|
+
* Update state for all layers that have signals
|
|
47
|
+
* @param time - Current time in seconds
|
|
48
|
+
*/
|
|
49
|
+
private updateAllStates;
|
|
50
|
+
/**
|
|
51
|
+
* Draw a layer or group
|
|
52
|
+
* @param layer - Layer to draw
|
|
53
|
+
*/
|
|
54
|
+
private drawLayer;
|
|
55
|
+
/**
|
|
56
|
+
* Render multiple frames and return as animation
|
|
57
|
+
* @param startTime - Start time in seconds
|
|
58
|
+
* @param endTime - End time in seconds
|
|
59
|
+
* @param fps - Frames per second (default: 30)
|
|
60
|
+
* @returns Array of PNG buffers for each frame
|
|
61
|
+
*/
|
|
62
|
+
renderAnimation(startTime: number, endTime: number, fps?: number): Promise<Buffer[]>;
|
|
63
|
+
/**
|
|
64
|
+
* Render multiple frames and return as ImageData array (much faster)
|
|
65
|
+
* @param startTime - Start time in seconds
|
|
66
|
+
* @param endTime - End time in seconds
|
|
67
|
+
* @param fps - Frames per second (default: 30)
|
|
68
|
+
* @returns Array of Uint8ClampedArray for each frame
|
|
69
|
+
*/
|
|
70
|
+
renderAnimationData(startTime: number, endTime: number, fps?: number): Promise<Uint8ClampedArray[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Get a layer by ID from the manager
|
|
73
|
+
* @param id - Layer ID
|
|
74
|
+
* @returns Layer or undefined
|
|
75
|
+
*/
|
|
76
|
+
getLayer(id: string): AnyLayer | Div | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Add animation generator to scheduler
|
|
79
|
+
* @param generator - Animation generator to run
|
|
80
|
+
*/
|
|
81
|
+
addAnimation(generator: ThreadGenerator): void;
|
|
82
|
+
/**
|
|
83
|
+
* Play animation on a signal
|
|
84
|
+
* @param signal - Signal to animate
|
|
85
|
+
* @param generator - Animation generator
|
|
86
|
+
*/
|
|
87
|
+
playAnimation<T>(signal: Signal<T>, generator: ThreadGenerator): void;
|
|
88
|
+
/**
|
|
89
|
+
* Clear all animations
|
|
90
|
+
*/
|
|
91
|
+
clearAnimations(): void;
|
|
92
|
+
/**
|
|
93
|
+
* Reset scene timeline
|
|
94
|
+
*/
|
|
95
|
+
resetTimeline(): void;
|
|
96
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Scene = void 0;
|
|
4
|
+
const ThreadScheduler_1 = require("./ThreadScheduler");
|
|
5
|
+
const LazyCanvas_1 = require("../structures/LazyCanvas");
|
|
6
|
+
/**
|
|
7
|
+
* Scene class - manages canvas, context, layers, and animation timeline
|
|
8
|
+
*/
|
|
9
|
+
class Scene {
|
|
10
|
+
/**
|
|
11
|
+
* Create a new Scene
|
|
12
|
+
* @param width - Canvas width in pixels
|
|
13
|
+
* @param height - Canvas height in pixels
|
|
14
|
+
*/
|
|
15
|
+
constructor(width, height) {
|
|
16
|
+
this.allLayers = [];
|
|
17
|
+
this.scheduler = new ThreadScheduler_1.ThreadScheduler();
|
|
18
|
+
this.lastFrameTime = 0;
|
|
19
|
+
this.lazyCanvas = new LazyCanvas_1.LazyCanvas().create(width, height);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Load a layer tree created via JSX
|
|
23
|
+
* Registers all layers with IDs into the manager
|
|
24
|
+
* @param tree - Root layer or group
|
|
25
|
+
*/
|
|
26
|
+
load(tree) {
|
|
27
|
+
this.lazyCanvas.manager.layers.add(tree);
|
|
28
|
+
this.allLayers = this.lazyCanvas.manager.layers.toArray();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Render a single frame at the given time
|
|
32
|
+
* @param time - Time in seconds
|
|
33
|
+
*/
|
|
34
|
+
async renderFrame(time) {
|
|
35
|
+
if (this.lazyCanvas.manager.layers.size() === 0) {
|
|
36
|
+
throw new Error("Scene: No root layer loaded. Call scene.load(tree) first.");
|
|
37
|
+
}
|
|
38
|
+
// 1. Update scheduler with current time
|
|
39
|
+
this.scheduler.update(time);
|
|
40
|
+
// 2. Clear canvas
|
|
41
|
+
this.lazyCanvas.ctx.clearRect(0, 0, this.lazyCanvas.canvas.width, this.lazyCanvas.canvas.height);
|
|
42
|
+
// 3. PHASE 1: Update all layer states from signals
|
|
43
|
+
this.updateAllStates(time);
|
|
44
|
+
// 4. PHASE 2: Draw the layer tree
|
|
45
|
+
await this.drawLayer(this.lazyCanvas.manager.layers.toArray()[0]);
|
|
46
|
+
this.lastFrameTime = time;
|
|
47
|
+
}
|
|
48
|
+
async renderFirstFrame() {
|
|
49
|
+
await this.renderFrame(0);
|
|
50
|
+
return this.lazyCanvas.canvas;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get current frame as ImageData (Uint8ClampedArray)
|
|
54
|
+
* Much faster than encoding to PNG
|
|
55
|
+
*/
|
|
56
|
+
getImageData() {
|
|
57
|
+
const imageData = this.lazyCanvas.ctx.getImageData(0, 0, this.width, this.height);
|
|
58
|
+
return imageData.data;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get canvas width
|
|
62
|
+
*/
|
|
63
|
+
get width() {
|
|
64
|
+
return this.lazyCanvas.canvas.width;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get canvas height
|
|
68
|
+
*/
|
|
69
|
+
get height() {
|
|
70
|
+
return this.lazyCanvas.canvas.height;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Update state for all layers that have signals
|
|
74
|
+
* @param time - Current time in seconds
|
|
75
|
+
*/
|
|
76
|
+
updateAllStates(time) {
|
|
77
|
+
for (const layer of this.allLayers) {
|
|
78
|
+
// Check if layer has updateState method (added by BaseLayer)
|
|
79
|
+
if ("updateState" in layer && typeof layer.updateState === "function") {
|
|
80
|
+
layer.updateState(time);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Draw a layer or group
|
|
86
|
+
* @param layer - Layer to draw
|
|
87
|
+
*/
|
|
88
|
+
async drawLayer(layer) {
|
|
89
|
+
if (!layer.visible)
|
|
90
|
+
return;
|
|
91
|
+
// Set global composite operation if present
|
|
92
|
+
if ("props" in layer && layer.props?.globalComposite) {
|
|
93
|
+
this.lazyCanvas.ctx.globalCompositeOperation = layer.props.globalComposite;
|
|
94
|
+
}
|
|
95
|
+
// Draw the layer
|
|
96
|
+
if ("draw" in layer && typeof layer.draw === "function") {
|
|
97
|
+
await layer.draw(this.lazyCanvas.ctx, this.lazyCanvas.canvas, this.lazyCanvas.manager.layers, false);
|
|
98
|
+
}
|
|
99
|
+
// Reset shadow after drawing
|
|
100
|
+
this.lazyCanvas.ctx.shadowColor = "transparent";
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Render multiple frames and return as animation
|
|
104
|
+
* @param startTime - Start time in seconds
|
|
105
|
+
* @param endTime - End time in seconds
|
|
106
|
+
* @param fps - Frames per second (default: 30)
|
|
107
|
+
* @returns Array of PNG buffers for each frame
|
|
108
|
+
*/
|
|
109
|
+
async renderAnimation(startTime, endTime, fps = 30) {
|
|
110
|
+
const frames = [];
|
|
111
|
+
const frameDuration = 1 / fps;
|
|
112
|
+
for (let time = startTime; time <= endTime; time += frameDuration) {
|
|
113
|
+
await this.renderFrame(time);
|
|
114
|
+
frames.push(await this.lazyCanvas.canvas.encode("png"));
|
|
115
|
+
}
|
|
116
|
+
return frames;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Render multiple frames and return as ImageData array (much faster)
|
|
120
|
+
* @param startTime - Start time in seconds
|
|
121
|
+
* @param endTime - End time in seconds
|
|
122
|
+
* @param fps - Frames per second (default: 30)
|
|
123
|
+
* @returns Array of Uint8ClampedArray for each frame
|
|
124
|
+
*/
|
|
125
|
+
async renderAnimationData(startTime, endTime, fps = 30) {
|
|
126
|
+
const frames = [];
|
|
127
|
+
const frameDuration = 1 / fps;
|
|
128
|
+
for (let time = startTime; time <= endTime; time += frameDuration) {
|
|
129
|
+
await this.renderFrame(time);
|
|
130
|
+
frames.push(this.getImageData());
|
|
131
|
+
}
|
|
132
|
+
return frames;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get a layer by ID from the manager
|
|
136
|
+
* @param id - Layer ID
|
|
137
|
+
* @returns Layer or undefined
|
|
138
|
+
*/
|
|
139
|
+
getLayer(id) {
|
|
140
|
+
return this.lazyCanvas.manager.layers.get(id, true);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Add animation generator to scheduler
|
|
144
|
+
* @param generator - Animation generator to run
|
|
145
|
+
*/
|
|
146
|
+
addAnimation(generator) {
|
|
147
|
+
this.scheduler.add(generator);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Play animation on a signal
|
|
151
|
+
* @param signal - Signal to animate
|
|
152
|
+
* @param generator - Animation generator
|
|
153
|
+
*/
|
|
154
|
+
playAnimation(signal, generator) {
|
|
155
|
+
signal.run(generator);
|
|
156
|
+
this.scheduler.add(generator);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Clear all animations
|
|
160
|
+
*/
|
|
161
|
+
clearAnimations() {
|
|
162
|
+
this.scheduler.clear();
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Reset scene timeline
|
|
166
|
+
*/
|
|
167
|
+
resetTimeline() {
|
|
168
|
+
this.scheduler.reset();
|
|
169
|
+
this.lastFrameTime = 0;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
exports.Scene = Scene;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Interpolator } from "./Interpolation";
|
|
2
|
+
/**
|
|
3
|
+
* Easing function type
|
|
4
|
+
*/
|
|
5
|
+
export type EasingFunction = (t: number) => number;
|
|
6
|
+
/**
|
|
7
|
+
* Standard easing functions
|
|
8
|
+
*/
|
|
9
|
+
export declare const Easing: {
|
|
10
|
+
linear: (t: number) => number;
|
|
11
|
+
easeIn: (t: number) => number;
|
|
12
|
+
easeOut: (t: number) => number;
|
|
13
|
+
easeInOut: (t: number) => number;
|
|
14
|
+
easeInCubic: (t: number) => number;
|
|
15
|
+
easeOutCubic: (t: number) => number;
|
|
16
|
+
easeInOutCubic: (t: number) => number;
|
|
17
|
+
easeInQuart: (t: number) => number;
|
|
18
|
+
easeOutQuart: (t: number) => number;
|
|
19
|
+
easeInOutQuart: (t: number) => number;
|
|
20
|
+
easeInQuint: (t: number) => number;
|
|
21
|
+
easeOutQuint: (t: number) => number;
|
|
22
|
+
easeInOutQuint: (t: number) => number;
|
|
23
|
+
easeInSine: (t: number) => number;
|
|
24
|
+
easeOutSine: (t: number) => number;
|
|
25
|
+
easeInOutSine: (t: number) => number;
|
|
26
|
+
easeInExpo: (t: number) => number;
|
|
27
|
+
easeOutExpo: (t: number) => number;
|
|
28
|
+
easeInOutExpo: (t: number) => number;
|
|
29
|
+
easeInCirc: (t: number) => number;
|
|
30
|
+
easeOutCirc: (t: number) => number;
|
|
31
|
+
easeInOutCirc: (t: number) => number;
|
|
32
|
+
easeInBack: (t: number) => number;
|
|
33
|
+
easeOutBack: (t: number) => number;
|
|
34
|
+
easeInOutBack: (t: number) => number;
|
|
35
|
+
easeInElastic: (t: number) => number;
|
|
36
|
+
easeOutElastic: (t: number) => number;
|
|
37
|
+
easeInOutElastic: (t: number) => number;
|
|
38
|
+
easeInBounce: (t: number) => number;
|
|
39
|
+
easeOutBounce: (t: number) => number;
|
|
40
|
+
easeInOutBounce: (t: number) => number;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Thread generator type - the core of animation system
|
|
44
|
+
*/
|
|
45
|
+
export type ThreadGenerator = Generator<void | number, void, number | void>;
|
|
46
|
+
/**
|
|
47
|
+
* Tween configuration
|
|
48
|
+
*/
|
|
49
|
+
export interface TweenConfig<T> {
|
|
50
|
+
to: T;
|
|
51
|
+
duration: number;
|
|
52
|
+
easing?: EasingFunction;
|
|
53
|
+
interpolator?: Interpolator<T>;
|
|
54
|
+
colorSpace?: "rgb" | "hsl";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Signal options
|
|
58
|
+
*/
|
|
59
|
+
export interface SignalOptions<T> {
|
|
60
|
+
interpolator?: Interpolator<T>;
|
|
61
|
+
colorSpace?: "rgb" | "hsl";
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Signal class - reactive value that can be animated
|
|
65
|
+
*/
|
|
66
|
+
export declare class Signal<T = number> {
|
|
67
|
+
private _value;
|
|
68
|
+
private _initialValue;
|
|
69
|
+
private _currentThread;
|
|
70
|
+
private _interpolator;
|
|
71
|
+
private _colorSpace;
|
|
72
|
+
constructor(initial: T, options?: SignalOptions<T>);
|
|
73
|
+
/**
|
|
74
|
+
* Get current value at specific time (backward compatibility)
|
|
75
|
+
*/
|
|
76
|
+
get(time: number): T;
|
|
77
|
+
/**
|
|
78
|
+
* Get current value (sync)
|
|
79
|
+
*/
|
|
80
|
+
value(): T;
|
|
81
|
+
/**
|
|
82
|
+
* Set value directly
|
|
83
|
+
*/
|
|
84
|
+
set(value: T): void;
|
|
85
|
+
/**
|
|
86
|
+
* Reset to initial value
|
|
87
|
+
*/
|
|
88
|
+
reset(): void;
|
|
89
|
+
/**
|
|
90
|
+
* Animate to a new value - returns a generator
|
|
91
|
+
*/
|
|
92
|
+
to(value: T, duration: number, config?: Partial<TweenConfig<T>> & {
|
|
93
|
+
easing?: EasingFunction;
|
|
94
|
+
}): ThreadGenerator;
|
|
95
|
+
/**
|
|
96
|
+
* Wait for a duration without changing value
|
|
97
|
+
*/
|
|
98
|
+
wait(duration: number): ThreadGenerator;
|
|
99
|
+
/**
|
|
100
|
+
* Run animation from generator
|
|
101
|
+
*/
|
|
102
|
+
run(generator: ThreadGenerator): this;
|
|
103
|
+
/**
|
|
104
|
+
* Update signal (called by scheduler)
|
|
105
|
+
*/
|
|
106
|
+
update(delta: number): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Check if signal has active animation
|
|
109
|
+
*/
|
|
110
|
+
isAnimating(): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Internal: get interpolator for values
|
|
113
|
+
*/
|
|
114
|
+
private _getInterpolator;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Create a new signal
|
|
118
|
+
*/
|
|
119
|
+
export declare function createSignal<T = number>(initial: T, options?: SignalOptions<T>): Signal<T>;
|
|
120
|
+
/**
|
|
121
|
+
* Type guard for signals
|
|
122
|
+
*/
|
|
123
|
+
export declare function isSignal<T>(value: any): value is Signal<T>;
|
|
124
|
+
/**
|
|
125
|
+
* Unwrap signal or return value
|
|
126
|
+
*/
|
|
127
|
+
export declare function unwrap<T>(value: T | Signal<T>): T extends Signal<infer U> ? U : T;
|
|
128
|
+
/**
|
|
129
|
+
* Reset multiple signals to their initial values
|
|
130
|
+
*/
|
|
131
|
+
export declare function resetSignals(...signals: Signal<any>[]): void;
|
|
132
|
+
export type SignalValue<T> = T | Signal<T>;
|
|
133
|
+
export default Signal;
|