canvasengine 2.0.0-beta.4 → 2.0.0-beta.41
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/DebugRenderer-BxfW34YG.js +172 -0
- package/dist/DebugRenderer-BxfW34YG.js.map +1 -0
- package/dist/components/Button.d.ts +183 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Canvas.d.ts +18 -0
- package/dist/components/Canvas.d.ts.map +1 -0
- package/dist/components/DOMElement.d.ts +44 -0
- package/dist/components/DOMElement.d.ts.map +1 -0
- package/dist/components/Graphic.d.ts +65 -0
- package/dist/components/Graphic.d.ts.map +1 -0
- package/dist/components/Joystick.d.ts +36 -0
- package/dist/components/Joystick.d.ts.map +1 -0
- package/dist/components/NineSliceSprite.d.ts +17 -0
- package/dist/components/NineSliceSprite.d.ts.map +1 -0
- package/dist/components/ParticleEmitter.d.ts +5 -0
- package/dist/components/ParticleEmitter.d.ts.map +1 -0
- package/dist/components/Scene.d.ts +2 -0
- package/dist/components/Scene.d.ts.map +1 -0
- package/dist/components/Text.d.ts +26 -0
- package/dist/components/Text.d.ts.map +1 -0
- package/dist/components/TilingSprite.d.ts +18 -0
- package/dist/components/TilingSprite.d.ts.map +1 -0
- package/dist/components/Video.d.ts +15 -0
- package/dist/components/Video.d.ts.map +1 -0
- package/dist/components/index.d.ts +18 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/types/DisplayObject.d.ts +110 -0
- package/dist/components/types/DisplayObject.d.ts.map +1 -0
- package/dist/components/types/MouseEvent.d.ts +4 -0
- package/dist/components/types/MouseEvent.d.ts.map +1 -0
- package/dist/components/types/Spritesheet.d.ts +248 -0
- package/dist/components/types/Spritesheet.d.ts.map +1 -0
- package/dist/components/types/index.d.ts +5 -0
- package/dist/components/types/index.d.ts.map +1 -0
- package/dist/directives/Controls.d.ts +113 -0
- package/dist/directives/Controls.d.ts.map +1 -0
- package/dist/directives/ControlsBase.d.ts +198 -0
- package/dist/directives/ControlsBase.d.ts.map +1 -0
- package/dist/directives/Drag.d.ts +70 -0
- package/dist/directives/Drag.d.ts.map +1 -0
- package/dist/directives/Flash.d.ts +117 -0
- package/dist/directives/Flash.d.ts.map +1 -0
- package/dist/directives/GamepadControls.d.ts +225 -0
- package/dist/directives/GamepadControls.d.ts.map +1 -0
- package/dist/directives/JoystickControls.d.ts +172 -0
- package/dist/directives/JoystickControls.d.ts.map +1 -0
- package/dist/directives/KeyboardControls.d.ts +219 -0
- package/dist/directives/KeyboardControls.d.ts.map +1 -0
- package/dist/directives/Scheduler.d.ts +36 -0
- package/dist/directives/Scheduler.d.ts.map +1 -0
- package/dist/directives/Shake.d.ts +98 -0
- package/dist/directives/Shake.d.ts.map +1 -0
- package/dist/directives/Sound.d.ts +26 -0
- package/dist/directives/Sound.d.ts.map +1 -0
- package/dist/directives/Transition.d.ts +11 -0
- package/dist/directives/Transition.d.ts.map +1 -0
- package/dist/directives/ViewportCull.d.ts +12 -0
- package/dist/directives/ViewportCull.d.ts.map +1 -0
- package/dist/directives/ViewportFollow.d.ts +19 -0
- package/dist/directives/ViewportFollow.d.ts.map +1 -0
- package/dist/directives/index.d.ts +13 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/engine/animation.d.ts +73 -0
- package/dist/engine/animation.d.ts.map +1 -0
- package/dist/engine/bootstrap.d.ts +16 -0
- package/dist/engine/bootstrap.d.ts.map +1 -0
- package/dist/engine/directive.d.ts +14 -0
- package/dist/engine/directive.d.ts.map +1 -0
- package/dist/engine/reactive.d.ts +105 -0
- package/dist/engine/reactive.d.ts.map +1 -0
- package/dist/engine/signal.d.ts +72 -0
- package/dist/engine/signal.d.ts.map +1 -0
- package/dist/engine/trigger.d.ts +54 -0
- package/dist/engine/trigger.d.ts.map +1 -0
- package/dist/engine/utils.d.ts +90 -0
- package/dist/engine/utils.d.ts.map +1 -0
- package/dist/hooks/addContext.d.ts +2 -0
- package/dist/hooks/addContext.d.ts.map +1 -0
- package/dist/hooks/useProps.d.ts +42 -0
- package/dist/hooks/useProps.d.ts.map +1 -0
- package/dist/hooks/useRef.d.ts +5 -0
- package/dist/hooks/useRef.d.ts.map +1 -0
- package/dist/index-BnuKipxl.js +12568 -0
- package/dist/index-BnuKipxl.js.map +1 -0
- package/dist/index.d.ts +15 -1083
- package/dist/index.d.ts.map +1 -0
- package/dist/index.global.js +29 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +81 -3041
- package/dist/index.js.map +1 -1
- package/dist/utils/Ease.d.ts +17 -0
- package/dist/utils/Ease.d.ts.map +1 -0
- package/dist/utils/GlobalAssetLoader.d.ts +141 -0
- package/dist/utils/GlobalAssetLoader.d.ts.map +1 -0
- package/dist/utils/RadialGradient.d.ts +58 -0
- package/dist/utils/RadialGradient.d.ts.map +1 -0
- package/dist/utils/functions.d.ts +2 -0
- package/dist/utils/functions.d.ts.map +1 -0
- package/package.json +13 -7
- package/src/components/Button.ts +396 -0
- package/src/components/Canvas.ts +61 -45
- package/src/components/Container.ts +21 -2
- package/src/components/DOMContainer.ts +123 -0
- package/src/components/DOMElement.ts +421 -0
- package/src/components/DisplayObject.ts +350 -197
- package/src/components/Graphic.ts +200 -34
- package/src/components/Joystick.ts +363 -0
- package/src/components/Mesh.ts +222 -0
- package/src/components/NineSliceSprite.ts +4 -1
- package/src/components/ParticleEmitter.ts +12 -8
- package/src/components/Sprite.ts +306 -30
- package/src/components/Text.ts +125 -18
- package/src/components/Video.ts +110 -0
- package/src/components/Viewport.ts +59 -43
- package/src/components/index.ts +8 -2
- package/src/components/types/DisplayObject.ts +34 -0
- package/src/components/types/Spritesheet.ts +0 -118
- package/src/directives/Controls.ts +254 -0
- package/src/directives/ControlsBase.ts +266 -0
- package/src/directives/Drag.ts +357 -52
- package/src/directives/Flash.ts +419 -0
- package/src/directives/GamepadControls.ts +537 -0
- package/src/directives/JoystickControls.ts +396 -0
- package/src/directives/KeyboardControls.ts +66 -424
- package/src/directives/Shake.ts +295 -0
- package/src/directives/Sound.ts +94 -31
- package/src/directives/ViewportFollow.ts +35 -7
- package/src/directives/index.ts +12 -6
- package/src/engine/animation.ts +175 -21
- package/src/engine/bootstrap.ts +23 -3
- package/src/engine/directive.ts +2 -2
- package/src/engine/reactive.ts +780 -177
- package/src/engine/signal.ts +35 -4
- package/src/engine/trigger.ts +34 -7
- package/src/engine/utils.ts +19 -3
- package/src/hooks/useProps.ts +1 -1
- package/src/index.ts +4 -2
- package/src/utils/GlobalAssetLoader.ts +257 -0
- package/src/utils/functions.ts +7 -0
- package/testing/index.ts +12 -0
- package/tsconfig.json +17 -0
- package/vite.config.ts +39 -0
package/src/engine/signal.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
Observable,
|
|
3
|
+
Subscription
|
|
3
4
|
} from "rxjs";
|
|
4
5
|
import type { Element } from "./reactive";
|
|
6
|
+
import { isElementFrozen } from "./reactive";
|
|
5
7
|
import { Tick } from "../directives/Scheduler";
|
|
8
|
+
import { Container } from "../components";
|
|
6
9
|
|
|
7
10
|
type MountFunction = (fn: (element: Element) => void) => void;
|
|
8
11
|
|
|
@@ -53,7 +56,11 @@ export function tick(fn: (tickValue: Tick, element: Element) => void) {
|
|
|
53
56
|
let subscription: Subscription | undefined
|
|
54
57
|
if (context.tick) {
|
|
55
58
|
subscription = context.tick.observable.subscribe(({ value }) => {
|
|
56
|
-
|
|
59
|
+
// Block tick if element is frozen
|
|
60
|
+
if (isElementFrozen(el)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
fn(value, el)
|
|
57
64
|
})
|
|
58
65
|
}
|
|
59
66
|
return () => {
|
|
@@ -91,7 +98,7 @@ export function tick(fn: (tickValue: Tick, element: Element) => void) {
|
|
|
91
98
|
* ```
|
|
92
99
|
*/
|
|
93
100
|
export function h<C extends ComponentFunction<any>>(
|
|
94
|
-
componentFunction: C,
|
|
101
|
+
componentFunction: C | Element,
|
|
95
102
|
props: Parameters<C>[0] = {} as Parameters<C>[0],
|
|
96
103
|
...children: any[]
|
|
97
104
|
): ReturnType<C> {
|
|
@@ -110,7 +117,25 @@ export function h<C extends ComponentFunction<any>>(
|
|
|
110
117
|
children = children[0]
|
|
111
118
|
}
|
|
112
119
|
|
|
113
|
-
let component
|
|
120
|
+
let component: Element
|
|
121
|
+
|
|
122
|
+
if (Array.isArray(componentFunction)) {
|
|
123
|
+
if (componentFunction.length === 1) {
|
|
124
|
+
component = componentFunction[0]
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
component = h(Container, {}, ...componentFunction) as Element
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if ('tag' in componentFunction) {
|
|
131
|
+
component = componentFunction
|
|
132
|
+
}
|
|
133
|
+
else if (componentFunction instanceof Observable) {
|
|
134
|
+
component = componentFunction as any
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
component = componentFunction({ ...props, children }) as Element;
|
|
138
|
+
}
|
|
114
139
|
|
|
115
140
|
if (!component) {
|
|
116
141
|
component = {} as any
|
|
@@ -122,6 +147,12 @@ export function h<C extends ComponentFunction<any>>(
|
|
|
122
147
|
...((component as any).effectMounts ?? [])
|
|
123
148
|
];
|
|
124
149
|
|
|
150
|
+
// Copy dependencies prop to the returned element so it can be used for delayed mounting
|
|
151
|
+
if (props?.dependencies) {
|
|
152
|
+
component.props = component.props || {};
|
|
153
|
+
component.props.dependencies = props.dependencies;
|
|
154
|
+
}
|
|
155
|
+
|
|
125
156
|
// call mount hook for root component
|
|
126
157
|
if (component instanceof Promise) {
|
|
127
158
|
component.then((component) => {
|
package/src/engine/trigger.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { effect, signal } from "@signe/reactive";
|
|
2
2
|
|
|
3
|
-
interface Listen<T = any> {
|
|
3
|
+
export interface Listen<T = any> {
|
|
4
4
|
config: T | undefined;
|
|
5
5
|
seed: {
|
|
6
6
|
config: T | undefined;
|
|
@@ -9,7 +9,7 @@ interface Listen<T = any> {
|
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
interface Trigger<T = any> {
|
|
12
|
+
export interface Trigger<T = any> {
|
|
13
13
|
start: () => Promise<void>;
|
|
14
14
|
listen: () => Listen<T> | undefined;
|
|
15
15
|
}
|
|
@@ -47,13 +47,14 @@ export function trigger<T = any>(globalConfig?: T): Trigger<T> {
|
|
|
47
47
|
return {
|
|
48
48
|
start: (config?: T) => {
|
|
49
49
|
return new Promise((resolve: (value: any) => void) => {
|
|
50
|
+
const newValue = Math.random();
|
|
50
51
|
_signal.set({
|
|
51
52
|
config: {
|
|
52
53
|
...globalConfig,
|
|
53
54
|
...config,
|
|
54
55
|
},
|
|
55
56
|
resolve,
|
|
56
|
-
value:
|
|
57
|
+
value: newValue,
|
|
57
58
|
});
|
|
58
59
|
});
|
|
59
60
|
},
|
|
@@ -70,27 +71,53 @@ export function trigger<T = any>(globalConfig?: T): Trigger<T> {
|
|
|
70
71
|
* Subscribes to a trigger and executes a callback when the trigger is activated
|
|
71
72
|
* @param triggerSignal - The trigger to subscribe to
|
|
72
73
|
* @param callback - Function to execute when the trigger is activated
|
|
74
|
+
* @returns Subscription that can be unsubscribed to stop listening
|
|
73
75
|
* @throws Error if triggerSignal is not a valid trigger
|
|
74
76
|
* @example
|
|
75
77
|
* ```ts
|
|
76
78
|
* const click = trigger()
|
|
77
79
|
*
|
|
78
|
-
* on(click, () => {
|
|
80
|
+
* const subscription = on(click, () => {
|
|
79
81
|
* console.log('Click triggered')
|
|
80
82
|
* })
|
|
83
|
+
*
|
|
84
|
+
* // Later, to stop listening:
|
|
85
|
+
* subscription.unsubscribe()
|
|
81
86
|
* ```
|
|
82
87
|
*/
|
|
83
88
|
export function on(triggerSignal: any, callback: (config: any) => void | Promise<void>) {
|
|
84
89
|
if (!isTrigger(triggerSignal)) {
|
|
85
90
|
throw new Error("In 'on(arg)' must have a trigger signal type");
|
|
86
91
|
}
|
|
87
|
-
|
|
92
|
+
let lastValue: number | undefined;
|
|
93
|
+
|
|
94
|
+
const effectResult = effect(() => {
|
|
88
95
|
const result = triggerSignal.listen();
|
|
89
|
-
|
|
96
|
+
const seed = result?.seed;
|
|
97
|
+
|
|
98
|
+
if (!seed) return;
|
|
99
|
+
|
|
100
|
+
// Only run callback when the trigger value actually changes
|
|
101
|
+
if (lastValue === undefined) {
|
|
102
|
+
lastValue = seed.value;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (seed.value === lastValue) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
lastValue = seed.value;
|
|
109
|
+
|
|
110
|
+
try {
|
|
90
111
|
const ret = callback(result?.seed.config);
|
|
91
112
|
if (ret && typeof ret.then === 'function') {
|
|
92
|
-
ret.then(
|
|
113
|
+
ret.then((value: any) => seed.resolve(value)).catch(() => seed.resolve(undefined));
|
|
114
|
+
} else {
|
|
115
|
+
seed.resolve(ret);
|
|
93
116
|
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
seed.resolve(undefined);
|
|
94
119
|
}
|
|
95
120
|
});
|
|
121
|
+
|
|
122
|
+
return effectResult.subscription;
|
|
96
123
|
}
|
package/src/engine/utils.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { isSignal } from "@signe/reactive"
|
|
1
2
|
import { ObservablePoint } from "pixi.js"
|
|
3
|
+
import { Observable } from "rxjs"
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Checks if code is running in a browser environment
|
|
@@ -86,12 +88,23 @@ export function isFunction(val: unknown): boolean {
|
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
/**
|
|
89
|
-
* Checks if a value is a plain object
|
|
91
|
+
* Checks if a value is a plain object (not an instance of a class)
|
|
90
92
|
* @param {unknown} val - Value to check
|
|
91
|
-
* @returns {boolean} True if value is
|
|
93
|
+
* @returns {boolean} True if value is a plain object (not null, not array, not instance), false otherwise
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* isObject({}) // true
|
|
97
|
+
* isObject(new Date()) // false
|
|
98
|
+
* isObject([]) // false
|
|
99
|
+
* isObject(null) // false
|
|
100
|
+
* ```
|
|
92
101
|
*/
|
|
93
102
|
export function isObject(val: unknown): boolean {
|
|
94
|
-
return typeof val == 'object' && val != null && !Array.isArray(val)
|
|
103
|
+
return typeof val == 'object' && val != null && !Array.isArray(val) && val.constructor === Object
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function isObservable(val: unknown): boolean {
|
|
107
|
+
return val instanceof Observable
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
/**
|
|
@@ -186,6 +199,9 @@ export function setObservablePoint(
|
|
|
186
199
|
else if (Array.isArray(point)) {
|
|
187
200
|
observablePoint.set(point[0], point[1]);
|
|
188
201
|
}
|
|
202
|
+
else if (isObject(point) && 'value' in point) {
|
|
203
|
+
observablePoint.set((point as any).value.x, (point as any).value.y);
|
|
204
|
+
}
|
|
189
205
|
else {
|
|
190
206
|
observablePoint.set(point.x, point.y);
|
|
191
207
|
}
|
package/src/hooks/useProps.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
export * from './directives'
|
|
2
3
|
export * from '@signe/reactive'
|
|
3
4
|
export { Howler } from 'howler'
|
|
4
5
|
export * from './components'
|
|
@@ -12,4 +13,5 @@ export * from './utils/Ease'
|
|
|
12
13
|
export * from './utils/RadialGradient'
|
|
13
14
|
export * from './components/DisplayObject'
|
|
14
15
|
export { isObservable } from 'rxjs'
|
|
15
|
-
export * as Utils from './engine/utils'
|
|
16
|
+
export * as Utils from './engine/utils'
|
|
17
|
+
export * as Howl from 'howler'
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global Asset Loader
|
|
3
|
+
*
|
|
4
|
+
* Tracks the loading progress of all assets (images, spritesheets, etc.) across all sprites in a component tree.
|
|
5
|
+
* This allows components to know when all assets are loaded, useful for displaying loaders or progress bars.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const loader = new GlobalAssetLoader();
|
|
10
|
+
*
|
|
11
|
+
* loader.onProgress((progress) => {
|
|
12
|
+
* console.log(`Loading: ${(progress * 100).toFixed(0)}%`);
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* loader.onComplete(() => {
|
|
16
|
+
* console.log('All assets loaded!');
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Register assets as they start loading
|
|
20
|
+
* const assetId = loader.registerAsset('path/to/image.png');
|
|
21
|
+
*
|
|
22
|
+
* // Update progress
|
|
23
|
+
* loader.updateProgress(assetId, 0.5);
|
|
24
|
+
*
|
|
25
|
+
* // Mark as complete
|
|
26
|
+
* loader.completeAsset(assetId);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export class GlobalAssetLoader {
|
|
30
|
+
private assets: Map<string, { progress: number; completed: boolean }> = new Map();
|
|
31
|
+
private onProgressCallbacks: Set<(progress: number) => void> = new Set();
|
|
32
|
+
private onCompleteCallbacks: Set<() => void> = new Set();
|
|
33
|
+
private assetCounter: number = 0;
|
|
34
|
+
private isComplete: boolean = false;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Registers a new asset to track
|
|
38
|
+
*
|
|
39
|
+
* @param assetPath - The path or identifier of the asset being loaded
|
|
40
|
+
* @returns A unique ID for this asset that should be used for progress updates
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const assetId = loader.registerAsset('path/to/image.png');
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
registerAsset(assetPath: string): string {
|
|
48
|
+
const assetId = `asset_${this.assetCounter++}_${assetPath}`;
|
|
49
|
+
this.assets.set(assetId, { progress: 0, completed: false });
|
|
50
|
+
this.isComplete = false;
|
|
51
|
+
this.updateGlobalProgress();
|
|
52
|
+
return assetId;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Updates the progress of a specific asset
|
|
57
|
+
*
|
|
58
|
+
* @param assetId - The ID returned by registerAsset
|
|
59
|
+
* @param progress - Progress value between 0 and 1
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* loader.updateProgress(assetId, 0.5); // 50% loaded
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
updateProgress(assetId: string, progress: number): void {
|
|
67
|
+
const asset = this.assets.get(assetId);
|
|
68
|
+
if (!asset) {
|
|
69
|
+
console.warn(`Asset ${assetId} not found in tracker`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
asset.progress = Math.max(0, Math.min(1, progress));
|
|
74
|
+
this.updateGlobalProgress();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Marks an asset as completely loaded
|
|
79
|
+
*
|
|
80
|
+
* @param assetId - The ID returned by registerAsset
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* loader.completeAsset(assetId);
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
completeAsset(assetId: string): void {
|
|
88
|
+
const asset = this.assets.get(assetId);
|
|
89
|
+
if (!asset) {
|
|
90
|
+
console.warn(`Asset ${assetId} not found in tracker`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
asset.progress = 1;
|
|
95
|
+
asset.completed = true;
|
|
96
|
+
this.updateGlobalProgress();
|
|
97
|
+
this.checkCompletion();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Removes an asset from tracking (useful for cleanup)
|
|
102
|
+
*
|
|
103
|
+
* @param assetId - The ID returned by registerAsset
|
|
104
|
+
*/
|
|
105
|
+
removeAsset(assetId: string): void {
|
|
106
|
+
this.assets.delete(assetId);
|
|
107
|
+
this.updateGlobalProgress();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Registers a callback that will be called whenever the global progress changes
|
|
112
|
+
*
|
|
113
|
+
* @param callback - Function that receives the global progress (0-1)
|
|
114
|
+
* @returns A function to unregister the callback
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* const unsubscribe = loader.onProgress((progress) => {
|
|
119
|
+
* console.log(`Loading: ${(progress * 100).toFixed(0)}%`);
|
|
120
|
+
* });
|
|
121
|
+
*
|
|
122
|
+
* // Later, to unsubscribe:
|
|
123
|
+
* unsubscribe();
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
onProgress(callback: (progress: number) => void): () => void {
|
|
127
|
+
this.onProgressCallbacks.add(callback);
|
|
128
|
+
|
|
129
|
+
// Immediately call with current progress (wrap in try-catch for safety)
|
|
130
|
+
try {
|
|
131
|
+
callback(this.getGlobalProgress());
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error('Error in onProgress callback:', error);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return () => {
|
|
137
|
+
this.onProgressCallbacks.delete(callback);
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Registers a callback that will be called when all assets are loaded
|
|
143
|
+
*
|
|
144
|
+
* @param callback - Function to call when all assets are complete
|
|
145
|
+
* @returns A function to unregister the callback
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* const unsubscribe = loader.onComplete(() => {
|
|
150
|
+
* console.log('All assets loaded!');
|
|
151
|
+
* });
|
|
152
|
+
*
|
|
153
|
+
* // Later, to unsubscribe:
|
|
154
|
+
* unsubscribe();
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
onComplete(callback: () => void): () => void {
|
|
158
|
+
this.onCompleteCallbacks.add(callback);
|
|
159
|
+
|
|
160
|
+
// If already complete, call immediately
|
|
161
|
+
if (this.isComplete) {
|
|
162
|
+
callback();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return () => {
|
|
166
|
+
this.onCompleteCallbacks.delete(callback);
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Gets the current global progress (0-1)
|
|
172
|
+
*
|
|
173
|
+
* @returns Progress value between 0 and 1
|
|
174
|
+
*/
|
|
175
|
+
getGlobalProgress(): number {
|
|
176
|
+
if (this.assets.size === 0) {
|
|
177
|
+
return 1; // No assets means everything is "loaded"
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let totalProgress = 0;
|
|
181
|
+
for (const asset of this.assets.values()) {
|
|
182
|
+
totalProgress += asset.progress;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return totalProgress / this.assets.size;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Gets the number of assets currently being tracked
|
|
190
|
+
*
|
|
191
|
+
* @returns Number of registered assets
|
|
192
|
+
*/
|
|
193
|
+
getAssetCount(): number {
|
|
194
|
+
return this.assets.size;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Gets the number of completed assets
|
|
199
|
+
*
|
|
200
|
+
* @returns Number of completed assets
|
|
201
|
+
*/
|
|
202
|
+
getCompletedCount(): number {
|
|
203
|
+
let count = 0;
|
|
204
|
+
for (const asset of this.assets.values()) {
|
|
205
|
+
if (asset.completed) count++;
|
|
206
|
+
}
|
|
207
|
+
return count;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Checks if all assets are loaded and triggers onComplete callbacks
|
|
212
|
+
*/
|
|
213
|
+
private checkCompletion(): void {
|
|
214
|
+
if (this.isComplete) return;
|
|
215
|
+
|
|
216
|
+
const allCompleted = Array.from(this.assets.values()).every(asset => asset.completed);
|
|
217
|
+
|
|
218
|
+
if (allCompleted && this.assets.size > 0) {
|
|
219
|
+
this.isComplete = true;
|
|
220
|
+
this.onCompleteCallbacks.forEach(callback => {
|
|
221
|
+
try {
|
|
222
|
+
callback();
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error('Error in onComplete callback:', error);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Updates global progress and notifies all progress callbacks
|
|
232
|
+
*/
|
|
233
|
+
private updateGlobalProgress(): void {
|
|
234
|
+
const progress = this.getGlobalProgress();
|
|
235
|
+
// Create a copy of callbacks to avoid issues if callbacks modify the set
|
|
236
|
+
const callbacks = Array.from(this.onProgressCallbacks);
|
|
237
|
+
callbacks.forEach(callback => {
|
|
238
|
+
try {
|
|
239
|
+
callback(progress);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error('Error in onProgress callback:', error);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Resets the loader, clearing all assets and callbacks
|
|
248
|
+
*/
|
|
249
|
+
reset(): void {
|
|
250
|
+
this.assets.clear();
|
|
251
|
+
this.onProgressCallbacks.clear();
|
|
252
|
+
this.onCompleteCallbacks.clear();
|
|
253
|
+
this.assetCounter = 0;
|
|
254
|
+
this.isComplete = false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
package/testing/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { bootstrapCanvas, Canvas, ComponentInstance, Element, h } from "canvasengine";
|
|
2
|
+
|
|
3
|
+
export class TestBed {
|
|
4
|
+
static async createComponent(component: any, props: any = {}, children: any = []): Promise<Element<ComponentInstance>> {
|
|
5
|
+
const comp = () => h(Canvas, {
|
|
6
|
+
tickStart: false
|
|
7
|
+
}, h(component, props, children))
|
|
8
|
+
const { canvasElement, app } = await bootstrapCanvas(document.getElementById('root'), comp)
|
|
9
|
+
app.render()
|
|
10
|
+
return canvasElement.props.children?.[0]
|
|
11
|
+
}
|
|
12
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "dist",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"emitDeclarationOnly": false,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"rootDir": "src"
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"src/**/*"
|
|
12
|
+
],
|
|
13
|
+
"exclude": [
|
|
14
|
+
"dist",
|
|
15
|
+
"node_modules"
|
|
16
|
+
]
|
|
17
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import { resolve } from 'path'
|
|
3
|
+
import dts from 'vite-plugin-dts'
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [dts()],
|
|
7
|
+
build: {
|
|
8
|
+
lib: {
|
|
9
|
+
entry: resolve(__dirname, 'src/index.ts'),
|
|
10
|
+
name: 'CanvasEngine'
|
|
11
|
+
},
|
|
12
|
+
rollupOptions: {
|
|
13
|
+
external: ['pixi.js'],
|
|
14
|
+
output: [
|
|
15
|
+
// ESM build
|
|
16
|
+
{
|
|
17
|
+
format: 'es',
|
|
18
|
+
dir: 'dist',
|
|
19
|
+
entryFileNames: 'index.js'
|
|
20
|
+
},
|
|
21
|
+
// IIFE build for global usage
|
|
22
|
+
{
|
|
23
|
+
format: 'iife',
|
|
24
|
+
dir: 'dist',
|
|
25
|
+
entryFileNames: 'index.global.js',
|
|
26
|
+
name: 'CanvasEngine',
|
|
27
|
+
globals: {
|
|
28
|
+
'pixi.js': 'PIXI'
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
minify: true,
|
|
34
|
+
sourcemap: true
|
|
35
|
+
},
|
|
36
|
+
define: {
|
|
37
|
+
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
|
|
38
|
+
}
|
|
39
|
+
})
|