canvasengine 1.3.0 → 2.0.1-beta.1
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/.cursorrules +0 -0
- package/.github/workflows/ci.yml +29 -0
- package/README.md +79 -0
- package/dist/compiler/vite.js +119 -0
- package/dist/compiler/vite.js.map +1 -0
- package/dist/index.d.ts +846 -0
- package/dist/index.js +3340 -0
- package/dist/index.js.map +1 -0
- package/index.d.ts +6 -0
- package/logo.png +0 -0
- package/package.json +84 -18
- package/src/compiler/grammar.pegjs +180 -0
- package/src/compiler/vite.ts +166 -0
- package/src/components/Canvas.ts +134 -0
- package/src/components/Container.ts +46 -0
- package/src/components/DisplayObject.ts +458 -0
- package/src/components/DrawMap/index.ts +65 -0
- package/src/components/Graphic.ts +147 -0
- package/src/components/NineSliceSprite.ts +46 -0
- package/src/components/ParticleEmitter.ts +39 -0
- package/src/components/Scene.ts +6 -0
- package/src/components/Sprite.ts +493 -0
- package/src/components/Text.ts +145 -0
- package/src/components/Tilemap/Tile.ts +79 -0
- package/src/components/Tilemap/TileGroup.ts +207 -0
- package/src/components/Tilemap/TileLayer.ts +163 -0
- package/src/components/Tilemap/TileSet.ts +41 -0
- package/src/components/Tilemap/index.ts +80 -0
- package/src/components/TilingSprite.ts +39 -0
- package/src/components/Viewport.ts +159 -0
- package/src/components/index.ts +12 -0
- package/src/components/types/DisplayObject.ts +68 -0
- package/src/components/types/MouseEvent.ts +3 -0
- package/src/components/types/Spritesheet.ts +389 -0
- package/src/components/types/index.ts +4 -0
- package/src/directives/Drag.ts +84 -0
- package/src/directives/KeyboardControls.ts +922 -0
- package/src/directives/Scheduler.ts +112 -0
- package/src/directives/Sound.ts +91 -0
- package/src/directives/Transition.ts +45 -0
- package/src/directives/ViewportCull.ts +40 -0
- package/src/directives/ViewportFollow.ts +26 -0
- package/src/directives/index.ts +7 -0
- package/src/engine/animation.ts +113 -0
- package/src/engine/bootstrap.ts +19 -0
- package/src/engine/directive.ts +23 -0
- package/src/engine/reactive.ts +379 -0
- package/src/engine/signal.ts +138 -0
- package/src/engine/trigger.ts +40 -0
- package/src/engine/utils.ts +135 -0
- package/src/hooks/addContext.ts +6 -0
- package/src/hooks/useProps.ts +155 -0
- package/src/hooks/useRef.ts +21 -0
- package/src/index.ts +14 -0
- package/src/presets/Bar.ts +89 -0
- package/src/presets/Button.ts +0 -0
- package/src/presets/Joystick.ts +286 -0
- package/src/presets/NightAmbiant.ts +122 -0
- package/src/presets/Particle.ts +53 -0
- package/src/utils/Ease.ts +33 -0
- package/src/utils/RadialGradient.ts +86 -0
- package/starter/assets/logo.png +0 -0
- package/starter/components/app.ce +18 -0
- package/starter/components/hello.ce +35 -0
- package/starter/index.html +21 -0
- package/starter/main.ts +6 -0
- package/starter/package.json +20 -0
- package/starter/tsconfig.json +32 -0
- package/starter/vite.config.ts +12 -0
- package/tsconfig.json +32 -0
- package/tsconfig.node.json +10 -0
- package/tsup.config.ts +28 -0
- package/vitest.config.ts +12 -0
- package/.gitattributes +0 -22
- package/.npmignore +0 -163
- package/canvasengine-1.3.0.all.min.js +0 -21
- package/canvasengine.js +0 -5802
- package/core/DB.js +0 -24
- package/core/ModelServer.js +0 -348
- package/core/Users.js +0 -190
- package/core/engine-common.js +0 -952
- package/doc/cocoonjs.md +0 -36
- package/doc/doc-lang.yml +0 -43
- package/doc/doc-router.yml +0 -14
- package/doc/doc-tuto.yml +0 -9
- package/doc/doc.yml +0 -39
- package/doc/element.md +0 -37
- package/doc/entity.md +0 -90
- package/doc/extend.md +0 -47
- package/doc/get_started.md +0 -19
- package/doc/images/entity.png +0 -0
- package/doc/multitouch.md +0 -58
- package/doc/nodejs.md +0 -142
- package/doc/scene.md +0 -44
- package/doc/text.md +0 -156
- package/examples/server/client.html +0 -31
- package/examples/server/server.js +0 -16
- package/examples/tiled_server/client.html +0 -52
- package/examples/tiled_server/images/tiles_spritesheet.png +0 -0
- package/examples/tiled_server/server/map.json +0 -50
- package/examples/tiled_server/server/map.tmx +0 -16
- package/examples/tiled_server/server/server.js +0 -16
- package/extends/Animation.js +0 -910
- package/extends/Effect.js +0 -252
- package/extends/Gleed2d.js +0 -252
- package/extends/Hit.js +0 -1509
- package/extends/Input.js +0 -699
- package/extends/Marshal.js +0 -716
- package/extends/Scrolling.js +0 -388
- package/extends/Soundmanager2.js +0 -5466
- package/extends/Spritesheet.js +0 -196
- package/extends/Text.js +0 -366
- package/extends/Tiled.js +0 -403
- package/extends/Window.js +0 -575
- package/extends/gamepad.js +0 -397
- package/extends/socket.io.min.js +0 -2
- package/extends/swf/soundmanager2.swf +0 -0
- package/extends/swf/soundmanager2_debug.swf +0 -0
- package/extends/swf/soundmanager2_flash9.swf +0 -0
- package/extends/swf/soundmanager2_flash9_debug.swf +0 -0
- package/extends/swf/soundmanager2_flash_xdomain.zip +0 -0
- package/extends/workers/transition.js +0 -43
- package/index.js +0 -46
- package/license.txt +0 -19
- package/readme.md +0 -483
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
{
|
|
2
|
+
function generateError(message, location) {
|
|
3
|
+
const { start, end } = location;
|
|
4
|
+
const errorMessage = `${message}\n` +
|
|
5
|
+
`at line ${start.line}, column ${start.column} to line ${end.line}, column ${end.column}`;
|
|
6
|
+
throw new Error(errorMessage);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
start
|
|
11
|
+
= _ elements:(element)* _ {
|
|
12
|
+
if (elements.length === 1) {
|
|
13
|
+
return elements[0];
|
|
14
|
+
}
|
|
15
|
+
return `[${elements.join(',')}]`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
element
|
|
19
|
+
= forLoop
|
|
20
|
+
/ ifCondition
|
|
21
|
+
/ selfClosingElement
|
|
22
|
+
/ openCloseElement
|
|
23
|
+
/ comment
|
|
24
|
+
|
|
25
|
+
selfClosingElement
|
|
26
|
+
= _ "<" _ tagName:tagName _ attributes:attributes _ "/>" _ {
|
|
27
|
+
const attrs = attributes.length > 0 ? `{ ${attributes.join(', ')} }` : null;
|
|
28
|
+
return attrs ? `h(${tagName}, ${attrs})` : `h(${tagName})`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
openCloseElement
|
|
32
|
+
= "<" _ tagName:tagName _ attributes:attributes _ ">" _ content:content _ "</" _ closingTagName:tagName _ ">" {
|
|
33
|
+
if (tagName !== closingTagName) {
|
|
34
|
+
error("Mismatched opening and closing tags");
|
|
35
|
+
}
|
|
36
|
+
const attrs = attributes.length > 0 ? `{ ${attributes.join(', ')} }` : null;
|
|
37
|
+
const children = content ? content : null;
|
|
38
|
+
if (attrs && children) {
|
|
39
|
+
return `h(${tagName}, ${attrs}, ${children})`;
|
|
40
|
+
} else if (attrs) {
|
|
41
|
+
return `h(${tagName}, ${attrs})`;
|
|
42
|
+
} else if (children) {
|
|
43
|
+
return `h(${tagName}, null, ${children})`;
|
|
44
|
+
} else {
|
|
45
|
+
return `h(${tagName})`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/ "<" _ tagName:tagName _ attributes:attributes _ {
|
|
50
|
+
generateError("Syntaxe d'élément invalide", location());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
attributes
|
|
54
|
+
= attrs:(attribute (_ attribute)*)? {
|
|
55
|
+
return attrs
|
|
56
|
+
? [attrs[0]].concat(attrs[1].map(a => a[1]))
|
|
57
|
+
: [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
attribute
|
|
61
|
+
= staticAttribute
|
|
62
|
+
/ dynamicAttribute
|
|
63
|
+
/ eventHandler
|
|
64
|
+
|
|
65
|
+
eventHandler
|
|
66
|
+
= "@" eventName:identifier _ "=" _ "{" _ handlerName:attributeValue _ "}" {
|
|
67
|
+
return `${eventName}: ${handlerName}`;
|
|
68
|
+
}
|
|
69
|
+
/ "@" eventName:attributeName _ {
|
|
70
|
+
return eventName;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
dynamicAttribute
|
|
74
|
+
= attributeName:attributeName _ "=" _ "{" _ attributeValue:attributeValue _ "}" {
|
|
75
|
+
if (attributeValue.trim().match(/^[a-zA-Z_]\w*$/)) {
|
|
76
|
+
return `${attributeName}: ${attributeValue}`;
|
|
77
|
+
} else {
|
|
78
|
+
return `${attributeName}: computed(() => ${attributeValue.replace(/@?[a-zA-Z_][a-zA-Z0-9_]*(?!:)/g, (match) => {
|
|
79
|
+
if (match.startsWith('@')) {
|
|
80
|
+
return match.substring(1);
|
|
81
|
+
}
|
|
82
|
+
return `${match}()`;
|
|
83
|
+
})})`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/ attributeName:attributeName _ {
|
|
87
|
+
return attributeName;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
attributeValue
|
|
91
|
+
= $([^{}]* ("{" [^{}]* "}" [^{}]*)*) {
|
|
92
|
+
const t = text().trim()
|
|
93
|
+
if (t.startsWith("{") && t.endsWith("}")) {
|
|
94
|
+
return `(${t})`;
|
|
95
|
+
}
|
|
96
|
+
return t
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
staticAttribute
|
|
100
|
+
= attributeName:attributeName _ "=" _ "\"" attributeValue:staticValue "\"" {
|
|
101
|
+
return `${attributeName}: ${attributeValue}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
eventAttribute
|
|
105
|
+
= "(" _ eventName:eventName _ ")" _ "=" _ "\"" eventAction:eventAction "\"" {
|
|
106
|
+
return `${eventName}: () => { ${eventAction} }`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
staticValue
|
|
110
|
+
= [^"]+ {
|
|
111
|
+
var val = text();
|
|
112
|
+
return isNaN(val) ? `'${val}'` : val;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
content
|
|
116
|
+
= elements:(element)* {
|
|
117
|
+
const filteredElements = elements.filter(el => el !== null);
|
|
118
|
+
if (filteredElements.length === 0) return null;
|
|
119
|
+
if (filteredElements.length === 1) return filteredElements[0];
|
|
120
|
+
return `[${filteredElements.join(', ')}]`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
textNode
|
|
124
|
+
= text:$([^<]+) {
|
|
125
|
+
const trimmed = text.trim();
|
|
126
|
+
return trimmed ? `'${trimmed}'` : null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
textElement
|
|
130
|
+
= text:[^<>]+ {
|
|
131
|
+
const trimmed = text.join('').trim();
|
|
132
|
+
return trimmed ? JSON.stringify(trimmed) : null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
forLoop
|
|
136
|
+
= _ "@for" _ "(" _ variableName:identifier _ "of" _ iterable:identifier _ ")" _ "{" _ content:content _ "}" _ {
|
|
137
|
+
return `loop(${iterable}, (${variableName}) => ${content})`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
ifCondition
|
|
141
|
+
= _ "@if" _ "(" _ condition:condition _ ")" _ "{" _ content:content _ "}" _ {
|
|
142
|
+
return `cond(${condition}, () => ${content})`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
tagName
|
|
146
|
+
= [a-zA-Z][a-zA-Z0-9]* { return text(); }
|
|
147
|
+
|
|
148
|
+
attributeName
|
|
149
|
+
= [a-zA-Z][a-zA-Z0-9-]* { return text(); }
|
|
150
|
+
|
|
151
|
+
eventName
|
|
152
|
+
= [a-zA-Z][a-zA-Z0-9-]* { return text(); }
|
|
153
|
+
|
|
154
|
+
variableName
|
|
155
|
+
= [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
|
|
156
|
+
|
|
157
|
+
iterable
|
|
158
|
+
= [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
|
|
159
|
+
|
|
160
|
+
condition
|
|
161
|
+
= $([^)]*) { return text().trim(); }
|
|
162
|
+
|
|
163
|
+
eventAction
|
|
164
|
+
= [^"]* { return text(); }
|
|
165
|
+
|
|
166
|
+
_ 'whitespace'
|
|
167
|
+
= [ \t\n\r]*
|
|
168
|
+
|
|
169
|
+
identifier
|
|
170
|
+
= [a-zA-Z_][a-zA-Z0-9_]* { return text(); }
|
|
171
|
+
|
|
172
|
+
comment
|
|
173
|
+
= singleComment+ {
|
|
174
|
+
return null
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
singleComment
|
|
178
|
+
= "<!--" _ content:((!("-->") .)* "-->") _ {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { createFilter } from "vite";
|
|
2
|
+
import { parse } from "acorn";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import pkg from "peggy";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import * as ts from "typescript"; // Import TypeScript package
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
const { generate } = pkg;
|
|
10
|
+
|
|
11
|
+
const DEV_SRC = "../../src"
|
|
12
|
+
|
|
13
|
+
export default function canvasengine() {
|
|
14
|
+
const filter = createFilter("**/*.ce");
|
|
15
|
+
|
|
16
|
+
// Convert import.meta.url to a file path
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = path.dirname(__filename);
|
|
19
|
+
|
|
20
|
+
const grammar = fs.readFileSync(
|
|
21
|
+
path.join(__dirname, "grammar.pegjs").replace("dist/compiler/grammar.pegjs", "src/compiler/grammar.pegjs"),
|
|
22
|
+
"utf8");
|
|
23
|
+
const parser = generate(grammar);
|
|
24
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
25
|
+
const FLAG_COMMENT = "/*--[TPL]--*/";
|
|
26
|
+
|
|
27
|
+
const PRIMITIVE_COMPONENTS = [
|
|
28
|
+
"Canvas",
|
|
29
|
+
"Sprite",
|
|
30
|
+
"Text",
|
|
31
|
+
"Joystick",
|
|
32
|
+
"Viewport",
|
|
33
|
+
"Graphics",
|
|
34
|
+
"Container",
|
|
35
|
+
"ImageMap",
|
|
36
|
+
"NineSliceSprite",
|
|
37
|
+
"Rect",
|
|
38
|
+
"Circle",
|
|
39
|
+
"svg"
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
name: "vite-plugin-ce",
|
|
44
|
+
transform(code: string, id: string) {
|
|
45
|
+
if (!filter(id)) return;
|
|
46
|
+
|
|
47
|
+
// Extract the script content
|
|
48
|
+
const scriptMatch = code.match(/<script>([\s\S]*?)<\/script>/);
|
|
49
|
+
let scriptContent = scriptMatch ? scriptMatch[1].trim() : "";
|
|
50
|
+
|
|
51
|
+
// Transform SVG tags to Svg components
|
|
52
|
+
let template = code.replace(/<script>[\s\S]*?<\/script>/, "")
|
|
53
|
+
.replace(/^\s+|\s+$/g, '');
|
|
54
|
+
|
|
55
|
+
// Add SVG transformation
|
|
56
|
+
template = template.replace(/<svg>([\s\S]*?)<\/svg>/g, (match, content) => {
|
|
57
|
+
return `<Svg content="${content.trim()}" />`;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const parsedTemplate = parser.parse(template);
|
|
61
|
+
|
|
62
|
+
// trick to avoid typescript remove imports in scriptContent
|
|
63
|
+
scriptContent += FLAG_COMMENT + parsedTemplate
|
|
64
|
+
|
|
65
|
+
let transpiledCode = ts.transpileModule(scriptContent, {
|
|
66
|
+
compilerOptions: {
|
|
67
|
+
module: ts.ModuleKind.Preserve,
|
|
68
|
+
},
|
|
69
|
+
}).outputText;
|
|
70
|
+
|
|
71
|
+
// remove code after /*---*/
|
|
72
|
+
transpiledCode = transpiledCode.split(FLAG_COMMENT)[0]
|
|
73
|
+
|
|
74
|
+
// Use Acorn to parse the script content
|
|
75
|
+
const parsed = parse(transpiledCode, {
|
|
76
|
+
sourceType: "module",
|
|
77
|
+
ecmaVersion: 2020,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Extract imports
|
|
81
|
+
const imports = parsed.body.filter(
|
|
82
|
+
(node) => node.type === "ImportDeclaration"
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Extract non-import statements from scriptContent
|
|
86
|
+
const nonImportCode = parsed.body
|
|
87
|
+
.filter((node) => node.type !== "ImportDeclaration")
|
|
88
|
+
.map((node) => transpiledCode.slice(node.start, node.end))
|
|
89
|
+
.join("\n");
|
|
90
|
+
|
|
91
|
+
let importsCode = imports
|
|
92
|
+
.map((imp) => {
|
|
93
|
+
let importCode = transpiledCode.slice(imp.start, imp.end);
|
|
94
|
+
if (isDev && importCode.includes("from 'canvasengine'")) {
|
|
95
|
+
importCode = importCode.replace(
|
|
96
|
+
"from 'canvasengine'",
|
|
97
|
+
`from '${DEV_SRC}'`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return importCode;
|
|
101
|
+
})
|
|
102
|
+
.join("\n");
|
|
103
|
+
|
|
104
|
+
// Define an array for required imports
|
|
105
|
+
const requiredImports = ["h", "computed", "cond", "loop"];
|
|
106
|
+
|
|
107
|
+
// Check for missing imports
|
|
108
|
+
const missingImports = requiredImports.filter(
|
|
109
|
+
(importName) =>
|
|
110
|
+
!imports.some(
|
|
111
|
+
(imp) =>
|
|
112
|
+
imp.specifiers &&
|
|
113
|
+
imp.specifiers.some(
|
|
114
|
+
(spec) =>
|
|
115
|
+
spec.type === "ImportSpecifier" &&
|
|
116
|
+
spec.imported &&
|
|
117
|
+
'name' in spec.imported &&
|
|
118
|
+
spec.imported.name === importName
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Add missing imports
|
|
124
|
+
if (missingImports.length > 0) {
|
|
125
|
+
const additionalImportCode = `import { ${missingImports.join(
|
|
126
|
+
", "
|
|
127
|
+
)} } from ${isDev ? `'${DEV_SRC}'` : "'canvasengine'"};`;
|
|
128
|
+
importsCode = `${additionalImportCode}\n${importsCode}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check for primitive components in parsedTemplate
|
|
132
|
+
const primitiveImports = PRIMITIVE_COMPONENTS.filter((component) =>
|
|
133
|
+
parsedTemplate.includes(`h(${component}`)
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Add missing imports for primitive components
|
|
137
|
+
primitiveImports.forEach((component) => {
|
|
138
|
+
const importStatement = `import { ${component} } from ${
|
|
139
|
+
isDev ? `'${DEV_SRC}'` : "'canvasengine'"
|
|
140
|
+
};`;
|
|
141
|
+
if (!importsCode.includes(importStatement)) {
|
|
142
|
+
importsCode = `${importStatement}\n${importsCode}`;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Generate the output
|
|
147
|
+
const output = String.raw`
|
|
148
|
+
${importsCode}
|
|
149
|
+
import { useProps, useDefineProps } from ${isDev ? `'${DEV_SRC}'` : "'canvasengine'"}
|
|
150
|
+
|
|
151
|
+
export default function component($$props) {
|
|
152
|
+
const $props = useProps($$props)
|
|
153
|
+
const defineProps = useDefineProps($$props)
|
|
154
|
+
${nonImportCode}
|
|
155
|
+
let $this = ${parsedTemplate}
|
|
156
|
+
return $this
|
|
157
|
+
}
|
|
158
|
+
`;
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
code: output,
|
|
162
|
+
map: null,
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { effect, Signal, signal } from "@signe/reactive";
|
|
2
|
+
import { Container, autoDetectRenderer } from "pixi.js";
|
|
3
|
+
import { loadYoga } from "yoga-layout";
|
|
4
|
+
import { Props, createComponent, registerComponent, Element } from "../engine/reactive";
|
|
5
|
+
import { useProps } from "../hooks/useProps";
|
|
6
|
+
import { ComponentInstance, DisplayObject } from "./DisplayObject";
|
|
7
|
+
import { ComponentFunction } from "../engine/signal";
|
|
8
|
+
import { SignalOrPrimitive } from "./types";
|
|
9
|
+
import { Size } from "./types/DisplayObject";
|
|
10
|
+
import { Scheduler, Tick } from "../directives/Scheduler";
|
|
11
|
+
|
|
12
|
+
interface CanvasElement extends Element<ComponentInstance> {
|
|
13
|
+
render: (rootElement: HTMLElement) => void;
|
|
14
|
+
directives: {
|
|
15
|
+
tick: Scheduler
|
|
16
|
+
};
|
|
17
|
+
propObservables: {
|
|
18
|
+
tick: Signal<Tick>
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
registerComponent("Canvas", class Canvas extends DisplayObject(Container) {});
|
|
23
|
+
|
|
24
|
+
export interface CanvasProps extends Props {
|
|
25
|
+
cursorStyles?: () => any;
|
|
26
|
+
width?: SignalOrPrimitive<Size>;
|
|
27
|
+
height?: SignalOrPrimitive<Size>;
|
|
28
|
+
canvasEl?: HTMLElement;
|
|
29
|
+
selector?: string;
|
|
30
|
+
isRoot?: boolean;
|
|
31
|
+
tick?: any;
|
|
32
|
+
class?: SignalOrPrimitive<string>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Canvas: ComponentFunction<CanvasProps> = async (props = {}) => {
|
|
36
|
+
let { cursorStyles, width, height, class: className } = useProps(props);
|
|
37
|
+
const Yoga = await loadYoga();
|
|
38
|
+
|
|
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
|
+
});
|
|
47
|
+
|
|
48
|
+
const canvasSize = signal({
|
|
49
|
+
width: renderer.width,
|
|
50
|
+
height: renderer.height,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
props.isRoot = true;
|
|
54
|
+
const options: CanvasProps = {
|
|
55
|
+
...props,
|
|
56
|
+
context: {
|
|
57
|
+
Yoga,
|
|
58
|
+
renderer,
|
|
59
|
+
canvasSize,
|
|
60
|
+
},
|
|
61
|
+
width: width?.(),
|
|
62
|
+
height: height?.(),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
if (!props.tick) {
|
|
66
|
+
options.context!.tick = options.tick = signal({
|
|
67
|
+
timestamp: 0,
|
|
68
|
+
deltaTime: 0,
|
|
69
|
+
frame: 0,
|
|
70
|
+
deltaRatio: 1,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const canvasElement = createComponent("Canvas", options) as CanvasElement;
|
|
74
|
+
|
|
75
|
+
canvasElement.render = (rootElement: HTMLElement) => {
|
|
76
|
+
const canvasEl = renderer.view.canvas as HTMLCanvasElement;
|
|
77
|
+
|
|
78
|
+
(globalThis as any).__PIXI_STAGE__ = canvasElement.componentInstance;
|
|
79
|
+
(globalThis as any).__PIXI_RENDERER__ = renderer;
|
|
80
|
+
|
|
81
|
+
if (props.tickStart !== false) canvasElement.directives.tick.start()
|
|
82
|
+
|
|
83
|
+
effect(() => {
|
|
84
|
+
canvasElement.propObservables!.tick();
|
|
85
|
+
renderer.render(canvasElement.componentInstance as any);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (cursorStyles) {
|
|
89
|
+
effect(() => {
|
|
90
|
+
renderer.events.cursorStyles = cursorStyles();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (className) {
|
|
95
|
+
effect(() => {
|
|
96
|
+
canvasEl.classList.add(className());
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const resizeCanvas = async () => {
|
|
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');
|
|
121
|
+
if (existingCanvas) {
|
|
122
|
+
// If it exists, replace it with the new canvas
|
|
123
|
+
rootElement.replaceChild(canvasEl, existingCanvas);
|
|
124
|
+
} else {
|
|
125
|
+
// If it doesn't exist, append the new canvas
|
|
126
|
+
rootElement.appendChild(canvasEl);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Initial resize
|
|
130
|
+
resizeCanvas();
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return canvasElement;
|
|
134
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Container as PixiContainer } from "pixi.js";
|
|
2
|
+
import { createComponent, registerComponent } from "../engine/reactive";
|
|
3
|
+
import { DisplayObject } from "./DisplayObject";
|
|
4
|
+
import { ComponentFunction } from "../engine/signal";
|
|
5
|
+
import { DisplayObjectProps } from "./types/DisplayObject";
|
|
6
|
+
import { setObservablePoint } from "../engine/utils";
|
|
7
|
+
|
|
8
|
+
interface ContainerProps extends DisplayObjectProps {
|
|
9
|
+
sortableChildren?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class CanvasContainer extends DisplayObject(PixiContainer) {
|
|
13
|
+
isCustomAnchor = true;
|
|
14
|
+
|
|
15
|
+
onUpdate(props) {
|
|
16
|
+
if (props.anchor) {
|
|
17
|
+
setObservablePoint(this._anchorPoints, props.anchor);
|
|
18
|
+
props.pivot = [
|
|
19
|
+
this.getWidth() * this._anchorPoints.x,
|
|
20
|
+
this.getHeight() * this._anchorPoints.y
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
super.onUpdate(props);
|
|
24
|
+
if (props.sortableChildren != undefined) {
|
|
25
|
+
this.sortableChildren = props.sortableChildren;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
onMount(args) {
|
|
29
|
+
super.onMount(args);
|
|
30
|
+
const { componentInstance, props } = args;
|
|
31
|
+
const { pixiChildren } = props;
|
|
32
|
+
if (pixiChildren) {
|
|
33
|
+
pixiChildren.forEach((child) => {
|
|
34
|
+
componentInstance.addChild(child);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface CanvasContainer extends DisplayObjectProps {}
|
|
41
|
+
|
|
42
|
+
registerComponent("Container", CanvasContainer);
|
|
43
|
+
|
|
44
|
+
export const Container: ComponentFunction<ContainerProps> = (props) => {
|
|
45
|
+
return createComponent("Container", props);
|
|
46
|
+
};
|