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