canvasengine 2.0.0-beta.3 → 2.0.0-beta.4
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/dist/index.d.ts +169 -5
- package/dist/index.js +101 -10
- package/dist/index.js.map +1 -1
- package/index.d.ts +4 -0
- package/package.json +1 -1
- package/src/engine/reactive.ts +17 -0
- package/src/engine/trigger.ts +65 -9
- package/src/engine/utils.ts +84 -8
- package/src/index.ts +2 -1
- package/src/utils/RadialGradient.ts +29 -0
package/index.d.ts
ADDED
package/package.json
CHANGED
package/src/engine/reactive.ts
CHANGED
|
@@ -201,6 +201,23 @@ export function createComponent(tag: string, props?: Props): Element {
|
|
|
201
201
|
if (props?.isRoot) {
|
|
202
202
|
// propagate recrusively context in all children
|
|
203
203
|
const propagateContext = async (element) => {
|
|
204
|
+
if (element.props.attach) {
|
|
205
|
+
const isReactiveAttach = isSignal(element.propObservables?.attach)
|
|
206
|
+
if (!isReactiveAttach) {
|
|
207
|
+
element.props.children.push(element.props.attach)
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
let lastElement = null
|
|
211
|
+
element.propObservables.attach.observable.subscribe(({ value, type }) => {
|
|
212
|
+
if (type != "init") {
|
|
213
|
+
destroyElement(lastElement)
|
|
214
|
+
}
|
|
215
|
+
lastElement = value
|
|
216
|
+
onMount(element, value);
|
|
217
|
+
propagateContext(value);
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
}
|
|
204
221
|
if (!element.props.children) {
|
|
205
222
|
return;
|
|
206
223
|
}
|
package/src/engine/trigger.ts
CHANGED
|
@@ -2,39 +2,95 @@ import { effect, signal } from "@signe/reactive";
|
|
|
2
2
|
|
|
3
3
|
interface Listen<T = any> {
|
|
4
4
|
config: T | undefined;
|
|
5
|
-
seed:
|
|
5
|
+
seed: {
|
|
6
|
+
config: T | undefined;
|
|
7
|
+
value: number;
|
|
8
|
+
resolve: (value: any) => void;
|
|
9
|
+
};
|
|
6
10
|
}
|
|
7
11
|
|
|
8
12
|
interface Trigger<T = any> {
|
|
9
|
-
start: () => void
|
|
13
|
+
start: () => Promise<void>;
|
|
10
14
|
listen: () => Listen<T> | undefined;
|
|
11
15
|
}
|
|
12
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Checks if the given argument is a Trigger object
|
|
19
|
+
* @param arg - The value to check
|
|
20
|
+
* @returns True if the argument is a Trigger object
|
|
21
|
+
*/
|
|
13
22
|
export function isTrigger(arg: any): arg is Trigger<any> {
|
|
14
23
|
return arg?.start && arg?.listen;
|
|
15
24
|
}
|
|
16
25
|
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Creates a new trigger that can be used to pass data between components
|
|
28
|
+
* @param globalConfig - Optional configuration data to be passed when the trigger is activated
|
|
29
|
+
* @returns A Trigger object with start and listen methods
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const myTrigger = trigger()
|
|
33
|
+
*
|
|
34
|
+
* on(myTrigger, (data) => {
|
|
35
|
+
* console.log('Triggered with data:', data)
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* myTrigger.start({ message: 'Hello' })
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function trigger<T = any>(globalConfig?: T): Trigger<T> {
|
|
42
|
+
const _signal = signal({
|
|
43
|
+
config: globalConfig,
|
|
44
|
+
value: 0,
|
|
45
|
+
resolve: (value: any) => void 0,
|
|
46
|
+
});
|
|
19
47
|
return {
|
|
20
|
-
start: () => {
|
|
21
|
-
|
|
48
|
+
start: (config?: T) => {
|
|
49
|
+
return new Promise((resolve: (value: any) => void) => {
|
|
50
|
+
_signal.set({
|
|
51
|
+
config: {
|
|
52
|
+
...globalConfig,
|
|
53
|
+
...config,
|
|
54
|
+
},
|
|
55
|
+
resolve,
|
|
56
|
+
value: Math.random(),
|
|
57
|
+
});
|
|
58
|
+
});
|
|
22
59
|
},
|
|
23
60
|
listen: (): Listen<T> | undefined => {
|
|
24
61
|
return {
|
|
25
|
-
config,
|
|
62
|
+
config: globalConfig,
|
|
26
63
|
seed: _signal(),
|
|
27
64
|
};
|
|
28
65
|
},
|
|
29
66
|
};
|
|
30
67
|
}
|
|
31
68
|
|
|
32
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Subscribes to a trigger and executes a callback when the trigger is activated
|
|
71
|
+
* @param triggerSignal - The trigger to subscribe to
|
|
72
|
+
* @param callback - Function to execute when the trigger is activated
|
|
73
|
+
* @throws Error if triggerSignal is not a valid trigger
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* const click = trigger()
|
|
77
|
+
*
|
|
78
|
+
* on(click, () => {
|
|
79
|
+
* console.log('Click triggered')
|
|
80
|
+
* })
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export function on(triggerSignal: any, callback: (config: any) => void | Promise<void>) {
|
|
33
84
|
if (!isTrigger(triggerSignal)) {
|
|
34
85
|
throw new Error("In 'on(arg)' must have a trigger signal type");
|
|
35
86
|
}
|
|
36
87
|
effect(() => {
|
|
37
88
|
const result = triggerSignal.listen();
|
|
38
|
-
if (result?.seed)
|
|
89
|
+
if (result?.seed.value) {
|
|
90
|
+
const ret = callback(result?.seed.config);
|
|
91
|
+
if (ret && typeof ret.then === 'function') {
|
|
92
|
+
ret.then(result?.seed.resolve);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
39
95
|
});
|
|
40
96
|
}
|
package/src/engine/utils.ts
CHANGED
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
import { ObservablePoint } from "pixi.js"
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Checks if code is running in a browser environment
|
|
5
|
+
* @returns {boolean} True if running in browser, false otherwise
|
|
6
|
+
*/
|
|
3
7
|
export function isBrowser(): boolean {
|
|
4
8
|
return typeof window !== 'undefined'
|
|
5
9
|
}
|
|
6
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Returns current high-resolution timestamp
|
|
13
|
+
* @returns {number} Current time in milliseconds
|
|
14
|
+
*/
|
|
7
15
|
export function preciseNow(): number {
|
|
8
16
|
return typeof performance !== 'undefined' ? performance.now() : Date.now()
|
|
9
17
|
}
|
|
10
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Converts frames per second to milliseconds
|
|
21
|
+
* @param {number} fps - Frames per second
|
|
22
|
+
* @returns {number} Milliseconds per frame
|
|
23
|
+
*/
|
|
11
24
|
export function fps2ms(fps: number): number {
|
|
12
25
|
return 1000 / fps
|
|
13
26
|
}
|
|
14
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Checks if a value is a Promise
|
|
30
|
+
* @param {any} value - Value to check
|
|
31
|
+
* @returns {boolean} True if value is a Promise, false otherwise
|
|
32
|
+
*/
|
|
15
33
|
export function isPromise(value: any): boolean {
|
|
16
34
|
return value && value instanceof Promise
|
|
17
35
|
}
|
|
@@ -58,15 +76,38 @@ function deepEquals(a: any, b: any): boolean {
|
|
|
58
76
|
return false;
|
|
59
77
|
}
|
|
60
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Checks if a value is a function
|
|
81
|
+
* @param {unknown} val - Value to check
|
|
82
|
+
* @returns {boolean} True if value is a function, false otherwise
|
|
83
|
+
*/
|
|
61
84
|
export function isFunction(val: unknown): boolean {
|
|
62
85
|
return {}.toString.call(val) === '[object Function]'
|
|
63
86
|
}
|
|
64
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Checks if a value is a plain object
|
|
90
|
+
* @param {unknown} val - Value to check
|
|
91
|
+
* @returns {boolean} True if value is an object (not null and not array), false otherwise
|
|
92
|
+
*/
|
|
65
93
|
export function isObject(val: unknown): boolean {
|
|
66
94
|
return typeof val == 'object' && val != null && !Array.isArray(val)
|
|
67
95
|
}
|
|
68
96
|
|
|
69
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Sets a value in an object using a dot notation path
|
|
99
|
+
* @param {Record<string, any>} obj - Target object
|
|
100
|
+
* @param {string | string[]} path - Path to set value at (e.g., 'a.b.c' or ['a', 'b', 'c'])
|
|
101
|
+
* @param {any} value - Value to set
|
|
102
|
+
* @param {boolean} onlyPlainObject - If true, only creates plain objects in path
|
|
103
|
+
* @returns {Record<string, any>} Modified object
|
|
104
|
+
*/
|
|
105
|
+
export function set(
|
|
106
|
+
obj: Record<string, any>,
|
|
107
|
+
path: string | string[],
|
|
108
|
+
value: any,
|
|
109
|
+
onlyPlainObject = false
|
|
110
|
+
): Record<string, any> {
|
|
70
111
|
if (Object(obj) !== obj) return obj;
|
|
71
112
|
|
|
72
113
|
if (typeof path === 'string') {
|
|
@@ -79,8 +120,8 @@ export function set(obj, path, value, onlyPlainObject = false) {
|
|
|
79
120
|
let current = obj;
|
|
80
121
|
for (let i = 0; i < len - 1; i++) {
|
|
81
122
|
let segment = path[i];
|
|
82
|
-
let nextSegment = path[i + 1];
|
|
83
|
-
let isNextNumeric = !isNaN(nextSegment) && isFinite(nextSegment);
|
|
123
|
+
let nextSegment: number | string = path[i + 1];
|
|
124
|
+
let isNextNumeric = !isNaN(Number(nextSegment)) && isFinite(Number(nextSegment));
|
|
84
125
|
|
|
85
126
|
if (!current[segment] || typeof current[segment] !== 'object') {
|
|
86
127
|
current[segment] = (isNextNumeric && !onlyPlainObject) ? [] : {};
|
|
@@ -94,7 +135,13 @@ export function set(obj, path, value, onlyPlainObject = false) {
|
|
|
94
135
|
return obj;
|
|
95
136
|
}
|
|
96
137
|
|
|
97
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Gets a value from an object using a dot notation path
|
|
140
|
+
* @param {Record<string, any>} obj - Source object
|
|
141
|
+
* @param {string} path - Path to get value from (e.g., 'a.b.c')
|
|
142
|
+
* @returns {any} Value at path or undefined if not found
|
|
143
|
+
*/
|
|
144
|
+
export function get(obj: Record<string, any>, path: string): any {
|
|
98
145
|
const keys = path.split('.');
|
|
99
146
|
let current = obj;
|
|
100
147
|
|
|
@@ -108,15 +155,31 @@ export function get(obj, path) {
|
|
|
108
155
|
return current;
|
|
109
156
|
}
|
|
110
157
|
|
|
111
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Logs a message to console
|
|
160
|
+
* @param {any} text - Message to log
|
|
161
|
+
*/
|
|
162
|
+
export function log(text: any): void {
|
|
112
163
|
console.log(text)
|
|
113
164
|
}
|
|
114
165
|
|
|
115
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Logs an error message to console
|
|
168
|
+
* @param {any} text - Error message to log
|
|
169
|
+
*/
|
|
170
|
+
export function error(text: any): void {
|
|
116
171
|
console.error(text)
|
|
117
172
|
}
|
|
118
173
|
|
|
119
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Sets the position of an ObservablePoint using various input formats
|
|
176
|
+
* @param {ObservablePoint} observablePoint - The point to modify
|
|
177
|
+
* @param {Object | number | [number, number]} point - New position value
|
|
178
|
+
*/
|
|
179
|
+
export function setObservablePoint(
|
|
180
|
+
observablePoint: ObservablePoint,
|
|
181
|
+
point: { x: number, y: number } | number | [number, number]
|
|
182
|
+
): void {
|
|
120
183
|
if (typeof point === 'number') {
|
|
121
184
|
observablePoint.set(point);
|
|
122
185
|
}
|
|
@@ -128,7 +191,20 @@ export function setObservablePoint(observablePoint: ObservablePoint, point: { x:
|
|
|
128
191
|
}
|
|
129
192
|
}
|
|
130
193
|
|
|
131
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Calculates the Euclidean distance between two points
|
|
196
|
+
* @param {number} x1 - X coordinate of first point
|
|
197
|
+
* @param {number} y1 - Y coordinate of first point
|
|
198
|
+
* @param {number} x2 - X coordinate of second point
|
|
199
|
+
* @param {number} y2 - Y coordinate of second point
|
|
200
|
+
* @returns {number} Distance between the points
|
|
201
|
+
*/
|
|
202
|
+
export function calculateDistance(
|
|
203
|
+
x1: number,
|
|
204
|
+
y1: number,
|
|
205
|
+
x2: number,
|
|
206
|
+
y2: number
|
|
207
|
+
): number {
|
|
132
208
|
const dx = x1 - x2;
|
|
133
209
|
const dy = y1 - y2;
|
|
134
210
|
return Math.sqrt(dx * dx + dy * dy);
|
package/src/index.ts
CHANGED
|
@@ -11,4 +11,5 @@ export { useProps, useDefineProps } from './hooks/useProps'
|
|
|
11
11
|
export * from './utils/Ease'
|
|
12
12
|
export * from './utils/RadialGradient'
|
|
13
13
|
export * from './components/DisplayObject'
|
|
14
|
-
export { isObservable } from 'rxjs'
|
|
14
|
+
export { isObservable } from 'rxjs'
|
|
15
|
+
export * as Utils from './engine/utils'
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { Texture, ImageSource, DOMAdapter, Matrix } from "pixi.js";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Creates a radial gradient texture that can be used in PixiJS.
|
|
5
|
+
* @example
|
|
6
|
+
* const gradient = new RadialGradient(size, size, 0, size, size, 0);
|
|
7
|
+
* gradient.addColorStop(0, "rgba(255, 255, 0, 1)");
|
|
8
|
+
* gradient.addColorStop(0.5, "rgba(255, 255, 0, 0.3)");
|
|
9
|
+
* gradient.addColorStop(0.8, "rgba(255, 255, 0, 0)");
|
|
10
|
+
*/
|
|
3
11
|
export class RadialGradient {
|
|
4
12
|
private canvas: HTMLCanvasElement;
|
|
5
13
|
private ctx: CanvasRenderingContext2D | null;
|
|
@@ -9,6 +17,16 @@ export class RadialGradient {
|
|
|
9
17
|
|
|
10
18
|
public size = 600;
|
|
11
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new RadialGradient instance
|
|
22
|
+
* @param x0 - The x-coordinate of the starting circle
|
|
23
|
+
* @param y0 - The y-coordinate of the starting circle
|
|
24
|
+
* @param x1 - The x-coordinate of the ending circle
|
|
25
|
+
* @param y1 - The y-coordinate of the ending circle
|
|
26
|
+
* @param x2 - The x-coordinate for gradient transformation
|
|
27
|
+
* @param y2 - The y-coordinate for gradient transformation
|
|
28
|
+
* @param focalPoint - The focal point of the gradient (0-1), defaults to 0
|
|
29
|
+
*/
|
|
12
30
|
constructor(
|
|
13
31
|
private x0: number,
|
|
14
32
|
private y0: number,
|
|
@@ -38,12 +56,23 @@ export class RadialGradient {
|
|
|
38
56
|
}
|
|
39
57
|
}
|
|
40
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Adds a color stop to the gradient
|
|
61
|
+
* @param offset - The position of the color stop (0-1)
|
|
62
|
+
* @param color - The color value (any valid CSS color string)
|
|
63
|
+
*/
|
|
41
64
|
addColorStop(offset: number, color: string) {
|
|
42
65
|
if (this.gradient) {
|
|
43
66
|
this.gradient.addColorStop(offset, color);
|
|
44
67
|
}
|
|
45
68
|
}
|
|
46
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Renders the gradient and returns the texture with its transformation matrix
|
|
72
|
+
* @param options - Render options
|
|
73
|
+
* @param options.translate - Optional translation coordinates
|
|
74
|
+
* @returns Object containing the texture and transformation matrix
|
|
75
|
+
*/
|
|
47
76
|
render({ translate }: { translate?: { x: number; y: number } } = {}) {
|
|
48
77
|
const { x0, y0, x1, y1, x2, y2, focalPoint } = this;
|
|
49
78
|
const defaultSize = this.size;
|