@playcanvas/web-components 0.16.0 → 0.18.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/README.md +5 -3
- package/dist/app.d.cts +18 -13
- package/dist/app.d.ts +18 -13
- package/dist/asset.d.cts +13 -3
- package/dist/asset.d.ts +13 -3
- package/dist/async-element.d.cts +13 -13
- package/dist/async-element.d.ts +13 -13
- package/dist/components/anim-clip.d.cts +4 -0
- package/dist/components/anim-clip.d.ts +4 -0
- package/dist/components/anim-component.d.cts +38 -5
- package/dist/components/anim-component.d.ts +38 -5
- package/dist/components/audio-listener-component.d.cts +26 -0
- package/dist/components/audio-listener-component.d.ts +26 -0
- package/dist/components/button-component.d.cts +10 -1
- package/dist/components/button-component.d.ts +10 -1
- package/dist/components/camera-component.d.cts +37 -1
- package/dist/components/camera-component.d.ts +37 -1
- package/dist/components/collision-component.d.cts +9 -1
- package/dist/components/collision-component.d.ts +9 -1
- package/dist/components/component.d.cts +3 -2
- package/dist/components/component.d.ts +3 -2
- package/dist/components/element-component.d.cts +11 -0
- package/dist/components/element-component.d.ts +11 -0
- package/dist/components/gsplat-component.d.cts +6 -0
- package/dist/components/gsplat-component.d.ts +6 -0
- package/dist/components/joint-component.d.cts +14 -2
- package/dist/components/joint-component.d.ts +14 -2
- package/dist/components/{layoutchild-component.d.cts → layout-child-component.d.cts} +7 -1
- package/dist/components/{layoutchild-component.d.ts → layout-child-component.d.ts} +7 -1
- package/dist/components/{layoutgroup-component.d.cts → layout-group-component.d.cts} +7 -1
- package/dist/components/{layoutgroup-component.d.ts → layout-group-component.d.ts} +7 -1
- package/dist/components/light-component.d.cts +52 -1
- package/dist/components/light-component.d.ts +52 -1
- package/dist/components/{particlesystem-component.d.cts → particle-system-component.d.cts} +7 -1
- package/dist/components/{particlesystem-component.d.ts → particle-system-component.d.ts} +7 -1
- package/dist/components/render-component.d.cts +7 -0
- package/dist/components/render-component.d.ts +7 -0
- package/dist/components/{rigidbody-component.d.cts → rigid-body-component.d.cts} +8 -1
- package/dist/components/{rigidbody-component.d.ts → rigid-body-component.d.ts} +8 -1
- package/dist/components/screen-component.d.cts +6 -0
- package/dist/components/screen-component.d.ts +6 -0
- package/dist/components/script-component.d.cts +26 -12
- package/dist/components/script-component.d.ts +26 -12
- package/dist/components/{script.d.cts → script-instance.d.cts} +11 -7
- package/dist/components/{script.d.ts → script-instance.d.ts} +11 -7
- package/dist/components/{scrollview-component.d.cts → scroll-view-component.d.cts} +7 -1
- package/dist/components/{scrollview-component.d.ts → scroll-view-component.d.ts} +7 -1
- package/dist/components/scrollbar-component.d.cts +6 -0
- package/dist/components/scrollbar-component.d.ts +6 -0
- package/dist/components/sound-component.d.cts +7 -1
- package/dist/components/sound-component.d.ts +7 -1
- package/dist/components/sound-slot.d.cts +5 -2
- package/dist/components/sound-slot.d.ts +5 -2
- package/dist/custom-elements.json +1852 -974
- package/dist/entity-base.d.cts +7 -6
- package/dist/entity-base.d.ts +7 -6
- package/dist/entity-owner.d.cts +118 -0
- package/dist/entity-owner.d.ts +118 -0
- package/dist/entity.d.cts +14 -102
- package/dist/entity.d.ts +14 -102
- package/dist/index.d.cts +26 -22
- package/dist/index.d.ts +26 -22
- package/dist/material.d.cts +18 -7
- package/dist/material.d.ts +18 -7
- package/dist/model.d.cts +72 -24
- package/dist/model.d.ts +72 -24
- package/dist/node.d.cts +6 -1
- package/dist/node.d.ts +6 -1
- package/dist/pwc.cjs +807 -293
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +807 -293
- 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.min.mjs +1 -1
- package/dist/pwc.min.mjs.map +1 -1
- package/dist/pwc.mjs +805 -292
- package/dist/pwc.mjs.map +1 -1
- package/dist/scene.d.cts +3 -0
- package/dist/scene.d.ts +3 -0
- package/dist/sky.d.cts +4 -0
- package/dist/sky.d.ts +4 -0
- package/dist/vscode.html-custom-data.json +351 -121
- package/dist/{module.d.cts → wasm.d.cts} +10 -6
- package/dist/{module.d.ts → wasm.d.ts} +10 -6
- package/dist/web-types.json +465 -291
- package/package.json +7 -7
- package/src/app.ts +49 -35
- package/src/asset.ts +13 -4
- package/src/async-element.ts +14 -14
- package/src/components/anim-clip.ts +4 -0
- package/src/components/anim-component.ts +89 -14
- package/src/components/audio-listener-component.ts +36 -0
- package/src/components/button-component.ts +10 -1
- package/src/components/camera-component.ts +65 -6
- package/src/components/collision-component.ts +9 -1
- package/src/components/component.ts +4 -3
- package/src/components/element-component.ts +11 -0
- package/src/components/gsplat-component.ts +6 -0
- package/src/components/joint-component.ts +14 -2
- package/src/components/{layoutchild-component.ts → layout-child-component.ts} +8 -2
- package/src/components/{layoutgroup-component.ts → layout-group-component.ts} +8 -2
- package/src/components/light-component.ts +110 -17
- package/src/components/{particlesystem-component.ts → particle-system-component.ts} +8 -2
- package/src/components/render-component.ts +7 -0
- package/src/components/{rigidbody-component.ts → rigid-body-component.ts} +9 -2
- package/src/components/screen-component.ts +6 -0
- package/src/components/script-component.ts +66 -42
- package/src/components/{script.ts → script-instance.ts} +18 -14
- package/src/components/{scrollview-component.ts → scroll-view-component.ts} +8 -2
- package/src/components/scrollbar-component.ts +6 -0
- package/src/components/sound-component.ts +8 -2
- package/src/components/sound-slot.ts +7 -4
- package/src/entity-base.ts +8 -7
- package/src/entity-owner.ts +298 -0
- package/src/entity.ts +21 -271
- package/src/index.ts +38 -25
- package/src/material.ts +19 -8
- package/src/model.ts +164 -72
- package/src/node.ts +15 -6
- package/src/scene.ts +3 -0
- package/src/sky.ts +4 -0
- package/src/{module.ts → wasm.ts} +12 -8
- package/dist/components/listener-component.d.cts +0 -20
- package/dist/components/listener-component.d.ts +0 -20
- package/src/components/listener-component.ts +0 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcanvas/web-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"author": "PlayCanvas <support@playcanvas.com>",
|
|
5
5
|
"homepage": "https://playcanvas.com",
|
|
6
6
|
"description": "Web Components for the PlayCanvas Engine",
|
|
@@ -91,25 +91,25 @@
|
|
|
91
91
|
"@rollup/plugin-terser": "1.0.0",
|
|
92
92
|
"@rollup/plugin-typescript": "12.3.0",
|
|
93
93
|
"@tweenjs/tween.js": "25.0.0",
|
|
94
|
-
"@vitest/coverage-v8": "4.1.
|
|
95
|
-
"concurrently": "10.0.
|
|
94
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
95
|
+
"concurrently": "10.0.5",
|
|
96
96
|
"custom-element-jet-brains-integration": "1.7.0",
|
|
97
97
|
"custom-element-vs-code-integration": "1.5.0",
|
|
98
98
|
"earcut": "3.2.3",
|
|
99
99
|
"eslint": "9.39.5",
|
|
100
100
|
"globals": "17.11.0",
|
|
101
101
|
"jsdom": "30.0.1",
|
|
102
|
-
"mediabunny": "1.
|
|
102
|
+
"mediabunny": "1.55.2",
|
|
103
103
|
"opentype.js": "2.0.0",
|
|
104
104
|
"playcanvas": "2.21.4",
|
|
105
105
|
"prettier": "3.9.6",
|
|
106
|
-
"publint": "0.3.
|
|
107
|
-
"rollup": "4.62.
|
|
106
|
+
"publint": "0.3.24",
|
|
107
|
+
"rollup": "4.62.5",
|
|
108
108
|
"serve": "14.2.6",
|
|
109
109
|
"tslib": "2.8.1",
|
|
110
110
|
"typedoc": "0.28.20",
|
|
111
111
|
"typedoc-plugin-mdn-links": "5.1.1",
|
|
112
112
|
"typescript": "6.0.3",
|
|
113
|
-
"vitest": "4.1.
|
|
113
|
+
"vitest": "4.1.11"
|
|
114
114
|
}
|
|
115
115
|
}
|
package/src/app.ts
CHANGED
|
@@ -66,16 +66,23 @@ import {
|
|
|
66
66
|
|
|
67
67
|
import type { AssetElement } from './asset';
|
|
68
68
|
import { AsyncElement } from './async-element';
|
|
69
|
-
import type { EntityElement } from './entity';
|
|
70
69
|
import type { EntityBaseElement } from './entity-base';
|
|
70
|
+
import type { EntityOwnerElement } from './entity-owner';
|
|
71
71
|
import { LoadingBar } from './loading-bar';
|
|
72
72
|
import type { MaterialElement } from './material';
|
|
73
|
-
import type { ModuleElement } from './module';
|
|
74
73
|
import { parseBool, parseEnum, parseNumber } from './parse';
|
|
74
|
+
import type { WasmElement } from './wasm';
|
|
75
75
|
|
|
76
76
|
/** The pointer event types the application synthesizes on `<pc-entity>` elements via picking. */
|
|
77
77
|
const pointerEventTypes = ['pointermove', 'pointerdown', 'pointerup', 'pointerenter', 'pointerleave'] as const;
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* The event types whose listeners make an element a hover target. Hover resolution walks past
|
|
81
|
+
* elements listening for none of them, so a silent element never swallows an ancestor's
|
|
82
|
+
* enter/leave pair.
|
|
83
|
+
*/
|
|
84
|
+
const hoverEventTypes = ['pointerenter', 'pointerleave', 'pointermove'] as const;
|
|
85
|
+
|
|
79
86
|
/**
|
|
80
87
|
* Gives `pc-app` the sizing contract of a replaced element (`<video>`, `<img>`): a block-level
|
|
81
88
|
* box that the page's CSS sizes, defaulting to the canvas's own 300x150 intrinsic size, with the
|
|
@@ -105,6 +112,10 @@ const ensureBaseStyles = () => {
|
|
|
105
112
|
* tracked live via a ResizeObserver — so the element can be embedded at any size, resized by
|
|
106
113
|
* its container, or made fullscreen with ordinary CSS such as `width: 100vw; height: 100dvh`.
|
|
107
114
|
*
|
|
115
|
+
* @elementSummary The `<pc-app>` element creates a PlayCanvas application and the canvas it renders
|
|
116
|
+
* into, and is the root of every scene. It holds the `<pc-asset>`, `<pc-material>`, `<pc-wasm>` and
|
|
117
|
+
* `<pc-scene>` elements, and the page's CSS sizes it, as it would a `<video>`.
|
|
118
|
+
*
|
|
108
119
|
* @fires {ProgressEvent} progress - Fired while the application preloads its assets. `loaded` and
|
|
109
120
|
* `total` are asset counts, not bytes, and an asset that fails to load still counts as loaded.
|
|
110
121
|
* Fired at least once per boot, and the final event always has `loaded` equal to `total`. Does
|
|
@@ -163,9 +174,9 @@ class AppElement extends AsyncElement {
|
|
|
163
174
|
|
|
164
175
|
/**
|
|
165
176
|
* The elements backing this application's entities, keyed by the entity itself. Registered
|
|
166
|
-
* by
|
|
167
|
-
*
|
|
168
|
-
* identity - never by name.
|
|
177
|
+
* by entity-owning elements at creation (pc-entity, and pc-model for its host) and by
|
|
178
|
+
* NodeElement at binding, and removed when an entity is destroyed or unbound, this joins
|
|
179
|
+
* engine scene nodes back to their owning elements by identity - never by name.
|
|
169
180
|
*/
|
|
170
181
|
private _entityElements = new Map<GraphNode, EntityBaseElement>();
|
|
171
182
|
|
|
@@ -260,7 +271,7 @@ class AppElement extends AsyncElement {
|
|
|
260
271
|
// there, leaving the element permanently unready: no canvas, no entities, no application.
|
|
261
272
|
//
|
|
262
273
|
// Upgrading is the fix here rather than skipping whatever has not upgraded, because a
|
|
263
|
-
// <pc-
|
|
274
|
+
// <pc-wasm> is the one child that nothing else ever builds on its own behalf - skipping
|
|
264
275
|
// it would drop the wasm module the app asked for, silently and only for cloned apps.
|
|
265
276
|
// Upgrading runs each descendant's connectedCallback synchronously, a few lines earlier
|
|
266
277
|
// than the parser's path runs them but into the same state they see there: no application
|
|
@@ -270,11 +281,11 @@ class AppElement extends AsyncElement {
|
|
|
270
281
|
// is left completely untouched.
|
|
271
282
|
customElements.upgrade(this);
|
|
272
283
|
|
|
273
|
-
// Get all pc-
|
|
274
|
-
const
|
|
284
|
+
// Get all pc-wasm elements that are direct children of the pc-app element
|
|
285
|
+
const wasmElements = this.querySelectorAll<WasmElement>(':scope > pc-wasm');
|
|
275
286
|
|
|
276
287
|
// Wait for all modules to load
|
|
277
|
-
await Promise.all(Array.from(
|
|
288
|
+
await Promise.all(Array.from(wasmElements).map((element) => element._getLoadPromise()));
|
|
278
289
|
|
|
279
290
|
// The element may have been removed while the modules loaded. Nothing beyond the loading
|
|
280
291
|
// bar exists yet, and disconnectedCallback has already destroyed that.
|
|
@@ -462,15 +473,16 @@ class AppElement extends AsyncElement {
|
|
|
462
473
|
materialElement._createMaterial();
|
|
463
474
|
});
|
|
464
475
|
|
|
465
|
-
// Create all entities
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
476
|
+
// Create all entities. pc-model joins the sweep because it owns a host entity of its
|
|
477
|
+
// own; its instantiated content arrives later, beneath that host.
|
|
478
|
+
const ownerElements = this.querySelectorAll<EntityOwnerElement>('pc-entity, pc-model');
|
|
479
|
+
Array.from(ownerElements).forEach((ownerElement) => {
|
|
480
|
+
ownerElement._createEntity(app);
|
|
469
481
|
});
|
|
470
482
|
|
|
471
483
|
// Build hierarchy
|
|
472
|
-
|
|
473
|
-
|
|
484
|
+
ownerElements.forEach((ownerElement) => {
|
|
485
|
+
ownerElement._buildHierarchy(app);
|
|
474
486
|
});
|
|
475
487
|
|
|
476
488
|
// Building the hierarchy dispatched each entity's ready event synchronously, and a
|
|
@@ -602,9 +614,9 @@ class AppElement extends AsyncElement {
|
|
|
602
614
|
// created from onpointer* attributes when their elements were first upgraded, or
|
|
603
615
|
// listeners carried over from before a re-boot)
|
|
604
616
|
pointerEventTypes.forEach((type) => {
|
|
605
|
-
const anyListeners = Array.from(
|
|
606
|
-
(entity
|
|
607
|
-
);
|
|
617
|
+
const anyListeners = Array.from(
|
|
618
|
+
this.querySelectorAll<EntityBaseElement>('pc-entity, pc-model, pc-node')
|
|
619
|
+
).some((entity) => entity._hasListeners(type));
|
|
608
620
|
if (anyListeners) {
|
|
609
621
|
this._onPointerListenerAdded(type);
|
|
610
622
|
}
|
|
@@ -659,10 +671,10 @@ class AppElement extends AsyncElement {
|
|
|
659
671
|
}
|
|
660
672
|
|
|
661
673
|
/**
|
|
662
|
-
* Returns the `<pc-entity>` or `<pc-node>` element whose backing entity is
|
|
663
|
-
* `null` if the entity is not fronted by an element of this application - for
|
|
664
|
-
* unbound node inside a model's instantiated hierarchy, or an entity created
|
|
665
|
-
* engine API.
|
|
674
|
+
* Returns the `<pc-entity>`, `<pc-model>` or `<pc-node>` element whose backing entity is
|
|
675
|
+
* `entity`, or `null` if the entity is not fronted by an element of this application - for
|
|
676
|
+
* example, an unbound node inside a model's instantiated hierarchy, or an entity created
|
|
677
|
+
* through the engine API.
|
|
666
678
|
*
|
|
667
679
|
* @param entity - The entity to look up.
|
|
668
680
|
* @returns The element fronting the entity, or `null`.
|
|
@@ -672,18 +684,19 @@ class AppElement extends AsyncElement {
|
|
|
672
684
|
}
|
|
673
685
|
|
|
674
686
|
/**
|
|
675
|
-
* Resolves the element that owns a picked node: the nearest node up the parent
|
|
676
|
-
* starting with the node itself -
|
|
677
|
-
*
|
|
678
|
-
*
|
|
687
|
+
* Resolves the element that owns hover for a picked node: the nearest node up the parent
|
|
688
|
+
* chain - starting with the node itself - whose element listens for any of the hover event
|
|
689
|
+
* types. Skipping silent elements matches {@link _elementWithListener}, so a registered
|
|
690
|
+
* element with no hover listeners (a `<pc-model>` host, a plain child entity) is transparent
|
|
691
|
+
* to hover rather than swallowing a listening ancestor's enter/leave pair.
|
|
679
692
|
*
|
|
680
693
|
* @param node - The picked node, or `null`.
|
|
681
|
-
* @returns The owning element, or `null`.
|
|
694
|
+
* @returns The hover-owning element, or `null`.
|
|
682
695
|
*/
|
|
683
|
-
private
|
|
696
|
+
private _hoverTarget(node: GraphNode | null): EntityBaseElement | null {
|
|
684
697
|
while (node !== null) {
|
|
685
698
|
const element = this._entityElements.get(node);
|
|
686
|
-
if (element) {
|
|
699
|
+
if (element && hoverEventTypes.some((type) => element._hasListeners(type))) {
|
|
687
700
|
return element;
|
|
688
701
|
}
|
|
689
702
|
node = node.parent;
|
|
@@ -831,9 +844,10 @@ class AppElement extends AsyncElement {
|
|
|
831
844
|
const node = await this._pickNode(event);
|
|
832
845
|
if (token !== this._pickToken || !this._picker) return;
|
|
833
846
|
|
|
834
|
-
// The hovered element is the nearest one up the node's parent chain
|
|
835
|
-
//
|
|
836
|
-
|
|
847
|
+
// The hovered element is the nearest one up the node's parent chain with a hover
|
|
848
|
+
// listener - the nearest-listener rule down/up use. Dispatch is still gated per event
|
|
849
|
+
// type below: having any hover listener selects the target, each event needs its own.
|
|
850
|
+
const newHoverEntity = this._hoverTarget(node);
|
|
837
851
|
|
|
838
852
|
// Handle enter/leave events
|
|
839
853
|
if (this._hoveredEntity !== newHoverEntity) {
|
|
@@ -898,9 +912,9 @@ class AppElement extends AsyncElement {
|
|
|
898
912
|
}
|
|
899
913
|
|
|
900
914
|
private _onPointerListenerRemoved(type: string) {
|
|
901
|
-
const hasListeners = Array.from(
|
|
902
|
-
|
|
903
|
-
);
|
|
915
|
+
const hasListeners = Array.from(
|
|
916
|
+
this.querySelectorAll<EntityBaseElement>('pc-entity, pc-model, pc-node')
|
|
917
|
+
).some((entity) => entity._hasListeners(type));
|
|
904
918
|
|
|
905
919
|
if (!hasListeners && this._canvas) {
|
|
906
920
|
this._hasPointerListeners[type] = false;
|
package/src/asset.ts
CHANGED
|
@@ -26,7 +26,8 @@ const renderModes = new Map<'simple' | 'sliced' | 'tiled', number>([
|
|
|
26
26
|
['tiled', SPRITE_RENDERMODE_TILED]
|
|
27
27
|
]);
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
/** The addressing modes for a texture asset. */
|
|
30
|
+
export type AddressMode = 'repeat' | 'clamp' | 'mirror';
|
|
30
31
|
|
|
31
32
|
const addressModes = new Map<AddressMode, number>([
|
|
32
33
|
['repeat', ADDRESS_REPEAT],
|
|
@@ -34,7 +35,8 @@ const addressModes = new Map<AddressMode, number>([
|
|
|
34
35
|
['mirror', ADDRESS_MIRRORED_REPEAT]
|
|
35
36
|
]);
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
/** The minification filter modes for a texture asset. */
|
|
39
|
+
export type MinFilterMode =
|
|
38
40
|
'nearest' | 'linear' | 'nearest-mip-nearest' | 'linear-mip-nearest' | 'nearest-mip-linear' | 'linear-mip-linear';
|
|
39
41
|
|
|
40
42
|
const minFilterModes = new Map<MinFilterMode, number>([
|
|
@@ -46,8 +48,11 @@ const minFilterModes = new Map<MinFilterMode, number>([
|
|
|
46
48
|
['linear-mip-linear', FILTER_LINEAR_MIPMAP_LINEAR]
|
|
47
49
|
]);
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
/**
|
|
52
|
+
* The magnification filter modes for a texture asset. Magnification has no mip variants - the
|
|
53
|
+
* engine (and the GPU) only accepts these two.
|
|
54
|
+
*/
|
|
55
|
+
export type MagFilterMode = 'nearest' | 'linear';
|
|
51
56
|
|
|
52
57
|
const magFilterModes = new Map<MagFilterMode, number>([
|
|
53
58
|
['nearest', FILTER_NEAREST],
|
|
@@ -168,6 +173,10 @@ const processBufferView = (
|
|
|
168
173
|
* Apart from `lazy` and the texture options, these attributes are read once when the asset is
|
|
169
174
|
* created, so changing them later has no effect.
|
|
170
175
|
*
|
|
176
|
+
* @elementSummary The `<pc-asset>` element declares an asset for the application to load — a model,
|
|
177
|
+
* texture, font, sound, script or JSON file — under an `id` that other elements reference. Must be
|
|
178
|
+
* a direct child of `<pc-app>`.
|
|
179
|
+
*
|
|
171
180
|
* @attribute {string} id - The identifier used to reference the asset from other elements.
|
|
172
181
|
* @attribute {string} src - The URL of the asset to load.
|
|
173
182
|
* @attribute {string} type - The asset type. Inferred from the `src` file extension when omitted.
|
package/src/async-element.ts
CHANGED
|
@@ -33,13 +33,13 @@ class AsyncElement extends HTMLElement {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* The nearest ancestor element that fronts an entity — `<pc-entity
|
|
37
|
-
* `null` if this element has no such ancestor. The search starts at the
|
|
38
|
-
* never resolves to itself.
|
|
36
|
+
* The nearest ancestor element that fronts an entity — `<pc-entity>`, `<pc-model>` or
|
|
37
|
+
* `<pc-node>` — or `null` if this element has no such ancestor. The search starts at the
|
|
38
|
+
* parent, so an element never resolves to itself.
|
|
39
39
|
* @returns The closest entity-fronting element, or `null`.
|
|
40
40
|
*/
|
|
41
41
|
get closestEntity(): EntityBaseElement | null {
|
|
42
|
-
return (this.parentElement?.closest('pc-entity, pc-node') as EntityBaseElement | null) ?? null;
|
|
42
|
+
return (this.parentElement?.closest('pc-entity, pc-model, pc-node') as EntityBaseElement | null) ?? null;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
@@ -95,11 +95,11 @@ type AsyncElementTagName = {
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* Waits for the first element matching the given tag name to be fully initialized. Note that the
|
|
98
|
-
* promise never settles if the element cannot finish initializing (for example, a `<pc-script>`
|
|
99
|
-
* that is not a direct child of `<pc-
|
|
100
|
-
* device — listen for its `error` event instead). A component element outside
|
|
101
|
-
* the exception: it still becomes ready, but its `component` is `null`. Either way, a
|
|
102
|
-
* element logs a warning naming the parent it requires.
|
|
98
|
+
* promise never settles if the element cannot finish initializing (for example, a `<pc-script-instance>`
|
|
99
|
+
* that is not a direct child of `<pc-script>`, or a `<pc-app>` that could not create a graphics
|
|
100
|
+
* device — listen for its `error` event instead). A component element outside an entity-fronting
|
|
101
|
+
* element is the exception: it still becomes ready, but its `component` is `null`. Either way, a
|
|
102
|
+
* misplaced element logs a warning naming the parent it requires.
|
|
103
103
|
* @param target - The tag name of the element to wait for (e.g. `'pc-app'`).
|
|
104
104
|
* @returns A promise that resolves with the element once it's ready.
|
|
105
105
|
* @example
|
|
@@ -120,11 +120,11 @@ function whenReady<K extends AsyncElementTagName>(target: K): Promise<HTMLElemen
|
|
|
120
120
|
function whenReady<T extends AsyncElement>(target: T): Promise<T>;
|
|
121
121
|
/**
|
|
122
122
|
* Waits for the first element matching the given CSS selector to be fully initialized. Note that
|
|
123
|
-
* the promise never settles if the element cannot finish initializing (for example, a `<pc-script>`
|
|
124
|
-
* that is not a direct child of `<pc-
|
|
125
|
-
* device — listen for its `error` event instead). A component element outside
|
|
126
|
-
* the exception: it still becomes ready, but its `component` is `null`. Either way, a
|
|
127
|
-
* element logs a warning naming the parent it requires.
|
|
123
|
+
* the promise never settles if the element cannot finish initializing (for example, a `<pc-script-instance>`
|
|
124
|
+
* that is not a direct child of `<pc-script>`, or a `<pc-app>` that could not create a graphics
|
|
125
|
+
* device — listen for its `error` event instead). A component element outside an entity-fronting
|
|
126
|
+
* element is the exception: it still becomes ready, but its `component` is `null`. Either way, a
|
|
127
|
+
* misplaced element logs a warning naming the parent it requires.
|
|
128
128
|
* @param target - A CSS selector matching the element to wait for (e.g. `'#my-app'`).
|
|
129
129
|
* @returns A promise that resolves with the element once it's ready.
|
|
130
130
|
* @example
|
|
@@ -22,6 +22,10 @@ import { AnimComponentElement } from './anim-component';
|
|
|
22
22
|
* named; in a multi-track source the track named `name` is chosen, falling back to the first
|
|
23
23
|
* with a warning. The element becomes ready once its resolved track is assigned.
|
|
24
24
|
*
|
|
25
|
+
* @elementSummary The `<pc-anim-clip>` element declares one named animation clip on its parent
|
|
26
|
+
* `<pc-anim>`, taken from the `asset` it names or, without one, from the enclosing `<pc-model>`'s
|
|
27
|
+
* own animations. Must be a direct child of `<pc-anim>`.
|
|
28
|
+
*
|
|
25
29
|
* @category Components
|
|
26
30
|
*/
|
|
27
31
|
class AnimClipElement extends AsyncElement {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnimComponent, Asset, ContainerResource } from 'playcanvas';
|
|
1
|
+
import type { AnimComponent, Asset, ContainerResource, Entity } from 'playcanvas';
|
|
2
2
|
import { ANIM_CONTROL_STATES, AnimTrack } from 'playcanvas';
|
|
3
3
|
|
|
4
4
|
import { AssetElement } from '../asset';
|
|
@@ -46,6 +46,13 @@ type PlaybackState = {
|
|
|
46
46
|
* the underlying {@link AnimComponent} (via {@link component}) for playback state beyond what
|
|
47
47
|
* this element exposes.
|
|
48
48
|
*
|
|
49
|
+
* Engine component: {@link AnimComponent} (`anim`).
|
|
50
|
+
*
|
|
51
|
+
* @elementSummary The `<pc-anim>` element plays animation clips over its entity's hierarchy, taken
|
|
52
|
+
* from `<pc-anim-clip>` children or from the enclosing `<pc-model>`'s own animations. The first
|
|
53
|
+
* clip plays automatically, and the `clip` attribute switches between them. Must be a child of a
|
|
54
|
+
* `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
55
|
+
*
|
|
49
56
|
* @category Components
|
|
50
57
|
*/
|
|
51
58
|
class AnimComponentElement extends ComponentElement {
|
|
@@ -71,6 +78,12 @@ class AnimComponentElement extends ComponentElement {
|
|
|
71
78
|
*/
|
|
72
79
|
private _clip = '';
|
|
73
80
|
|
|
81
|
+
/**
|
|
82
|
+
* The binding root this element last assigned through {@link _applyRootBone}, distinguishing
|
|
83
|
+
* its own writes from a `rootBone` assigned through the engine API — which is left alone.
|
|
84
|
+
*/
|
|
85
|
+
private _managedRootBone: Entity | null = null;
|
|
86
|
+
|
|
74
87
|
/**
|
|
75
88
|
* The element the model-readiness listener is attached to, held so disconnection can detach
|
|
76
89
|
* it after `closestEntity` no longer resolves.
|
|
@@ -101,28 +114,76 @@ class AnimComponentElement extends ComponentElement {
|
|
|
101
114
|
private _warnedClip: string | null = null;
|
|
102
115
|
|
|
103
116
|
/**
|
|
104
|
-
* Rebinds when a model
|
|
105
|
-
*
|
|
117
|
+
* Rebinds when a model that is not this component's host announces readiness — a sibling or
|
|
118
|
+
* deeper model whose content changed under the assigned clips. The engine resolves each
|
|
119
|
+
* curve once, at the first tick after assignment, and never retries — and its mesh-instance
|
|
106
120
|
* broadcast fires before an instantiated hierarchy is parented, so a model that loads after
|
|
107
|
-
* the clips were assigned would otherwise stay silently unbound.
|
|
108
|
-
*
|
|
109
|
-
*
|
|
121
|
+
* the clips were assigned would otherwise stay silently unbound. The host model's own cycles
|
|
122
|
+
* are excluded: those re-enter through {@link initComponent}, which refreshes the whole clip
|
|
123
|
+
* set against the new container.
|
|
110
124
|
*/
|
|
111
125
|
private _onModelReady = (event: Event) => {
|
|
112
126
|
if (!(event.target instanceof ModelElement) || !this.component) {
|
|
113
127
|
return;
|
|
114
128
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
129
|
+
// The host model's own readiness cycle already re-initialized this component through
|
|
130
|
+
// ComponentElement's host-ready listener, which is attached first and so has run by now.
|
|
131
|
+
// Acting here too would resolve every track twice and capture the playhead-restore
|
|
132
|
+
// snapshot mid-rebuild.
|
|
133
|
+
if (event.target === this._modelListenerTarget) {
|
|
134
|
+
return;
|
|
122
135
|
}
|
|
136
|
+
// A model cycle can replace the skeleton source's host entity (a rebuild under a
|
|
137
|
+
// retargeting pc-node), so the binding root is re-asserted before the rebind.
|
|
138
|
+
this._applyRootBone();
|
|
123
139
|
this.component.rebind();
|
|
124
140
|
};
|
|
125
141
|
|
|
142
|
+
/**
|
|
143
|
+
* The model whose host entity scopes this component's curve binding: the parent `pc-model`,
|
|
144
|
+
* or the sole `pc-model` among the parent's direct children (the arrangement where clips
|
|
145
|
+
* live in a library asset beside the skeleton). `null` when there is no such model, or more
|
|
146
|
+
* than one — an ambiguous skeleton is left to the engine's name-based resolution.
|
|
147
|
+
*/
|
|
148
|
+
private _skeletonSource(): ModelElement | null {
|
|
149
|
+
const parent = this.parentElement;
|
|
150
|
+
if (parent instanceof ModelElement) {
|
|
151
|
+
return parent;
|
|
152
|
+
}
|
|
153
|
+
const models = parent ? parent.querySelectorAll(':scope > pc-model') : null;
|
|
154
|
+
return models?.length === 1 && models[0] instanceof ModelElement ? models[0] : null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Keeps the component's binding root pointing at the skeleton source's host entity. The host
|
|
159
|
+
* wraps the instantiated content, so left at its default — the component's own entity — the
|
|
160
|
+
* engine binder mis-resolves curves that target the asset's root node: its fallback treats
|
|
161
|
+
* the graph as the asset root once the root is no longer a direct child.
|
|
162
|
+
*
|
|
163
|
+
* Authoritative in both directions for values this element assigned: a source appearing pins
|
|
164
|
+
* its host, and a source dissolving (the model gone, or a second model making the skeleton
|
|
165
|
+
* ambiguous) clears the pin rather than leaving it on a stale host. A root assigned through
|
|
166
|
+
* the engine API is never overwritten — the user's choice outranks the managed default.
|
|
167
|
+
* Writes are skipped while unchanged, because the engine setter itself triggers a rebind.
|
|
168
|
+
*/
|
|
169
|
+
private _applyRootBone() {
|
|
170
|
+
const component = this.component;
|
|
171
|
+
|
|
172
|
+
// A non-null root this element did not assign came through the engine API. A fresh
|
|
173
|
+
// component starts at null, which is always reclaimable.
|
|
174
|
+
if (component.rootBone !== null && component.rootBone !== this._managedRootBone) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const host = this._skeletonSource()?.entity ?? null;
|
|
179
|
+
if (component.rootBone !== host) {
|
|
180
|
+
// The engine setter accepts null - restoring the component's own entity as the
|
|
181
|
+
// binding graph - but its declared type does not
|
|
182
|
+
component.rootBone = host as Entity;
|
|
183
|
+
}
|
|
184
|
+
this._managedRootBone = host;
|
|
185
|
+
}
|
|
186
|
+
|
|
126
187
|
/** @ignore */
|
|
127
188
|
constructor() {
|
|
128
189
|
super('anim');
|
|
@@ -151,7 +212,16 @@ class AnimComponentElement extends ComponentElement {
|
|
|
151
212
|
this._modelListenerTarget = host;
|
|
152
213
|
}
|
|
153
214
|
|
|
154
|
-
this.
|
|
215
|
+
this._applyRootBone();
|
|
216
|
+
|
|
217
|
+
if (this.component.baseLayer) {
|
|
218
|
+
// The component survived the host's readiness cycle (a pc-model reloading content on
|
|
219
|
+
// its stable host entity). A loaded graph cannot be reassigned in place, so drop it
|
|
220
|
+
// and reassign from the current source, restoring the active clip and playhead.
|
|
221
|
+
this._refreshClips();
|
|
222
|
+
} else {
|
|
223
|
+
this._applyClips();
|
|
224
|
+
}
|
|
155
225
|
}
|
|
156
226
|
|
|
157
227
|
disconnectedCallback() {
|
|
@@ -163,6 +233,7 @@ class AnimComponentElement extends ComponentElement {
|
|
|
163
233
|
this._sourceGeneration++;
|
|
164
234
|
this._assignedClips.clear();
|
|
165
235
|
this._autoAssigned = false;
|
|
236
|
+
this._managedRootBone = null;
|
|
166
237
|
|
|
167
238
|
super.disconnectedCallback();
|
|
168
239
|
}
|
|
@@ -353,6 +424,10 @@ class AnimComponentElement extends ComponentElement {
|
|
|
353
424
|
if (!component) {
|
|
354
425
|
return;
|
|
355
426
|
}
|
|
427
|
+
// A clip-set change is also a chance for the skeleton source to have changed shape (a
|
|
428
|
+
// clip child appearing or leaving can accompany a model coming or going) - re-derive the
|
|
429
|
+
// binding root before the reassignment binds against it.
|
|
430
|
+
this._applyRootBone();
|
|
356
431
|
const layer = component.baseLayer;
|
|
357
432
|
const restore = layer ? {
|
|
358
433
|
state: layer.activeState,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AudioListenerComponent } from 'playcanvas';
|
|
2
|
+
|
|
3
|
+
import { ComponentElement } from './component';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The AudioListenerComponentElement interface provides properties and methods for manipulating
|
|
7
|
+
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-audio-listener/ | `<pc-audio-listener>`} elements.
|
|
8
|
+
* The AudioListenerComponentElement interface also inherits the properties and methods of the
|
|
9
|
+
* {@link HTMLElement} interface.
|
|
10
|
+
*
|
|
11
|
+
* Engine component: {@link AudioListenerComponent} (`audiolistener`).
|
|
12
|
+
*
|
|
13
|
+
* @elementSummary The `<pc-audio-listener>` element makes its entity the point from which
|
|
14
|
+
* positional sounds are heard, typically the entity holding the `<pc-camera>`. Must be a child of a
|
|
15
|
+
* `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
16
|
+
*
|
|
17
|
+
* @category Components
|
|
18
|
+
*/
|
|
19
|
+
class AudioListenerComponentElement extends ComponentElement {
|
|
20
|
+
/** @ignore */
|
|
21
|
+
constructor() {
|
|
22
|
+
super('audiolistener');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Gets the underlying PlayCanvas audio listener component.
|
|
27
|
+
* @returns The audio listener component.
|
|
28
|
+
*/
|
|
29
|
+
get component(): AudioListenerComponent {
|
|
30
|
+
return super.component as AudioListenerComponent;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
customElements.define('pc-audio-listener', AudioListenerComponentElement);
|
|
35
|
+
|
|
36
|
+
export { AudioListenerComponentElement };
|
|
@@ -17,6 +17,13 @@ const transitionModes = new Map<'tint' | 'sprite', number>([
|
|
|
17
17
|
* The ButtonComponentElement interface also inherits the properties and methods of the
|
|
18
18
|
* {@link HTMLElement} interface.
|
|
19
19
|
*
|
|
20
|
+
* Engine component: {@link ButtonComponent} (`button`).
|
|
21
|
+
*
|
|
22
|
+
* @elementSummary The `<pc-button>` element makes its entity respond to pointer input, tinting or
|
|
23
|
+
* swapping its image as the pointer hovers, presses and releases it. The entity also needs a
|
|
24
|
+
* `<pc-element>` with `use-input` set. Must be a child of a `<pc-entity>`, `<pc-model>` or
|
|
25
|
+
* `<pc-node>`.
|
|
26
|
+
*
|
|
20
27
|
* @category Components
|
|
21
28
|
*/
|
|
22
29
|
class ButtonComponentElement extends ComponentElement {
|
|
@@ -121,7 +128,9 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
121
128
|
|
|
122
129
|
/**
|
|
123
130
|
* Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` whose image
|
|
124
|
-
* element is used for visual transitions. Defaults to the button's own entity
|
|
131
|
+
* element is used for visual transitions. Defaults to the button's own entity — inside a
|
|
132
|
+
* `<pc-model>`, that is the model's host entity, so supply an explicit reference to target a
|
|
133
|
+
* UI entity instead.
|
|
125
134
|
* @param value - The image entity reference.
|
|
126
135
|
*/
|
|
127
136
|
set image(value: string) {
|