@playcanvas/web-components 0.8.1 → 0.9.0
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/app.d.ts +10 -2
- package/dist/asset.d.ts +18 -3
- package/dist/async-element.d.ts +51 -4
- package/dist/components/button-component.d.ts +10 -4
- package/dist/components/camera-component.d.ts +9 -4
- package/dist/components/collision-component.d.ts +9 -4
- package/dist/components/component.d.ts +7 -1
- package/dist/components/element-component.d.ts +17 -12
- package/dist/components/gsplat-component.d.ts +6 -1
- package/dist/components/layoutchild-component.d.ts +6 -1
- package/dist/components/layoutgroup-component.d.ts +21 -15
- package/dist/components/light-component.d.ts +12 -6
- package/dist/components/listener-component.d.ts +6 -1
- package/dist/components/particlesystem-component.d.ts +6 -1
- package/dist/components/render-component.d.ts +13 -4
- package/dist/components/rigidbody-component.d.ts +9 -4
- package/dist/components/screen-component.d.ts +6 -1
- package/dist/components/script-component.d.ts +116 -16
- package/dist/components/script.d.ts +72 -8
- package/dist/components/scrollbar-component.d.ts +10 -4
- package/dist/components/scrollview-component.d.ts +17 -10
- package/dist/components/sound-component.d.ts +6 -1
- package/dist/components/sound-slot.d.ts +8 -3
- package/dist/entity.d.ts +26 -2
- package/dist/index.d.ts +3 -2
- package/dist/material.d.ts +10 -0
- package/dist/model.d.ts +5 -0
- package/dist/module.d.ts +5 -0
- package/dist/pwc.cjs +1574 -853
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +1574 -853
- package/dist/pwc.js.map +1 -1
- package/dist/pwc.min.js +1 -1
- package/dist/pwc.min.js.map +1 -1
- package/dist/pwc.mjs +1575 -855
- package/dist/pwc.mjs.map +1 -1
- package/dist/scene.d.ts +14 -5
- package/dist/sky.d.ts +6 -0
- package/dist/utils.d.ts +81 -23
- package/package.json +23 -13
- package/src/app.ts +38 -12
- package/src/asset.ts +61 -8
- package/src/async-element.ts +86 -5
- package/src/components/button-component.ts +26 -19
- package/src/components/camera-component.ts +29 -23
- package/src/components/collision-component.ts +19 -13
- package/src/components/component.ts +32 -13
- package/src/components/element-component.ts +58 -52
- package/src/components/gsplat-component.ts +14 -7
- package/src/components/layoutchild-component.ts +16 -9
- package/src/components/layoutgroup-component.ts +38 -31
- package/src/components/light-component.ts +33 -31
- package/src/components/listener-component.ts +8 -2
- package/src/components/particlesystem-component.ts +8 -2
- package/src/components/render-component.ts +19 -8
- package/src/components/rigidbody-component.ts +21 -15
- package/src/components/screen-component.ts +15 -9
- package/src/components/script-component.ts +512 -125
- package/src/components/script.ts +116 -16
- package/src/components/scrollbar-component.ts +19 -12
- package/src/components/scrollview-component.ts +37 -29
- package/src/components/sound-component.ts +16 -9
- package/src/components/sound-slot.ts +23 -14
- package/src/entity.ts +61 -71
- package/src/index.ts +5 -2
- package/src/material.ts +34 -1
- package/src/model.ts +6 -0
- package/src/module.ts +6 -0
- package/src/scene.ts +25 -12
- package/src/sky.ts +34 -16
- package/src/utils.ts +180 -40
package/dist/pwc.cjs
CHANGED
|
@@ -22,21 +22,51 @@ class AsyncElement extends HTMLElement {
|
|
|
22
22
|
return (_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.closest('pc-entity');
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* Called when the element is fully initialized and ready.
|
|
26
|
-
*
|
|
25
|
+
* Called when the element is fully initialized and ready. Subclasses should call this when
|
|
26
|
+
* they're ready. Resolves the ready promise and dispatches a bubbling, composed `ready`
|
|
27
|
+
* event.
|
|
27
28
|
*/
|
|
28
29
|
_onReady() {
|
|
29
30
|
this._readyResolve();
|
|
30
|
-
this.dispatchEvent(new CustomEvent('ready'));
|
|
31
|
+
this.dispatchEvent(new CustomEvent('ready', { bubbles: true, composed: true }));
|
|
31
32
|
}
|
|
32
33
|
/**
|
|
33
|
-
* Returns a promise that resolves with this element when it's ready.
|
|
34
|
+
* Returns a promise that resolves with this element when it's ready. This is the low-level
|
|
35
|
+
* primitive underlying {@link whenReady}, which is the recommended way to wait for elements.
|
|
34
36
|
* @returns A promise that resolves with this element when it's ready.
|
|
35
37
|
*/
|
|
36
38
|
ready() {
|
|
37
39
|
return this._readyPromise.then(() => this);
|
|
38
40
|
}
|
|
39
41
|
}
|
|
42
|
+
async function whenReady(target) {
|
|
43
|
+
let element;
|
|
44
|
+
if (typeof target === 'string') {
|
|
45
|
+
if (document.readyState === 'loading') {
|
|
46
|
+
await new Promise((resolve) => {
|
|
47
|
+
document.addEventListener('DOMContentLoaded', resolve, { once: true });
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
element = document.querySelector(target);
|
|
52
|
+
}
|
|
53
|
+
catch (_a) {
|
|
54
|
+
throw new Error(`whenReady: '${target}' is not a valid CSS selector`);
|
|
55
|
+
}
|
|
56
|
+
if (!element) {
|
|
57
|
+
throw new Error(`whenReady: no element found matching '${target}'`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
element = target;
|
|
62
|
+
}
|
|
63
|
+
if (!(element instanceof AsyncElement)) {
|
|
64
|
+
const description = element instanceof Element ? `<${element.tagName.toLowerCase()}>` : String(target);
|
|
65
|
+
throw new Error(`whenReady: ${description} does not initialize asynchronously`);
|
|
66
|
+
}
|
|
67
|
+
await element.ready();
|
|
68
|
+
return element;
|
|
69
|
+
}
|
|
40
70
|
|
|
41
71
|
/**
|
|
42
72
|
* The ModuleElement interface provides properties and methods for manipulating
|
|
@@ -72,6 +102,393 @@ class ModuleElement extends HTMLElement {
|
|
|
72
102
|
}
|
|
73
103
|
customElements.define('pc-module', ModuleElement);
|
|
74
104
|
|
|
105
|
+
const CSS_COLORS = {
|
|
106
|
+
aliceblue: '#f0f8ff',
|
|
107
|
+
antiquewhite: '#faebd7',
|
|
108
|
+
aqua: '#00ffff',
|
|
109
|
+
aquamarine: '#7fffd4',
|
|
110
|
+
azure: '#f0ffff',
|
|
111
|
+
beige: '#f5f5dc',
|
|
112
|
+
bisque: '#ffe4c4',
|
|
113
|
+
black: '#000000',
|
|
114
|
+
blanchedalmond: '#ffebcd',
|
|
115
|
+
blue: '#0000ff',
|
|
116
|
+
blueviolet: '#8a2be2',
|
|
117
|
+
brown: '#a52a2a',
|
|
118
|
+
burlywood: '#deb887',
|
|
119
|
+
cadetblue: '#5f9ea0',
|
|
120
|
+
chartreuse: '#7fff00',
|
|
121
|
+
chocolate: '#d2691e',
|
|
122
|
+
coral: '#ff7f50',
|
|
123
|
+
cornflowerblue: '#6495ed',
|
|
124
|
+
cornsilk: '#fff8dc',
|
|
125
|
+
crimson: '#dc143c',
|
|
126
|
+
cyan: '#00ffff',
|
|
127
|
+
darkblue: '#00008b',
|
|
128
|
+
darkcyan: '#008b8b',
|
|
129
|
+
darkgoldenrod: '#b8860b',
|
|
130
|
+
darkgray: '#a9a9a9',
|
|
131
|
+
darkgreen: '#006400',
|
|
132
|
+
darkgrey: '#a9a9a9',
|
|
133
|
+
darkkhaki: '#bdb76b',
|
|
134
|
+
darkmagenta: '#8b008b',
|
|
135
|
+
darkolivegreen: '#556b2f',
|
|
136
|
+
darkorange: '#ff8c00',
|
|
137
|
+
darkorchid: '#9932cc',
|
|
138
|
+
darkred: '#8b0000',
|
|
139
|
+
darksalmon: '#e9967a',
|
|
140
|
+
darkseagreen: '#8fbc8f',
|
|
141
|
+
darkslateblue: '#483d8b',
|
|
142
|
+
darkslategray: '#2f4f4f',
|
|
143
|
+
darkslategrey: '#2f4f4f',
|
|
144
|
+
darkturquoise: '#00ced1',
|
|
145
|
+
darkviolet: '#9400d3',
|
|
146
|
+
deeppink: '#ff1493',
|
|
147
|
+
deepskyblue: '#00bfff',
|
|
148
|
+
dimgray: '#696969',
|
|
149
|
+
dimgrey: '#696969',
|
|
150
|
+
dodgerblue: '#1e90ff',
|
|
151
|
+
firebrick: '#b22222',
|
|
152
|
+
floralwhite: '#fffaf0',
|
|
153
|
+
forestgreen: '#228b22',
|
|
154
|
+
fuchsia: '#ff00ff',
|
|
155
|
+
gainsboro: '#dcdcdc',
|
|
156
|
+
ghostwhite: '#f8f8ff',
|
|
157
|
+
gold: '#ffd700',
|
|
158
|
+
goldenrod: '#daa520',
|
|
159
|
+
gray: '#808080',
|
|
160
|
+
green: '#008000',
|
|
161
|
+
greenyellow: '#adff2f',
|
|
162
|
+
grey: '#808080',
|
|
163
|
+
honeydew: '#f0fff0',
|
|
164
|
+
hotpink: '#ff69b4',
|
|
165
|
+
indianred: '#cd5c5c',
|
|
166
|
+
indigo: '#4b0082',
|
|
167
|
+
ivory: '#fffff0',
|
|
168
|
+
khaki: '#f0e68c',
|
|
169
|
+
lavender: '#e6e6fa',
|
|
170
|
+
lavenderblush: '#fff0f5',
|
|
171
|
+
lawngreen: '#7cfc00',
|
|
172
|
+
lemonchiffon: '#fffacd',
|
|
173
|
+
lightblue: '#add8e6',
|
|
174
|
+
lightcoral: '#f08080',
|
|
175
|
+
lightcyan: '#e0ffff',
|
|
176
|
+
lightgoldenrodyellow: '#fafad2',
|
|
177
|
+
lightgray: '#d3d3d3',
|
|
178
|
+
lightgreen: '#90ee90',
|
|
179
|
+
lightgrey: '#d3d3d3',
|
|
180
|
+
lightpink: '#ffb6c1',
|
|
181
|
+
lightsalmon: '#ffa07a',
|
|
182
|
+
lightseagreen: '#20b2aa',
|
|
183
|
+
lightskyblue: '#87cefa',
|
|
184
|
+
lightslategray: '#778899',
|
|
185
|
+
lightslategrey: '#778899',
|
|
186
|
+
lightsteelblue: '#b0c4de',
|
|
187
|
+
lightyellow: '#ffffe0',
|
|
188
|
+
lime: '#00ff00',
|
|
189
|
+
limegreen: '#32cd32',
|
|
190
|
+
linen: '#faf0e6',
|
|
191
|
+
magenta: '#ff00ff',
|
|
192
|
+
maroon: '#800000',
|
|
193
|
+
mediumaquamarine: '#66cdaa',
|
|
194
|
+
mediumblue: '#0000cd',
|
|
195
|
+
mediumorchid: '#ba55d3',
|
|
196
|
+
mediumpurple: '#9370db',
|
|
197
|
+
mediumseagreen: '#3cb371',
|
|
198
|
+
mediumslateblue: '#7b68ee',
|
|
199
|
+
mediumspringgreen: '#00fa9a',
|
|
200
|
+
mediumturquoise: '#48d1cc',
|
|
201
|
+
mediumvioletred: '#c71585',
|
|
202
|
+
midnightblue: '#191970',
|
|
203
|
+
mintcream: '#f5fffa',
|
|
204
|
+
mistyrose: '#ffe4e1',
|
|
205
|
+
moccasin: '#ffe4b5',
|
|
206
|
+
navajowhite: '#ffdead',
|
|
207
|
+
navy: '#000080',
|
|
208
|
+
oldlace: '#fdf5e6',
|
|
209
|
+
olive: '#808000',
|
|
210
|
+
olivedrab: '#6b8e23',
|
|
211
|
+
orange: '#ffa500',
|
|
212
|
+
orangered: '#ff4500',
|
|
213
|
+
orchid: '#da70d6',
|
|
214
|
+
palegoldenrod: '#eee8aa',
|
|
215
|
+
palegreen: '#98fb98',
|
|
216
|
+
paleturquoise: '#afeeee',
|
|
217
|
+
palevioletred: '#db7093',
|
|
218
|
+
papayawhip: '#ffefd5',
|
|
219
|
+
peachpuff: '#ffdab9',
|
|
220
|
+
peru: '#cd853f',
|
|
221
|
+
pink: '#ffc0cb',
|
|
222
|
+
plum: '#dda0dd',
|
|
223
|
+
powderblue: '#b0e0e6',
|
|
224
|
+
purple: '#800080',
|
|
225
|
+
rebeccapurple: '#663399',
|
|
226
|
+
red: '#ff0000',
|
|
227
|
+
rosybrown: '#bc8f8f',
|
|
228
|
+
royalblue: '#4169e1',
|
|
229
|
+
saddlebrown: '#8b4513',
|
|
230
|
+
salmon: '#fa8072',
|
|
231
|
+
sandybrown: '#f4a460',
|
|
232
|
+
seagreen: '#2e8b57',
|
|
233
|
+
seashell: '#fff5ee',
|
|
234
|
+
sienna: '#a0522d',
|
|
235
|
+
silver: '#c0c0c0',
|
|
236
|
+
skyblue: '#87ceeb',
|
|
237
|
+
slateblue: '#6a5acd',
|
|
238
|
+
slategray: '#708090',
|
|
239
|
+
slategrey: '#708090',
|
|
240
|
+
snow: '#fffafa',
|
|
241
|
+
springgreen: '#00ff7f',
|
|
242
|
+
steelblue: '#4682b4',
|
|
243
|
+
tan: '#d2b48c',
|
|
244
|
+
teal: '#008080',
|
|
245
|
+
thistle: '#d8bfd8',
|
|
246
|
+
tomato: '#ff6347',
|
|
247
|
+
turquoise: '#40e0d0',
|
|
248
|
+
violet: '#ee82ee',
|
|
249
|
+
wheat: '#f5deb3',
|
|
250
|
+
white: '#ffffff',
|
|
251
|
+
whitesmoke: '#f5f5f5',
|
|
252
|
+
yellow: '#ffff00',
|
|
253
|
+
yellowgreen: '#9acd32'
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Parse a boolean attribute value. The same rules apply to every boolean attribute:
|
|
258
|
+
*
|
|
259
|
+
* - Attribute absent (or removed): the supplied default is used.
|
|
260
|
+
* - Attribute set to the string 'false': `false`.
|
|
261
|
+
* - Attribute present with any other value, including the empty string of a bare boolean
|
|
262
|
+
* attribute (e.g. `<pc-light cast-shadows>`): `true`.
|
|
263
|
+
*
|
|
264
|
+
* @param value - The attribute value to parse (`null` when the attribute is absent).
|
|
265
|
+
* @param defaultValue - The value to use when the attribute is absent or removed.
|
|
266
|
+
* @returns The parsed boolean.
|
|
267
|
+
*/
|
|
268
|
+
const parseBool = (value, defaultValue) => {
|
|
269
|
+
return value === null ? defaultValue : value !== 'false';
|
|
270
|
+
};
|
|
271
|
+
/**
|
|
272
|
+
* Splits an attribute value into exactly `count` numeric components. Returns `null` when the
|
|
273
|
+
* value does not consist of exactly `count` whitespace-separated finite numbers.
|
|
274
|
+
*
|
|
275
|
+
* @param value - The value to split.
|
|
276
|
+
* @param count - The required number of components.
|
|
277
|
+
* @returns The parsed components, or `null`.
|
|
278
|
+
* @ignore
|
|
279
|
+
*/
|
|
280
|
+
const parseComponents = (value, count) => {
|
|
281
|
+
const components = value.trim().split(/\s+/).map(Number);
|
|
282
|
+
if (components.length !== count || components.some(component => !Number.isFinite(component))) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
return components;
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* Clones a math-type default so parsed results never alias the caller's default instance. This
|
|
289
|
+
* is what makes it safe to pass the engine's shared frozen constants (e.g. `Vec3.ZERO`,
|
|
290
|
+
* `Color.WHITE`) as defaults.
|
|
291
|
+
*
|
|
292
|
+
* @param value - The default value to clone (`null` is passed through).
|
|
293
|
+
* @returns The cloned value.
|
|
294
|
+
*/
|
|
295
|
+
const cloneDefault = (value) => {
|
|
296
|
+
return (value === null ? null : value.clone());
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* Parse a color attribute value. The expected format is a CSS color name (e.g. 'rebeccapurple'),
|
|
300
|
+
* a hex color (e.g. '#ff0000' or '#f00'), or 3 or 4 space-separated numbers in the range 0 to 1
|
|
301
|
+
* (e.g. '1 0.5 0.5' or '1 0.5 0.5 0.5'). Returns `defaultValue` (cloned, when it is a color)
|
|
302
|
+
* when the attribute is absent (`null`), or when the value is malformed — the latter also logs
|
|
303
|
+
* a warning.
|
|
304
|
+
*
|
|
305
|
+
* @param value - The attribute value to parse (`null` when the attribute is absent).
|
|
306
|
+
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
307
|
+
* @param attribute - The attribute name, used in the warning message.
|
|
308
|
+
* @returns The parsed Color object.
|
|
309
|
+
*/
|
|
310
|
+
const parseColor = (value, defaultValue, attribute) => {
|
|
311
|
+
var _a;
|
|
312
|
+
if (value === null) {
|
|
313
|
+
return cloneDefault(defaultValue);
|
|
314
|
+
}
|
|
315
|
+
// A CSS color name (e.g. 'rebeccapurple')
|
|
316
|
+
const hexColor = CSS_COLORS[value.toLowerCase()];
|
|
317
|
+
if (hexColor) {
|
|
318
|
+
return new playcanvas.Color().fromString(hexColor);
|
|
319
|
+
}
|
|
320
|
+
// A hex color (e.g. '#ff0000'), expanding short forms (e.g. '#f00') for Color.fromString
|
|
321
|
+
if (/^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(value)) {
|
|
322
|
+
let hex = value.slice(1);
|
|
323
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
324
|
+
hex = hex.split('').map(char => char + char).join('');
|
|
325
|
+
}
|
|
326
|
+
return new playcanvas.Color().fromString(`#${hex}`);
|
|
327
|
+
}
|
|
328
|
+
// 3 or 4 space-separated components (e.g. '1 0.5 0.5')
|
|
329
|
+
const components = (_a = parseComponents(value, 4)) !== null && _a !== void 0 ? _a : parseComponents(value, 3);
|
|
330
|
+
if (components) {
|
|
331
|
+
return new playcanvas.Color(components);
|
|
332
|
+
}
|
|
333
|
+
console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected a CSS color name, a hex color or 3 or 4 space-separated numbers. Using '${defaultValue}'.`);
|
|
334
|
+
return cloneDefault(defaultValue);
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* Parse an Euler-angles attribute value into a quaternion. The expected format is 3
|
|
338
|
+
* space-separated angles in degrees (e.g. '0 90 0'). Returns `defaultValue` (cloned, when it is
|
|
339
|
+
* a quaternion) when the attribute is absent (`null`), or when the value is malformed — the
|
|
340
|
+
* latter also logs a warning.
|
|
341
|
+
*
|
|
342
|
+
* @param value - The attribute value to parse (`null` when the attribute is absent).
|
|
343
|
+
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
344
|
+
* @param attribute - The attribute name, used in the warning message.
|
|
345
|
+
* @returns The parsed Quat object.
|
|
346
|
+
*/
|
|
347
|
+
const parseQuat = (value, defaultValue, attribute) => {
|
|
348
|
+
if (value === null) {
|
|
349
|
+
return cloneDefault(defaultValue);
|
|
350
|
+
}
|
|
351
|
+
const components = parseComponents(value, 3);
|
|
352
|
+
if (!components) {
|
|
353
|
+
console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected 3 space-separated numbers. Using '${defaultValue}'.`);
|
|
354
|
+
return cloneDefault(defaultValue);
|
|
355
|
+
}
|
|
356
|
+
return new playcanvas.Quat().setFromEulerAngles(components[0], components[1], components[2]);
|
|
357
|
+
};
|
|
358
|
+
/**
|
|
359
|
+
* Parse a Vec2 attribute value. The expected format is 2 space-separated numbers (e.g. '1 2').
|
|
360
|
+
* Returns `defaultValue` (cloned, when it is a vector) when the attribute is absent (`null`),
|
|
361
|
+
* or when the value is malformed — the latter also logs a warning.
|
|
362
|
+
*
|
|
363
|
+
* @param value - The attribute value to parse (`null` when the attribute is absent).
|
|
364
|
+
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
365
|
+
* @param attribute - The attribute name, used in the warning message.
|
|
366
|
+
* @returns The parsed Vec2 object.
|
|
367
|
+
*/
|
|
368
|
+
const parseVec2 = (value, defaultValue, attribute) => {
|
|
369
|
+
if (value === null) {
|
|
370
|
+
return cloneDefault(defaultValue);
|
|
371
|
+
}
|
|
372
|
+
const components = parseComponents(value, 2);
|
|
373
|
+
if (!components) {
|
|
374
|
+
console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected 2 space-separated numbers. Using '${defaultValue}'.`);
|
|
375
|
+
return cloneDefault(defaultValue);
|
|
376
|
+
}
|
|
377
|
+
return new playcanvas.Vec2(components);
|
|
378
|
+
};
|
|
379
|
+
/**
|
|
380
|
+
* Parse a Vec3 attribute value. The expected format is 3 space-separated numbers (e.g. '1 2 3').
|
|
381
|
+
* Returns `defaultValue` (cloned, when it is a vector) when the attribute is absent (`null`),
|
|
382
|
+
* or when the value is malformed — the latter also logs a warning.
|
|
383
|
+
*
|
|
384
|
+
* @param value - The attribute value to parse (`null` when the attribute is absent).
|
|
385
|
+
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
386
|
+
* @param attribute - The attribute name, used in the warning message.
|
|
387
|
+
* @returns The parsed Vec3 object.
|
|
388
|
+
*/
|
|
389
|
+
const parseVec3 = (value, defaultValue, attribute) => {
|
|
390
|
+
if (value === null) {
|
|
391
|
+
return cloneDefault(defaultValue);
|
|
392
|
+
}
|
|
393
|
+
const components = parseComponents(value, 3);
|
|
394
|
+
if (!components) {
|
|
395
|
+
console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected 3 space-separated numbers. Using '${defaultValue}'.`);
|
|
396
|
+
return cloneDefault(defaultValue);
|
|
397
|
+
}
|
|
398
|
+
return new playcanvas.Vec3(components);
|
|
399
|
+
};
|
|
400
|
+
/**
|
|
401
|
+
* Parse a Vec4 attribute value. The expected format is 4 space-separated numbers
|
|
402
|
+
* (e.g. '1 2 3 4'). Returns `defaultValue` (cloned, when it is a vector) when the attribute is
|
|
403
|
+
* absent (`null`), or when the value is malformed — the latter also logs a warning.
|
|
404
|
+
*
|
|
405
|
+
* @param value - The attribute value to parse (`null` when the attribute is absent).
|
|
406
|
+
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
407
|
+
* @param attribute - The attribute name, used in the warning message.
|
|
408
|
+
* @returns The parsed Vec4 object.
|
|
409
|
+
*/
|
|
410
|
+
const parseVec4 = (value, defaultValue, attribute) => {
|
|
411
|
+
if (value === null) {
|
|
412
|
+
return cloneDefault(defaultValue);
|
|
413
|
+
}
|
|
414
|
+
const components = parseComponents(value, 4);
|
|
415
|
+
if (!components) {
|
|
416
|
+
console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected 4 space-separated numbers. Using '${defaultValue}'.`);
|
|
417
|
+
return cloneDefault(defaultValue);
|
|
418
|
+
}
|
|
419
|
+
return new playcanvas.Vec4(components);
|
|
420
|
+
};
|
|
421
|
+
/**
|
|
422
|
+
* Resolves an enum attribute value against its set of valid names. Returns the value when it is
|
|
423
|
+
* one of the valid names. Returns `defaultValue` when the attribute is absent (`null`), or when
|
|
424
|
+
* the value is invalid — the latter also logs a warning listing the valid names.
|
|
425
|
+
*
|
|
426
|
+
* @param value - The attribute value to parse (`null` when the attribute is absent).
|
|
427
|
+
* @param valid - The valid names: an array, or a map whose keys are the valid names.
|
|
428
|
+
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
429
|
+
* @param attribute - The attribute name, used in the warning message.
|
|
430
|
+
* @returns The resolved enum name.
|
|
431
|
+
*/
|
|
432
|
+
const parseEnum = (value, valid, defaultValue, attribute) => {
|
|
433
|
+
if (value === null) {
|
|
434
|
+
return defaultValue;
|
|
435
|
+
}
|
|
436
|
+
const names = Array.isArray(valid) ? valid : [...valid.keys()];
|
|
437
|
+
if (names.includes(value)) {
|
|
438
|
+
return value;
|
|
439
|
+
}
|
|
440
|
+
console.warn(`Invalid value '${value}' for attribute '${attribute}'. Valid values: ${names.join(', ')}. Using '${defaultValue}'.`);
|
|
441
|
+
return defaultValue;
|
|
442
|
+
};
|
|
443
|
+
/**
|
|
444
|
+
* Parses a number attribute value. Returns the parsed number when the value is a finite number.
|
|
445
|
+
* Returns `defaultValue` when the attribute is absent (`null`), or when the value is not a
|
|
446
|
+
* finite number — the latter also logs a warning.
|
|
447
|
+
*
|
|
448
|
+
* @param value - The attribute value to parse (`null` when the attribute is absent).
|
|
449
|
+
* @param defaultValue - The value to use when the attribute is absent or invalid.
|
|
450
|
+
* @param attribute - The attribute name, used in the warning message.
|
|
451
|
+
* @returns The parsed number.
|
|
452
|
+
*/
|
|
453
|
+
const parseNumber = (value, defaultValue, attribute) => {
|
|
454
|
+
if (value === null) {
|
|
455
|
+
return defaultValue;
|
|
456
|
+
}
|
|
457
|
+
const number = value.trim() === '' ? NaN : Number(value);
|
|
458
|
+
if (!Number.isFinite(number)) {
|
|
459
|
+
console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected a finite number. Using '${defaultValue}'.`);
|
|
460
|
+
return defaultValue;
|
|
461
|
+
}
|
|
462
|
+
return number;
|
|
463
|
+
};
|
|
464
|
+
/**
|
|
465
|
+
* Resolves a reference string to the {@link Entity} backing a `<pc-entity>` element. The reference
|
|
466
|
+
* can be a CSS selector (e.g. `#my-id`, `pc-entity[name="Foo"]`), a bare element id, or a bare
|
|
467
|
+
* entity name. Returns `null` if no matching element (or backing entity) is found.
|
|
468
|
+
*
|
|
469
|
+
* @param ref - The reference string to resolve.
|
|
470
|
+
* @returns The resolved entity, or `null`.
|
|
471
|
+
*/
|
|
472
|
+
const getEntity = (ref) => {
|
|
473
|
+
var _a, _b;
|
|
474
|
+
if (!ref) {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
let element = null;
|
|
478
|
+
// Try the reference as a CSS selector. An invalid selector (e.g. a bare name containing
|
|
479
|
+
// spaces) throws, in which case we fall back to id/name lookups below.
|
|
480
|
+
try {
|
|
481
|
+
element = document.querySelector(ref);
|
|
482
|
+
}
|
|
483
|
+
catch (_c) {
|
|
484
|
+
element = null;
|
|
485
|
+
}
|
|
486
|
+
if (!element) {
|
|
487
|
+
element = (_a = document.getElementById(ref)) !== null && _a !== void 0 ? _a : document.querySelector(`pc-entity[name="${ref}"]`);
|
|
488
|
+
}
|
|
489
|
+
return (_b = element === null || element === void 0 ? void 0 : element.entity) !== null && _b !== void 0 ? _b : null;
|
|
490
|
+
};
|
|
491
|
+
|
|
75
492
|
/**
|
|
76
493
|
* The AppElement interface provides properties and methods for manipulating
|
|
77
494
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-app/ | `<pc-app>`} elements.
|
|
@@ -79,6 +496,14 @@ customElements.define('pc-module', ModuleElement);
|
|
|
79
496
|
* {@link HTMLElement} interface.
|
|
80
497
|
*/
|
|
81
498
|
class AppElement extends AsyncElement {
|
|
499
|
+
/**
|
|
500
|
+
* The PlayCanvas application instance. Available once the element is ready — await
|
|
501
|
+
* {@link whenReady} or the element's `ready()` promise before accessing it.
|
|
502
|
+
* @returns The application instance.
|
|
503
|
+
*/
|
|
504
|
+
get app() {
|
|
505
|
+
return this._app;
|
|
506
|
+
}
|
|
82
507
|
/**
|
|
83
508
|
* Creates a new AppElement instance.
|
|
84
509
|
*
|
|
@@ -111,10 +536,7 @@ class AppElement extends AsyncElement {
|
|
|
111
536
|
pointerdown: null,
|
|
112
537
|
pointerup: null
|
|
113
538
|
};
|
|
114
|
-
|
|
115
|
-
* The PlayCanvas application instance.
|
|
116
|
-
*/
|
|
117
|
-
this.app = null;
|
|
539
|
+
this._app = null;
|
|
118
540
|
// Bind methods to maintain 'this' context
|
|
119
541
|
this._onWindowResize = this._onWindowResize.bind(this);
|
|
120
542
|
}
|
|
@@ -206,7 +628,7 @@ class AppElement extends AsyncElement {
|
|
|
206
628
|
createOptions.lightmapper = playcanvas.Lightmapper;
|
|
207
629
|
createOptions.batchManager = playcanvas.BatchManager;
|
|
208
630
|
createOptions.xr = playcanvas.XrManager;
|
|
209
|
-
this.
|
|
631
|
+
this._app = new playcanvas.AppBase(this._canvas);
|
|
210
632
|
this.app.init(createOptions);
|
|
211
633
|
this.app.setCanvasFillMode(playcanvas.FILLMODE_FILL_WINDOW);
|
|
212
634
|
this.app.setCanvasResolution(playcanvas.RESOLUTION_AUTO);
|
|
@@ -249,7 +671,7 @@ class AppElement extends AsyncElement {
|
|
|
249
671
|
// Clean up the application
|
|
250
672
|
if (this.app) {
|
|
251
673
|
this.app.destroy();
|
|
252
|
-
this.
|
|
674
|
+
this._app = null;
|
|
253
675
|
}
|
|
254
676
|
// Remove event listeners
|
|
255
677
|
window.removeEventListener('resize', this._onWindowResize);
|
|
@@ -275,6 +697,13 @@ class AppElement extends AsyncElement {
|
|
|
275
697
|
['pointermove', 'pointerdown', 'pointerup', 'pointerenter', 'pointerleave'].forEach((type) => {
|
|
276
698
|
this.addEventListener(`${type}:connect`, () => this._onPointerListenerAdded(type));
|
|
277
699
|
this.addEventListener(`${type}:disconnect`, () => this._onPointerListenerRemoved(type));
|
|
700
|
+
// Attach canvas handlers for listeners registered before this point (e.g. handlers
|
|
701
|
+
// created from onpointer* attributes when their elements were first upgraded)
|
|
702
|
+
const anyListeners = Array.from(this.querySelectorAll('pc-entity'))
|
|
703
|
+
.some(entity => entity.hasListeners(type));
|
|
704
|
+
if (anyListeners) {
|
|
705
|
+
this._onPointerListenerAdded(type);
|
|
706
|
+
}
|
|
278
707
|
});
|
|
279
708
|
}
|
|
280
709
|
_pickerDestroy() {
|
|
@@ -291,6 +720,13 @@ class AppElement extends AsyncElement {
|
|
|
291
720
|
pointerdown: null,
|
|
292
721
|
pointerup: null
|
|
293
722
|
};
|
|
723
|
+
this._hasPointerListeners = {
|
|
724
|
+
pointerenter: false,
|
|
725
|
+
pointerleave: false,
|
|
726
|
+
pointerdown: false,
|
|
727
|
+
pointerup: false,
|
|
728
|
+
pointermove: false
|
|
729
|
+
};
|
|
294
730
|
}
|
|
295
731
|
// New helper to convert CSS coordinates to canvas (picker) coordinates
|
|
296
732
|
_getPickerCoordinates(event) {
|
|
@@ -475,324 +911,65 @@ class AppElement extends AsyncElement {
|
|
|
475
911
|
get hierarchyReady() {
|
|
476
912
|
return this._hierarchyReady;
|
|
477
913
|
}
|
|
478
|
-
/**
|
|
479
|
-
* Sets the high resolution flag. When true, the application will render at the device's
|
|
480
|
-
* physical resolution. When false, the application will render at CSS resolution.
|
|
481
|
-
* @param value - The high resolution flag.
|
|
482
|
-
*/
|
|
483
|
-
set highResolution(value) {
|
|
484
|
-
this._highResolution = value;
|
|
485
|
-
if (this.app) {
|
|
486
|
-
this.app.graphicsDevice.maxPixelRatio = value ? window.devicePixelRatio : 1;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Gets the high resolution flag.
|
|
491
|
-
* @returns The high resolution flag.
|
|
492
|
-
*/
|
|
493
|
-
get highResolution() {
|
|
494
|
-
return this._highResolution;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Sets the stencil flag.
|
|
498
|
-
* @param value - The stencil flag.
|
|
499
|
-
*/
|
|
500
|
-
set stencil(value) {
|
|
501
|
-
this._stencil = value;
|
|
502
|
-
}
|
|
503
|
-
/**
|
|
504
|
-
* Gets the stencil flag.
|
|
505
|
-
* @returns The stencil flag.
|
|
506
|
-
*/
|
|
507
|
-
get stencil() {
|
|
508
|
-
return this._stencil;
|
|
509
|
-
}
|
|
510
|
-
static get observedAttributes() {
|
|
511
|
-
return ['alpha', 'antialias', 'backend', 'depth', 'stencil', 'high-resolution'];
|
|
512
|
-
}
|
|
513
|
-
attributeChangedCallback(name, _oldValue, newValue) {
|
|
514
|
-
switch (name) {
|
|
515
|
-
case 'alpha':
|
|
516
|
-
this.alpha = newValue !== 'false';
|
|
517
|
-
break;
|
|
518
|
-
case 'antialias':
|
|
519
|
-
this.antialias = newValue !== 'false';
|
|
520
|
-
break;
|
|
521
|
-
case 'backend':
|
|
522
|
-
if (newValue === 'webgpu' || newValue === 'webgl2' || newValue === 'null') {
|
|
523
|
-
this.backend = newValue;
|
|
524
|
-
}
|
|
525
|
-
break;
|
|
526
|
-
case 'depth':
|
|
527
|
-
this.depth = newValue !== 'false';
|
|
528
|
-
break;
|
|
529
|
-
case 'high-resolution':
|
|
530
|
-
this.highResolution = newValue !== 'false';
|
|
531
|
-
break;
|
|
532
|
-
case 'stencil':
|
|
533
|
-
this.stencil = newValue !== 'false';
|
|
534
|
-
break;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
customElements.define('pc-app', AppElement);
|
|
539
|
-
|
|
540
|
-
const CSS_COLORS = {
|
|
541
|
-
aliceblue: '#f0f8ff',
|
|
542
|
-
antiquewhite: '#faebd7',
|
|
543
|
-
aqua: '#00ffff',
|
|
544
|
-
aquamarine: '#7fffd4',
|
|
545
|
-
azure: '#f0ffff',
|
|
546
|
-
beige: '#f5f5dc',
|
|
547
|
-
bisque: '#ffe4c4',
|
|
548
|
-
black: '#000000',
|
|
549
|
-
blanchedalmond: '#ffebcd',
|
|
550
|
-
blue: '#0000ff',
|
|
551
|
-
blueviolet: '#8a2be2',
|
|
552
|
-
brown: '#a52a2a',
|
|
553
|
-
burlywood: '#deb887',
|
|
554
|
-
cadetblue: '#5f9ea0',
|
|
555
|
-
chartreuse: '#7fff00',
|
|
556
|
-
chocolate: '#d2691e',
|
|
557
|
-
coral: '#ff7f50',
|
|
558
|
-
cornflowerblue: '#6495ed',
|
|
559
|
-
cornsilk: '#fff8dc',
|
|
560
|
-
crimson: '#dc143c',
|
|
561
|
-
cyan: '#00ffff',
|
|
562
|
-
darkblue: '#00008b',
|
|
563
|
-
darkcyan: '#008b8b',
|
|
564
|
-
darkgoldenrod: '#b8860b',
|
|
565
|
-
darkgray: '#a9a9a9',
|
|
566
|
-
darkgreen: '#006400',
|
|
567
|
-
darkgrey: '#a9a9a9',
|
|
568
|
-
darkkhaki: '#bdb76b',
|
|
569
|
-
darkmagenta: '#8b008b',
|
|
570
|
-
darkolivegreen: '#556b2f',
|
|
571
|
-
darkorange: '#ff8c00',
|
|
572
|
-
darkorchid: '#9932cc',
|
|
573
|
-
darkred: '#8b0000',
|
|
574
|
-
darksalmon: '#e9967a',
|
|
575
|
-
darkseagreen: '#8fbc8f',
|
|
576
|
-
darkslateblue: '#483d8b',
|
|
577
|
-
darkslategray: '#2f4f4f',
|
|
578
|
-
darkslategrey: '#2f4f4f',
|
|
579
|
-
darkturquoise: '#00ced1',
|
|
580
|
-
darkviolet: '#9400d3',
|
|
581
|
-
deeppink: '#ff1493',
|
|
582
|
-
deepskyblue: '#00bfff',
|
|
583
|
-
dimgray: '#696969',
|
|
584
|
-
dimgrey: '#696969',
|
|
585
|
-
dodgerblue: '#1e90ff',
|
|
586
|
-
firebrick: '#b22222',
|
|
587
|
-
floralwhite: '#fffaf0',
|
|
588
|
-
forestgreen: '#228b22',
|
|
589
|
-
fuchsia: '#ff00ff',
|
|
590
|
-
gainsboro: '#dcdcdc',
|
|
591
|
-
ghostwhite: '#f8f8ff',
|
|
592
|
-
gold: '#ffd700',
|
|
593
|
-
goldenrod: '#daa520',
|
|
594
|
-
gray: '#808080',
|
|
595
|
-
green: '#008000',
|
|
596
|
-
greenyellow: '#adff2f',
|
|
597
|
-
grey: '#808080',
|
|
598
|
-
honeydew: '#f0fff0',
|
|
599
|
-
hotpink: '#ff69b4',
|
|
600
|
-
indianred: '#cd5c5c',
|
|
601
|
-
indigo: '#4b0082',
|
|
602
|
-
ivory: '#fffff0',
|
|
603
|
-
khaki: '#f0e68c',
|
|
604
|
-
lavender: '#e6e6fa',
|
|
605
|
-
lavenderblush: '#fff0f5',
|
|
606
|
-
lawngreen: '#7cfc00',
|
|
607
|
-
lemonchiffon: '#fffacd',
|
|
608
|
-
lightblue: '#add8e6',
|
|
609
|
-
lightcoral: '#f08080',
|
|
610
|
-
lightcyan: '#e0ffff',
|
|
611
|
-
lightgoldenrodyellow: '#fafad2',
|
|
612
|
-
lightgray: '#d3d3d3',
|
|
613
|
-
lightgreen: '#90ee90',
|
|
614
|
-
lightgrey: '#d3d3d3',
|
|
615
|
-
lightpink: '#ffb6c1',
|
|
616
|
-
lightsalmon: '#ffa07a',
|
|
617
|
-
lightseagreen: '#20b2aa',
|
|
618
|
-
lightskyblue: '#87cefa',
|
|
619
|
-
lightslategray: '#778899',
|
|
620
|
-
lightslategrey: '#778899',
|
|
621
|
-
lightsteelblue: '#b0c4de',
|
|
622
|
-
lightyellow: '#ffffe0',
|
|
623
|
-
lime: '#00ff00',
|
|
624
|
-
limegreen: '#32cd32',
|
|
625
|
-
linen: '#faf0e6',
|
|
626
|
-
magenta: '#ff00ff',
|
|
627
|
-
maroon: '#800000',
|
|
628
|
-
mediumaquamarine: '#66cdaa',
|
|
629
|
-
mediumblue: '#0000cd',
|
|
630
|
-
mediumorchid: '#ba55d3',
|
|
631
|
-
mediumpurple: '#9370db',
|
|
632
|
-
mediumseagreen: '#3cb371',
|
|
633
|
-
mediumslateblue: '#7b68ee',
|
|
634
|
-
mediumspringgreen: '#00fa9a',
|
|
635
|
-
mediumturquoise: '#48d1cc',
|
|
636
|
-
mediumvioletred: '#c71585',
|
|
637
|
-
midnightblue: '#191970',
|
|
638
|
-
mintcream: '#f5fffa',
|
|
639
|
-
mistyrose: '#ffe4e1',
|
|
640
|
-
moccasin: '#ffe4b5',
|
|
641
|
-
navajowhite: '#ffdead',
|
|
642
|
-
navy: '#000080',
|
|
643
|
-
oldlace: '#fdf5e6',
|
|
644
|
-
olive: '#808000',
|
|
645
|
-
olivedrab: '#6b8e23',
|
|
646
|
-
orange: '#ffa500',
|
|
647
|
-
orangered: '#ff4500',
|
|
648
|
-
orchid: '#da70d6',
|
|
649
|
-
palegoldenrod: '#eee8aa',
|
|
650
|
-
palegreen: '#98fb98',
|
|
651
|
-
paleturquoise: '#afeeee',
|
|
652
|
-
palevioletred: '#db7093',
|
|
653
|
-
papayawhip: '#ffefd5',
|
|
654
|
-
peachpuff: '#ffdab9',
|
|
655
|
-
peru: '#cd853f',
|
|
656
|
-
pink: '#ffc0cb',
|
|
657
|
-
plum: '#dda0dd',
|
|
658
|
-
powderblue: '#b0e0e6',
|
|
659
|
-
purple: '#800080',
|
|
660
|
-
rebeccapurple: '#663399',
|
|
661
|
-
red: '#ff0000',
|
|
662
|
-
rosybrown: '#bc8f8f',
|
|
663
|
-
royalblue: '#4169e1',
|
|
664
|
-
saddlebrown: '#8b4513',
|
|
665
|
-
salmon: '#fa8072',
|
|
666
|
-
sandybrown: '#f4a460',
|
|
667
|
-
seagreen: '#2e8b57',
|
|
668
|
-
seashell: '#fff5ee',
|
|
669
|
-
sienna: '#a0522d',
|
|
670
|
-
silver: '#c0c0c0',
|
|
671
|
-
skyblue: '#87ceeb',
|
|
672
|
-
slateblue: '#6a5acd',
|
|
673
|
-
slategray: '#708090',
|
|
674
|
-
slategrey: '#708090',
|
|
675
|
-
snow: '#fffafa',
|
|
676
|
-
springgreen: '#00ff7f',
|
|
677
|
-
steelblue: '#4682b4',
|
|
678
|
-
tan: '#d2b48c',
|
|
679
|
-
teal: '#008080',
|
|
680
|
-
thistle: '#d8bfd8',
|
|
681
|
-
tomato: '#ff6347',
|
|
682
|
-
turquoise: '#40e0d0',
|
|
683
|
-
violet: '#ee82ee',
|
|
684
|
-
wheat: '#f5deb3',
|
|
685
|
-
white: '#ffffff',
|
|
686
|
-
whitesmoke: '#f5f5f5',
|
|
687
|
-
yellow: '#ffff00',
|
|
688
|
-
yellowgreen: '#9acd32'
|
|
689
|
-
};
|
|
690
|
-
|
|
691
|
-
/**
|
|
692
|
-
* Parse a color string into a Color object. String can be in the format of '#rgb', '#rgba',
|
|
693
|
-
* '#rrggbb', '#rrggbbaa', or a string of 3 or 4 comma-delimited numbers.
|
|
694
|
-
*
|
|
695
|
-
* @param value - The color string to parse.
|
|
696
|
-
* @returns The parsed Color object.
|
|
697
|
-
*/
|
|
698
|
-
const parseColor = (value) => {
|
|
699
|
-
// Check if it's a CSS color name first
|
|
700
|
-
const hexColor = CSS_COLORS[value.toLowerCase()];
|
|
701
|
-
if (hexColor) {
|
|
702
|
-
return new playcanvas.Color().fromString(hexColor);
|
|
703
|
-
}
|
|
704
|
-
if (value.startsWith('#')) {
|
|
705
|
-
return new playcanvas.Color().fromString(value);
|
|
706
|
-
}
|
|
707
|
-
const components = value.split(' ').map(Number);
|
|
708
|
-
return new playcanvas.Color(components);
|
|
709
|
-
};
|
|
710
|
-
/**
|
|
711
|
-
* Parse an Euler angles string into a Quat object. String can be in the format of 'x,y,z'.
|
|
712
|
-
*
|
|
713
|
-
* @param value - The Euler angles string to parse.
|
|
714
|
-
* @returns The parsed Quat object.
|
|
715
|
-
*/
|
|
716
|
-
const parseQuat = (value) => {
|
|
717
|
-
const [x, y, z] = value.split(' ').map(Number);
|
|
718
|
-
const q = new playcanvas.Quat();
|
|
719
|
-
q.setFromEulerAngles(x, y, z);
|
|
720
|
-
return q;
|
|
721
|
-
};
|
|
722
|
-
/**
|
|
723
|
-
* Parse a Vec2 string into a Vec2 object. String can be in the format of 'x,y'.
|
|
724
|
-
*
|
|
725
|
-
* @param value - The Vec2 string to parse.
|
|
726
|
-
* @returns The parsed Vec2 object.
|
|
727
|
-
*/
|
|
728
|
-
const parseVec2 = (value) => {
|
|
729
|
-
const components = value.split(' ').map(Number);
|
|
730
|
-
return new playcanvas.Vec2(components);
|
|
731
|
-
};
|
|
732
|
-
/**
|
|
733
|
-
* Parse a Vec3 string into a Vec3 object. String can be in the format of 'x,y,z'.
|
|
734
|
-
*
|
|
735
|
-
* @param value - The Vec3 string to parse.
|
|
736
|
-
* @returns The parsed Vec3 object.
|
|
737
|
-
*/
|
|
738
|
-
const parseVec3 = (value) => {
|
|
739
|
-
const components = value.split(' ').map(Number);
|
|
740
|
-
return new playcanvas.Vec3(components);
|
|
741
|
-
};
|
|
742
|
-
/**
|
|
743
|
-
* Parse a Vec4 string into a Vec4 object. String can be in the format of 'x,y,z,w'.
|
|
744
|
-
*
|
|
745
|
-
* @param value - The Vec4 string to parse.
|
|
746
|
-
* @returns The parsed Vec4 object.
|
|
747
|
-
*/
|
|
748
|
-
const parseVec4 = (value) => {
|
|
749
|
-
const components = value.split(' ').map(Number);
|
|
750
|
-
return new playcanvas.Vec4(components);
|
|
751
|
-
};
|
|
752
|
-
/**
|
|
753
|
-
* Resolves an enum value supplied as either a named string (looked up in `map`) or a numeric
|
|
754
|
-
* string. Falls back to `defaultValue` when the value is neither a known name nor a finite number.
|
|
755
|
-
*
|
|
756
|
-
* @param value - The attribute value to parse.
|
|
757
|
-
* @param map - A map of named values to their numeric enum equivalents.
|
|
758
|
-
* @param defaultValue - The value to return when parsing fails.
|
|
759
|
-
* @returns The resolved numeric enum value.
|
|
760
|
-
*/
|
|
761
|
-
const parseEnum = (value, map, defaultValue) => {
|
|
762
|
-
const named = map.get(value);
|
|
763
|
-
if (named !== undefined) {
|
|
764
|
-
return named;
|
|
765
|
-
}
|
|
766
|
-
const numeric = Number(value);
|
|
767
|
-
return Number.isFinite(numeric) ? numeric : defaultValue;
|
|
768
|
-
};
|
|
769
|
-
/**
|
|
770
|
-
* Resolves a reference string to the {@link Entity} backing a `<pc-entity>` element. The reference
|
|
771
|
-
* can be a CSS selector (e.g. `#my-id`, `pc-entity[name="Foo"]`), a bare element id, or a bare
|
|
772
|
-
* entity name. Returns `null` if no matching element (or backing entity) is found.
|
|
773
|
-
*
|
|
774
|
-
* @param ref - The reference string to resolve.
|
|
775
|
-
* @returns The resolved entity, or `null`.
|
|
776
|
-
*/
|
|
777
|
-
const getEntity = (ref) => {
|
|
778
|
-
var _a, _b;
|
|
779
|
-
if (!ref) {
|
|
780
|
-
return null;
|
|
914
|
+
/**
|
|
915
|
+
* Sets the high resolution flag. When true, the application will render at the device's
|
|
916
|
+
* physical resolution. When false, the application will render at CSS resolution.
|
|
917
|
+
* @param value - The high resolution flag.
|
|
918
|
+
*/
|
|
919
|
+
set highResolution(value) {
|
|
920
|
+
this._highResolution = value;
|
|
921
|
+
if (this.app) {
|
|
922
|
+
this.app.graphicsDevice.maxPixelRatio = value ? window.devicePixelRatio : 1;
|
|
923
|
+
}
|
|
781
924
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
925
|
+
/**
|
|
926
|
+
* Gets the high resolution flag.
|
|
927
|
+
* @returns The high resolution flag.
|
|
928
|
+
*/
|
|
929
|
+
get highResolution() {
|
|
930
|
+
return this._highResolution;
|
|
787
931
|
}
|
|
788
|
-
|
|
789
|
-
|
|
932
|
+
/**
|
|
933
|
+
* Sets the stencil flag.
|
|
934
|
+
* @param value - The stencil flag.
|
|
935
|
+
*/
|
|
936
|
+
set stencil(value) {
|
|
937
|
+
this._stencil = value;
|
|
790
938
|
}
|
|
791
|
-
|
|
792
|
-
|
|
939
|
+
/**
|
|
940
|
+
* Gets the stencil flag.
|
|
941
|
+
* @returns The stencil flag.
|
|
942
|
+
*/
|
|
943
|
+
get stencil() {
|
|
944
|
+
return this._stencil;
|
|
793
945
|
}
|
|
794
|
-
|
|
795
|
-
|
|
946
|
+
static get observedAttributes() {
|
|
947
|
+
return ['alpha', 'antialias', 'backend', 'depth', 'stencil', 'high-resolution'];
|
|
948
|
+
}
|
|
949
|
+
attributeChangedCallback(name, _oldValue, newValue) {
|
|
950
|
+
switch (name) {
|
|
951
|
+
case 'alpha':
|
|
952
|
+
this.alpha = parseBool(newValue, true);
|
|
953
|
+
break;
|
|
954
|
+
case 'antialias':
|
|
955
|
+
this.antialias = parseBool(newValue, true);
|
|
956
|
+
break;
|
|
957
|
+
case 'backend':
|
|
958
|
+
this.backend = parseEnum(newValue, ['webgpu', 'webgl2', 'null'], 'webgl2', name);
|
|
959
|
+
break;
|
|
960
|
+
case 'depth':
|
|
961
|
+
this.depth = parseBool(newValue, true);
|
|
962
|
+
break;
|
|
963
|
+
case 'high-resolution':
|
|
964
|
+
this.highResolution = parseBool(newValue, true);
|
|
965
|
+
break;
|
|
966
|
+
case 'stencil':
|
|
967
|
+
this.stencil = parseBool(newValue, true);
|
|
968
|
+
break;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
customElements.define('pc-app', AppElement);
|
|
796
973
|
|
|
797
974
|
/**
|
|
798
975
|
* The EntityElement interface provides properties and methods for manipulating
|
|
@@ -832,67 +1009,41 @@ class EntityElement extends AsyncElement {
|
|
|
832
1009
|
*/
|
|
833
1010
|
this._listeners = {};
|
|
834
1011
|
/**
|
|
835
|
-
*
|
|
1012
|
+
* The event types for which an inline `onpointer*` attribute is currently present.
|
|
836
1013
|
*/
|
|
837
|
-
this.
|
|
1014
|
+
this._inlineHandlerTypes = new Set();
|
|
838
1015
|
/**
|
|
839
|
-
*
|
|
1016
|
+
* Whether the hierarchy has been built for this entity.
|
|
840
1017
|
*/
|
|
841
|
-
this.
|
|
1018
|
+
this._built = false;
|
|
1019
|
+
this._entity = null;
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* The PlayCanvas entity instance. Available once the element is ready — await
|
|
1023
|
+
* {@link whenReady} or the element's `ready()` promise before accessing it.
|
|
1024
|
+
* @returns The entity instance.
|
|
1025
|
+
*/
|
|
1026
|
+
get entity() {
|
|
1027
|
+
return this._entity;
|
|
842
1028
|
}
|
|
843
1029
|
createEntity(app) {
|
|
844
1030
|
// Guard against double creation. When a subtree is inserted at runtime (e.g. cloning a
|
|
845
1031
|
// `<template>`), an ancestor's connectedCallback eagerly creates descendant entities; the
|
|
846
1032
|
// descendants' own connectedCallbacks would otherwise create them a second time.
|
|
847
|
-
if (this.
|
|
1033
|
+
if (this._entity) {
|
|
848
1034
|
return;
|
|
849
1035
|
}
|
|
850
1036
|
// Create a new entity
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
if (position) {
|
|
858
|
-
this.entity.setLocalPosition(parseVec3(position));
|
|
859
|
-
}
|
|
860
|
-
const rotation = this.getAttribute('rotation');
|
|
861
|
-
if (rotation) {
|
|
862
|
-
this.entity.setLocalEulerAngles(parseVec3(rotation));
|
|
863
|
-
}
|
|
864
|
-
const scale = this.getAttribute('scale');
|
|
865
|
-
if (scale) {
|
|
866
|
-
this.entity.setLocalScale(parseVec3(scale));
|
|
867
|
-
}
|
|
1037
|
+
const entity = new playcanvas.Entity(this.getAttribute('name') || this._name, app);
|
|
1038
|
+
this._entity = entity;
|
|
1039
|
+
entity.enabled = parseBool(this.getAttribute('enabled'), true);
|
|
1040
|
+
entity.setLocalPosition(parseVec3(this.getAttribute('position'), playcanvas.Vec3.ZERO, 'position'));
|
|
1041
|
+
entity.setLocalEulerAngles(parseVec3(this.getAttribute('rotation'), playcanvas.Vec3.ZERO, 'rotation'));
|
|
1042
|
+
entity.setLocalScale(parseVec3(this.getAttribute('scale'), playcanvas.Vec3.ONE, 'scale'));
|
|
868
1043
|
const tags = this.getAttribute('tags');
|
|
869
1044
|
if (tags) {
|
|
870
|
-
|
|
1045
|
+
entity.tags.add(tags.split(',').map(tag => tag.trim()));
|
|
871
1046
|
}
|
|
872
|
-
// Handle pointer events
|
|
873
|
-
const pointerEvents = [
|
|
874
|
-
'onpointerenter',
|
|
875
|
-
'onpointerleave',
|
|
876
|
-
'onpointerdown',
|
|
877
|
-
'onpointerup',
|
|
878
|
-
'onpointermove'
|
|
879
|
-
];
|
|
880
|
-
pointerEvents.forEach((eventName) => {
|
|
881
|
-
const handler = this.getAttribute(eventName);
|
|
882
|
-
if (handler) {
|
|
883
|
-
const eventType = eventName.substring(2); // remove 'on' prefix
|
|
884
|
-
const eventHandler = (event) => {
|
|
885
|
-
try {
|
|
886
|
-
/* eslint-disable-next-line no-new-func */
|
|
887
|
-
new Function('event', handler).call(this, event);
|
|
888
|
-
}
|
|
889
|
-
catch (e) {
|
|
890
|
-
console.error('Error in event handler:', e);
|
|
891
|
-
}
|
|
892
|
-
};
|
|
893
|
-
this.addEventListener(eventType, eventHandler);
|
|
894
|
-
}
|
|
895
|
-
});
|
|
896
1047
|
}
|
|
897
1048
|
buildHierarchy(app) {
|
|
898
1049
|
if (!this.entity || this._built)
|
|
@@ -932,11 +1083,11 @@ class EntityElement extends AsyncElement {
|
|
|
932
1083
|
// Notify all children that their entities are about to become invalid
|
|
933
1084
|
const children = this.querySelectorAll('pc-entity');
|
|
934
1085
|
children.forEach((child) => {
|
|
935
|
-
child.
|
|
1086
|
+
child._entity = null;
|
|
936
1087
|
});
|
|
937
1088
|
// Destroy the entity
|
|
938
1089
|
this.entity.destroy();
|
|
939
|
-
this.
|
|
1090
|
+
this._entity = null;
|
|
940
1091
|
this._built = false;
|
|
941
1092
|
}
|
|
942
1093
|
}
|
|
@@ -1043,6 +1194,30 @@ class EntityElement extends AsyncElement {
|
|
|
1043
1194
|
get tags() {
|
|
1044
1195
|
return this._tags;
|
|
1045
1196
|
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Tracks whether an inline `onpointer*` attribute is present. The browser itself compiles and
|
|
1199
|
+
* runs these attributes — they are standard `GlobalEventHandlers`, so setting one replaces
|
|
1200
|
+
* the previous handler and removing it removes the handler, exactly like `onclick` on any
|
|
1201
|
+
* HTML element. But because they bypass {@link addEventListener}, the connect/disconnect
|
|
1202
|
+
* bookkeeping that lets the application lazily attach its canvas pointer handlers must be
|
|
1203
|
+
* kept in sync here.
|
|
1204
|
+
*
|
|
1205
|
+
* @param name - The attribute name (e.g. 'onpointerdown').
|
|
1206
|
+
* @param value - The attribute value, or `null` when the attribute has been removed.
|
|
1207
|
+
*/
|
|
1208
|
+
_updateInlineHandler(name, value) {
|
|
1209
|
+
const type = name.substring(2);
|
|
1210
|
+
const had = this._inlineHandlerTypes.has(type);
|
|
1211
|
+
const has = value !== null;
|
|
1212
|
+
if (has && !had) {
|
|
1213
|
+
this._inlineHandlerTypes.add(type);
|
|
1214
|
+
this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
|
|
1215
|
+
}
|
|
1216
|
+
else if (!has && had) {
|
|
1217
|
+
this._inlineHandlerTypes.delete(type);
|
|
1218
|
+
this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1046
1221
|
static get observedAttributes() {
|
|
1047
1222
|
return [
|
|
1048
1223
|
'enabled',
|
|
@@ -1061,19 +1236,19 @@ class EntityElement extends AsyncElement {
|
|
|
1061
1236
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
1062
1237
|
switch (name) {
|
|
1063
1238
|
case 'enabled':
|
|
1064
|
-
this.enabled = newValue
|
|
1239
|
+
this.enabled = parseBool(newValue, true);
|
|
1065
1240
|
break;
|
|
1066
1241
|
case 'name':
|
|
1067
1242
|
this.name = newValue;
|
|
1068
1243
|
break;
|
|
1069
1244
|
case 'position':
|
|
1070
|
-
this.position = parseVec3(newValue);
|
|
1245
|
+
this.position = parseVec3(newValue, playcanvas.Vec3.ZERO, name);
|
|
1071
1246
|
break;
|
|
1072
1247
|
case 'rotation':
|
|
1073
|
-
this.rotation = parseVec3(newValue);
|
|
1248
|
+
this.rotation = parseVec3(newValue, playcanvas.Vec3.ZERO, name);
|
|
1074
1249
|
break;
|
|
1075
1250
|
case 'scale':
|
|
1076
|
-
this.scale = parseVec3(newValue);
|
|
1251
|
+
this.scale = parseVec3(newValue, playcanvas.Vec3.ONE, name);
|
|
1077
1252
|
break;
|
|
1078
1253
|
case 'tags':
|
|
1079
1254
|
this.tags = newValue.split(',').map(tag => tag.trim());
|
|
@@ -1083,21 +1258,7 @@ class EntityElement extends AsyncElement {
|
|
|
1083
1258
|
case 'onpointerdown':
|
|
1084
1259
|
case 'onpointerup':
|
|
1085
1260
|
case 'onpointermove':
|
|
1086
|
-
|
|
1087
|
-
const eventName = name.substring(2);
|
|
1088
|
-
// Use Function.prototype.bind to avoid new Function
|
|
1089
|
-
const handler = (event) => {
|
|
1090
|
-
try {
|
|
1091
|
-
const handlerStr = this.getAttribute(eventName) || '';
|
|
1092
|
-
/* eslint-disable-next-line no-new-func */
|
|
1093
|
-
new Function('event', handlerStr).call(this, event);
|
|
1094
|
-
}
|
|
1095
|
-
catch (e) {
|
|
1096
|
-
console.error('Error in event handler:', e);
|
|
1097
|
-
}
|
|
1098
|
-
};
|
|
1099
|
-
this.addEventListener(eventName, handler);
|
|
1100
|
-
}
|
|
1261
|
+
this._updateInlineHandler(name, newValue);
|
|
1101
1262
|
break;
|
|
1102
1263
|
}
|
|
1103
1264
|
}
|
|
@@ -1122,7 +1283,7 @@ class EntityElement extends AsyncElement {
|
|
|
1122
1283
|
}
|
|
1123
1284
|
hasListeners(type) {
|
|
1124
1285
|
var _a;
|
|
1125
|
-
return Boolean((_a = this._listeners[type]) === null || _a === void 0 ? void 0 : _a.length);
|
|
1286
|
+
return Boolean((_a = this._listeners[type]) === null || _a === void 0 ? void 0 : _a.length) || this._inlineHandlerTypes.has(type);
|
|
1126
1287
|
}
|
|
1127
1288
|
}
|
|
1128
1289
|
customElements.define('pc-entity', EntityElement);
|
|
@@ -1354,16 +1515,57 @@ const processBufferView = (gltfBuffer, buffers, continuation) => {
|
|
|
1354
1515
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-asset/ | `<pc-asset>`} elements.
|
|
1355
1516
|
* The AssetElement interface also inherits the properties and methods of the
|
|
1356
1517
|
* {@link HTMLElement} interface.
|
|
1518
|
+
*
|
|
1519
|
+
* The element becomes ready once the containing application has started and the asset is in the
|
|
1520
|
+
* state declared by the markup: loaded for preloaded assets (even if loading failed — check the
|
|
1521
|
+
* asset's `resource`), or registered and awaiting a load for `lazy` assets. Elements inserted
|
|
1522
|
+
* while the application is running are created and registered on insertion, and begin loading
|
|
1523
|
+
* immediately unless `lazy`. A `pc-asset` must be a direct child of `pc-app` — elements placed
|
|
1524
|
+
* elsewhere, or with an unsupported asset type, never become ready.
|
|
1357
1525
|
*/
|
|
1358
|
-
class AssetElement extends
|
|
1526
|
+
class AssetElement extends AsyncElement {
|
|
1359
1527
|
constructor() {
|
|
1360
1528
|
super(...arguments);
|
|
1361
1529
|
this._lazy = false;
|
|
1362
1530
|
/**
|
|
1363
|
-
* The asset that is loaded.
|
|
1531
|
+
* The asset that is loaded. Available once the element is ready — await
|
|
1532
|
+
* {@link whenReady} or the element's `ready()` promise before accessing it.
|
|
1364
1533
|
*/
|
|
1365
1534
|
this.asset = null;
|
|
1366
1535
|
}
|
|
1536
|
+
async connectedCallback() {
|
|
1537
|
+
var _a;
|
|
1538
|
+
const appElement = this.closestApp;
|
|
1539
|
+
if (!appElement)
|
|
1540
|
+
return;
|
|
1541
|
+
// Assets must be direct children of pc-app (matches the boot query ':scope > pc-asset')
|
|
1542
|
+
if (this.parentElement !== appElement) {
|
|
1543
|
+
console.warn(`pc-asset '${(_a = this.getAttribute('id')) !== null && _a !== void 0 ? _a : this.getAttribute('src')}' must be a direct child of pc-app - asset not created`);
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
await appElement.ready();
|
|
1547
|
+
// The element may have been removed or re-parented while waiting for the app
|
|
1548
|
+
if (!this.isConnected || this.parentElement !== appElement)
|
|
1549
|
+
return;
|
|
1550
|
+
// Assets present at startup are created by AppElement's boot; this branch handles
|
|
1551
|
+
// elements inserted (or re-inserted) after the app is already running
|
|
1552
|
+
if (!this.asset) {
|
|
1553
|
+
const app = appElement.app;
|
|
1554
|
+
if (!app)
|
|
1555
|
+
return; // pc-app is re-connecting; its own boot will create this asset
|
|
1556
|
+
this.createAsset();
|
|
1557
|
+
if (this.asset) {
|
|
1558
|
+
app.assets.add(this.asset); // add() auto-loads when preload is true
|
|
1559
|
+
if (!this.lazy) {
|
|
1560
|
+
app.assets.load(this.asset);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
// Never ready if createAsset failed (unsupported asset type)
|
|
1565
|
+
if (this.asset) {
|
|
1566
|
+
this._onReady();
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1367
1569
|
disconnectedCallback() {
|
|
1368
1570
|
this.destroyAsset();
|
|
1369
1571
|
}
|
|
@@ -1442,11 +1644,11 @@ class AssetElement extends HTMLElement {
|
|
|
1442
1644
|
}
|
|
1443
1645
|
const pixelsPerUnit = this.getAttribute('pixels-per-unit');
|
|
1444
1646
|
if (pixelsPerUnit !== null) {
|
|
1445
|
-
data.pixelsPerUnit =
|
|
1647
|
+
data.pixelsPerUnit = parseNumber(pixelsPerUnit, 1, 'pixels-per-unit');
|
|
1446
1648
|
}
|
|
1447
1649
|
const renderMode = this.getAttribute('render-mode');
|
|
1448
1650
|
if (renderMode !== null) {
|
|
1449
|
-
data.renderMode = parseEnum(renderMode, renderModes,
|
|
1651
|
+
data.renderMode = renderModes.get(parseEnum(renderMode, renderModes, 'simple', 'render-mode'));
|
|
1450
1652
|
}
|
|
1451
1653
|
// Apply engine defaults for any values not supplied.
|
|
1452
1654
|
data.renderMode = (_b = data.renderMode) !== null && _b !== void 0 ? _b : playcanvas.SPRITE_RENDERMODE_SIMPLE;
|
|
@@ -1456,7 +1658,10 @@ class AssetElement extends HTMLElement {
|
|
|
1456
1658
|
return data;
|
|
1457
1659
|
}
|
|
1458
1660
|
destroyAsset() {
|
|
1661
|
+
var _a;
|
|
1459
1662
|
if (this.asset) {
|
|
1663
|
+
// Deregister first so unload() can still notify the registry
|
|
1664
|
+
(_a = this.asset.registry) === null || _a === void 0 ? void 0 : _a.remove(this.asset);
|
|
1460
1665
|
this.asset.unload();
|
|
1461
1666
|
this.asset = null;
|
|
1462
1667
|
}
|
|
@@ -1485,9 +1690,9 @@ class AssetElement extends HTMLElement {
|
|
|
1485
1690
|
static get observedAttributes() {
|
|
1486
1691
|
return ['lazy'];
|
|
1487
1692
|
}
|
|
1488
|
-
attributeChangedCallback(name, _oldValue,
|
|
1693
|
+
attributeChangedCallback(name, _oldValue, newValue) {
|
|
1489
1694
|
if (name === 'lazy') {
|
|
1490
|
-
this.lazy =
|
|
1695
|
+
this.lazy = parseBool(newValue, false);
|
|
1491
1696
|
}
|
|
1492
1697
|
}
|
|
1493
1698
|
}
|
|
@@ -1509,6 +1714,7 @@ class ComponentElement extends AsyncElement {
|
|
|
1509
1714
|
super();
|
|
1510
1715
|
this._enabled = true;
|
|
1511
1716
|
this._component = null;
|
|
1717
|
+
this._appElement = null;
|
|
1512
1718
|
this._componentName = componentName;
|
|
1513
1719
|
}
|
|
1514
1720
|
// Method to be overridden by subclasses to provide initial component data
|
|
@@ -1517,28 +1723,43 @@ class ComponentElement extends AsyncElement {
|
|
|
1517
1723
|
}
|
|
1518
1724
|
async addComponent() {
|
|
1519
1725
|
const entityElement = this.closestEntity;
|
|
1520
|
-
if (entityElement) {
|
|
1521
|
-
|
|
1522
|
-
//
|
|
1523
|
-
const
|
|
1524
|
-
this.
|
|
1726
|
+
if (!entityElement) {
|
|
1727
|
+
// A component can only exist on an entity, so an element placed outside one is inert.
|
|
1728
|
+
// It still becomes ready (with a null `component`), so warn rather than fail silently
|
|
1729
|
+
const label = this.id ? ` '${this.id}'` : '';
|
|
1730
|
+
console.warn(`${this.tagName.toLowerCase()}${label} must be a descendant of pc-entity - component not added`);
|
|
1731
|
+
return;
|
|
1525
1732
|
}
|
|
1733
|
+
await entityElement.ready();
|
|
1734
|
+
// Add the component to the entity
|
|
1735
|
+
const data = this.getInitialComponentData();
|
|
1736
|
+
this._component = entityElement.entity.addComponent(this._componentName, data);
|
|
1526
1737
|
}
|
|
1527
1738
|
initComponent() { }
|
|
1528
1739
|
async connectedCallback() {
|
|
1529
|
-
var _a;
|
|
1530
|
-
|
|
1740
|
+
var _a, _b;
|
|
1741
|
+
this._appElement = (_a = this.closestApp) !== null && _a !== void 0 ? _a : null;
|
|
1742
|
+
await ((_b = this._appElement) === null || _b === void 0 ? void 0 : _b.ready());
|
|
1531
1743
|
await this.addComponent();
|
|
1532
1744
|
this.initComponent();
|
|
1533
1745
|
this._onReady();
|
|
1534
1746
|
}
|
|
1535
1747
|
disconnectedCallback() {
|
|
1536
|
-
|
|
1537
|
-
|
|
1748
|
+
var _a, _b;
|
|
1749
|
+
// Remove the component when the element is disconnected. Skip this when the owning
|
|
1750
|
+
// application has already been destroyed — removing a <pc-app> disconnects it before
|
|
1751
|
+
// its children, taking the component systems with it.
|
|
1752
|
+
if (((_a = this._appElement) === null || _a === void 0 ? void 0 : _a.app) && ((_b = this._component) === null || _b === void 0 ? void 0 : _b.entity)) {
|
|
1538
1753
|
this._component.entity.removeComponent(this._componentName);
|
|
1539
|
-
this._component = null;
|
|
1540
1754
|
}
|
|
1755
|
+
this._component = null;
|
|
1756
|
+
this._appElement = null;
|
|
1541
1757
|
}
|
|
1758
|
+
/**
|
|
1759
|
+
* The PlayCanvas component instance. Available once the element is ready — await
|
|
1760
|
+
* {@link whenReady} or the element's `ready()` promise before accessing it.
|
|
1761
|
+
* @returns The component instance.
|
|
1762
|
+
*/
|
|
1542
1763
|
get component() {
|
|
1543
1764
|
return this._component;
|
|
1544
1765
|
}
|
|
@@ -1565,7 +1786,7 @@ class ComponentElement extends AsyncElement {
|
|
|
1565
1786
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
1566
1787
|
switch (name) {
|
|
1567
1788
|
case 'enabled':
|
|
1568
|
-
this.enabled = newValue
|
|
1789
|
+
this.enabled = parseBool(newValue, true);
|
|
1569
1790
|
break;
|
|
1570
1791
|
}
|
|
1571
1792
|
}
|
|
@@ -1613,7 +1834,7 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
1613
1834
|
this._active = true;
|
|
1614
1835
|
this._image = '';
|
|
1615
1836
|
this._hitPadding = new playcanvas.Vec4(0, 0, 0, 0);
|
|
1616
|
-
this._transitionMode =
|
|
1837
|
+
this._transitionMode = 'tint';
|
|
1617
1838
|
this._hoverTint = new playcanvas.Color(1, 1, 1, 1);
|
|
1618
1839
|
this._pressedTint = new playcanvas.Color(1, 1, 1, 1);
|
|
1619
1840
|
this._inactiveTint = new playcanvas.Color(1, 1, 1, 1);
|
|
@@ -1630,7 +1851,7 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
1630
1851
|
const data = {
|
|
1631
1852
|
active: this._active,
|
|
1632
1853
|
hitPadding: this._hitPadding,
|
|
1633
|
-
transitionMode: this._transitionMode,
|
|
1854
|
+
transitionMode: transitionModes.get(this._transitionMode),
|
|
1634
1855
|
hoverTint: this._hoverTint,
|
|
1635
1856
|
pressedTint: this._pressedTint,
|
|
1636
1857
|
inactiveTint: this._inactiveTint,
|
|
@@ -1720,13 +1941,15 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
1720
1941
|
return this._hitPadding;
|
|
1721
1942
|
}
|
|
1722
1943
|
/**
|
|
1723
|
-
* Sets how the button reacts to being hovered/pressed. Can be `tint`
|
|
1944
|
+
* Sets how the button reacts to being hovered/pressed. Can be `tint` or `sprite`. Defaults to
|
|
1945
|
+
* `tint`.
|
|
1724
1946
|
* @param value - The transition mode.
|
|
1725
1947
|
*/
|
|
1726
1948
|
set transitionMode(value) {
|
|
1949
|
+
var _a;
|
|
1727
1950
|
this._transitionMode = value;
|
|
1728
1951
|
if (this.component) {
|
|
1729
|
-
this.component.transitionMode = value;
|
|
1952
|
+
this.component.transitionMode = (_a = transitionModes.get(value)) !== null && _a !== void 0 ? _a : playcanvas.BUTTON_TRANSITION_MODE_TINT;
|
|
1730
1953
|
}
|
|
1731
1954
|
}
|
|
1732
1955
|
/**
|
|
@@ -1938,46 +2161,46 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
1938
2161
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
1939
2162
|
switch (name) {
|
|
1940
2163
|
case 'active':
|
|
1941
|
-
this.active = newValue
|
|
2164
|
+
this.active = parseBool(newValue, true);
|
|
1942
2165
|
break;
|
|
1943
2166
|
case 'image':
|
|
1944
2167
|
this.image = newValue;
|
|
1945
2168
|
break;
|
|
1946
2169
|
case 'hit-padding':
|
|
1947
|
-
this.hitPadding = parseVec4(newValue);
|
|
2170
|
+
this.hitPadding = parseVec4(newValue, playcanvas.Vec4.ZERO, name);
|
|
1948
2171
|
break;
|
|
1949
2172
|
case 'transition-mode':
|
|
1950
|
-
this.transitionMode = parseEnum(newValue, transitionModes,
|
|
2173
|
+
this.transitionMode = parseEnum(newValue, transitionModes, 'tint', name);
|
|
1951
2174
|
break;
|
|
1952
2175
|
case 'hover-tint':
|
|
1953
|
-
this.hoverTint = parseColor(newValue);
|
|
2176
|
+
this.hoverTint = parseColor(newValue, playcanvas.Color.WHITE, name);
|
|
1954
2177
|
break;
|
|
1955
2178
|
case 'pressed-tint':
|
|
1956
|
-
this.pressedTint = parseColor(newValue);
|
|
2179
|
+
this.pressedTint = parseColor(newValue, playcanvas.Color.WHITE, name);
|
|
1957
2180
|
break;
|
|
1958
2181
|
case 'inactive-tint':
|
|
1959
|
-
this.inactiveTint = parseColor(newValue);
|
|
2182
|
+
this.inactiveTint = parseColor(newValue, playcanvas.Color.WHITE, name);
|
|
1960
2183
|
break;
|
|
1961
2184
|
case 'fade-duration':
|
|
1962
|
-
this.fadeDuration =
|
|
2185
|
+
this.fadeDuration = parseNumber(newValue, 0, name);
|
|
1963
2186
|
break;
|
|
1964
2187
|
case 'hover-sprite-asset':
|
|
1965
2188
|
this.hoverSpriteAsset = newValue;
|
|
1966
2189
|
break;
|
|
1967
2190
|
case 'hover-sprite-frame':
|
|
1968
|
-
this.hoverSpriteFrame =
|
|
2191
|
+
this.hoverSpriteFrame = parseNumber(newValue, 0, name);
|
|
1969
2192
|
break;
|
|
1970
2193
|
case 'pressed-sprite-asset':
|
|
1971
2194
|
this.pressedSpriteAsset = newValue;
|
|
1972
2195
|
break;
|
|
1973
2196
|
case 'pressed-sprite-frame':
|
|
1974
|
-
this.pressedSpriteFrame =
|
|
2197
|
+
this.pressedSpriteFrame = parseNumber(newValue, 0, name);
|
|
1975
2198
|
break;
|
|
1976
2199
|
case 'inactive-sprite-asset':
|
|
1977
2200
|
this.inactiveSpriteAsset = newValue;
|
|
1978
2201
|
break;
|
|
1979
2202
|
case 'inactive-sprite-frame':
|
|
1980
|
-
this.inactiveSpriteFrame =
|
|
2203
|
+
this.inactiveSpriteFrame = parseNumber(newValue, 0, name);
|
|
1981
2204
|
break;
|
|
1982
2205
|
}
|
|
1983
2206
|
}
|
|
@@ -2038,7 +2261,7 @@ class CameraComponentElement extends ComponentElement {
|
|
|
2038
2261
|
gammaCorrection: this._gamma === 'srgb' ? playcanvas.GAMMA_SRGB : playcanvas.GAMMA_NONE,
|
|
2039
2262
|
horizontalFov: this._horizontalFov,
|
|
2040
2263
|
nearClip: this._nearClip,
|
|
2041
|
-
|
|
2264
|
+
projection: this._orthographic ? playcanvas.PROJECTION_ORTHOGRAPHIC : playcanvas.PROJECTION_PERSPECTIVE,
|
|
2042
2265
|
orthoHeight: this._orthoHeight,
|
|
2043
2266
|
priority: this._priority,
|
|
2044
2267
|
rect: this._rect,
|
|
@@ -2416,58 +2639,58 @@ class CameraComponentElement extends ComponentElement {
|
|
|
2416
2639
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
2417
2640
|
switch (name) {
|
|
2418
2641
|
case 'clear-color':
|
|
2419
|
-
this.clearColor = parseColor(newValue);
|
|
2642
|
+
this.clearColor = parseColor(newValue, new playcanvas.Color(0.75, 0.75, 0.75, 1), name);
|
|
2420
2643
|
break;
|
|
2421
2644
|
case 'clear-color-buffer':
|
|
2422
|
-
this.clearColorBuffer = newValue
|
|
2645
|
+
this.clearColorBuffer = parseBool(newValue, true);
|
|
2423
2646
|
break;
|
|
2424
2647
|
case 'clear-depth-buffer':
|
|
2425
|
-
this.clearDepthBuffer = newValue
|
|
2648
|
+
this.clearDepthBuffer = parseBool(newValue, true);
|
|
2426
2649
|
break;
|
|
2427
2650
|
case 'clear-stencil-buffer':
|
|
2428
|
-
this.clearStencilBuffer = newValue
|
|
2651
|
+
this.clearStencilBuffer = parseBool(newValue, false);
|
|
2429
2652
|
break;
|
|
2430
2653
|
case 'cull-faces':
|
|
2431
|
-
this.cullFaces = newValue
|
|
2654
|
+
this.cullFaces = parseBool(newValue, true);
|
|
2432
2655
|
break;
|
|
2433
2656
|
case 'far-clip':
|
|
2434
|
-
this.farClip =
|
|
2657
|
+
this.farClip = parseNumber(newValue, 1000, name);
|
|
2435
2658
|
break;
|
|
2436
2659
|
case 'flip-faces':
|
|
2437
|
-
this.flipFaces = newValue
|
|
2660
|
+
this.flipFaces = parseBool(newValue, false);
|
|
2438
2661
|
break;
|
|
2439
2662
|
case 'fov':
|
|
2440
|
-
this.fov =
|
|
2663
|
+
this.fov = parseNumber(newValue, 45, name);
|
|
2441
2664
|
break;
|
|
2442
2665
|
case 'frustum-culling':
|
|
2443
|
-
this.frustumCulling = newValue
|
|
2666
|
+
this.frustumCulling = parseBool(newValue, true);
|
|
2444
2667
|
break;
|
|
2445
2668
|
case 'gamma':
|
|
2446
|
-
this.gamma = newValue;
|
|
2669
|
+
this.gamma = parseEnum(newValue, ['linear', 'srgb'], 'srgb', name);
|
|
2447
2670
|
break;
|
|
2448
2671
|
case 'horizontal-fov':
|
|
2449
|
-
this.horizontalFov =
|
|
2672
|
+
this.horizontalFov = parseBool(newValue, false);
|
|
2450
2673
|
break;
|
|
2451
2674
|
case 'near-clip':
|
|
2452
|
-
this.nearClip =
|
|
2675
|
+
this.nearClip = parseNumber(newValue, 0.1, name);
|
|
2453
2676
|
break;
|
|
2454
2677
|
case 'orthographic':
|
|
2455
|
-
this.orthographic =
|
|
2678
|
+
this.orthographic = parseBool(newValue, false);
|
|
2456
2679
|
break;
|
|
2457
2680
|
case 'ortho-height':
|
|
2458
|
-
this.orthoHeight =
|
|
2681
|
+
this.orthoHeight = parseNumber(newValue, 10, name);
|
|
2459
2682
|
break;
|
|
2460
2683
|
case 'priority':
|
|
2461
|
-
this.priority =
|
|
2684
|
+
this.priority = parseNumber(newValue, 0, name);
|
|
2462
2685
|
break;
|
|
2463
2686
|
case 'rect':
|
|
2464
|
-
this.rect = parseVec4(newValue);
|
|
2687
|
+
this.rect = parseVec4(newValue, new playcanvas.Vec4(0, 0, 1, 1), name);
|
|
2465
2688
|
break;
|
|
2466
2689
|
case 'scissor-rect':
|
|
2467
|
-
this.scissorRect = parseVec4(newValue);
|
|
2690
|
+
this.scissorRect = parseVec4(newValue, new playcanvas.Vec4(0, 0, 1, 1), name);
|
|
2468
2691
|
break;
|
|
2469
2692
|
case 'tonemap':
|
|
2470
|
-
this.tonemap = newValue;
|
|
2693
|
+
this.tonemap = parseEnum(newValue, tonemaps, 'none', name);
|
|
2471
2694
|
break;
|
|
2472
2695
|
}
|
|
2473
2696
|
}
|
|
@@ -2593,28 +2816,28 @@ class CollisionComponentElement extends ComponentElement {
|
|
|
2593
2816
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
2594
2817
|
switch (name) {
|
|
2595
2818
|
case 'angular-offset':
|
|
2596
|
-
this.angularOffset = parseQuat(newValue);
|
|
2819
|
+
this.angularOffset = parseQuat(newValue, playcanvas.Quat.IDENTITY, name);
|
|
2597
2820
|
break;
|
|
2598
2821
|
case 'axis':
|
|
2599
|
-
this.axis =
|
|
2822
|
+
this.axis = parseNumber(newValue, 1, name);
|
|
2600
2823
|
break;
|
|
2601
2824
|
case 'convex-hull':
|
|
2602
|
-
this.convexHull =
|
|
2825
|
+
this.convexHull = parseBool(newValue, false);
|
|
2603
2826
|
break;
|
|
2604
2827
|
case 'half-extents':
|
|
2605
|
-
this.halfExtents = parseVec3(newValue);
|
|
2828
|
+
this.halfExtents = parseVec3(newValue, new playcanvas.Vec3(0.5, 0.5, 0.5), name);
|
|
2606
2829
|
break;
|
|
2607
2830
|
case 'height':
|
|
2608
|
-
this.height =
|
|
2831
|
+
this.height = parseNumber(newValue, 2, name);
|
|
2609
2832
|
break;
|
|
2610
2833
|
case 'linear-offset':
|
|
2611
|
-
this.linearOffset = parseVec3(newValue);
|
|
2834
|
+
this.linearOffset = parseVec3(newValue, playcanvas.Vec3.ZERO, name);
|
|
2612
2835
|
break;
|
|
2613
2836
|
case 'radius':
|
|
2614
|
-
this.radius =
|
|
2837
|
+
this.radius = parseNumber(newValue, 0.5, name);
|
|
2615
2838
|
break;
|
|
2616
2839
|
case 'type':
|
|
2617
|
-
this.type = newValue;
|
|
2840
|
+
this.type = parseEnum(newValue, ['box', 'capsule', 'compound', 'cone', 'cylinder', 'mesh', 'sphere'], 'box', name);
|
|
2618
2841
|
break;
|
|
2619
2842
|
}
|
|
2620
2843
|
}
|
|
@@ -2634,13 +2857,13 @@ class ElementComponentElement extends ComponentElement {
|
|
|
2634
2857
|
constructor() {
|
|
2635
2858
|
super('element');
|
|
2636
2859
|
this._anchor = new playcanvas.Vec4(0.5, 0.5, 0.5, 0.5);
|
|
2637
|
-
this._asset = '';
|
|
2638
2860
|
this._autoWidth = true;
|
|
2639
2861
|
this._autoHeight = true;
|
|
2640
2862
|
this._autoFitWidth = false;
|
|
2641
2863
|
this._autoFitHeight = false;
|
|
2642
2864
|
this._color = new playcanvas.Color(1, 1, 1, 1);
|
|
2643
2865
|
this._enableMarkup = false;
|
|
2866
|
+
this._fontAsset = '';
|
|
2644
2867
|
this._fontSize = 32;
|
|
2645
2868
|
this._maxFontSize = 32;
|
|
2646
2869
|
this._minFontSize = 8;
|
|
@@ -2703,7 +2926,7 @@ class ElementComponentElement extends ComponentElement {
|
|
|
2703
2926
|
};
|
|
2704
2927
|
// Asset references are resolved from `<pc-asset>` element ids to engine asset ids. They are
|
|
2705
2928
|
// only included when they resolve, so image/group elements (with no font) don't error.
|
|
2706
|
-
const fontAsset = AssetElement.get(this.
|
|
2929
|
+
const fontAsset = AssetElement.get(this._fontAsset);
|
|
2707
2930
|
if (fontAsset) {
|
|
2708
2931
|
data.fontAsset = fontAsset.id;
|
|
2709
2932
|
}
|
|
@@ -2749,24 +2972,6 @@ class ElementComponentElement extends ComponentElement {
|
|
|
2749
2972
|
get anchor() {
|
|
2750
2973
|
return this._anchor;
|
|
2751
2974
|
}
|
|
2752
|
-
/**
|
|
2753
|
-
* Sets the id of the `pc-asset` to use for the font (text elements).
|
|
2754
|
-
* @param value - The asset ID.
|
|
2755
|
-
*/
|
|
2756
|
-
set asset(value) {
|
|
2757
|
-
this._asset = value;
|
|
2758
|
-
const asset = AssetElement.get(value);
|
|
2759
|
-
if (this.component && asset) {
|
|
2760
|
-
this.component.fontAsset = asset.id;
|
|
2761
|
-
}
|
|
2762
|
-
}
|
|
2763
|
-
/**
|
|
2764
|
-
* Gets the id of the `pc-asset` to use for the font.
|
|
2765
|
-
* @returns The asset ID.
|
|
2766
|
-
*/
|
|
2767
|
-
get asset() {
|
|
2768
|
-
return this._asset;
|
|
2769
|
-
}
|
|
2770
2975
|
/**
|
|
2771
2976
|
* Sets whether the element component should automatically adjust its width to the text content
|
|
2772
2977
|
* (text elements only).
|
|
@@ -2837,6 +3042,24 @@ class ElementComponentElement extends ComponentElement {
|
|
|
2837
3042
|
get enableMarkup() {
|
|
2838
3043
|
return this._enableMarkup;
|
|
2839
3044
|
}
|
|
3045
|
+
/**
|
|
3046
|
+
* Sets the id of the `pc-asset` to use for the font (text elements).
|
|
3047
|
+
* @param value - The font asset ID.
|
|
3048
|
+
*/
|
|
3049
|
+
set fontAsset(value) {
|
|
3050
|
+
this._fontAsset = value;
|
|
3051
|
+
const asset = AssetElement.get(value);
|
|
3052
|
+
if (this.component && asset) {
|
|
3053
|
+
this.component.fontAsset = asset.id;
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
/**
|
|
3057
|
+
* Gets the id of the `pc-asset` to use for the font.
|
|
3058
|
+
* @returns The font asset ID.
|
|
3059
|
+
*/
|
|
3060
|
+
get fontAsset() {
|
|
3061
|
+
return this._fontAsset;
|
|
3062
|
+
}
|
|
2840
3063
|
/**
|
|
2841
3064
|
* Sets the font size of the element component.
|
|
2842
3065
|
* @param value - The font size.
|
|
@@ -3186,13 +3409,13 @@ class ElementComponentElement extends ComponentElement {
|
|
|
3186
3409
|
return [
|
|
3187
3410
|
...super.observedAttributes,
|
|
3188
3411
|
'anchor',
|
|
3189
|
-
'asset',
|
|
3190
3412
|
'auto-width',
|
|
3191
3413
|
'auto-height',
|
|
3192
3414
|
'auto-fit-width',
|
|
3193
3415
|
'auto-fit-height',
|
|
3194
3416
|
'color',
|
|
3195
3417
|
'enable-markup',
|
|
3418
|
+
'font-asset',
|
|
3196
3419
|
'font-size',
|
|
3197
3420
|
'max-font-size',
|
|
3198
3421
|
'min-font-size',
|
|
@@ -3217,64 +3440,64 @@ class ElementComponentElement extends ComponentElement {
|
|
|
3217
3440
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
3218
3441
|
switch (name) {
|
|
3219
3442
|
case 'anchor':
|
|
3220
|
-
this.anchor = parseVec4(newValue);
|
|
3221
|
-
break;
|
|
3222
|
-
case 'asset':
|
|
3223
|
-
this.asset = newValue;
|
|
3443
|
+
this.anchor = parseVec4(newValue, new playcanvas.Vec4(0.5, 0.5, 0.5, 0.5), name);
|
|
3224
3444
|
break;
|
|
3225
3445
|
case 'auto-width':
|
|
3226
|
-
this.autoWidth = newValue
|
|
3446
|
+
this.autoWidth = parseBool(newValue, true);
|
|
3227
3447
|
break;
|
|
3228
3448
|
case 'auto-height':
|
|
3229
|
-
this.autoHeight = newValue
|
|
3449
|
+
this.autoHeight = parseBool(newValue, true);
|
|
3230
3450
|
break;
|
|
3231
3451
|
case 'auto-fit-width':
|
|
3232
|
-
this.autoFitWidth = newValue
|
|
3452
|
+
this.autoFitWidth = parseBool(newValue, false);
|
|
3233
3453
|
break;
|
|
3234
3454
|
case 'auto-fit-height':
|
|
3235
|
-
this.autoFitHeight = newValue
|
|
3455
|
+
this.autoFitHeight = parseBool(newValue, false);
|
|
3236
3456
|
break;
|
|
3237
3457
|
case 'color':
|
|
3238
|
-
this.color = parseColor(newValue);
|
|
3458
|
+
this.color = parseColor(newValue, playcanvas.Color.WHITE, name);
|
|
3239
3459
|
break;
|
|
3240
3460
|
case 'enable-markup':
|
|
3241
|
-
this.enableMarkup =
|
|
3461
|
+
this.enableMarkup = parseBool(newValue, false);
|
|
3462
|
+
break;
|
|
3463
|
+
case 'font-asset':
|
|
3464
|
+
this.fontAsset = newValue;
|
|
3242
3465
|
break;
|
|
3243
3466
|
case 'font-size':
|
|
3244
|
-
this.fontSize =
|
|
3467
|
+
this.fontSize = parseNumber(newValue, 32, name);
|
|
3245
3468
|
break;
|
|
3246
3469
|
case 'max-font-size':
|
|
3247
|
-
this.maxFontSize =
|
|
3470
|
+
this.maxFontSize = parseNumber(newValue, 32, name);
|
|
3248
3471
|
break;
|
|
3249
3472
|
case 'min-font-size':
|
|
3250
|
-
this.minFontSize =
|
|
3473
|
+
this.minFontSize = parseNumber(newValue, 8, name);
|
|
3251
3474
|
break;
|
|
3252
3475
|
case 'height':
|
|
3253
|
-
this.height =
|
|
3476
|
+
this.height = parseNumber(newValue, 0, name);
|
|
3254
3477
|
break;
|
|
3255
3478
|
case 'line-height':
|
|
3256
|
-
this.lineHeight =
|
|
3479
|
+
this.lineHeight = parseNumber(newValue, 32, name);
|
|
3257
3480
|
break;
|
|
3258
3481
|
case 'margin':
|
|
3259
|
-
this.margin = parseVec4(newValue);
|
|
3482
|
+
this.margin = parseVec4(newValue, null, name);
|
|
3260
3483
|
break;
|
|
3261
3484
|
case 'mask':
|
|
3262
|
-
this.mask =
|
|
3485
|
+
this.mask = parseBool(newValue, false);
|
|
3263
3486
|
break;
|
|
3264
3487
|
case 'opacity':
|
|
3265
|
-
this.opacity =
|
|
3488
|
+
this.opacity = parseNumber(newValue, 1, name);
|
|
3266
3489
|
break;
|
|
3267
3490
|
case 'pivot':
|
|
3268
|
-
this.pivot = parseVec2(newValue);
|
|
3491
|
+
this.pivot = parseVec2(newValue, new playcanvas.Vec2(0.5, 0.5), name);
|
|
3269
3492
|
break;
|
|
3270
3493
|
case 'pixels-per-unit':
|
|
3271
|
-
this.pixelsPerUnit =
|
|
3494
|
+
this.pixelsPerUnit = parseNumber(newValue, null, name);
|
|
3272
3495
|
break;
|
|
3273
3496
|
case 'sprite-asset':
|
|
3274
3497
|
this.spriteAsset = newValue;
|
|
3275
3498
|
break;
|
|
3276
3499
|
case 'sprite-frame':
|
|
3277
|
-
this.spriteFrame =
|
|
3500
|
+
this.spriteFrame = parseNumber(newValue, 0, name);
|
|
3278
3501
|
break;
|
|
3279
3502
|
case 'text':
|
|
3280
3503
|
this.text = newValue;
|
|
@@ -3283,16 +3506,16 @@ class ElementComponentElement extends ComponentElement {
|
|
|
3283
3506
|
this.textureAsset = newValue;
|
|
3284
3507
|
break;
|
|
3285
3508
|
case 'type':
|
|
3286
|
-
this.type = newValue;
|
|
3509
|
+
this.type = parseEnum(newValue, ['group', 'image', 'text'], 'group', name);
|
|
3287
3510
|
break;
|
|
3288
3511
|
case 'use-input':
|
|
3289
|
-
this.useInput =
|
|
3512
|
+
this.useInput = parseBool(newValue, false);
|
|
3290
3513
|
break;
|
|
3291
3514
|
case 'width':
|
|
3292
|
-
this.width =
|
|
3515
|
+
this.width = parseNumber(newValue, 0, name);
|
|
3293
3516
|
break;
|
|
3294
3517
|
case 'wrap-lines':
|
|
3295
|
-
this.wrapLines =
|
|
3518
|
+
this.wrapLines = parseBool(newValue, false);
|
|
3296
3519
|
break;
|
|
3297
3520
|
}
|
|
3298
3521
|
}
|
|
@@ -3474,25 +3697,25 @@ class LayoutChildComponentElement extends ComponentElement {
|
|
|
3474
3697
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
3475
3698
|
switch (name) {
|
|
3476
3699
|
case 'min-width':
|
|
3477
|
-
this.minWidth =
|
|
3700
|
+
this.minWidth = parseNumber(newValue, 0, name);
|
|
3478
3701
|
break;
|
|
3479
3702
|
case 'min-height':
|
|
3480
|
-
this.minHeight =
|
|
3703
|
+
this.minHeight = parseNumber(newValue, 0, name);
|
|
3481
3704
|
break;
|
|
3482
3705
|
case 'max-width':
|
|
3483
|
-
this.maxWidth = newValue
|
|
3706
|
+
this.maxWidth = parseNumber(newValue, null, name);
|
|
3484
3707
|
break;
|
|
3485
3708
|
case 'max-height':
|
|
3486
|
-
this.maxHeight = newValue
|
|
3709
|
+
this.maxHeight = parseNumber(newValue, null, name);
|
|
3487
3710
|
break;
|
|
3488
3711
|
case 'fit-width-proportion':
|
|
3489
|
-
this.fitWidthProportion =
|
|
3712
|
+
this.fitWidthProportion = parseNumber(newValue, 0, name);
|
|
3490
3713
|
break;
|
|
3491
3714
|
case 'fit-height-proportion':
|
|
3492
|
-
this.fitHeightProportion =
|
|
3715
|
+
this.fitHeightProportion = parseNumber(newValue, 0, name);
|
|
3493
3716
|
break;
|
|
3494
3717
|
case 'exclude-from-layout':
|
|
3495
|
-
this.excludeFromLayout = newValue
|
|
3718
|
+
this.excludeFromLayout = parseBool(newValue, false);
|
|
3496
3719
|
break;
|
|
3497
3720
|
}
|
|
3498
3721
|
}
|
|
@@ -3521,26 +3744,26 @@ class LayoutGroupComponentElement extends ComponentElement {
|
|
|
3521
3744
|
/** @ignore */
|
|
3522
3745
|
constructor() {
|
|
3523
3746
|
super('layoutgroup');
|
|
3524
|
-
this._orientation =
|
|
3747
|
+
this._orientation = 'horizontal';
|
|
3525
3748
|
this._reverseX = false;
|
|
3526
3749
|
this._reverseY = false;
|
|
3527
3750
|
this._alignment = new playcanvas.Vec2(0, 1);
|
|
3528
3751
|
this._padding = new playcanvas.Vec4(0, 0, 0, 0);
|
|
3529
3752
|
this._spacing = new playcanvas.Vec2(0, 0);
|
|
3530
|
-
this._widthFitting =
|
|
3531
|
-
this._heightFitting =
|
|
3753
|
+
this._widthFitting = 'none';
|
|
3754
|
+
this._heightFitting = 'none';
|
|
3532
3755
|
this._wrap = false;
|
|
3533
3756
|
}
|
|
3534
3757
|
getInitialComponentData() {
|
|
3535
3758
|
return {
|
|
3536
|
-
orientation: this._orientation,
|
|
3759
|
+
orientation: orientations$1.get(this._orientation),
|
|
3537
3760
|
reverseX: this._reverseX,
|
|
3538
3761
|
reverseY: this._reverseY,
|
|
3539
3762
|
alignment: this._alignment,
|
|
3540
3763
|
padding: this._padding,
|
|
3541
3764
|
spacing: this._spacing,
|
|
3542
|
-
widthFitting: this._widthFitting,
|
|
3543
|
-
heightFitting: this._heightFitting,
|
|
3765
|
+
widthFitting: fittings.get(this._widthFitting),
|
|
3766
|
+
heightFitting: fittings.get(this._heightFitting),
|
|
3544
3767
|
wrap: this._wrap
|
|
3545
3768
|
};
|
|
3546
3769
|
}
|
|
@@ -3552,13 +3775,15 @@ class LayoutGroupComponentElement extends ComponentElement {
|
|
|
3552
3775
|
return super.component;
|
|
3553
3776
|
}
|
|
3554
3777
|
/**
|
|
3555
|
-
* Sets the orientation of the layout group. Can be `horizontal`
|
|
3778
|
+
* Sets the orientation of the layout group. Can be `horizontal` or `vertical`. Defaults to
|
|
3779
|
+
* `horizontal`.
|
|
3556
3780
|
* @param value - The orientation.
|
|
3557
3781
|
*/
|
|
3558
3782
|
set orientation(value) {
|
|
3783
|
+
var _a;
|
|
3559
3784
|
this._orientation = value;
|
|
3560
3785
|
if (this.component) {
|
|
3561
|
-
this.component.orientation = value;
|
|
3786
|
+
this.component.orientation = (_a = orientations$1.get(value)) !== null && _a !== void 0 ? _a : playcanvas.ORIENTATION_HORIZONTAL;
|
|
3562
3787
|
}
|
|
3563
3788
|
}
|
|
3564
3789
|
/**
|
|
@@ -3654,14 +3879,15 @@ class LayoutGroupComponentElement extends ComponentElement {
|
|
|
3654
3879
|
return this._spacing;
|
|
3655
3880
|
}
|
|
3656
3881
|
/**
|
|
3657
|
-
* Sets the fitting mode along the horizontal axis. Can be `none
|
|
3658
|
-
*
|
|
3882
|
+
* Sets the fitting mode along the horizontal axis. Can be `none`, `stretch`, `shrink` or
|
|
3883
|
+
* `both`. Defaults to `none`.
|
|
3659
3884
|
* @param value - The width fitting mode.
|
|
3660
3885
|
*/
|
|
3661
3886
|
set widthFitting(value) {
|
|
3887
|
+
var _a;
|
|
3662
3888
|
this._widthFitting = value;
|
|
3663
3889
|
if (this.component) {
|
|
3664
|
-
this.component.widthFitting = value;
|
|
3890
|
+
this.component.widthFitting = (_a = fittings.get(value)) !== null && _a !== void 0 ? _a : playcanvas.FITTING_NONE;
|
|
3665
3891
|
}
|
|
3666
3892
|
}
|
|
3667
3893
|
/**
|
|
@@ -3672,14 +3898,15 @@ class LayoutGroupComponentElement extends ComponentElement {
|
|
|
3672
3898
|
return this._widthFitting;
|
|
3673
3899
|
}
|
|
3674
3900
|
/**
|
|
3675
|
-
* Sets the fitting mode along the vertical axis. Can be `none
|
|
3676
|
-
*
|
|
3901
|
+
* Sets the fitting mode along the vertical axis. Can be `none`, `stretch`, `shrink` or
|
|
3902
|
+
* `both`. Defaults to `none`.
|
|
3677
3903
|
* @param value - The height fitting mode.
|
|
3678
3904
|
*/
|
|
3679
3905
|
set heightFitting(value) {
|
|
3906
|
+
var _a;
|
|
3680
3907
|
this._heightFitting = value;
|
|
3681
3908
|
if (this.component) {
|
|
3682
|
-
this.component.heightFitting = value;
|
|
3909
|
+
this.component.heightFitting = (_a = fittings.get(value)) !== null && _a !== void 0 ? _a : playcanvas.FITTING_NONE;
|
|
3683
3910
|
}
|
|
3684
3911
|
}
|
|
3685
3912
|
/**
|
|
@@ -3724,31 +3951,31 @@ class LayoutGroupComponentElement extends ComponentElement {
|
|
|
3724
3951
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
3725
3952
|
switch (name) {
|
|
3726
3953
|
case 'orientation':
|
|
3727
|
-
this.orientation = parseEnum(newValue, orientations$1,
|
|
3954
|
+
this.orientation = parseEnum(newValue, orientations$1, 'horizontal', name);
|
|
3728
3955
|
break;
|
|
3729
3956
|
case 'reverse-x':
|
|
3730
|
-
this.reverseX = newValue
|
|
3957
|
+
this.reverseX = parseBool(newValue, false);
|
|
3731
3958
|
break;
|
|
3732
3959
|
case 'reverse-y':
|
|
3733
|
-
this.reverseY = newValue
|
|
3960
|
+
this.reverseY = parseBool(newValue, false);
|
|
3734
3961
|
break;
|
|
3735
3962
|
case 'alignment':
|
|
3736
|
-
this.alignment = parseVec2(newValue);
|
|
3963
|
+
this.alignment = parseVec2(newValue, new playcanvas.Vec2(0, 1), name);
|
|
3737
3964
|
break;
|
|
3738
3965
|
case 'padding':
|
|
3739
|
-
this.padding = parseVec4(newValue);
|
|
3966
|
+
this.padding = parseVec4(newValue, playcanvas.Vec4.ZERO, name);
|
|
3740
3967
|
break;
|
|
3741
3968
|
case 'spacing':
|
|
3742
|
-
this.spacing = parseVec2(newValue);
|
|
3969
|
+
this.spacing = parseVec2(newValue, playcanvas.Vec2.ZERO, name);
|
|
3743
3970
|
break;
|
|
3744
3971
|
case 'width-fitting':
|
|
3745
|
-
this.widthFitting = parseEnum(newValue, fittings,
|
|
3972
|
+
this.widthFitting = parseEnum(newValue, fittings, 'none', name);
|
|
3746
3973
|
break;
|
|
3747
3974
|
case 'height-fitting':
|
|
3748
|
-
this.heightFitting = parseEnum(newValue, fittings,
|
|
3975
|
+
this.heightFitting = parseEnum(newValue, fittings, 'none', name);
|
|
3749
3976
|
break;
|
|
3750
3977
|
case 'wrap':
|
|
3751
|
-
this.wrap = newValue
|
|
3978
|
+
this.wrap = parseBool(newValue, false);
|
|
3752
3979
|
break;
|
|
3753
3980
|
}
|
|
3754
3981
|
}
|
|
@@ -4044,14 +4271,11 @@ class LightComponentElement extends ComponentElement {
|
|
|
4044
4271
|
return this._shadowType;
|
|
4045
4272
|
}
|
|
4046
4273
|
/**
|
|
4047
|
-
* Sets the type of the light.
|
|
4274
|
+
* Sets the type of the light. Can be `directional`, `omni` or `spot`. Defaults to
|
|
4275
|
+
* `directional`.
|
|
4048
4276
|
* @param value - The type.
|
|
4049
4277
|
*/
|
|
4050
4278
|
set type(value) {
|
|
4051
|
-
if (!['directional', 'omni', 'spot'].includes(value)) {
|
|
4052
|
-
console.warn(`Invalid light type '${value}', using default type '${this._type}'.`);
|
|
4053
|
-
return;
|
|
4054
|
-
}
|
|
4055
4279
|
this._type = value;
|
|
4056
4280
|
if (this.component) {
|
|
4057
4281
|
this.component.type = value;
|
|
@@ -4194,61 +4418,61 @@ class LightComponentElement extends ComponentElement {
|
|
|
4194
4418
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
4195
4419
|
switch (name) {
|
|
4196
4420
|
case 'color':
|
|
4197
|
-
this.color = parseColor(newValue);
|
|
4421
|
+
this.color = parseColor(newValue, playcanvas.Color.WHITE, name);
|
|
4198
4422
|
break;
|
|
4199
4423
|
case 'cast-shadows':
|
|
4200
|
-
this.castShadows =
|
|
4424
|
+
this.castShadows = parseBool(newValue, false);
|
|
4201
4425
|
break;
|
|
4202
4426
|
case 'inner-cone-angle':
|
|
4203
|
-
this.innerConeAngle =
|
|
4427
|
+
this.innerConeAngle = parseNumber(newValue, 40, name);
|
|
4204
4428
|
break;
|
|
4205
4429
|
case 'intensity':
|
|
4206
|
-
this.intensity =
|
|
4430
|
+
this.intensity = parseNumber(newValue, 1, name);
|
|
4207
4431
|
break;
|
|
4208
4432
|
case 'normal-offset-bias':
|
|
4209
|
-
this.normalOffsetBias =
|
|
4433
|
+
this.normalOffsetBias = parseNumber(newValue, 0.05, name);
|
|
4210
4434
|
break;
|
|
4211
4435
|
case 'outer-cone-angle':
|
|
4212
|
-
this.outerConeAngle =
|
|
4436
|
+
this.outerConeAngle = parseNumber(newValue, 45, name);
|
|
4213
4437
|
break;
|
|
4214
4438
|
case 'penumbra-falloff':
|
|
4215
|
-
this.penumbraFalloff =
|
|
4439
|
+
this.penumbraFalloff = parseNumber(newValue, 1, name);
|
|
4216
4440
|
break;
|
|
4217
4441
|
case 'penumbra-size':
|
|
4218
|
-
this.penumbraSize =
|
|
4442
|
+
this.penumbraSize = parseNumber(newValue, 1, name);
|
|
4219
4443
|
break;
|
|
4220
4444
|
case 'range':
|
|
4221
|
-
this.range =
|
|
4445
|
+
this.range = parseNumber(newValue, 10, name);
|
|
4222
4446
|
break;
|
|
4223
4447
|
case 'shadow-bias':
|
|
4224
|
-
this.shadowBias =
|
|
4448
|
+
this.shadowBias = parseNumber(newValue, 0.2, name);
|
|
4225
4449
|
break;
|
|
4226
4450
|
case 'shadow-distance':
|
|
4227
|
-
this.shadowDistance =
|
|
4451
|
+
this.shadowDistance = parseNumber(newValue, 16, name);
|
|
4228
4452
|
break;
|
|
4229
4453
|
case 'shadow-blocker-samples':
|
|
4230
|
-
this.shadowBlockerSamples =
|
|
4454
|
+
this.shadowBlockerSamples = parseNumber(newValue, 16, name);
|
|
4231
4455
|
break;
|
|
4232
4456
|
case 'shadow-resolution':
|
|
4233
|
-
this.shadowResolution =
|
|
4457
|
+
this.shadowResolution = parseNumber(newValue, 1024, name);
|
|
4234
4458
|
break;
|
|
4235
4459
|
case 'shadow-intensity':
|
|
4236
|
-
this.shadowIntensity =
|
|
4460
|
+
this.shadowIntensity = parseNumber(newValue, 1, name);
|
|
4237
4461
|
break;
|
|
4238
4462
|
case 'shadow-samples':
|
|
4239
|
-
this.shadowSamples =
|
|
4463
|
+
this.shadowSamples = parseNumber(newValue, 16, name);
|
|
4240
4464
|
break;
|
|
4241
4465
|
case 'shadow-type':
|
|
4242
|
-
this.shadowType = newValue;
|
|
4466
|
+
this.shadowType = parseEnum(newValue, shadowTypes, 'pcf3-32f', name);
|
|
4243
4467
|
break;
|
|
4244
4468
|
case 'type':
|
|
4245
|
-
this.type = newValue;
|
|
4469
|
+
this.type = parseEnum(newValue, ['directional', 'omni', 'spot'], 'directional', name);
|
|
4246
4470
|
break;
|
|
4247
4471
|
case 'vsm-bias':
|
|
4248
|
-
this.vsmBias =
|
|
4472
|
+
this.vsmBias = parseNumber(newValue, 0.01, name);
|
|
4249
4473
|
break;
|
|
4250
4474
|
case 'vsm-blur-size':
|
|
4251
|
-
this.vsmBlurSize =
|
|
4475
|
+
this.vsmBlurSize = parseNumber(newValue, 11, name);
|
|
4252
4476
|
break;
|
|
4253
4477
|
}
|
|
4254
4478
|
}
|
|
@@ -4392,6 +4616,10 @@ customElements.define('pc-particles', ParticleSystemComponentElement);
|
|
|
4392
4616
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-material/ | `<pc-material>`} elements.
|
|
4393
4617
|
* The MaterialElement interface also inherits the properties and methods of the
|
|
4394
4618
|
* {@link HTMLElement} interface.
|
|
4619
|
+
*
|
|
4620
|
+
* A `pc-material` must be a direct child of `pc-app` — elements placed elsewhere log a warning
|
|
4621
|
+
* and never create a material. Elements inserted while the application is already running are
|
|
4622
|
+
* created on insertion.
|
|
4395
4623
|
*/
|
|
4396
4624
|
class MaterialElement extends HTMLElement {
|
|
4397
4625
|
constructor() {
|
|
@@ -4403,6 +4631,26 @@ class MaterialElement extends HTMLElement {
|
|
|
4403
4631
|
this._roughnessMap = '';
|
|
4404
4632
|
this.material = null;
|
|
4405
4633
|
}
|
|
4634
|
+
async connectedCallback() {
|
|
4635
|
+
var _a, _b;
|
|
4636
|
+
const appElement = (_b = (_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.closest('pc-app')) !== null && _b !== void 0 ? _b : null;
|
|
4637
|
+
// Materials must be direct children of pc-app (matches the boot query ':scope > pc-material')
|
|
4638
|
+
if (!appElement || this.parentElement !== appElement) {
|
|
4639
|
+
console.warn(`pc-material '${this.id}' must be a direct child of pc-app - material not created`);
|
|
4640
|
+
return;
|
|
4641
|
+
}
|
|
4642
|
+
await appElement.ready();
|
|
4643
|
+
// The element may have been removed or re-parented while waiting for the app
|
|
4644
|
+
if (!this.isConnected || this.parentElement !== appElement)
|
|
4645
|
+
return;
|
|
4646
|
+
// Materials present at startup are created by AppElement's boot; this branch handles
|
|
4647
|
+
// elements inserted (or re-inserted) after the app is already running
|
|
4648
|
+
if (!this.material) {
|
|
4649
|
+
if (!appElement.app)
|
|
4650
|
+
return; // pc-app is re-connecting; its own boot will create this
|
|
4651
|
+
this.createMaterial();
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4406
4654
|
createMaterial() {
|
|
4407
4655
|
this.material = new playcanvas.StandardMaterial();
|
|
4408
4656
|
this.material.glossInvert = false;
|
|
@@ -4485,7 +4733,7 @@ class MaterialElement extends HTMLElement {
|
|
|
4485
4733
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
4486
4734
|
switch (name) {
|
|
4487
4735
|
case 'diffuse':
|
|
4488
|
-
this.diffuse = parseColor(newValue);
|
|
4736
|
+
this.diffuse = parseColor(newValue, playcanvas.Color.WHITE, name);
|
|
4489
4737
|
break;
|
|
4490
4738
|
case 'diffuse-map':
|
|
4491
4739
|
this.diffuseMap = newValue;
|
|
@@ -4510,6 +4758,10 @@ customElements.define('pc-material', MaterialElement);
|
|
|
4510
4758
|
* The RenderComponentElement interface also inherits the properties and methods of the
|
|
4511
4759
|
* {@link HTMLElement} interface.
|
|
4512
4760
|
*
|
|
4761
|
+
* This element renders one of the engine's built-in primitives, selected with `type` (defaulting
|
|
4762
|
+
* to `box`). It does not cover the engine's `asset` render type, since there is no way to supply
|
|
4763
|
+
* a render asset here — use `pc-model` for glTF content instead.
|
|
4764
|
+
*
|
|
4513
4765
|
* @category Components
|
|
4514
4766
|
*/
|
|
4515
4767
|
class RenderComponentElement extends ComponentElement {
|
|
@@ -4519,7 +4771,7 @@ class RenderComponentElement extends ComponentElement {
|
|
|
4519
4771
|
this._castShadows = true;
|
|
4520
4772
|
this._material = '';
|
|
4521
4773
|
this._receiveShadows = true;
|
|
4522
|
-
this._type = '
|
|
4774
|
+
this._type = 'box';
|
|
4523
4775
|
}
|
|
4524
4776
|
getInitialComponentData() {
|
|
4525
4777
|
return {
|
|
@@ -4611,16 +4863,16 @@ class RenderComponentElement extends ComponentElement {
|
|
|
4611
4863
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
4612
4864
|
switch (name) {
|
|
4613
4865
|
case 'cast-shadows':
|
|
4614
|
-
this.castShadows = newValue
|
|
4866
|
+
this.castShadows = parseBool(newValue, true);
|
|
4615
4867
|
break;
|
|
4616
4868
|
case 'material':
|
|
4617
4869
|
this.material = newValue;
|
|
4618
4870
|
break;
|
|
4619
4871
|
case 'receive-shadows':
|
|
4620
|
-
this.receiveShadows = newValue
|
|
4872
|
+
this.receiveShadows = parseBool(newValue, true);
|
|
4621
4873
|
break;
|
|
4622
4874
|
case 'type':
|
|
4623
|
-
this.type = newValue;
|
|
4875
|
+
this.type = parseEnum(newValue, ['box', 'capsule', 'cone', 'cylinder', 'plane', 'sphere'], 'box', name);
|
|
4624
4876
|
break;
|
|
4625
4877
|
}
|
|
4626
4878
|
}
|
|
@@ -4784,31 +5036,31 @@ class RigidBodyComponentElement extends ComponentElement {
|
|
|
4784
5036
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
4785
5037
|
switch (name) {
|
|
4786
5038
|
case 'angular-damping':
|
|
4787
|
-
this.angularDamping =
|
|
5039
|
+
this.angularDamping = parseNumber(newValue, 0, name);
|
|
4788
5040
|
break;
|
|
4789
5041
|
case 'angular-factor':
|
|
4790
|
-
this.angularFactor = parseVec3(newValue);
|
|
5042
|
+
this.angularFactor = parseVec3(newValue, playcanvas.Vec3.ONE, name);
|
|
4791
5043
|
break;
|
|
4792
5044
|
case 'friction':
|
|
4793
|
-
this.friction =
|
|
5045
|
+
this.friction = parseNumber(newValue, 0.5, name);
|
|
4794
5046
|
break;
|
|
4795
5047
|
case 'linear-damping':
|
|
4796
|
-
this.linearDamping =
|
|
5048
|
+
this.linearDamping = parseNumber(newValue, 0, name);
|
|
4797
5049
|
break;
|
|
4798
5050
|
case 'linear-factor':
|
|
4799
|
-
this.linearFactor = parseVec3(newValue);
|
|
5051
|
+
this.linearFactor = parseVec3(newValue, playcanvas.Vec3.ONE, name);
|
|
4800
5052
|
break;
|
|
4801
5053
|
case 'mass':
|
|
4802
|
-
this.mass =
|
|
5054
|
+
this.mass = parseNumber(newValue, 1, name);
|
|
4803
5055
|
break;
|
|
4804
5056
|
case 'restitution':
|
|
4805
|
-
this.restitution =
|
|
5057
|
+
this.restitution = parseNumber(newValue, 0, name);
|
|
4806
5058
|
break;
|
|
4807
5059
|
case 'rolling-friction':
|
|
4808
|
-
this.rollingFriction =
|
|
5060
|
+
this.rollingFriction = parseNumber(newValue, 0, name);
|
|
4809
5061
|
break;
|
|
4810
5062
|
case 'type':
|
|
4811
|
-
this.type = newValue;
|
|
5063
|
+
this.type = parseEnum(newValue, ['static', 'dynamic', 'kinematic'], 'static', name);
|
|
4812
5064
|
break;
|
|
4813
5065
|
}
|
|
4814
5066
|
}
|
|
@@ -4920,22 +5172,22 @@ class ScreenComponentElement extends ComponentElement {
|
|
|
4920
5172
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
4921
5173
|
switch (name) {
|
|
4922
5174
|
case 'priority':
|
|
4923
|
-
this.priority =
|
|
5175
|
+
this.priority = parseNumber(newValue, 0, name);
|
|
4924
5176
|
break;
|
|
4925
5177
|
case 'reference-resolution':
|
|
4926
|
-
this.referenceResolution = parseVec2(newValue);
|
|
5178
|
+
this.referenceResolution = parseVec2(newValue, new playcanvas.Vec2(640, 320), name);
|
|
4927
5179
|
break;
|
|
4928
5180
|
case 'resolution':
|
|
4929
|
-
this.resolution = parseVec2(newValue);
|
|
5181
|
+
this.resolution = parseVec2(newValue, new playcanvas.Vec2(640, 320), name);
|
|
4930
5182
|
break;
|
|
4931
5183
|
case 'scale-blend':
|
|
4932
|
-
this.scaleBlend =
|
|
5184
|
+
this.scaleBlend = parseNumber(newValue, 0.5, name);
|
|
4933
5185
|
break;
|
|
4934
5186
|
case 'blend':
|
|
4935
|
-
this.blend =
|
|
5187
|
+
this.blend = parseBool(newValue, false);
|
|
4936
5188
|
break;
|
|
4937
5189
|
case 'screen-space':
|
|
4938
|
-
this.screenSpace =
|
|
5190
|
+
this.screenSpace = parseBool(newValue, false);
|
|
4939
5191
|
break;
|
|
4940
5192
|
}
|
|
4941
5193
|
}
|
|
@@ -4958,14 +5210,14 @@ class ScrollbarComponentElement extends ComponentElement {
|
|
|
4958
5210
|
/** @ignore */
|
|
4959
5211
|
constructor() {
|
|
4960
5212
|
super('scrollbar');
|
|
4961
|
-
this._orientation =
|
|
5213
|
+
this._orientation = 'horizontal';
|
|
4962
5214
|
this._value = 0;
|
|
4963
5215
|
this._handleSize = 0.5;
|
|
4964
5216
|
this._handle = '';
|
|
4965
5217
|
}
|
|
4966
5218
|
getInitialComponentData() {
|
|
4967
5219
|
const data = {
|
|
4968
|
-
orientation: this._orientation,
|
|
5220
|
+
orientation: orientations.get(this._orientation),
|
|
4969
5221
|
value: this._value,
|
|
4970
5222
|
handleSize: this._handleSize
|
|
4971
5223
|
};
|
|
@@ -4983,13 +5235,15 @@ class ScrollbarComponentElement extends ComponentElement {
|
|
|
4983
5235
|
return super.component;
|
|
4984
5236
|
}
|
|
4985
5237
|
/**
|
|
4986
|
-
* Sets the orientation of the scrollbar. Can be `horizontal`
|
|
5238
|
+
* Sets the orientation of the scrollbar. Can be `horizontal` or `vertical`. Defaults to
|
|
5239
|
+
* `horizontal`.
|
|
4987
5240
|
* @param value - The orientation.
|
|
4988
5241
|
*/
|
|
4989
5242
|
set orientation(value) {
|
|
5243
|
+
var _a;
|
|
4990
5244
|
this._orientation = value;
|
|
4991
5245
|
if (this.component) {
|
|
4992
|
-
this.component.orientation = value;
|
|
5246
|
+
this.component.orientation = (_a = orientations.get(value)) !== null && _a !== void 0 ? _a : playcanvas.ORIENTATION_HORIZONTAL;
|
|
4993
5247
|
}
|
|
4994
5248
|
}
|
|
4995
5249
|
/**
|
|
@@ -5065,13 +5319,13 @@ class ScrollbarComponentElement extends ComponentElement {
|
|
|
5065
5319
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
5066
5320
|
switch (name) {
|
|
5067
5321
|
case 'orientation':
|
|
5068
|
-
this.orientation = parseEnum(newValue, orientations,
|
|
5322
|
+
this.orientation = parseEnum(newValue, orientations, 'horizontal', name);
|
|
5069
5323
|
break;
|
|
5070
5324
|
case 'value':
|
|
5071
|
-
this.value =
|
|
5325
|
+
this.value = parseNumber(newValue, 0, name);
|
|
5072
5326
|
break;
|
|
5073
5327
|
case 'handle-size':
|
|
5074
|
-
this.handleSize =
|
|
5328
|
+
this.handleSize = parseNumber(newValue, 0.5, name);
|
|
5075
5329
|
break;
|
|
5076
5330
|
case 'handle':
|
|
5077
5331
|
this.handle = newValue;
|
|
@@ -5104,13 +5358,13 @@ class ScrollViewComponentElement extends ComponentElement {
|
|
|
5104
5358
|
super('scrollview');
|
|
5105
5359
|
this._horizontal = true;
|
|
5106
5360
|
this._vertical = true;
|
|
5107
|
-
this._scrollMode =
|
|
5361
|
+
this._scrollMode = 'bounce';
|
|
5108
5362
|
this._bounceAmount = 0.1;
|
|
5109
5363
|
this._friction = 0.05;
|
|
5110
5364
|
this._useMouseWheel = true;
|
|
5111
5365
|
this._mouseWheelSensitivity = new playcanvas.Vec2(1, 1);
|
|
5112
|
-
this._horizontalScrollbarVisibility =
|
|
5113
|
-
this._verticalScrollbarVisibility =
|
|
5366
|
+
this._horizontalScrollbarVisibility = 'when-required';
|
|
5367
|
+
this._verticalScrollbarVisibility = 'when-required';
|
|
5114
5368
|
this._viewport = '';
|
|
5115
5369
|
this._content = '';
|
|
5116
5370
|
this._horizontalScrollbar = '';
|
|
@@ -5120,13 +5374,13 @@ class ScrollViewComponentElement extends ComponentElement {
|
|
|
5120
5374
|
const data = {
|
|
5121
5375
|
horizontal: this._horizontal,
|
|
5122
5376
|
vertical: this._vertical,
|
|
5123
|
-
scrollMode: this._scrollMode,
|
|
5377
|
+
scrollMode: scrollModes.get(this._scrollMode),
|
|
5124
5378
|
bounceAmount: this._bounceAmount,
|
|
5125
5379
|
friction: this._friction,
|
|
5126
5380
|
useMouseWheel: this._useMouseWheel,
|
|
5127
5381
|
mouseWheelSensitivity: this._mouseWheelSensitivity,
|
|
5128
|
-
horizontalScrollbarVisibility: this._horizontalScrollbarVisibility,
|
|
5129
|
-
verticalScrollbarVisibility: this._verticalScrollbarVisibility
|
|
5382
|
+
horizontalScrollbarVisibility: visibilities.get(this._horizontalScrollbarVisibility),
|
|
5383
|
+
verticalScrollbarVisibility: visibilities.get(this._verticalScrollbarVisibility)
|
|
5130
5384
|
};
|
|
5131
5385
|
const viewport = getEntity(this._viewport);
|
|
5132
5386
|
if (viewport) {
|
|
@@ -5189,13 +5443,14 @@ class ScrollViewComponentElement extends ComponentElement {
|
|
|
5189
5443
|
}
|
|
5190
5444
|
/**
|
|
5191
5445
|
* Sets how the scroll view should behave when the content is scrolled beyond its bounds. Can be
|
|
5192
|
-
* `clamp
|
|
5446
|
+
* `clamp`, `bounce` or `infinite`. Defaults to `bounce`.
|
|
5193
5447
|
* @param value - The scroll mode.
|
|
5194
5448
|
*/
|
|
5195
5449
|
set scrollMode(value) {
|
|
5450
|
+
var _a;
|
|
5196
5451
|
this._scrollMode = value;
|
|
5197
5452
|
if (this.component) {
|
|
5198
|
-
this.component.scrollMode = value;
|
|
5453
|
+
this.component.scrollMode = (_a = scrollModes.get(value)) !== null && _a !== void 0 ? _a : playcanvas.SCROLL_MODE_BOUNCE;
|
|
5199
5454
|
}
|
|
5200
5455
|
}
|
|
5201
5456
|
/**
|
|
@@ -5276,13 +5531,15 @@ class ScrollViewComponentElement extends ComponentElement {
|
|
|
5276
5531
|
return this._mouseWheelSensitivity;
|
|
5277
5532
|
}
|
|
5278
5533
|
/**
|
|
5279
|
-
* Sets the visibility of the horizontal scrollbar. Can be `always`
|
|
5534
|
+
* Sets the visibility of the horizontal scrollbar. Can be `always` or `when-required`.
|
|
5535
|
+
* Defaults to `when-required`.
|
|
5280
5536
|
* @param value - The horizontal scrollbar visibility.
|
|
5281
5537
|
*/
|
|
5282
5538
|
set horizontalScrollbarVisibility(value) {
|
|
5539
|
+
var _a;
|
|
5283
5540
|
this._horizontalScrollbarVisibility = value;
|
|
5284
5541
|
if (this.component) {
|
|
5285
|
-
this.component.horizontalScrollbarVisibility = value;
|
|
5542
|
+
this.component.horizontalScrollbarVisibility = (_a = visibilities.get(value)) !== null && _a !== void 0 ? _a : playcanvas.SCROLLBAR_VISIBILITY_SHOW_WHEN_REQUIRED;
|
|
5286
5543
|
}
|
|
5287
5544
|
}
|
|
5288
5545
|
/**
|
|
@@ -5293,13 +5550,15 @@ class ScrollViewComponentElement extends ComponentElement {
|
|
|
5293
5550
|
return this._horizontalScrollbarVisibility;
|
|
5294
5551
|
}
|
|
5295
5552
|
/**
|
|
5296
|
-
* Sets the visibility of the vertical scrollbar. Can be `always`
|
|
5553
|
+
* Sets the visibility of the vertical scrollbar. Can be `always` or `when-required`.
|
|
5554
|
+
* Defaults to `when-required`.
|
|
5297
5555
|
* @param value - The vertical scrollbar visibility.
|
|
5298
5556
|
*/
|
|
5299
5557
|
set verticalScrollbarVisibility(value) {
|
|
5558
|
+
var _a;
|
|
5300
5559
|
this._verticalScrollbarVisibility = value;
|
|
5301
5560
|
if (this.component) {
|
|
5302
|
-
this.component.verticalScrollbarVisibility = value;
|
|
5561
|
+
this.component.verticalScrollbarVisibility = (_a = visibilities.get(value)) !== null && _a !== void 0 ? _a : playcanvas.SCROLLBAR_VISIBILITY_SHOW_WHEN_REQUIRED;
|
|
5303
5562
|
}
|
|
5304
5563
|
}
|
|
5305
5564
|
/**
|
|
@@ -5407,31 +5666,31 @@ class ScrollViewComponentElement extends ComponentElement {
|
|
|
5407
5666
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
5408
5667
|
switch (name) {
|
|
5409
5668
|
case 'horizontal':
|
|
5410
|
-
this.horizontal = newValue
|
|
5669
|
+
this.horizontal = parseBool(newValue, true);
|
|
5411
5670
|
break;
|
|
5412
5671
|
case 'vertical':
|
|
5413
|
-
this.vertical = newValue
|
|
5672
|
+
this.vertical = parseBool(newValue, true);
|
|
5414
5673
|
break;
|
|
5415
5674
|
case 'scroll-mode':
|
|
5416
|
-
this.scrollMode = parseEnum(newValue, scrollModes,
|
|
5675
|
+
this.scrollMode = parseEnum(newValue, scrollModes, 'bounce', name);
|
|
5417
5676
|
break;
|
|
5418
5677
|
case 'bounce-amount':
|
|
5419
|
-
this.bounceAmount =
|
|
5678
|
+
this.bounceAmount = parseNumber(newValue, 0.1, name);
|
|
5420
5679
|
break;
|
|
5421
5680
|
case 'friction':
|
|
5422
|
-
this.friction =
|
|
5681
|
+
this.friction = parseNumber(newValue, 0.05, name);
|
|
5423
5682
|
break;
|
|
5424
5683
|
case 'use-mouse-wheel':
|
|
5425
|
-
this.useMouseWheel = newValue
|
|
5684
|
+
this.useMouseWheel = parseBool(newValue, true);
|
|
5426
5685
|
break;
|
|
5427
5686
|
case 'mouse-wheel-sensitivity':
|
|
5428
|
-
this.mouseWheelSensitivity = parseVec2(newValue);
|
|
5687
|
+
this.mouseWheelSensitivity = parseVec2(newValue, playcanvas.Vec2.ONE, name);
|
|
5429
5688
|
break;
|
|
5430
5689
|
case 'horizontal-scrollbar-visibility':
|
|
5431
|
-
this.horizontalScrollbarVisibility = parseEnum(newValue, visibilities,
|
|
5690
|
+
this.horizontalScrollbarVisibility = parseEnum(newValue, visibilities, 'when-required', name);
|
|
5432
5691
|
break;
|
|
5433
5692
|
case 'vertical-scrollbar-visibility':
|
|
5434
|
-
this.verticalScrollbarVisibility = parseEnum(newValue, visibilities,
|
|
5693
|
+
this.verticalScrollbarVisibility = parseEnum(newValue, visibilities, 'when-required', name);
|
|
5435
5694
|
break;
|
|
5436
5695
|
case 'viewport':
|
|
5437
5696
|
this.viewport = newValue;
|
|
@@ -5439,17 +5698,347 @@ class ScrollViewComponentElement extends ComponentElement {
|
|
|
5439
5698
|
case 'content':
|
|
5440
5699
|
this.content = newValue;
|
|
5441
5700
|
break;
|
|
5442
|
-
case 'horizontal-scrollbar':
|
|
5443
|
-
this.horizontalScrollbar = newValue;
|
|
5701
|
+
case 'horizontal-scrollbar':
|
|
5702
|
+
this.horizontalScrollbar = newValue;
|
|
5703
|
+
break;
|
|
5704
|
+
case 'vertical-scrollbar':
|
|
5705
|
+
this.verticalScrollbar = newValue;
|
|
5706
|
+
break;
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
customElements.define('pc-scrollview', ScrollViewComponentElement);
|
|
5711
|
+
|
|
5712
|
+
/**
|
|
5713
|
+
* The ScriptElement interface provides properties and methods for manipulating
|
|
5714
|
+
* `<pc-script>` elements. The ScriptElement interface also inherits the properties and
|
|
5715
|
+
* methods of the {@link AsyncElement} interface.
|
|
5716
|
+
*
|
|
5717
|
+
* Script attributes can be supplied through two channels:
|
|
5718
|
+
*
|
|
5719
|
+
* - **Per-property attributes**: any non-reserved attribute on the element maps to the script
|
|
5720
|
+
* attribute of the same name (kebab-case to camelCase, e.g. `focus-point` → `focusPoint`).
|
|
5721
|
+
* Values are parsed according to the type of the attribute's current value — initially the
|
|
5722
|
+
* script's declared default (numbers, booleans, strings, Vec2/3/4, Color, Quat as Euler
|
|
5723
|
+
* angles) — and the `asset:`/`entity:`/`vec2:`/`vec3:`/`vec4:`/`color:` prefixes may be used
|
|
5724
|
+
* to be explicit.
|
|
5725
|
+
* - **The `attributes` JSON attribute**: an object supporting nested structures and attribute
|
|
5726
|
+
* names that collide with reserved HTML attribute names (e.g. `title`).
|
|
5727
|
+
*
|
|
5728
|
+
* When both specify the same attribute, the per-property attribute wins — at creation and
|
|
5729
|
+
* whenever either channel changes at runtime. The element's own `name` and `enabled`
|
|
5730
|
+
* attributes configure the element itself and are not script attributes.
|
|
5731
|
+
*
|
|
5732
|
+
* Changing `name` on a live element destroys the old-name script instance and creates the
|
|
5733
|
+
* new-name one, re-applying both attribute channels to it.
|
|
5734
|
+
*
|
|
5735
|
+
* The element becomes ready once its script instance has been created by the parent
|
|
5736
|
+
* `<pc-scripts>` element.
|
|
5737
|
+
*/
|
|
5738
|
+
class ScriptElement extends AsyncElement {
|
|
5739
|
+
constructor() {
|
|
5740
|
+
super(...arguments);
|
|
5741
|
+
this._attributes = {};
|
|
5742
|
+
this._enabled = true;
|
|
5743
|
+
/**
|
|
5744
|
+
* Whether readiness has been signalled. Creation can happen more than once over an
|
|
5745
|
+
* element's life (a runtime `name` change recreates the instance), but `ready` is a
|
|
5746
|
+
* one-shot signal, so only the first successful creation fires it.
|
|
5747
|
+
*/
|
|
5748
|
+
this._readySignalled = false;
|
|
5749
|
+
/**
|
|
5750
|
+
* The Script instance created for this element by its parent `<pc-scripts>` element.
|
|
5751
|
+
* @ignore
|
|
5752
|
+
*/
|
|
5753
|
+
this._script = null;
|
|
5754
|
+
}
|
|
5755
|
+
/**
|
|
5756
|
+
* Sets the attributes of the script as an object. Values are converted with the same rules
|
|
5757
|
+
* as the `attributes` attribute: `asset:`/`entity:` references and `vec2:`/`vec3:`/`vec4:`/
|
|
5758
|
+
* `color:` prefixed strings are resolved, and a plain numeric array is converted to the
|
|
5759
|
+
* type of the attribute it targets when that attribute currently holds a Vec2, Vec3, Vec4
|
|
5760
|
+
* or Color.
|
|
5761
|
+
* @param value - The attributes of the script.
|
|
5762
|
+
*/
|
|
5763
|
+
set scriptAttributes(value) {
|
|
5764
|
+
this._attributes = value !== null && value !== void 0 ? value : {};
|
|
5765
|
+
this.dispatchEvent(new CustomEvent('scriptattributeschange', {
|
|
5766
|
+
detail: { attributes: this._attributes },
|
|
5767
|
+
bubbles: true
|
|
5768
|
+
}));
|
|
5769
|
+
}
|
|
5770
|
+
/**
|
|
5771
|
+
* Gets the attributes of the script.
|
|
5772
|
+
* @returns The attributes of the script.
|
|
5773
|
+
*/
|
|
5774
|
+
get scriptAttributes() {
|
|
5775
|
+
return this._attributes;
|
|
5776
|
+
}
|
|
5777
|
+
/**
|
|
5778
|
+
* Sets the enabled state of the script.
|
|
5779
|
+
* @param value - The enabled state of the script.
|
|
5780
|
+
*/
|
|
5781
|
+
set enabled(value) {
|
|
5782
|
+
this._enabled = value;
|
|
5783
|
+
this.dispatchEvent(new CustomEvent('scriptenablechange', {
|
|
5784
|
+
detail: { enabled: value },
|
|
5785
|
+
bubbles: true
|
|
5786
|
+
}));
|
|
5787
|
+
}
|
|
5788
|
+
/**
|
|
5789
|
+
* Gets the enabled state of the script.
|
|
5790
|
+
* @returns The enabled state of the script.
|
|
5791
|
+
*/
|
|
5792
|
+
get enabled() {
|
|
5793
|
+
return this._enabled;
|
|
5794
|
+
}
|
|
5795
|
+
/**
|
|
5796
|
+
* Sets the name of the script to create. The `name` attribute is the single source of truth
|
|
5797
|
+
* (it is what the parent `<pc-scripts>` element reads when creating the instance), so the
|
|
5798
|
+
* property writes through to it — assigning before insertion works as expected:
|
|
5799
|
+
*
|
|
5800
|
+
* ```js
|
|
5801
|
+
* const script = document.createElement('pc-script');
|
|
5802
|
+
* script.name = 'rotate';
|
|
5803
|
+
* scriptsElement.appendChild(script);
|
|
5804
|
+
* await script.ready();
|
|
5805
|
+
* ```
|
|
5806
|
+
* @param value - The name.
|
|
5807
|
+
*/
|
|
5808
|
+
set name(value) {
|
|
5809
|
+
this.setAttribute('name', value);
|
|
5810
|
+
}
|
|
5811
|
+
/**
|
|
5812
|
+
* Gets the name of the script.
|
|
5813
|
+
* @returns The name.
|
|
5814
|
+
*/
|
|
5815
|
+
get name() {
|
|
5816
|
+
var _a;
|
|
5817
|
+
return (_a = this.getAttribute('name')) !== null && _a !== void 0 ? _a : '';
|
|
5818
|
+
}
|
|
5819
|
+
/**
|
|
5820
|
+
* Gets the {@link Script} instance created for this element. Returns `null` until the
|
|
5821
|
+
* instance exists — await {@link whenReady} or the element's `ready()` promise before
|
|
5822
|
+
* accessing it.
|
|
5823
|
+
* @returns The script instance, or `null`.
|
|
5824
|
+
*/
|
|
5825
|
+
get script() {
|
|
5826
|
+
return this._script;
|
|
5827
|
+
}
|
|
5828
|
+
connectedCallback() {
|
|
5829
|
+
var _a;
|
|
5830
|
+
// Script instances are created by the parent pc-scripts element, so an element placed
|
|
5831
|
+
// anywhere else is inert and never becomes ready - warn rather than hang silently
|
|
5832
|
+
if (((_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.tagName) !== 'PC-SCRIPTS') {
|
|
5833
|
+
console.warn(`pc-script '${this.getAttribute('name')}' must be a direct child of pc-scripts - script not created`);
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5836
|
+
/**
|
|
5837
|
+
* Called by the parent `<pc-scripts>` element when the script instance has been created.
|
|
5838
|
+
* @ignore
|
|
5839
|
+
*/
|
|
5840
|
+
_onScriptCreated() {
|
|
5841
|
+
if (this._readySignalled)
|
|
5842
|
+
return;
|
|
5843
|
+
this._readySignalled = true;
|
|
5844
|
+
this._onReady();
|
|
5845
|
+
}
|
|
5846
|
+
static get observedAttributes() {
|
|
5847
|
+
return ['attributes', 'enabled', 'name'];
|
|
5848
|
+
}
|
|
5849
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
5850
|
+
switch (name) {
|
|
5851
|
+
case 'attributes':
|
|
5852
|
+
if (newValue === null) {
|
|
5853
|
+
this.scriptAttributes = {};
|
|
5854
|
+
break;
|
|
5855
|
+
}
|
|
5856
|
+
try {
|
|
5857
|
+
this.scriptAttributes = JSON.parse(newValue);
|
|
5858
|
+
}
|
|
5859
|
+
catch (error) {
|
|
5860
|
+
console.warn(`Invalid 'attributes' JSON on pc-script '${this.getAttribute('name')}': ${error.message}`);
|
|
5861
|
+
}
|
|
5862
|
+
break;
|
|
5863
|
+
case 'enabled':
|
|
5864
|
+
this.enabled = parseBool(newValue, true);
|
|
5444
5865
|
break;
|
|
5445
|
-
case '
|
|
5446
|
-
|
|
5866
|
+
case 'name':
|
|
5867
|
+
// The first set is handled by the parent's creation paths (the boot query and
|
|
5868
|
+
// the added-node mutation), so only a genuine rename is signalled here. Note
|
|
5869
|
+
// that setAttribute fires this callback even when the value is unchanged.
|
|
5870
|
+
if (oldValue !== null && oldValue !== newValue) {
|
|
5871
|
+
this.dispatchEvent(new CustomEvent('scriptnamechange', {
|
|
5872
|
+
detail: { oldName: oldValue, newName: newValue },
|
|
5873
|
+
bubbles: true
|
|
5874
|
+
}));
|
|
5875
|
+
}
|
|
5447
5876
|
break;
|
|
5448
5877
|
}
|
|
5449
5878
|
}
|
|
5450
5879
|
}
|
|
5451
|
-
customElements.define('pc-
|
|
5880
|
+
customElements.define('pc-script', ScriptElement);
|
|
5452
5881
|
|
|
5882
|
+
/**
|
|
5883
|
+
* Attributes on `pc-script` that never map to script attributes: the element's own API (derived
|
|
5884
|
+
* from its observed attributes) plus reserved and global HTML attribute names.
|
|
5885
|
+
*/
|
|
5886
|
+
const RESERVED_ATTRIBUTES = new Set([
|
|
5887
|
+
...ScriptElement.observedAttributes,
|
|
5888
|
+
'accesskey', 'autocapitalize', 'autofocus', 'class', 'contenteditable', 'dir', 'draggable',
|
|
5889
|
+
'exportparts', 'hidden', 'id', 'inert', 'is', 'itemid', 'itemprop', 'itemref', 'itemscope',
|
|
5890
|
+
'itemtype', 'lang', 'nonce', 'part', 'popover', 'role', 'slot', 'spellcheck', 'style',
|
|
5891
|
+
'tabindex', 'title', 'translate'
|
|
5892
|
+
]);
|
|
5893
|
+
/**
|
|
5894
|
+
* Checks whether a `pc-script` attribute name is reserved (and so never maps to a script
|
|
5895
|
+
* attribute). Reserved names are the element's own API, global HTML attribute names, `data-*`
|
|
5896
|
+
* and `aria-*` attributes, names starting with `_` (framework-stamped attributes), and real
|
|
5897
|
+
* inline event handler names (`onclick` etc. — detected via the platform, so script attributes
|
|
5898
|
+
* that merely start with 'on', like `once`, still map).
|
|
5899
|
+
* @param name - The attribute name.
|
|
5900
|
+
* @returns Whether the attribute name is reserved.
|
|
5901
|
+
*/
|
|
5902
|
+
const isReservedAttribute = (name) => {
|
|
5903
|
+
return RESERVED_ATTRIBUTES.has(name) ||
|
|
5904
|
+
name.startsWith('data-') ||
|
|
5905
|
+
name.startsWith('aria-') ||
|
|
5906
|
+
name.startsWith('_') ||
|
|
5907
|
+
(name.startsWith('on') && name in HTMLElement.prototype);
|
|
5908
|
+
};
|
|
5909
|
+
/**
|
|
5910
|
+
* Script API members that per-property attributes must never overwrite: the engine bindings and
|
|
5911
|
+
* the (optional, so possibly undefined) lifecycle methods.
|
|
5912
|
+
*/
|
|
5913
|
+
const SCRIPT_API_MEMBERS = new Set([
|
|
5914
|
+
'app', 'entity', 'destroy', 'initialize', 'postInitialize', 'postUpdate', 'swap', 'update'
|
|
5915
|
+
]);
|
|
5916
|
+
/**
|
|
5917
|
+
* Converts a kebab-case attribute name to the camelCase script attribute name.
|
|
5918
|
+
* @param name - The attribute name.
|
|
5919
|
+
* @returns The camelCase name.
|
|
5920
|
+
*/
|
|
5921
|
+
const kebabToCamel = (name) => {
|
|
5922
|
+
return name.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
5923
|
+
};
|
|
5924
|
+
/**
|
|
5925
|
+
* Converts a camelCase script attribute name to its kebab-case attribute spelling.
|
|
5926
|
+
* @param name - The camelCase name.
|
|
5927
|
+
* @returns The kebab-case name.
|
|
5928
|
+
*/
|
|
5929
|
+
const camelToKebab = (name) => {
|
|
5930
|
+
return name.replace(/[A-Z]/g, char => `-${char.toLowerCase()}`);
|
|
5931
|
+
};
|
|
5932
|
+
/**
|
|
5933
|
+
* Resolves an `asset:` prefix to the Asset created by the `pc-asset` element with that id.
|
|
5934
|
+
* @param rest - The asset id.
|
|
5935
|
+
* @param raw - The raw value, returned unchanged when the id does not resolve.
|
|
5936
|
+
* @returns The asset, or `raw`.
|
|
5937
|
+
*/
|
|
5938
|
+
const assetConversion = (rest, raw) => {
|
|
5939
|
+
const asset = AssetElement.get(rest);
|
|
5940
|
+
if (asset) {
|
|
5941
|
+
return asset;
|
|
5942
|
+
}
|
|
5943
|
+
console.warn(`Unable to resolve '${raw}' in script attributes - no pc-asset found with id '${rest}'.`);
|
|
5944
|
+
return raw;
|
|
5945
|
+
};
|
|
5946
|
+
/**
|
|
5947
|
+
* Resolves an `entity:` prefix to the Entity backing a `pc-entity` element. The reference can be a
|
|
5948
|
+
* CSS selector, an element id or an entity name.
|
|
5949
|
+
* @param rest - The entity reference.
|
|
5950
|
+
* @param raw - The raw value, returned unchanged when the reference does not resolve.
|
|
5951
|
+
* @returns The entity, or `raw`.
|
|
5952
|
+
*/
|
|
5953
|
+
const entityConversion = (rest, raw) => {
|
|
5954
|
+
const entity = getEntity(rest);
|
|
5955
|
+
if (entity) {
|
|
5956
|
+
return entity;
|
|
5957
|
+
}
|
|
5958
|
+
console.warn(`Unable to resolve '${raw}' in script attributes - no pc-entity found matching '${rest}'.`);
|
|
5959
|
+
return raw;
|
|
5960
|
+
};
|
|
5961
|
+
/**
|
|
5962
|
+
* Builds the conversion for a `vec2:`/`vec3:`/`vec4:` prefix.
|
|
5963
|
+
* @param length - The number of components the prefix carries.
|
|
5964
|
+
* @param Ctor - The vector type to construct.
|
|
5965
|
+
* @returns The conversion.
|
|
5966
|
+
*/
|
|
5967
|
+
const vectorConversion = (length, Ctor) => {
|
|
5968
|
+
return (rest, raw) => {
|
|
5969
|
+
const components = parseComponents(rest, length);
|
|
5970
|
+
if (components) {
|
|
5971
|
+
return new Ctor(components);
|
|
5972
|
+
}
|
|
5973
|
+
console.warn(`Invalid script attribute value '${raw}'. Expected ${length} space-separated numbers after 'vec${length}:'.`);
|
|
5974
|
+
return raw;
|
|
5975
|
+
};
|
|
5976
|
+
};
|
|
5977
|
+
/**
|
|
5978
|
+
* Converts a `color:` prefix to a Color, accepting 3 or 4 components.
|
|
5979
|
+
* @param rest - The space-separated components.
|
|
5980
|
+
* @param raw - The raw value, returned unchanged when the components do not parse.
|
|
5981
|
+
* @returns The color, or `raw`.
|
|
5982
|
+
*/
|
|
5983
|
+
const colorConversion = (rest, raw) => {
|
|
5984
|
+
var _a;
|
|
5985
|
+
const components = (_a = parseComponents(rest, 4)) !== null && _a !== void 0 ? _a : parseComponents(rest, 3);
|
|
5986
|
+
if (components) {
|
|
5987
|
+
return new playcanvas.Color(components);
|
|
5988
|
+
}
|
|
5989
|
+
console.warn(`Invalid script attribute value '${raw}'. Expected 3 or 4 space-separated numbers after 'color:'.`);
|
|
5990
|
+
return raw;
|
|
5991
|
+
};
|
|
5992
|
+
/**
|
|
5993
|
+
* The conversion prefixes recognised in script attribute values, mapped to the conversion each
|
|
5994
|
+
* performs. These keys are the single source of truth for the prefix vocabulary: they drive both
|
|
5995
|
+
* the conversion in `convertAttributes` and the has-a-prefix test in `setScriptProperty`, so a
|
|
5996
|
+
* prefix added here is automatically known to both.
|
|
5997
|
+
*/
|
|
5998
|
+
const CONVERSIONS = new Map([
|
|
5999
|
+
['asset', assetConversion],
|
|
6000
|
+
['entity', entityConversion],
|
|
6001
|
+
['vec2', vectorConversion(2, playcanvas.Vec2)],
|
|
6002
|
+
['vec3', vectorConversion(3, playcanvas.Vec3)],
|
|
6003
|
+
['vec4', vectorConversion(4, playcanvas.Vec4)],
|
|
6004
|
+
['color', colorConversion]
|
|
6005
|
+
]);
|
|
6006
|
+
/**
|
|
6007
|
+
* Matches a value against the conversion prefixes. A prefix is the text before the first colon,
|
|
6008
|
+
* so a value whose remainder itself contains colons (`asset:a:b`) still resolves, and a value
|
|
6009
|
+
* with an unrecognised prefix (`https://...`) or no colon does not match.
|
|
6010
|
+
* @param value - The value to inspect.
|
|
6011
|
+
* @returns The matching converter and the text after the prefix, or `null` if the value carries
|
|
6012
|
+
* no recognised prefix.
|
|
6013
|
+
*/
|
|
6014
|
+
const matchConversion = (value) => {
|
|
6015
|
+
const index = value.indexOf(':');
|
|
6016
|
+
if (index <= 0) {
|
|
6017
|
+
return null;
|
|
6018
|
+
}
|
|
6019
|
+
const convert = CONVERSIONS.get(value.slice(0, index));
|
|
6020
|
+
return convert ? { convert, rest: value.slice(index + 1) } : null;
|
|
6021
|
+
};
|
|
6022
|
+
/**
|
|
6023
|
+
* Finds a script property whose name matches `key` case-insensitively (but not exactly). Used
|
|
6024
|
+
* to suggest the kebab-case spelling when a camelCase attribute has been lowercased by the HTML
|
|
6025
|
+
* parser (e.g. `focusPoint` arriving as 'focuspoint').
|
|
6026
|
+
* @param script - The script instance to search.
|
|
6027
|
+
* @param key - The lowercased key that failed to match.
|
|
6028
|
+
* @returns The matching property name, or `null`.
|
|
6029
|
+
*/
|
|
6030
|
+
const findCaseMatch = (script, key) => {
|
|
6031
|
+
const names = new Set(Object.keys(script));
|
|
6032
|
+
for (const name of Object.getOwnPropertyNames(Object.getPrototypeOf(script))) {
|
|
6033
|
+
names.add(name);
|
|
6034
|
+
}
|
|
6035
|
+
for (const name of names) {
|
|
6036
|
+
if (name !== key && name.toLowerCase() === key.toLowerCase()) {
|
|
6037
|
+
return name;
|
|
6038
|
+
}
|
|
6039
|
+
}
|
|
6040
|
+
return null;
|
|
6041
|
+
};
|
|
5453
6042
|
/**
|
|
5454
6043
|
* The ScriptComponentElement interface provides properties and methods for manipulating
|
|
5455
6044
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-scripts/ | `<pc-scripts>`} elements.
|
|
@@ -5464,83 +6053,46 @@ class ScriptComponentElement extends ComponentElement {
|
|
|
5464
6053
|
super('script');
|
|
5465
6054
|
// Create mutation observer to watch for child script elements
|
|
5466
6055
|
this.observer = new MutationObserver(this.handleMutations.bind(this));
|
|
5467
|
-
|
|
5468
|
-
childList: true
|
|
5469
|
-
});
|
|
5470
|
-
// Listen for script attribute and enable changes
|
|
6056
|
+
// Listen for script attribute, enable and name changes
|
|
5471
6057
|
this.addEventListener('scriptattributeschange', this.handleScriptAttributesChange.bind(this));
|
|
5472
6058
|
this.addEventListener('scriptenablechange', this.handleScriptEnableChange.bind(this));
|
|
6059
|
+
this.addEventListener('scriptnamechange', this.handleScriptNameChange.bind(this));
|
|
6060
|
+
}
|
|
6061
|
+
connectedCallback() {
|
|
6062
|
+
// (Re-)observe on every connection - disconnectedCallback disconnects the observer.
|
|
6063
|
+
// Attribute changes on child pc-script elements are watched here too: per-property
|
|
6064
|
+
// script attributes are not statically known, so they cannot use observedAttributes.
|
|
6065
|
+
this.observer.observe(this, { childList: true, subtree: true, attributes: true });
|
|
6066
|
+
return super.connectedCallback();
|
|
5473
6067
|
}
|
|
5474
6068
|
initComponent() {
|
|
5475
6069
|
// Handle initial script elements
|
|
5476
6070
|
this.querySelectorAll(':scope > pc-script').forEach((scriptElement) => {
|
|
5477
|
-
|
|
5478
|
-
const attributes = scriptElement.getAttribute('attributes');
|
|
5479
|
-
if (scriptName) {
|
|
5480
|
-
this.createScript(scriptName, attributes);
|
|
5481
|
-
}
|
|
6071
|
+
this.createScript(scriptElement);
|
|
5482
6072
|
});
|
|
5483
6073
|
}
|
|
5484
6074
|
/**
|
|
5485
6075
|
* Recursively converts raw attribute data into proper PlayCanvas types. Supported conversions:
|
|
5486
|
-
* - "asset:
|
|
5487
|
-
* - "entity:
|
|
5488
|
-
*
|
|
5489
|
-
* - "
|
|
5490
|
-
* - "
|
|
5491
|
-
* - "
|
|
6076
|
+
* - "asset:id" → the Asset created by the `pc-asset` element with that id
|
|
6077
|
+
* - "entity:ref" → the Entity backing a `pc-entity` element. The reference can be a CSS
|
|
6078
|
+
* selector, an element id or an entity name.
|
|
6079
|
+
* - "vec2:1 2" → new Vec2(1, 2)
|
|
6080
|
+
* - "vec3:1 2 3" → new Vec3(1, 2, 3)
|
|
6081
|
+
* - "vec4:1 2 3 4" → new Vec4(1, 2, 3, 4)
|
|
6082
|
+
* - "color:1 0.5 0.5 1" → new Color(1, 0.5, 0.5, 1)
|
|
6083
|
+
*
|
|
6084
|
+
* A prefixed string that fails to resolve or parse logs a warning and is left as the raw
|
|
6085
|
+
* string.
|
|
5492
6086
|
* @param item - The item to convert.
|
|
5493
6087
|
* @returns The converted item.
|
|
5494
6088
|
*/
|
|
5495
6089
|
convertAttributes(item) {
|
|
5496
6090
|
if (typeof item === 'string') {
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
const assetElement = document.querySelector(`pc-asset#${assetId}`);
|
|
5500
|
-
if (assetElement) {
|
|
5501
|
-
return assetElement.asset;
|
|
5502
|
-
}
|
|
5503
|
-
}
|
|
5504
|
-
if (item.startsWith('entity:')) {
|
|
5505
|
-
const entityId = item.slice(7);
|
|
5506
|
-
const entityElement = document.querySelector(`pc-entity[name="${entityId}"]`);
|
|
5507
|
-
if (entityElement) {
|
|
5508
|
-
return entityElement.entity;
|
|
5509
|
-
}
|
|
5510
|
-
}
|
|
5511
|
-
if (item.startsWith('vec2:')) {
|
|
5512
|
-
const parts = item.slice(5).split(',').map(Number);
|
|
5513
|
-
if (parts.length === 2 && parts.every(v => !isNaN(v))) {
|
|
5514
|
-
return new playcanvas.Vec2(parts[0], parts[1]);
|
|
5515
|
-
}
|
|
5516
|
-
}
|
|
5517
|
-
if (item.startsWith('vec3:')) {
|
|
5518
|
-
const parts = item.slice(5).split(',').map(Number);
|
|
5519
|
-
if (parts.length === 3 && parts.every(v => !isNaN(v))) {
|
|
5520
|
-
return new playcanvas.Vec3(parts[0], parts[1], parts[2]);
|
|
5521
|
-
}
|
|
5522
|
-
}
|
|
5523
|
-
if (item.startsWith('vec4:')) {
|
|
5524
|
-
const parts = item.slice(5).split(',').map(Number);
|
|
5525
|
-
if (parts.length === 4 && parts.every(v => !isNaN(v))) {
|
|
5526
|
-
return new playcanvas.Vec4(parts[0], parts[1], parts[2], parts[3]);
|
|
5527
|
-
}
|
|
5528
|
-
}
|
|
5529
|
-
if (item.startsWith('color:')) {
|
|
5530
|
-
const parts = item.slice(6).split(',').map(Number);
|
|
5531
|
-
if (parts.length === 4 && parts.every(v => !isNaN(v))) {
|
|
5532
|
-
return new playcanvas.Color(parts[0], parts[1], parts[2], parts[3]);
|
|
5533
|
-
}
|
|
5534
|
-
}
|
|
5535
|
-
return item;
|
|
6091
|
+
const match = matchConversion(item);
|
|
6092
|
+
return match ? match.convert(match.rest, item) : item;
|
|
5536
6093
|
}
|
|
5537
6094
|
if (Array.isArray(item)) {
|
|
5538
|
-
|
|
5539
|
-
if (item.length > 0 && typeof item[0] === 'object') {
|
|
5540
|
-
return item.map((el) => this.convertAttributes(el));
|
|
5541
|
-
}
|
|
5542
|
-
// Otherwise, leave the numeric array unchanged but process each element.
|
|
5543
|
-
return item.map((el) => this.convertAttributes(el));
|
|
6095
|
+
return item.map((element) => this.convertAttributes(element));
|
|
5544
6096
|
}
|
|
5545
6097
|
if (item && typeof item === 'object') {
|
|
5546
6098
|
const result = {};
|
|
@@ -5552,87 +6104,301 @@ class ScriptComponentElement extends ComponentElement {
|
|
|
5552
6104
|
return item;
|
|
5553
6105
|
}
|
|
5554
6106
|
/**
|
|
5555
|
-
*
|
|
5556
|
-
*
|
|
5557
|
-
*
|
|
5558
|
-
|
|
5559
|
-
preprocessAttributes(attrs) {
|
|
5560
|
-
return this.convertAttributes(attrs);
|
|
5561
|
-
}
|
|
5562
|
-
/**
|
|
5563
|
-
* Recursively merge properties from source into target.
|
|
6107
|
+
* Recursively merge properties from source into target. When the target value is a Vec2,
|
|
6108
|
+
* Vec3, Vec4 or Color and the source value is a plain numeric array, the array is converted
|
|
6109
|
+
* to the target's type — so script attributes with math-typed defaults can be written as
|
|
6110
|
+
* plain JSON arrays (e.g. `"focusPoint": [0, 1.75, 0]`).
|
|
5564
6111
|
* @param target - The target object to merge into.
|
|
5565
6112
|
* @param source - The source object to merge from.
|
|
5566
6113
|
* @returns The merged object.
|
|
5567
6114
|
*/
|
|
5568
6115
|
mergeDeep(target, source) {
|
|
5569
6116
|
for (const key in source) {
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
6117
|
+
const value = source[key];
|
|
6118
|
+
const current = target[key];
|
|
6119
|
+
if (this.isMathType(current) && Array.isArray(value)) {
|
|
6120
|
+
const converted = this.arrayToMathType(current, value, key);
|
|
6121
|
+
if (converted) {
|
|
6122
|
+
target[key] = converted;
|
|
6123
|
+
}
|
|
6124
|
+
continue;
|
|
6125
|
+
}
|
|
6126
|
+
// Only recurse into plain objects. Class instances (Vec3, Color, Asset, Entity...)
|
|
6127
|
+
// are leaf values assigned whole, so accessor-typed script attributes receive them
|
|
6128
|
+
// through their setters instead of having a getter's returned copy mutated.
|
|
6129
|
+
if (value && typeof value === 'object' && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype) {
|
|
6130
|
+
if (!current || typeof current !== 'object') {
|
|
5574
6131
|
target[key] = {};
|
|
5575
6132
|
}
|
|
5576
|
-
this.mergeDeep(target[key],
|
|
6133
|
+
this.mergeDeep(target[key], value);
|
|
5577
6134
|
}
|
|
5578
6135
|
else {
|
|
5579
|
-
target[key] =
|
|
6136
|
+
target[key] = value;
|
|
5580
6137
|
}
|
|
5581
6138
|
}
|
|
5582
6139
|
return target;
|
|
5583
6140
|
}
|
|
5584
6141
|
/**
|
|
5585
|
-
*
|
|
6142
|
+
* Checks whether a value is one of the math types that plain numeric arrays convert to.
|
|
6143
|
+
* @param value - The value to check.
|
|
6144
|
+
* @returns Whether the value is a math type.
|
|
6145
|
+
*/
|
|
6146
|
+
isMathType(value) {
|
|
6147
|
+
return value instanceof playcanvas.Vec2 || value instanceof playcanvas.Vec3 || value instanceof playcanvas.Vec4 || value instanceof playcanvas.Color || value instanceof playcanvas.Quat;
|
|
6148
|
+
}
|
|
6149
|
+
/**
|
|
6150
|
+
* Converts a plain numeric array to the math type of `current`. A 3-element array targeting
|
|
6151
|
+
* a Quat is interpreted as Euler angles in degrees, mirroring the `parseQuat` attribute
|
|
6152
|
+
* grammar. Returns `null` (and logs a warning) when the array's length or contents don't
|
|
6153
|
+
* match the type.
|
|
6154
|
+
* @param current - The current (typed) value of the property.
|
|
6155
|
+
* @param value - The incoming array.
|
|
6156
|
+
* @param key - The property name, used in the warning message.
|
|
6157
|
+
* @returns The converted value, or `null`.
|
|
6158
|
+
*/
|
|
6159
|
+
arrayToMathType(current, value, key) {
|
|
6160
|
+
if (value.every(component => typeof component === 'number' && Number.isFinite(component))) {
|
|
6161
|
+
if (current instanceof playcanvas.Vec2 && value.length === 2)
|
|
6162
|
+
return new playcanvas.Vec2(value);
|
|
6163
|
+
if (current instanceof playcanvas.Vec3 && value.length === 3)
|
|
6164
|
+
return new playcanvas.Vec3(value);
|
|
6165
|
+
if (current instanceof playcanvas.Vec4 && value.length === 4)
|
|
6166
|
+
return new playcanvas.Vec4(value);
|
|
6167
|
+
if (current instanceof playcanvas.Color && (value.length === 3 || value.length === 4))
|
|
6168
|
+
return new playcanvas.Color(value);
|
|
6169
|
+
if (current instanceof playcanvas.Quat && value.length === 3)
|
|
6170
|
+
return new playcanvas.Quat().setFromEulerAngles(value[0], value[1], value[2]);
|
|
6171
|
+
}
|
|
6172
|
+
console.warn(`Cannot convert script attribute '${key}' array [${value}] to ${current.constructor.name}. Keeping the current value.`);
|
|
6173
|
+
return null;
|
|
6174
|
+
}
|
|
6175
|
+
/**
|
|
6176
|
+
* Update script attributes by merging converted values into the script. `enabled` is always
|
|
6177
|
+
* excluded (it is configured through the element's `enabled` attribute, not the JSON blob),
|
|
6178
|
+
* as are any keys in `exclude` — used to keep per-property attributes authoritative over
|
|
6179
|
+
* the blob without writing a property twice.
|
|
5586
6180
|
* @param script - The script to update.
|
|
5587
6181
|
* @param attributes - The attributes to merge into the script.
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
6182
|
+
* @param exclude - Keys to strip from the merge.
|
|
6183
|
+
*/
|
|
6184
|
+
applyAttributes(script, attributes, exclude) {
|
|
6185
|
+
const converted = this.convertAttributes(attributes);
|
|
6186
|
+
if (converted && typeof converted === 'object') {
|
|
6187
|
+
delete converted.enabled;
|
|
6188
|
+
if (exclude) {
|
|
6189
|
+
for (const key of exclude) {
|
|
6190
|
+
delete converted[key];
|
|
6191
|
+
}
|
|
6192
|
+
}
|
|
5594
6193
|
}
|
|
5595
|
-
|
|
5596
|
-
|
|
6194
|
+
this.mergeDeep(script, converted);
|
|
6195
|
+
}
|
|
6196
|
+
/**
|
|
6197
|
+
* Returns the camelCase keys of the per-property attributes present on a `pc-script`
|
|
6198
|
+
* element.
|
|
6199
|
+
* @param scriptElement - The `pc-script` element.
|
|
6200
|
+
* @returns The camelCase keys.
|
|
6201
|
+
*/
|
|
6202
|
+
inlineKeys(scriptElement) {
|
|
6203
|
+
const keys = new Set();
|
|
6204
|
+
for (const attr of Array.from(scriptElement.attributes)) {
|
|
6205
|
+
if (!isReservedAttribute(attr.name)) {
|
|
6206
|
+
keys.add(kebabToCamel(attr.name));
|
|
6207
|
+
}
|
|
5597
6208
|
}
|
|
6209
|
+
return keys;
|
|
6210
|
+
}
|
|
6211
|
+
/**
|
|
6212
|
+
* Resolves the script instance owned by a `pc-script` element. Returns `null` when the
|
|
6213
|
+
* element has no created script, or when its name resolves to a script created by a
|
|
6214
|
+
* different element (e.g. a duplicate-named sibling).
|
|
6215
|
+
* @param scriptElement - The `pc-script` element.
|
|
6216
|
+
* @returns The owned script, or `null`.
|
|
6217
|
+
*/
|
|
6218
|
+
scriptFor(scriptElement) {
|
|
6219
|
+
const name = scriptElement.getAttribute('name');
|
|
6220
|
+
if (!name || !this.component)
|
|
6221
|
+
return null;
|
|
6222
|
+
const script = this.component.get(name);
|
|
6223
|
+
return script && script === scriptElement._script ? script : null;
|
|
5598
6224
|
}
|
|
5599
6225
|
handleScriptAttributesChange(event) {
|
|
5600
6226
|
const scriptElement = event.target;
|
|
5601
|
-
const
|
|
5602
|
-
if (!scriptName || !this.component)
|
|
5603
|
-
return;
|
|
5604
|
-
const script = this.component.get(scriptName);
|
|
6227
|
+
const script = this.scriptFor(scriptElement);
|
|
5605
6228
|
if (script) {
|
|
5606
|
-
|
|
6229
|
+
// Per-property attributes stay authoritative: keys they pin are excluded here
|
|
6230
|
+
this.applyAttributes(script, event.detail.attributes, this.inlineKeys(scriptElement));
|
|
5607
6231
|
}
|
|
5608
6232
|
}
|
|
5609
6233
|
handleScriptEnableChange(event) {
|
|
5610
6234
|
const scriptElement = event.target;
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
const script = this.
|
|
6235
|
+
// Apply any queued per-property changes first, so that initialize() (fired by the
|
|
6236
|
+
// engine on first effective enable) sees every attribute value set this tick
|
|
6237
|
+
this.handleMutations(this.observer.takeRecords());
|
|
6238
|
+
const script = this.scriptFor(scriptElement);
|
|
5615
6239
|
if (script) {
|
|
5616
6240
|
script.enabled = event.detail.enabled;
|
|
5617
6241
|
}
|
|
5618
6242
|
}
|
|
5619
|
-
|
|
6243
|
+
/**
|
|
6244
|
+
* Handles a runtime `name` change on a child `pc-script`, swapping the engine script instance
|
|
6245
|
+
* to match. Without this the element would keep pointing at the old-name instance: the old
|
|
6246
|
+
* script would go on running while every subsequent update (attribute changes, enable
|
|
6247
|
+
* changes, destruction on removal) resolved the new name and silently no-opped.
|
|
6248
|
+
*
|
|
6249
|
+
* The new instance is built by the normal creation path, so both attribute channels are
|
|
6250
|
+
* re-applied to it and the declared enabled state is restored.
|
|
6251
|
+
* @param event - The name change event.
|
|
6252
|
+
*/
|
|
6253
|
+
handleScriptNameChange(event) {
|
|
6254
|
+
const scriptElement = event.target;
|
|
6255
|
+
// Only direct children are managed, matching initComponent's ':scope > pc-script'
|
|
6256
|
+
// contract - the event bubbles, so a deeper pc-script must not be created here
|
|
6257
|
+
if (scriptElement.parentElement !== this)
|
|
6258
|
+
return;
|
|
6259
|
+
// Before the component exists there is nothing to swap: initComponent creates from
|
|
6260
|
+
// whatever the name is by then
|
|
5620
6261
|
if (!this.component)
|
|
6262
|
+
return;
|
|
6263
|
+
// Only tear down the old-name script if this element actually owns it - a duplicate-named
|
|
6264
|
+
// element whose own create() failed must not take down the live script on rename
|
|
6265
|
+
const { oldName } = event.detail;
|
|
6266
|
+
if (oldName && scriptElement._script && this.component.get(oldName) === scriptElement._script) {
|
|
6267
|
+
this.destroyScript(oldName);
|
|
6268
|
+
}
|
|
6269
|
+
scriptElement._script = null;
|
|
6270
|
+
this.createScript(scriptElement);
|
|
6271
|
+
}
|
|
6272
|
+
/**
|
|
6273
|
+
* Creates the script instance for a `pc-script` element. The instance is created disabled,
|
|
6274
|
+
* the element's converted attributes are merged over the instance's defaults (which is what
|
|
6275
|
+
* allows plain numeric arrays to be typed against those defaults), and only then is the
|
|
6276
|
+
* declared enabled state applied — so `initialize()` runs with every attribute in place.
|
|
6277
|
+
* @param scriptElement - The `pc-script` element to create the script instance for.
|
|
6278
|
+
* @returns The created script, or `null`.
|
|
6279
|
+
*/
|
|
6280
|
+
createScript(scriptElement) {
|
|
6281
|
+
const name = scriptElement.getAttribute('name');
|
|
6282
|
+
if (!name || !this.component)
|
|
5621
6283
|
return null;
|
|
5622
|
-
|
|
5623
|
-
if (
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
6284
|
+
const script = this.component.create(name, { enabled: false });
|
|
6285
|
+
if (!script)
|
|
6286
|
+
return null;
|
|
6287
|
+
scriptElement._script = script;
|
|
6288
|
+
// The JSON blob first with per-property-shadowed keys stripped, then the per-property
|
|
6289
|
+
// attributes: each property is written exactly once and individual attributes win
|
|
6290
|
+
this.applyAttributes(script, scriptElement.scriptAttributes, this.inlineKeys(scriptElement));
|
|
6291
|
+
this.applyInlineAttributes(script, scriptElement);
|
|
6292
|
+
script.enabled = scriptElement.enabled;
|
|
6293
|
+
scriptElement._onScriptCreated();
|
|
6294
|
+
return script;
|
|
6295
|
+
}
|
|
6296
|
+
/**
|
|
6297
|
+
* Applies the per-property attributes present on a `pc-script` element — any attribute that
|
|
6298
|
+
* is not part of the element's own API or a reserved HTML attribute name. These are applied
|
|
6299
|
+
* after the `attributes` JSON, so an individual attribute always takes precedence over the
|
|
6300
|
+
* blob.
|
|
6301
|
+
* @param script - The script to apply the attributes to.
|
|
6302
|
+
* @param scriptElement - The `pc-script` element holding the attributes.
|
|
6303
|
+
*/
|
|
6304
|
+
applyInlineAttributes(script, scriptElement) {
|
|
6305
|
+
var _a;
|
|
6306
|
+
const scriptName = (_a = scriptElement.getAttribute('name')) !== null && _a !== void 0 ? _a : '';
|
|
6307
|
+
for (const attr of Array.from(scriptElement.attributes)) {
|
|
6308
|
+
if (!isReservedAttribute(attr.name)) {
|
|
6309
|
+
this.setScriptProperty(script, scriptName, attr.name, attr.value);
|
|
5628
6310
|
}
|
|
5629
|
-
|
|
5630
|
-
|
|
6311
|
+
}
|
|
6312
|
+
}
|
|
6313
|
+
/**
|
|
6314
|
+
* Applies a single per-property attribute change to the script of a `pc-script` element.
|
|
6315
|
+
* When the attribute has been removed, the value from the `attributes` JSON (if any) takes
|
|
6316
|
+
* effect again.
|
|
6317
|
+
* @param scriptElement - The `pc-script` element whose attribute changed.
|
|
6318
|
+
* @param attributeName - The name of the changed attribute.
|
|
6319
|
+
*/
|
|
6320
|
+
applyScriptProperty(scriptElement, attributeName) {
|
|
6321
|
+
var _a;
|
|
6322
|
+
const script = this.scriptFor(scriptElement);
|
|
6323
|
+
if (!script)
|
|
6324
|
+
return;
|
|
6325
|
+
const value = scriptElement.getAttribute(attributeName);
|
|
6326
|
+
if (value === null) {
|
|
6327
|
+
const key = kebabToCamel(attributeName);
|
|
6328
|
+
const fallback = scriptElement.scriptAttributes[key];
|
|
6329
|
+
if (fallback !== undefined) {
|
|
6330
|
+
this.applyAttributes(script, { [key]: fallback });
|
|
5631
6331
|
}
|
|
6332
|
+
return;
|
|
6333
|
+
}
|
|
6334
|
+
this.setScriptProperty(script, (_a = scriptElement.getAttribute('name')) !== null && _a !== void 0 ? _a : '', attributeName, value);
|
|
6335
|
+
}
|
|
6336
|
+
/**
|
|
6337
|
+
* Applies one attribute string to a script property. A string-typed attribute takes the
|
|
6338
|
+
* value verbatim (so literals like 'color:red' are never hijacked by prefix conversion).
|
|
6339
|
+
* Otherwise, explicit prefixes (`asset:`, `entity:`, `vec2:`, `vec3:`, `vec4:`, `color:`)
|
|
6340
|
+
* carry their own type, and unprefixed values are parsed according to the type of the
|
|
6341
|
+
* attribute's current value. The Script API itself (methods, `entity`, `app`) is never
|
|
6342
|
+
* overwritten, invalid values keep the current value, and exceptions thrown by user
|
|
6343
|
+
* getters/setters are contained so one bad attribute cannot abort the rest of a batch.
|
|
6344
|
+
* @param script - The script to apply the value to.
|
|
6345
|
+
* @param scriptName - The script name, used in warning messages.
|
|
6346
|
+
* @param attributeName - The (kebab-case) element attribute name.
|
|
6347
|
+
* @param value - The attribute value.
|
|
6348
|
+
*/
|
|
6349
|
+
setScriptProperty(script, scriptName, attributeName, value) {
|
|
6350
|
+
const key = kebabToCamel(attributeName);
|
|
6351
|
+
try {
|
|
6352
|
+
const current = script[key];
|
|
6353
|
+
if (typeof current === 'function' || SCRIPT_API_MEMBERS.has(key)) {
|
|
6354
|
+
console.warn(`Ignoring attribute '${attributeName}' on pc-script '${scriptName}' - '${key}' is part of the Script API.`);
|
|
6355
|
+
return;
|
|
6356
|
+
}
|
|
6357
|
+
if (typeof current === 'string') {
|
|
6358
|
+
script[key] = value;
|
|
6359
|
+
}
|
|
6360
|
+
else if (matchConversion(value)) {
|
|
6361
|
+
const converted = this.convertAttributes(value);
|
|
6362
|
+
// A prefix that failed to resolve or parse comes back as the raw string
|
|
6363
|
+
// (convertAttributes already warned) - never clobber a typed value with it
|
|
6364
|
+
if (converted !== value || current === undefined || current === null) {
|
|
6365
|
+
script[key] = converted;
|
|
6366
|
+
}
|
|
6367
|
+
}
|
|
6368
|
+
else if (typeof current === 'number') {
|
|
6369
|
+
script[key] = parseNumber(value, current, attributeName);
|
|
6370
|
+
}
|
|
6371
|
+
else if (typeof current === 'boolean') {
|
|
6372
|
+
script[key] = parseBool(value, current);
|
|
6373
|
+
}
|
|
6374
|
+
else if (current instanceof playcanvas.Vec2) {
|
|
6375
|
+
script[key] = parseVec2(value, current, attributeName);
|
|
6376
|
+
}
|
|
6377
|
+
else if (current instanceof playcanvas.Vec3) {
|
|
6378
|
+
script[key] = parseVec3(value, current, attributeName);
|
|
6379
|
+
}
|
|
6380
|
+
else if (current instanceof playcanvas.Vec4) {
|
|
6381
|
+
script[key] = parseVec4(value, current, attributeName);
|
|
6382
|
+
}
|
|
6383
|
+
else if (current instanceof playcanvas.Color) {
|
|
6384
|
+
script[key] = parseColor(value, current, attributeName);
|
|
6385
|
+
}
|
|
6386
|
+
else if (current instanceof playcanvas.Quat) {
|
|
6387
|
+
script[key] = parseQuat(value, current, attributeName);
|
|
6388
|
+
}
|
|
6389
|
+
else {
|
|
6390
|
+
const match = findCaseMatch(script, key);
|
|
6391
|
+
if (match) {
|
|
6392
|
+
console.warn(`Script '${scriptName}' has no attribute '${key}' - did you mean '${camelToKebab(match)}'? Attribute names are kebab-case.`);
|
|
6393
|
+
return;
|
|
6394
|
+
}
|
|
6395
|
+
console.warn(`Script '${scriptName}' has no typed attribute '${key}' - assigning the raw string from '${attributeName}'.`);
|
|
6396
|
+
script[key] = value;
|
|
6397
|
+
}
|
|
6398
|
+
}
|
|
6399
|
+
catch (error) {
|
|
6400
|
+
console.warn(`Error applying attribute '${attributeName}' to script '${scriptName}': ${error.message}`);
|
|
5632
6401
|
}
|
|
5633
|
-
return this.component.create(name, {
|
|
5634
|
-
properties: attributesObject
|
|
5635
|
-
});
|
|
5636
6402
|
}
|
|
5637
6403
|
destroyScript(name) {
|
|
5638
6404
|
if (!this.component)
|
|
@@ -5641,23 +6407,40 @@ class ScriptComponentElement extends ComponentElement {
|
|
|
5641
6407
|
}
|
|
5642
6408
|
handleMutations(mutations) {
|
|
5643
6409
|
for (const mutation of mutations) {
|
|
5644
|
-
// Handle
|
|
5645
|
-
mutation.
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
6410
|
+
// Handle per-property attribute changes on child pc-script elements
|
|
6411
|
+
if (mutation.type === 'attributes') {
|
|
6412
|
+
const target = mutation.target;
|
|
6413
|
+
if (target instanceof ScriptElement &&
|
|
6414
|
+
target.parentElement === this &&
|
|
6415
|
+
mutation.attributeName &&
|
|
6416
|
+
!isReservedAttribute(mutation.attributeName)) {
|
|
6417
|
+
this.applyScriptProperty(target, mutation.attributeName);
|
|
5652
6418
|
}
|
|
5653
|
-
|
|
5654
|
-
|
|
6419
|
+
continue;
|
|
6420
|
+
}
|
|
6421
|
+
// Only direct children are managed - the observer watches the subtree for attribute
|
|
6422
|
+
// changes, but deeper childList records must not create or destroy scripts
|
|
6423
|
+
// (matching initComponent's ':scope > pc-script' contract)
|
|
6424
|
+
if (mutation.target !== this) {
|
|
6425
|
+
continue;
|
|
6426
|
+
}
|
|
6427
|
+
// Handle removed nodes first, so that replacing a pc-script with a same-named one
|
|
6428
|
+
// destroys the old script before the replacement is created. Only destroy a script
|
|
6429
|
+
// this element actually owns - a duplicate-named element whose own create() failed
|
|
6430
|
+
// must not take down the live script on removal.
|
|
5655
6431
|
mutation.removedNodes.forEach((node) => {
|
|
5656
|
-
if (node instanceof
|
|
6432
|
+
if (node instanceof ScriptElement) {
|
|
5657
6433
|
const scriptName = node.getAttribute('name');
|
|
5658
|
-
if (scriptName) {
|
|
6434
|
+
if (scriptName && node._script && this.component && this.component.get(scriptName) === node._script) {
|
|
5659
6435
|
this.destroyScript(scriptName);
|
|
5660
6436
|
}
|
|
6437
|
+
node._script = null;
|
|
6438
|
+
}
|
|
6439
|
+
});
|
|
6440
|
+
// Handle added nodes
|
|
6441
|
+
mutation.addedNodes.forEach((node) => {
|
|
6442
|
+
if (node instanceof ScriptElement) {
|
|
6443
|
+
this.createScript(node);
|
|
5661
6444
|
}
|
|
5662
6445
|
});
|
|
5663
6446
|
}
|
|
@@ -5677,87 +6460,6 @@ class ScriptComponentElement extends ComponentElement {
|
|
|
5677
6460
|
}
|
|
5678
6461
|
customElements.define('pc-scripts', ScriptComponentElement);
|
|
5679
6462
|
|
|
5680
|
-
/**
|
|
5681
|
-
* The ScriptElement interface provides properties and methods for manipulating
|
|
5682
|
-
* `<pc-script>` elements. The ScriptElement interface also inherits the properties and
|
|
5683
|
-
* methods of the {@link HTMLElement} interface.
|
|
5684
|
-
*/
|
|
5685
|
-
class ScriptElement extends HTMLElement {
|
|
5686
|
-
constructor() {
|
|
5687
|
-
super(...arguments);
|
|
5688
|
-
this._attributes = '{}';
|
|
5689
|
-
this._enabled = true;
|
|
5690
|
-
this._name = '';
|
|
5691
|
-
}
|
|
5692
|
-
/**
|
|
5693
|
-
* Sets the attributes of the script.
|
|
5694
|
-
* @param value - The attributes of the script.
|
|
5695
|
-
*/
|
|
5696
|
-
set scriptAttributes(value) {
|
|
5697
|
-
this._attributes = value;
|
|
5698
|
-
this.dispatchEvent(new CustomEvent('scriptattributeschange', {
|
|
5699
|
-
detail: { attributes: value },
|
|
5700
|
-
bubbles: true
|
|
5701
|
-
}));
|
|
5702
|
-
}
|
|
5703
|
-
/**
|
|
5704
|
-
* Gets the attributes of the script.
|
|
5705
|
-
* @returns The attributes of the script.
|
|
5706
|
-
*/
|
|
5707
|
-
get scriptAttributes() {
|
|
5708
|
-
return this._attributes;
|
|
5709
|
-
}
|
|
5710
|
-
/**
|
|
5711
|
-
* Sets the enabled state of the script.
|
|
5712
|
-
* @param value - The enabled state of the script.
|
|
5713
|
-
*/
|
|
5714
|
-
set enabled(value) {
|
|
5715
|
-
this._enabled = value;
|
|
5716
|
-
this.dispatchEvent(new CustomEvent('scriptenablechange', {
|
|
5717
|
-
detail: { enabled: value },
|
|
5718
|
-
bubbles: true
|
|
5719
|
-
}));
|
|
5720
|
-
}
|
|
5721
|
-
/**
|
|
5722
|
-
* Gets the enabled state of the script.
|
|
5723
|
-
* @returns The enabled state of the script.
|
|
5724
|
-
*/
|
|
5725
|
-
get enabled() {
|
|
5726
|
-
return this._enabled;
|
|
5727
|
-
}
|
|
5728
|
-
/**
|
|
5729
|
-
* Sets the name of the script to create.
|
|
5730
|
-
* @param value - The name.
|
|
5731
|
-
*/
|
|
5732
|
-
set name(value) {
|
|
5733
|
-
this._name = value;
|
|
5734
|
-
}
|
|
5735
|
-
/**
|
|
5736
|
-
* Gets the name of the script.
|
|
5737
|
-
* @returns The name.
|
|
5738
|
-
*/
|
|
5739
|
-
get name() {
|
|
5740
|
-
return this._name;
|
|
5741
|
-
}
|
|
5742
|
-
static get observedAttributes() {
|
|
5743
|
-
return ['attributes', 'enabled', 'name'];
|
|
5744
|
-
}
|
|
5745
|
-
attributeChangedCallback(name, _oldValue, newValue) {
|
|
5746
|
-
switch (name) {
|
|
5747
|
-
case 'attributes':
|
|
5748
|
-
this.scriptAttributes = newValue;
|
|
5749
|
-
break;
|
|
5750
|
-
case 'enabled':
|
|
5751
|
-
this.enabled = newValue !== 'false';
|
|
5752
|
-
break;
|
|
5753
|
-
case 'name':
|
|
5754
|
-
this.name = newValue;
|
|
5755
|
-
break;
|
|
5756
|
-
}
|
|
5757
|
-
}
|
|
5758
|
-
}
|
|
5759
|
-
customElements.define('pc-script', ScriptElement);
|
|
5760
|
-
|
|
5761
6463
|
/**
|
|
5762
6464
|
* The SoundComponentElement interface provides properties and methods for manipulating
|
|
5763
6465
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-sounds/ | `<pc-sounds>`} elements.
|
|
@@ -5931,25 +6633,25 @@ class SoundComponentElement extends ComponentElement {
|
|
|
5931
6633
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
5932
6634
|
switch (name) {
|
|
5933
6635
|
case 'distance-model':
|
|
5934
|
-
this.distanceModel = newValue;
|
|
6636
|
+
this.distanceModel = parseEnum(newValue, ['exponential', 'inverse', 'linear'], 'linear', name);
|
|
5935
6637
|
break;
|
|
5936
6638
|
case 'max-distance':
|
|
5937
|
-
this.maxDistance =
|
|
6639
|
+
this.maxDistance = parseNumber(newValue, 10000, name);
|
|
5938
6640
|
break;
|
|
5939
6641
|
case 'pitch':
|
|
5940
|
-
this.pitch =
|
|
6642
|
+
this.pitch = parseNumber(newValue, 1, name);
|
|
5941
6643
|
break;
|
|
5942
6644
|
case 'positional':
|
|
5943
|
-
this.positional =
|
|
6645
|
+
this.positional = parseBool(newValue, false);
|
|
5944
6646
|
break;
|
|
5945
6647
|
case 'ref-distance':
|
|
5946
|
-
this.refDistance =
|
|
6648
|
+
this.refDistance = parseNumber(newValue, 1, name);
|
|
5947
6649
|
break;
|
|
5948
6650
|
case 'roll-off-factor':
|
|
5949
|
-
this.rollOffFactor =
|
|
6651
|
+
this.rollOffFactor = parseNumber(newValue, 1, name);
|
|
5950
6652
|
break;
|
|
5951
6653
|
case 'volume':
|
|
5952
|
-
this.volume =
|
|
6654
|
+
this.volume = parseNumber(newValue, 1, name);
|
|
5953
6655
|
break;
|
|
5954
6656
|
}
|
|
5955
6657
|
}
|
|
@@ -6000,12 +6702,15 @@ class SoundSlotElement extends AsyncElement {
|
|
|
6000
6702
|
this._onReady();
|
|
6001
6703
|
}
|
|
6002
6704
|
disconnectedCallback() {
|
|
6003
|
-
|
|
6705
|
+
var _a, _b;
|
|
6706
|
+
// The component is null if the parent <pc-sound> (or the whole <pc-app>) is being
|
|
6707
|
+
// torn down — parents disconnect first and have already removed the component.
|
|
6708
|
+
(_b = (_a = this.soundElement) === null || _a === void 0 ? void 0 : _a.component) === null || _b === void 0 ? void 0 : _b.removeSlot(this._name);
|
|
6004
6709
|
}
|
|
6005
6710
|
get soundElement() {
|
|
6006
6711
|
const soundElement = this.parentElement;
|
|
6007
6712
|
if (!(soundElement instanceof SoundComponentElement)) {
|
|
6008
|
-
console.warn('pc-sound
|
|
6713
|
+
console.warn('pc-sound must be a direct child of a pc-sounds element');
|
|
6009
6714
|
return null;
|
|
6010
6715
|
}
|
|
6011
6716
|
return soundElement;
|
|
@@ -6049,12 +6754,12 @@ class SoundSlotElement extends AsyncElement {
|
|
|
6049
6754
|
return this._autoPlay;
|
|
6050
6755
|
}
|
|
6051
6756
|
/**
|
|
6052
|
-
* Sets the duration of the sound slot.
|
|
6757
|
+
* Sets the duration of the sound slot, in seconds (or `null` to play the whole clip).
|
|
6053
6758
|
* @param value - The duration.
|
|
6054
6759
|
*/
|
|
6055
6760
|
set duration(value) {
|
|
6056
6761
|
this._duration = value;
|
|
6057
|
-
if (this.soundSlot) {
|
|
6762
|
+
if (this.soundSlot && value !== null) {
|
|
6058
6763
|
this.soundSlot.duration = value;
|
|
6059
6764
|
}
|
|
6060
6765
|
}
|
|
@@ -6176,28 +6881,28 @@ class SoundSlotElement extends AsyncElement {
|
|
|
6176
6881
|
this.asset = newValue;
|
|
6177
6882
|
break;
|
|
6178
6883
|
case 'auto-play':
|
|
6179
|
-
this.autoPlay =
|
|
6884
|
+
this.autoPlay = parseBool(newValue, false);
|
|
6180
6885
|
break;
|
|
6181
6886
|
case 'duration':
|
|
6182
|
-
this.duration =
|
|
6887
|
+
this.duration = parseNumber(newValue, null, name);
|
|
6183
6888
|
break;
|
|
6184
6889
|
case 'loop':
|
|
6185
|
-
this.loop =
|
|
6890
|
+
this.loop = parseBool(newValue, false);
|
|
6186
6891
|
break;
|
|
6187
6892
|
case 'name':
|
|
6188
6893
|
this.name = newValue;
|
|
6189
6894
|
break;
|
|
6190
6895
|
case 'overlap':
|
|
6191
|
-
this.overlap =
|
|
6896
|
+
this.overlap = parseBool(newValue, false);
|
|
6192
6897
|
break;
|
|
6193
6898
|
case 'pitch':
|
|
6194
|
-
this.pitch =
|
|
6899
|
+
this.pitch = parseNumber(newValue, 1, name);
|
|
6195
6900
|
break;
|
|
6196
6901
|
case 'start-time':
|
|
6197
|
-
this.startTime =
|
|
6902
|
+
this.startTime = parseNumber(newValue, 0, name);
|
|
6198
6903
|
break;
|
|
6199
6904
|
case 'volume':
|
|
6200
|
-
this.volume =
|
|
6905
|
+
this.volume = parseNumber(newValue, 1, name);
|
|
6201
6906
|
break;
|
|
6202
6907
|
}
|
|
6203
6908
|
}
|
|
@@ -6373,19 +7078,19 @@ class GSplatComponentElement extends ComponentElement {
|
|
|
6373
7078
|
this.asset = newValue;
|
|
6374
7079
|
break;
|
|
6375
7080
|
case 'cast-shadows':
|
|
6376
|
-
this.castShadows =
|
|
7081
|
+
this.castShadows = parseBool(newValue, false);
|
|
6377
7082
|
break;
|
|
6378
7083
|
case 'lod-base-distance':
|
|
6379
|
-
this.lodBaseDistance =
|
|
7084
|
+
this.lodBaseDistance = parseNumber(newValue, 5, name);
|
|
6380
7085
|
break;
|
|
6381
7086
|
case 'lod-multiplier':
|
|
6382
|
-
this.lodMultiplier =
|
|
7087
|
+
this.lodMultiplier = parseNumber(newValue, 3, name);
|
|
6383
7088
|
break;
|
|
6384
7089
|
case 'lod-range-min':
|
|
6385
|
-
this.lodRangeMin =
|
|
7090
|
+
this.lodRangeMin = parseNumber(newValue, 0, name);
|
|
6386
7091
|
break;
|
|
6387
7092
|
case 'lod-range-max':
|
|
6388
|
-
this.lodRangeMax =
|
|
7093
|
+
this.lodRangeMax = parseNumber(newValue, 99, name);
|
|
6389
7094
|
break;
|
|
6390
7095
|
}
|
|
6391
7096
|
}
|
|
@@ -6500,7 +7205,7 @@ class SceneElement extends AsyncElement {
|
|
|
6500
7205
|
/**
|
|
6501
7206
|
* The fog type of the scene.
|
|
6502
7207
|
*/
|
|
6503
|
-
this._fog = 'none';
|
|
7208
|
+
this._fog = 'none';
|
|
6504
7209
|
/**
|
|
6505
7210
|
* The color of the fog.
|
|
6506
7211
|
*/
|
|
@@ -6521,15 +7226,20 @@ class SceneElement extends AsyncElement {
|
|
|
6521
7226
|
* The gravity of the scene.
|
|
6522
7227
|
*/
|
|
6523
7228
|
this._gravity = new playcanvas.Vec3(0, -9.81, 0);
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
7229
|
+
this._scene = null;
|
|
7230
|
+
}
|
|
7231
|
+
/**
|
|
7232
|
+
* The PlayCanvas scene instance. Available once the element is ready — await
|
|
7233
|
+
* {@link whenReady} or the element's `ready()` promise before accessing it.
|
|
7234
|
+
* @returns The scene instance.
|
|
7235
|
+
*/
|
|
7236
|
+
get scene() {
|
|
7237
|
+
return this._scene;
|
|
6528
7238
|
}
|
|
6529
7239
|
async connectedCallback() {
|
|
6530
7240
|
var _a;
|
|
6531
7241
|
await ((_a = this.closestApp) === null || _a === void 0 ? void 0 : _a.ready());
|
|
6532
|
-
this.
|
|
7242
|
+
this._scene = this.closestApp.app.scene;
|
|
6533
7243
|
this.updateSceneSettings();
|
|
6534
7244
|
this._onReady();
|
|
6535
7245
|
}
|
|
@@ -6545,7 +7255,8 @@ class SceneElement extends AsyncElement {
|
|
|
6545
7255
|
}
|
|
6546
7256
|
}
|
|
6547
7257
|
/**
|
|
6548
|
-
* Sets the fog type of the scene.
|
|
7258
|
+
* Sets the fog type of the scene. Can be `none`, `linear`, `exp` or `exp2`. Defaults to
|
|
7259
|
+
* `none`.
|
|
6549
7260
|
* @param value - The fog type.
|
|
6550
7261
|
*/
|
|
6551
7262
|
set fog(value) {
|
|
@@ -6653,22 +7364,22 @@ class SceneElement extends AsyncElement {
|
|
|
6653
7364
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
6654
7365
|
switch (name) {
|
|
6655
7366
|
case 'fog':
|
|
6656
|
-
this.fog = newValue;
|
|
7367
|
+
this.fog = parseEnum(newValue, ['none', 'linear', 'exp', 'exp2'], 'none', name);
|
|
6657
7368
|
break;
|
|
6658
7369
|
case 'fog-color':
|
|
6659
|
-
this.fogColor = parseColor(newValue);
|
|
7370
|
+
this.fogColor = parseColor(newValue, playcanvas.Color.WHITE, name);
|
|
6660
7371
|
break;
|
|
6661
7372
|
case 'fog-density':
|
|
6662
|
-
this.fogDensity =
|
|
7373
|
+
this.fogDensity = parseNumber(newValue, 0, name);
|
|
6663
7374
|
break;
|
|
6664
7375
|
case 'fog-start':
|
|
6665
|
-
this.fogStart =
|
|
7376
|
+
this.fogStart = parseNumber(newValue, 0, name);
|
|
6666
7377
|
break;
|
|
6667
7378
|
case 'fog-end':
|
|
6668
|
-
this.fogEnd =
|
|
7379
|
+
this.fogEnd = parseNumber(newValue, 1000, name);
|
|
6669
7380
|
break;
|
|
6670
7381
|
case 'gravity':
|
|
6671
|
-
this.gravity = parseVec3(newValue);
|
|
7382
|
+
this.gravity = parseVec3(newValue, new playcanvas.Vec3(0, -9.81, 0), name);
|
|
6672
7383
|
break;
|
|
6673
7384
|
// ... handle other attributes as well
|
|
6674
7385
|
}
|
|
@@ -6693,6 +7404,7 @@ class SkyElement extends AsyncElement {
|
|
|
6693
7404
|
this._scale = new playcanvas.Vec3(100, 100, 100);
|
|
6694
7405
|
this._type = 'infinite';
|
|
6695
7406
|
this._scene = null;
|
|
7407
|
+
this._appElement = null;
|
|
6696
7408
|
}
|
|
6697
7409
|
connectedCallback() {
|
|
6698
7410
|
this._loadSkybox();
|
|
@@ -6700,6 +7412,7 @@ class SkyElement extends AsyncElement {
|
|
|
6700
7412
|
}
|
|
6701
7413
|
disconnectedCallback() {
|
|
6702
7414
|
this._unloadSkybox();
|
|
7415
|
+
this._appElement = null;
|
|
6703
7416
|
}
|
|
6704
7417
|
_generateSkybox(asset) {
|
|
6705
7418
|
if (!this._scene)
|
|
@@ -6727,9 +7440,10 @@ class SkyElement extends AsyncElement {
|
|
|
6727
7440
|
var _a;
|
|
6728
7441
|
const appElement = await ((_a = this.closestApp) === null || _a === void 0 ? void 0 : _a.ready());
|
|
6729
7442
|
const app = appElement === null || appElement === void 0 ? void 0 : appElement.app;
|
|
6730
|
-
if (!app) {
|
|
7443
|
+
if (!appElement || !app) {
|
|
6731
7444
|
return;
|
|
6732
7445
|
}
|
|
7446
|
+
this._appElement = appElement;
|
|
6733
7447
|
const asset = AssetElement.get(this._asset);
|
|
6734
7448
|
if (!asset) {
|
|
6735
7449
|
return;
|
|
@@ -6746,16 +7460,22 @@ class SkyElement extends AsyncElement {
|
|
|
6746
7460
|
}
|
|
6747
7461
|
}
|
|
6748
7462
|
_unloadSkybox() {
|
|
6749
|
-
var _a, _b;
|
|
6750
|
-
|
|
7463
|
+
var _a, _b, _c;
|
|
7464
|
+
const scene = this._scene;
|
|
7465
|
+
if (!scene)
|
|
7466
|
+
return;
|
|
7467
|
+
this._scene = null;
|
|
7468
|
+
// If the owning application has already been destroyed (removing a <pc-app>
|
|
7469
|
+
// disconnects it before its children), the scene, graphics device and skybox
|
|
7470
|
+
// textures have all been destroyed along with it — nothing left to clean up.
|
|
7471
|
+
if (!((_a = this._appElement) === null || _a === void 0 ? void 0 : _a.app))
|
|
6751
7472
|
return;
|
|
6752
|
-
(
|
|
7473
|
+
(_b = scene.skybox) === null || _b === void 0 ? void 0 : _b.destroy();
|
|
6753
7474
|
// @ts-ignore
|
|
6754
|
-
|
|
6755
|
-
(
|
|
7475
|
+
scene.skybox = null;
|
|
7476
|
+
(_c = scene.envAtlas) === null || _c === void 0 ? void 0 : _c.destroy();
|
|
6756
7477
|
// @ts-ignore
|
|
6757
|
-
|
|
6758
|
-
this._scene = null;
|
|
7478
|
+
scene.envAtlas = null;
|
|
6759
7479
|
}
|
|
6760
7480
|
/**
|
|
6761
7481
|
* Sets the id of the `pc-asset` to use for the skybox.
|
|
@@ -6903,25 +7623,25 @@ class SkyElement extends AsyncElement {
|
|
|
6903
7623
|
this.asset = newValue;
|
|
6904
7624
|
break;
|
|
6905
7625
|
case 'center':
|
|
6906
|
-
this.center = parseVec3(newValue);
|
|
7626
|
+
this.center = parseVec3(newValue, new playcanvas.Vec3(0, 0.01, 0), name);
|
|
6907
7627
|
break;
|
|
6908
7628
|
case 'intensity':
|
|
6909
|
-
this.intensity =
|
|
7629
|
+
this.intensity = parseNumber(newValue, 1, name);
|
|
6910
7630
|
break;
|
|
6911
7631
|
case 'level':
|
|
6912
|
-
this.level =
|
|
7632
|
+
this.level = parseNumber(newValue, 0, name);
|
|
6913
7633
|
break;
|
|
6914
7634
|
case 'lighting':
|
|
6915
|
-
this.lighting =
|
|
7635
|
+
this.lighting = parseBool(newValue, false);
|
|
6916
7636
|
break;
|
|
6917
7637
|
case 'rotation':
|
|
6918
|
-
this.rotation = parseVec3(newValue);
|
|
7638
|
+
this.rotation = parseVec3(newValue, playcanvas.Vec3.ZERO, name);
|
|
6919
7639
|
break;
|
|
6920
7640
|
case 'scale':
|
|
6921
|
-
this.scale = parseVec3(newValue);
|
|
7641
|
+
this.scale = parseVec3(newValue, new playcanvas.Vec3(100, 100, 100), name);
|
|
6922
7642
|
break;
|
|
6923
7643
|
case 'type':
|
|
6924
|
-
this.type = newValue;
|
|
7644
|
+
this.type = parseEnum(newValue, ['box', 'dome', 'infinite', 'none'], 'infinite', name);
|
|
6925
7645
|
break;
|
|
6926
7646
|
}
|
|
6927
7647
|
}
|
|
@@ -6957,4 +7677,5 @@ exports.ScrollbarComponentElement = ScrollbarComponentElement;
|
|
|
6957
7677
|
exports.SkyElement = SkyElement;
|
|
6958
7678
|
exports.SoundComponentElement = SoundComponentElement;
|
|
6959
7679
|
exports.SoundSlotElement = SoundSlotElement;
|
|
7680
|
+
exports.whenReady = whenReady;
|
|
6960
7681
|
//# sourceMappingURL=pwc.cjs.map
|