canvasengine 2.0.0-beta.5 → 2.0.0-beta.50
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/components/Button.d.ts +185 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Canvas.d.ts +17 -0
- package/dist/components/Canvas.d.ts.map +1 -0
- package/dist/components/Container.d.ts +86 -0
- package/dist/components/Container.d.ts.map +1 -0
- package/dist/components/DOMContainer.d.ts +98 -0
- package/dist/components/DOMContainer.d.ts.map +1 -0
- package/dist/components/DOMElement.d.ts +54 -0
- package/dist/components/DOMElement.d.ts.map +1 -0
- package/dist/components/DOMSprite.d.ts +127 -0
- package/dist/components/DOMSprite.d.ts.map +1 -0
- package/dist/components/DisplayObject.d.ts +94 -0
- package/dist/components/DisplayObject.d.ts.map +1 -0
- package/dist/components/FocusContainer.d.ts +129 -0
- package/dist/components/FocusContainer.d.ts.map +1 -0
- package/dist/components/Graphic.d.ts +64 -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/Mesh.d.ts +208 -0
- package/dist/components/Mesh.d.ts.map +1 -0
- package/dist/components/NineSliceSprite.d.ts +16 -0
- package/dist/components/NineSliceSprite.d.ts.map +1 -0
- package/dist/components/ParticleEmitter.d.ts +4 -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/Sprite.d.ts +242 -0
- package/dist/components/Sprite.d.ts.map +1 -0
- package/dist/components/Text.d.ts +25 -0
- package/dist/components/Text.d.ts.map +1 -0
- package/dist/components/TilingSprite.d.ts +17 -0
- package/dist/components/TilingSprite.d.ts.map +1 -0
- package/dist/components/Video.d.ts +14 -0
- package/dist/components/Video.d.ts.map +1 -0
- package/dist/components/Viewport.d.ts +121 -0
- package/dist/components/Viewport.d.ts.map +1 -0
- package/dist/components/index.d.ts +20 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/types/DisplayObject.d.ts +106 -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 +4 -0
- package/dist/components/types/index.d.ts.map +1 -0
- package/dist/directives/Controls.d.ts +112 -0
- package/dist/directives/Controls.d.ts.map +1 -0
- package/dist/directives/ControlsBase.d.ts +199 -0
- package/dist/directives/ControlsBase.d.ts.map +1 -0
- package/dist/directives/Drag.d.ts +69 -0
- package/dist/directives/Drag.d.ts.map +1 -0
- package/dist/directives/Flash.d.ts +116 -0
- package/dist/directives/Flash.d.ts.map +1 -0
- package/dist/directives/FocusNavigation.d.ts +52 -0
- package/dist/directives/FocusNavigation.d.ts.map +1 -0
- package/dist/directives/GamepadControls.d.ts +224 -0
- package/dist/directives/GamepadControls.d.ts.map +1 -0
- package/dist/directives/JoystickControls.d.ts +171 -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 +35 -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 +25 -0
- package/dist/directives/Sound.d.ts.map +1 -0
- package/dist/directives/Transition.d.ts +10 -0
- package/dist/directives/Transition.d.ts.map +1 -0
- package/dist/directives/ViewportCull.d.ts +11 -0
- package/dist/directives/ViewportCull.d.ts.map +1 -0
- package/dist/directives/ViewportFollow.d.ts +18 -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/FocusManager.d.ts +174 -0
- package/dist/engine/FocusManager.d.ts.map +1 -0
- package/dist/engine/animation.d.ts +72 -0
- package/dist/engine/animation.d.ts.map +1 -0
- package/dist/engine/bootstrap.d.ts +48 -0
- package/dist/engine/bootstrap.d.ts.map +1 -0
- package/dist/engine/directive.d.ts +13 -0
- package/dist/engine/directive.d.ts.map +1 -0
- package/dist/engine/reactive.d.ts +134 -0
- package/dist/engine/reactive.d.ts.map +1 -0
- package/dist/engine/signal.d.ts +71 -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 +89 -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/useFocus.d.ts +60 -0
- package/dist/hooks/useFocus.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 +4 -0
- package/dist/hooks/useRef.d.ts.map +1 -0
- package/dist/index-DaGekQUW.js +2218 -0
- package/dist/index-DaGekQUW.js.map +1 -0
- package/dist/index.d.ts +19 -1099
- package/dist/index.d.ts.map +1 -0
- package/dist/index.global.js +5 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +11749 -2901
- 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 +57 -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/dist/utils/tabindex.d.ts +16 -0
- package/dist/utils/tabindex.d.ts.map +1 -0
- package/package.json +13 -7
- package/src/components/Button.ts +399 -0
- package/src/components/Canvas.ts +62 -46
- package/src/components/Container.ts +21 -2
- package/src/components/DOMContainer.ts +379 -0
- package/src/components/DOMElement.ts +556 -0
- package/src/components/DOMSprite.ts +1040 -0
- package/src/components/DisplayObject.ts +392 -201
- package/src/components/FocusContainer.ts +368 -0
- package/src/components/Graphic.ts +227 -66
- 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 +297 -31
- package/src/components/Text.ts +125 -18
- package/src/components/Video.ts +2 -2
- package/src/components/Viewport.ts +118 -63
- package/src/components/index.ts +9 -2
- package/src/components/types/DisplayObject.ts +41 -4
- package/src/components/types/Spritesheet.ts +0 -118
- package/src/directives/Controls.ts +254 -0
- package/src/directives/ControlsBase.ts +267 -0
- package/src/directives/Drag.ts +357 -52
- package/src/directives/Flash.ts +419 -0
- package/src/directives/FocusNavigation.ts +113 -0
- package/src/directives/GamepadControls.ts +537 -0
- package/src/directives/JoystickControls.ts +396 -0
- package/src/directives/KeyboardControls.ts +85 -430
- package/src/directives/Scheduler.ts +12 -4
- package/src/directives/Shake.ts +298 -0
- package/src/directives/Sound.ts +94 -31
- package/src/directives/ViewportFollow.ts +40 -9
- package/src/directives/index.ts +12 -6
- package/src/engine/FocusManager.ts +510 -0
- package/src/engine/animation.ts +175 -21
- package/src/engine/bootstrap.ts +93 -3
- package/src/engine/directive.ts +4 -4
- package/src/engine/reactive.ts +901 -161
- package/src/engine/signal.ts +113 -25
- package/src/engine/trigger.ts +34 -7
- package/src/engine/utils.ts +19 -3
- package/src/hooks/useFocus.ts +91 -0
- package/src/hooks/useProps.ts +1 -1
- package/src/index.ts +8 -2
- package/src/types/pixi-cull.d.ts +7 -0
- package/src/utils/GlobalAssetLoader.ts +257 -0
- package/src/utils/functions.ts +7 -0
- package/src/utils/tabindex.ts +70 -0
- package/testing/index.ts +35 -4
- package/tsconfig.json +18 -0
- package/vite.config.ts +39 -0
package/src/components/Canvas.ts
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import { effect, Signal, signal } from "@signe/reactive";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { Application, Container } from "pixi.js";
|
|
3
|
+
import {
|
|
4
|
+
Props,
|
|
5
|
+
createComponent,
|
|
6
|
+
registerComponent,
|
|
7
|
+
Element,
|
|
8
|
+
} from "../engine/reactive";
|
|
5
9
|
import { useProps } from "../hooks/useProps";
|
|
6
10
|
import { ComponentInstance, DisplayObject } from "./DisplayObject";
|
|
7
11
|
import { ComponentFunction } from "../engine/signal";
|
|
8
12
|
import { SignalOrPrimitive } from "./types";
|
|
9
13
|
import { Size } from "./types/DisplayObject";
|
|
10
14
|
import { Scheduler, Tick } from "../directives/Scheduler";
|
|
15
|
+
import { GlobalAssetLoader } from "../utils/GlobalAssetLoader";
|
|
11
16
|
|
|
12
17
|
interface CanvasElement extends Element<ComponentInstance> {
|
|
13
|
-
render: (rootElement: HTMLElement) => void;
|
|
18
|
+
render: (rootElement: HTMLElement, app?: Application) => void;
|
|
14
19
|
directives: {
|
|
15
|
-
tick: Scheduler
|
|
20
|
+
tick: Scheduler;
|
|
16
21
|
};
|
|
17
22
|
propObservables: {
|
|
18
|
-
tick: Signal<Tick
|
|
23
|
+
tick: Signal<Tick>;
|
|
19
24
|
};
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
registerComponent("Canvas", class Canvas extends DisplayObject(Container) {});
|
|
27
|
+
registerComponent("Canvas", class Canvas extends DisplayObject(Container) { });
|
|
23
28
|
|
|
24
29
|
export interface CanvasProps extends Props {
|
|
25
30
|
cursorStyles?: () => any;
|
|
@@ -30,33 +35,28 @@ export interface CanvasProps extends Props {
|
|
|
30
35
|
isRoot?: boolean;
|
|
31
36
|
tick?: any;
|
|
32
37
|
class?: SignalOrPrimitive<string>;
|
|
38
|
+
background?: string;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
export const Canvas: ComponentFunction<CanvasProps> = async (props = {}) => {
|
|
36
42
|
let { cursorStyles, width, height, class: className } = useProps(props);
|
|
37
|
-
const Yoga = await loadYoga();
|
|
38
43
|
|
|
39
|
-
if (!props.width) width = signal<Size>(800)
|
|
40
|
-
if (!props.height) height = signal<Size>(600)
|
|
41
|
-
|
|
42
|
-
const renderer = await autoDetectRenderer({
|
|
43
|
-
...props,
|
|
44
|
-
width: width?.(),
|
|
45
|
-
height: height?.(),
|
|
46
|
-
});
|
|
44
|
+
if (!props.width) width = signal<Size>(800);
|
|
45
|
+
if (!props.height) height = signal<Size>(600);
|
|
47
46
|
|
|
48
47
|
const canvasSize = signal({
|
|
49
|
-
width:
|
|
50
|
-
height:
|
|
48
|
+
width: 0,
|
|
49
|
+
height: 0,
|
|
51
50
|
});
|
|
52
51
|
|
|
53
52
|
props.isRoot = true;
|
|
53
|
+
const globalLoader = new GlobalAssetLoader();
|
|
54
54
|
const options: CanvasProps = {
|
|
55
55
|
...props,
|
|
56
56
|
context: {
|
|
57
|
-
Yoga,
|
|
58
|
-
renderer,
|
|
59
57
|
canvasSize,
|
|
58
|
+
app: signal(null),
|
|
59
|
+
globalLoader,
|
|
60
60
|
},
|
|
61
61
|
width: width?.(),
|
|
62
62
|
height: height?.(),
|
|
@@ -69,10 +69,21 @@ export const Canvas: ComponentFunction<CanvasProps> = async (props = {}) => {
|
|
|
69
69
|
frame: 0,
|
|
70
70
|
deltaRatio: 1,
|
|
71
71
|
});
|
|
72
|
+
} else {
|
|
73
|
+
options.context!.tick = props.tick;
|
|
72
74
|
}
|
|
75
|
+
|
|
76
|
+
// Register the tick signal globally so animatedSignal can use it by default
|
|
77
|
+
(globalThis as any).__CANVAS_ENGINE_TICK__ = options.context!.tick;
|
|
78
|
+
|
|
73
79
|
const canvasElement = createComponent("Canvas", options) as CanvasElement;
|
|
74
80
|
|
|
75
|
-
canvasElement.render = (rootElement: HTMLElement) => {
|
|
81
|
+
canvasElement.render = (rootElement: HTMLElement, app?: Application) => {
|
|
82
|
+
if (!app) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const renderer = app.renderer;
|
|
76
87
|
const canvasEl = renderer.view.canvas as HTMLCanvasElement;
|
|
77
88
|
|
|
78
89
|
(globalThis as any).__PIXI_STAGE__ = canvasElement.componentInstance;
|
|
@@ -85,6 +96,34 @@ export const Canvas: ComponentFunction<CanvasProps> = async (props = {}) => {
|
|
|
85
96
|
renderer.render(canvasElement.componentInstance as any);
|
|
86
97
|
});
|
|
87
98
|
|
|
99
|
+
app.stage = canvasElement.componentInstance as any;
|
|
100
|
+
|
|
101
|
+
app.stage.layout = {
|
|
102
|
+
width: app.screen.width,
|
|
103
|
+
height: app.screen.height,
|
|
104
|
+
justifyContent: props.justifyContent,
|
|
105
|
+
alignItems: props.alignItems,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
canvasSize.set({ width: app.screen.width, height: app.screen.height })
|
|
109
|
+
|
|
110
|
+
app.renderer.on('resize', (width: number, height: number) => {
|
|
111
|
+
canvasSize.set({ width, height });
|
|
112
|
+
|
|
113
|
+
if (app.stage.layout) {
|
|
114
|
+
app.stage.layout = {
|
|
115
|
+
width,
|
|
116
|
+
height
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (props.tickStart !== false) canvasElement.directives.tick.start();
|
|
122
|
+
|
|
123
|
+
app.ticker.add(() => {
|
|
124
|
+
canvasElement.propObservables!.tick();
|
|
125
|
+
});
|
|
126
|
+
|
|
88
127
|
if (cursorStyles) {
|
|
89
128
|
effect(() => {
|
|
90
129
|
renderer.events.cursorStyles = cursorStyles();
|
|
@@ -97,37 +136,14 @@ export const Canvas: ComponentFunction<CanvasProps> = async (props = {}) => {
|
|
|
97
136
|
});
|
|
98
137
|
}
|
|
99
138
|
|
|
100
|
-
const
|
|
101
|
-
let w, h;
|
|
102
|
-
if (width?.() === "100%" && height?.() === "100%") {
|
|
103
|
-
const parent = canvasEl.parentElement;
|
|
104
|
-
w = parent ? parent.clientWidth : window.innerWidth;
|
|
105
|
-
h = parent ? parent.clientHeight : window.innerHeight;
|
|
106
|
-
} else {
|
|
107
|
-
w = width?.() ?? canvasEl.offsetWidth;
|
|
108
|
-
h = height?.() ?? canvasEl.offsetHeight;
|
|
109
|
-
}
|
|
110
|
-
renderer.resize(w, h);
|
|
111
|
-
canvasSize.set({ width: w, height: h });
|
|
112
|
-
canvasElement.componentInstance.setWidth(w)
|
|
113
|
-
canvasElement.componentInstance.setHeight(h)
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// Listen for window resize events
|
|
117
|
-
window.addEventListener("resize", resizeCanvas);
|
|
118
|
-
|
|
119
|
-
// Check if a canvas already exists in the rootElement
|
|
120
|
-
const existingCanvas = rootElement.querySelector('canvas');
|
|
139
|
+
const existingCanvas = rootElement.querySelector("canvas");
|
|
121
140
|
if (existingCanvas) {
|
|
122
|
-
// If it exists, replace it with the new canvas
|
|
123
141
|
rootElement.replaceChild(canvasEl, existingCanvas);
|
|
124
142
|
} else {
|
|
125
|
-
// If it doesn't exist, append the new canvas
|
|
126
143
|
rootElement.appendChild(canvasEl);
|
|
127
144
|
}
|
|
128
145
|
|
|
129
|
-
|
|
130
|
-
resizeCanvas();
|
|
146
|
+
options.context!.app.set(app)
|
|
131
147
|
};
|
|
132
148
|
|
|
133
149
|
return canvasElement;
|
|
@@ -4,6 +4,7 @@ import { DisplayObject } from "./DisplayObject";
|
|
|
4
4
|
import { ComponentFunction } from "../engine/signal";
|
|
5
5
|
import { DisplayObjectProps } from "./types/DisplayObject";
|
|
6
6
|
import { setObservablePoint } from "../engine/utils";
|
|
7
|
+
import { isPercent } from "../utils/functions";
|
|
7
8
|
|
|
8
9
|
interface ContainerProps extends DisplayObjectProps {
|
|
9
10
|
sortableChildren?: boolean;
|
|
@@ -25,8 +26,8 @@ export class CanvasContainer extends DisplayObject(PixiContainer) {
|
|
|
25
26
|
this.sortableChildren = props.sortableChildren;
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
|
-
onMount(args) {
|
|
29
|
-
super.onMount(args);
|
|
29
|
+
async onMount(args) {
|
|
30
|
+
await super.onMount(args);
|
|
30
31
|
const { componentInstance, props } = args;
|
|
31
32
|
const { pixiChildren } = props;
|
|
32
33
|
if (pixiChildren) {
|
|
@@ -34,6 +35,22 @@ export class CanvasContainer extends DisplayObject(PixiContainer) {
|
|
|
34
35
|
componentInstance.addChild(child);
|
|
35
36
|
});
|
|
36
37
|
}
|
|
38
|
+
|
|
39
|
+
// Listen to layout events to update displayWidth and displayHeight with computed values
|
|
40
|
+
const isWidthPercentage = isPercent(props.width);
|
|
41
|
+
const isHeightPercentage = isPercent(props.height);
|
|
42
|
+
|
|
43
|
+
if (isWidthPercentage || isHeightPercentage) {
|
|
44
|
+
this.on('layout', (event) => {
|
|
45
|
+
const layoutBox = event.computedLayout;
|
|
46
|
+
if (isWidthPercentage && layoutBox.width !== undefined) {
|
|
47
|
+
this.displayWidth.set(layoutBox.width);
|
|
48
|
+
}
|
|
49
|
+
if (isHeightPercentage && layoutBox.height !== undefined) {
|
|
50
|
+
this.displayHeight.set(layoutBox.height);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
37
54
|
}
|
|
38
55
|
}
|
|
39
56
|
|
|
@@ -42,5 +59,7 @@ export interface CanvasContainer extends DisplayObjectProps {}
|
|
|
42
59
|
registerComponent("Container", CanvasContainer);
|
|
43
60
|
|
|
44
61
|
export const Container: ComponentFunction<ContainerProps> = (props) => {
|
|
62
|
+
// Ensure component is registered (useful in tests if module cache differs)
|
|
63
|
+
registerComponent("Container", CanvasContainer);
|
|
45
64
|
return createComponent("Container", props);
|
|
46
65
|
};
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { DOMContainer as PixiDOMContainer } from "pixi.js";
|
|
2
|
+
import { effect } from "@signe/reactive";
|
|
3
|
+
import {
|
|
4
|
+
createComponent,
|
|
5
|
+
Element,
|
|
6
|
+
isElement,
|
|
7
|
+
registerComponent,
|
|
8
|
+
} from "../engine/reactive";
|
|
9
|
+
import { ComponentInstance, DisplayObject } from "./DisplayObject";
|
|
10
|
+
import { ComponentFunction, h } from "../engine/signal";
|
|
11
|
+
import { DisplayObjectProps } from "./types/DisplayObject";
|
|
12
|
+
import { CanvasDOMElement, DOMElement } from "./DOMElement";
|
|
13
|
+
import { isPercent } from "../utils/functions";
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* DOMContainer class for managing DOM elements within the canvas engine
|
|
18
|
+
*
|
|
19
|
+
* This class extends the DisplayObject functionality to handle DOM elements using
|
|
20
|
+
* PixiJS's native DOMContainer. It provides a bridge between the canvas rendering
|
|
21
|
+
* system and traditional DOM manipulation with proper transform hierarchy and visibility.
|
|
22
|
+
*
|
|
23
|
+
* The DOMContainer is especially useful for rendering standard DOM elements that handle
|
|
24
|
+
* user input, such as `<input>` or `<textarea>`. This is often simpler and more flexible
|
|
25
|
+
* than trying to implement text input directly in PixiJS.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* // Basic usage with input element
|
|
30
|
+
* const element = document.createElement('input');
|
|
31
|
+
* element.type = 'text';
|
|
32
|
+
* element.placeholder = 'Enter text...';
|
|
33
|
+
*
|
|
34
|
+
* const domContainer = new DOMContainer({
|
|
35
|
+
* element,
|
|
36
|
+
* x: 100,
|
|
37
|
+
* y: 50,
|
|
38
|
+
* anchor: { x: 0.5, y: 0.5 }
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Using different class and style formats
|
|
42
|
+
* const containerWithClasses = new DOMContainer({
|
|
43
|
+
* element: 'div',
|
|
44
|
+
* attrs: {
|
|
45
|
+
* // String format: space-separated classes
|
|
46
|
+
* class: 'container primary-theme',
|
|
47
|
+
*
|
|
48
|
+
* // Array format: array of class names
|
|
49
|
+
* // class: ['container', 'primary-theme'],
|
|
50
|
+
*
|
|
51
|
+
* // Object format: conditional classes
|
|
52
|
+
* // class: {
|
|
53
|
+
* // 'container': true,
|
|
54
|
+
* // 'primary-theme': true,
|
|
55
|
+
* // 'disabled': false
|
|
56
|
+
* // }
|
|
57
|
+
*
|
|
58
|
+
* // String format: CSS style string
|
|
59
|
+
* style: 'background-color: red; padding: 10px;',
|
|
60
|
+
*
|
|
61
|
+
* // Object format: style properties
|
|
62
|
+
* // style: {
|
|
63
|
+
* // backgroundColor: 'red',
|
|
64
|
+
* // padding: '10px',
|
|
65
|
+
* // fontSize: 16
|
|
66
|
+
* // }
|
|
67
|
+
* }
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
const EVENTS = [
|
|
72
|
+
"click",
|
|
73
|
+
"mouseover",
|
|
74
|
+
"mouseout",
|
|
75
|
+
"mouseenter",
|
|
76
|
+
"mouseleave",
|
|
77
|
+
"mousemove",
|
|
78
|
+
"mouseup",
|
|
79
|
+
"mousedown",
|
|
80
|
+
"touchstart",
|
|
81
|
+
"touchend",
|
|
82
|
+
"touchmove",
|
|
83
|
+
"touchcancel",
|
|
84
|
+
"wheel",
|
|
85
|
+
"scroll",
|
|
86
|
+
"resize",
|
|
87
|
+
"focus",
|
|
88
|
+
"blur",
|
|
89
|
+
"change",
|
|
90
|
+
"input",
|
|
91
|
+
"submit",
|
|
92
|
+
"reset",
|
|
93
|
+
"keydown",
|
|
94
|
+
"keyup",
|
|
95
|
+
"keypress",
|
|
96
|
+
"contextmenu",
|
|
97
|
+
"drag",
|
|
98
|
+
"dragend",
|
|
99
|
+
"dragenter",
|
|
100
|
+
"dragleave",
|
|
101
|
+
"dragover",
|
|
102
|
+
"drop",
|
|
103
|
+
"dragstart",
|
|
104
|
+
"select",
|
|
105
|
+
"selectstart",
|
|
106
|
+
"selectend",
|
|
107
|
+
"selectall",
|
|
108
|
+
"selectnone",
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
export class CanvasDOMContainer extends DisplayObject(PixiDOMContainer) {
|
|
112
|
+
disableLayout = true;
|
|
113
|
+
private canvasSizeEffect: any = null;
|
|
114
|
+
private static readonly DOM_ROUTING_MAP: Record<string, string> = {
|
|
115
|
+
Sprite: "DOMSprite",
|
|
116
|
+
};
|
|
117
|
+
private static readonly DOM_ALLOWED_TAGS = new Set([
|
|
118
|
+
"DOMContainer",
|
|
119
|
+
"DOMElement",
|
|
120
|
+
"DOMSprite",
|
|
121
|
+
]);
|
|
122
|
+
private static readonly DOM_UNSUPPORTED_TAGS = new Set([
|
|
123
|
+
"Canvas",
|
|
124
|
+
"Container",
|
|
125
|
+
"Graphics",
|
|
126
|
+
"Rect",
|
|
127
|
+
"Circle",
|
|
128
|
+
"Ellipse",
|
|
129
|
+
"Triangle",
|
|
130
|
+
"Svg",
|
|
131
|
+
"Mesh",
|
|
132
|
+
"Scene",
|
|
133
|
+
"ParticlesEmitter",
|
|
134
|
+
"Sprite",
|
|
135
|
+
"Video",
|
|
136
|
+
"Text",
|
|
137
|
+
"TilingSprite",
|
|
138
|
+
"Viewport",
|
|
139
|
+
"NineSliceSprite",
|
|
140
|
+
"Button",
|
|
141
|
+
"Joystick",
|
|
142
|
+
"FocusContainer",
|
|
143
|
+
]);
|
|
144
|
+
|
|
145
|
+
private hasDomContainerAncestor(): boolean {
|
|
146
|
+
const element = this.getElement();
|
|
147
|
+
let parent = element?.parent;
|
|
148
|
+
while (parent) {
|
|
149
|
+
if (parent.tag === "DOMContainer") return true;
|
|
150
|
+
parent = parent.parent;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private getPercentRatio(value: string): number | null {
|
|
156
|
+
const parsed = parseFloat(value);
|
|
157
|
+
if (Number.isNaN(parsed)) return null;
|
|
158
|
+
return parsed / 100;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private getCanvasSize() {
|
|
162
|
+
const canvasSize = this.fullProps?.context?.canvasSize;
|
|
163
|
+
return typeof canvasSize === "function" ? canvasSize() : canvasSize;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private shouldUseCanvasPercent(): boolean {
|
|
167
|
+
const widthProp = this.fullProps?.width;
|
|
168
|
+
const heightProp = this.fullProps?.height;
|
|
169
|
+
if (!isPercent(widthProp) && !isPercent(heightProp)) return false;
|
|
170
|
+
return !this.hasDomContainerAncestor();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private syncCanvasSizeEffect() {
|
|
174
|
+
const shouldTrack = this.shouldUseCanvasPercent();
|
|
175
|
+
if (shouldTrack && !this.canvasSizeEffect) {
|
|
176
|
+
const canvasSize = this.fullProps?.context?.canvasSize;
|
|
177
|
+
if (typeof canvasSize === "function") {
|
|
178
|
+
this.canvasSizeEffect = effect(() => {
|
|
179
|
+
canvasSize();
|
|
180
|
+
this.applyElementSize();
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
} else if (!shouldTrack && this.canvasSizeEffect) {
|
|
184
|
+
this.canvasSizeEffect.subscription?.unsubscribe();
|
|
185
|
+
this.canvasSizeEffect = null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private applyElementSize() {
|
|
190
|
+
if (!this.element) return;
|
|
191
|
+
const widthProp = this.fullProps?.width;
|
|
192
|
+
const heightProp = this.fullProps?.height;
|
|
193
|
+
const useCanvasSize = this.shouldUseCanvasPercent();
|
|
194
|
+
const canvasSize = useCanvasSize ? this.getCanvasSize() : null;
|
|
195
|
+
|
|
196
|
+
if (widthProp !== undefined) {
|
|
197
|
+
if (isPercent(widthProp)) {
|
|
198
|
+
if (useCanvasSize) {
|
|
199
|
+
const ratio = this.getPercentRatio(widthProp);
|
|
200
|
+
if (ratio !== null) {
|
|
201
|
+
const baseWidth = (canvasSize?.width !== undefined)
|
|
202
|
+
? canvasSize.width
|
|
203
|
+
: this.getWidth();
|
|
204
|
+
this.element.style.width = `${baseWidth * ratio}px`;
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
this.element.style.width = widthProp;
|
|
208
|
+
}
|
|
209
|
+
} else if (typeof widthProp === "number") {
|
|
210
|
+
this.element.style.width = `${widthProp}px`;
|
|
211
|
+
} else if (typeof widthProp === "string") {
|
|
212
|
+
this.element.style.width = widthProp;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (heightProp !== undefined) {
|
|
217
|
+
if (isPercent(heightProp)) {
|
|
218
|
+
if (useCanvasSize) {
|
|
219
|
+
const ratio = this.getPercentRatio(heightProp);
|
|
220
|
+
if (ratio !== null) {
|
|
221
|
+
const baseHeight = (canvasSize?.height !== undefined)
|
|
222
|
+
? canvasSize.height
|
|
223
|
+
: this.getHeight();
|
|
224
|
+
this.element.style.height = `${baseHeight * ratio}px`;
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
this.element.style.height = heightProp;
|
|
228
|
+
}
|
|
229
|
+
} else if (typeof heightProp === "number") {
|
|
230
|
+
this.element.style.height = `${heightProp}px`;
|
|
231
|
+
} else if (typeof heightProp === "string") {
|
|
232
|
+
this.element.style.height = heightProp;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private routeDomChildren(children: any): any {
|
|
238
|
+
if (!children) return children;
|
|
239
|
+
if (Array.isArray(children)) {
|
|
240
|
+
return children.map((child) => this.routeDomChildren(child));
|
|
241
|
+
}
|
|
242
|
+
if (isElement(children)) {
|
|
243
|
+
if (CanvasDOMContainer.DOM_ALLOWED_TAGS.has(children.tag)) {
|
|
244
|
+
return children;
|
|
245
|
+
}
|
|
246
|
+
const routedTag = CanvasDOMContainer.DOM_ROUTING_MAP[children.tag];
|
|
247
|
+
if (routedTag) {
|
|
248
|
+
children.propSubscriptions?.forEach((sub) => sub.unsubscribe());
|
|
249
|
+
children.effectSubscriptions?.forEach((sub) => sub.unsubscribe());
|
|
250
|
+
children.effectUnmounts?.forEach((fn) => fn?.());
|
|
251
|
+
const routedProps = children.propObservables ?? children.props;
|
|
252
|
+
return createComponent(routedTag, routedProps);
|
|
253
|
+
}
|
|
254
|
+
if (CanvasDOMContainer.DOM_UNSUPPORTED_TAGS.has(children.tag)) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`Component ${children.tag} is not implemented for DOMContainer context yet. Only Sprite is supported.`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
if (children.props?.children) {
|
|
260
|
+
children.props.children = this.routeDomChildren(children.props.children);
|
|
261
|
+
}
|
|
262
|
+
return children;
|
|
263
|
+
}
|
|
264
|
+
return children;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
onInit(props: any) {
|
|
268
|
+
// Handle internal _scopeClass prop for scoped CSS
|
|
269
|
+
const scopeClass = props._scopeClass;
|
|
270
|
+
let divProps: any = { element: "div" };
|
|
271
|
+
const divAttrs = { ...(props.attrs || {}) };
|
|
272
|
+
|
|
273
|
+
const mergeScopeClass = (classValue: any) => {
|
|
274
|
+
if (!scopeClass) return classValue;
|
|
275
|
+
if (classValue == null) return scopeClass;
|
|
276
|
+
if (typeof classValue === "string") {
|
|
277
|
+
return `${scopeClass} ${classValue}`;
|
|
278
|
+
}
|
|
279
|
+
if (Array.isArray(classValue)) {
|
|
280
|
+
return [scopeClass, ...classValue];
|
|
281
|
+
}
|
|
282
|
+
if (typeof classValue === "object") {
|
|
283
|
+
if ("items" in classValue) {
|
|
284
|
+
const itemsValue = (classValue as any).items;
|
|
285
|
+
return { ...classValue, items: [scopeClass, itemsValue] };
|
|
286
|
+
}
|
|
287
|
+
if ("value" in classValue) {
|
|
288
|
+
const valueValue = (classValue as any).value;
|
|
289
|
+
return { ...classValue, value: [scopeClass, valueValue] };
|
|
290
|
+
}
|
|
291
|
+
return { [scopeClass]: true, ...classValue };
|
|
292
|
+
}
|
|
293
|
+
return [scopeClass, classValue];
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
if (props.class !== undefined) {
|
|
297
|
+
if (divAttrs.class) {
|
|
298
|
+
divAttrs.class = [props.class, divAttrs.class];
|
|
299
|
+
} else {
|
|
300
|
+
divAttrs.class = props.class;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (props.style !== undefined) {
|
|
305
|
+
if (
|
|
306
|
+
typeof divAttrs.style === "object"
|
|
307
|
+
&& divAttrs.style !== null
|
|
308
|
+
&& typeof props.style === "object"
|
|
309
|
+
&& props.style !== null
|
|
310
|
+
) {
|
|
311
|
+
divAttrs.style = { ...divAttrs.style, ...props.style };
|
|
312
|
+
} else if (divAttrs.style === undefined) {
|
|
313
|
+
divAttrs.style = props.style;
|
|
314
|
+
} else if (typeof divAttrs.style === "string" && typeof props.style === "string") {
|
|
315
|
+
divAttrs.style = `${divAttrs.style}; ${props.style}`;
|
|
316
|
+
} else {
|
|
317
|
+
divAttrs.style = props.style;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (props.zIndex !== undefined) {
|
|
322
|
+
if (typeof divAttrs.style === "object" && divAttrs.style !== null) {
|
|
323
|
+
divAttrs.style = { ...divAttrs.style, zIndex: props.zIndex };
|
|
324
|
+
} else if (typeof divAttrs.style === "string") {
|
|
325
|
+
divAttrs.style = `${divAttrs.style}; z-index: ${props.zIndex}`;
|
|
326
|
+
} else {
|
|
327
|
+
divAttrs.style = { zIndex: props.zIndex };
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (scopeClass) {
|
|
332
|
+
// Merge scope class with existing attrs.class
|
|
333
|
+
divProps.attrs = { ...divAttrs };
|
|
334
|
+
divProps.attrs.class = mergeScopeClass(divProps.attrs.class);
|
|
335
|
+
} else if (Object.keys(divAttrs).length > 0) {
|
|
336
|
+
divProps.attrs = divAttrs;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const routedChildren = this.routeDomChildren(props.children);
|
|
340
|
+
props.children = routedChildren;
|
|
341
|
+
const div = h(DOMElement, divProps, routedChildren) as unknown as Element<CanvasDOMElement>;
|
|
342
|
+
this.element = div.componentInstance.element;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async onMount(element: Element<any>, index?: number) {
|
|
346
|
+
await super.onMount(element, index);
|
|
347
|
+
this.syncCanvasSizeEffect();
|
|
348
|
+
this.applyElementSize();
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
onUpdate(props: any) {
|
|
352
|
+
super.onUpdate(props);
|
|
353
|
+
this.syncCanvasSizeEffect();
|
|
354
|
+
this.applyElementSize();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
onLayoutComputed() {
|
|
358
|
+
this.applyElementSize();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async onDestroy(parent: Element<any>, afterDestroy?: () => void) {
|
|
362
|
+
const _afterDestroy = () => {
|
|
363
|
+
if (this.canvasSizeEffect) {
|
|
364
|
+
this.canvasSizeEffect.subscription?.unsubscribe();
|
|
365
|
+
this.canvasSizeEffect = null;
|
|
366
|
+
}
|
|
367
|
+
if (afterDestroy) afterDestroy();
|
|
368
|
+
};
|
|
369
|
+
await super.onDestroy(parent, _afterDestroy);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export interface CanvasDOMContainer extends DisplayObjectProps { }
|
|
374
|
+
|
|
375
|
+
registerComponent("DOMContainer", CanvasDOMContainer);
|
|
376
|
+
|
|
377
|
+
export const DOMContainer: ComponentFunction<any> = (props) => {
|
|
378
|
+
return createComponent("DOMContainer", props);
|
|
379
|
+
};
|