@playcanvas/web-components 0.1.0 → 0.1.2

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/src/asset.ts ADDED
@@ -0,0 +1,99 @@
1
+ import { Asset } from 'playcanvas';
2
+
3
+ const extToType = new Map([
4
+ ['bin', 'binary'],
5
+ ['css', 'css'],
6
+ ['frag', 'shader'],
7
+ ['glb', 'container'],
8
+ ['glsl', 'shader'],
9
+ ['html', 'html'],
10
+ ['jpg', 'texture'],
11
+ ['js', 'script'],
12
+ ['json', 'json'],
13
+ ['mp3', 'audio'],
14
+ ['mjs', 'script'],
15
+ ['ply', 'gsplat'],
16
+ ['png', 'texture'],
17
+ ['txt', 'text'],
18
+ ['vert', 'shader'],
19
+ ['webp', 'texture']
20
+ ]);
21
+
22
+ /**
23
+ * Loads an asset into the PlayCanvas engine.
24
+ */
25
+ class AssetElement extends HTMLElement {
26
+ private _preload: boolean = false;
27
+
28
+ /**
29
+ * The asset that is loaded.
30
+ */
31
+ asset: Asset | null = null;
32
+
33
+ async connectedCallback() {
34
+ }
35
+
36
+ disconnectedCallback() {
37
+ this.destroyAsset();
38
+ }
39
+
40
+ createAsset() {
41
+ const id = this.getAttribute('id') || '';
42
+ const src = this.getAttribute('src') || '';
43
+ let type = this.getAttribute('type');
44
+
45
+ // If no type is specified, try to infer it from the file extension.
46
+ if (!type) {
47
+ const ext = src.split('.').pop();
48
+ type = extToType.get(ext || '') ?? null;
49
+ }
50
+
51
+ if (!type) {
52
+ console.warn(`Unsupported asset type: ${src}`);
53
+ return;
54
+ }
55
+
56
+ this.asset = new Asset(id, type, { url: src });
57
+ this.asset.preload = this.preload;
58
+ }
59
+
60
+ destroyAsset() {
61
+ if (this.asset) {
62
+ this.asset.unload();
63
+ this.asset = null;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Sets the preload flag of the asset.
69
+ * @param value - The preload flag.
70
+ */
71
+ set preload(value: boolean) {
72
+ this._preload = value;
73
+ if (this.asset) {
74
+ this.asset.preload = value;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Gets the preload flag of the asset.
80
+ * @returns The preload flag.
81
+ */
82
+ get preload() {
83
+ return this._preload;
84
+ }
85
+
86
+ static get observedAttributes() {
87
+ return ['preload'];
88
+ }
89
+
90
+ attributeChangedCallback(name: string, _oldValue: string, _newValue: string) {
91
+ if (name === 'preload') {
92
+ this.preload = this.hasAttribute('preload');
93
+ }
94
+ }
95
+ }
96
+
97
+ customElements.define('pc-asset', AssetElement);
98
+
99
+ export { AssetElement };
@@ -0,0 +1,196 @@
1
+ import { PROJECTION_ORTHOGRAPHIC, PROJECTION_PERSPECTIVE, CameraComponent, Color } from 'playcanvas';
2
+
3
+ import { ComponentElement } from './component';
4
+ import { parseColor } from '../utils';
5
+
6
+ /**
7
+ * Represents a camera component in the PlayCanvas engine.
8
+ *
9
+ * @category Components
10
+ */
11
+ class CameraComponentElement extends ComponentElement {
12
+ private _clearColor = new Color(1, 1, 1, 1);
13
+
14
+ private _farClip = 1000;
15
+
16
+ private _fov = 45;
17
+
18
+ private _nearClip = 0.1;
19
+
20
+ private _orthographic = false;
21
+
22
+ private _orthoHeight = 10;
23
+
24
+ /**
25
+ * Creates a new CameraComponentElement.
26
+ */
27
+ constructor() {
28
+ super('camera');
29
+ }
30
+
31
+ getInitialComponentData() {
32
+ return {
33
+ clearColor: this._clearColor,
34
+ farClip: this._farClip,
35
+ fov: this._fov,
36
+ nearClip: this._nearClip,
37
+ projection: this._orthographic ? PROJECTION_ORTHOGRAPHIC : PROJECTION_PERSPECTIVE,
38
+ orthoHeight: this._orthoHeight
39
+ };
40
+ }
41
+
42
+ /**
43
+ * Gets the camera component.
44
+ * @returns The camera component.
45
+ */
46
+ get component(): CameraComponent | null {
47
+ return super.component as CameraComponent | null;
48
+ }
49
+
50
+ /**
51
+ * Sets the clear color of the camera.
52
+ * @param value - The clear color.
53
+ */
54
+ set clearColor(value) {
55
+ this._clearColor = value;
56
+ if (this.component) {
57
+ this.component.clearColor = value;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Gets the clear color of the camera.
63
+ * @returns The clear color.
64
+ */
65
+ get clearColor(): Color {
66
+ return this._clearColor;
67
+ }
68
+
69
+ /**
70
+ * Sets the far clip distance of the camera.
71
+ * @param value - The far clip distance.
72
+ */
73
+ set farClip(value: number) {
74
+ this._farClip = value;
75
+ if (this.component) {
76
+ this.component.farClip = value;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Gets the far clip distance of the camera.
82
+ * @returns The far clip distance.
83
+ */
84
+ get farClip(): number {
85
+ return this._farClip;
86
+ }
87
+
88
+ /**
89
+ * Sets the field of view of the camera.
90
+ * @param value - The field of view.
91
+ */
92
+ set fov(value: number) {
93
+ this._fov = value;
94
+ if (this.component) {
95
+ this.component.fov = value;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Gets the field of view of the camera.
101
+ * @returns The field of view.
102
+ */
103
+ get fov(): number {
104
+ return this._fov;
105
+ }
106
+
107
+ /**
108
+ * Sets the near clip distance of the camera.
109
+ * @param value - The near clip distance.
110
+ */
111
+ set nearClip(value: number) {
112
+ this._nearClip = value;
113
+ if (this.component) {
114
+ this.component.nearClip = value;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Gets the near clip distance of the camera.
120
+ * @returns The near clip distance.
121
+ */
122
+ get nearClip(): number {
123
+ return this._nearClip;
124
+ }
125
+
126
+ /**
127
+ * Sets the orthographic projection of the camera.
128
+ * @param value - The orthographic projection.
129
+ */
130
+ set orthographic(value) {
131
+ this._orthographic = value;
132
+ if (this.component) {
133
+ this.component.projection = value ? PROJECTION_ORTHOGRAPHIC : PROJECTION_PERSPECTIVE;
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Gets the orthographic projection of the camera.
139
+ * @returns The orthographic projection.
140
+ */
141
+ get orthographic(): boolean {
142
+ return this._orthographic;
143
+ }
144
+
145
+ /**
146
+ * Sets the orthographic height of the camera.
147
+ * @param value - The orthographic height.
148
+ */
149
+ set orthoHeight(value: number) {
150
+ this._orthoHeight = value;
151
+ if (this.component) {
152
+ this.component.orthoHeight = value;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Gets the orthographic height of the camera.
158
+ * @returns The orthographic height.
159
+ */
160
+ get orthoHeight() {
161
+ return this._orthoHeight;
162
+ }
163
+
164
+ static get observedAttributes() {
165
+ return [...super.observedAttributes, 'clear-color', 'near-clip', 'far-clip', 'fov', 'orthographic', 'ortho-height'];
166
+ }
167
+
168
+ attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
169
+ super.attributeChangedCallback(name, _oldValue, newValue);
170
+
171
+ switch (name) {
172
+ case 'clear-color':
173
+ this.clearColor = parseColor(newValue);
174
+ break;
175
+ case 'far-clip':
176
+ this.farClip = parseFloat(newValue);
177
+ break;
178
+ case 'fov':
179
+ this.fov = parseFloat(newValue);
180
+ break;
181
+ case 'near-clip':
182
+ this.nearClip = parseFloat(newValue);
183
+ break;
184
+ case 'orthographic':
185
+ this.orthographic = this.hasAttribute('orthographic');
186
+ break;
187
+ case 'ortho-height':
188
+ this.orthoHeight = parseFloat(newValue);
189
+ break;
190
+ }
191
+ }
192
+ }
193
+
194
+ customElements.define('pc-camera', CameraComponentElement);
195
+
196
+ export { CameraComponentElement };
@@ -0,0 +1,148 @@
1
+ import { CollisionComponent, Vec3 } from 'playcanvas';
2
+
3
+ import { ComponentElement } from './component';
4
+ import { parseVec3 } from '../utils';
5
+
6
+ /**
7
+ * Represents a collision component in the PlayCanvas engine.
8
+ *
9
+ * @category Components
10
+ */
11
+ class CollisionComponentElement extends ComponentElement {
12
+ private _axis: number = 1;
13
+
14
+ private _convexHull: boolean = false;
15
+
16
+ private _halfExtents: Vec3 = new Vec3(0.5, 0.5, 0.5);
17
+
18
+ private _height: number = 2;
19
+
20
+ private _radius: number = 0.5;
21
+
22
+ private _type: string = 'box';
23
+
24
+ /**
25
+ * Creates a new CollisionComponentElement.
26
+ */
27
+ constructor() {
28
+ super('collision');
29
+ }
30
+
31
+ getInitialComponentData() {
32
+ return {
33
+ axis: this._axis,
34
+ convexHull: this._convexHull,
35
+ halfExtents: this._halfExtents,
36
+ height: this._height,
37
+ radius: this._radius,
38
+ type: this._type
39
+ };
40
+ }
41
+
42
+ /**
43
+ * Gets the collision component.
44
+ * @returns The collision component.
45
+ */
46
+ get component(): CollisionComponent | null {
47
+ return super.component as CollisionComponent | null;
48
+ }
49
+
50
+ set axis(value: number) {
51
+ this._axis = value;
52
+ if (this.component) {
53
+ this.component.axis = value;
54
+ }
55
+ }
56
+
57
+ get axis() {
58
+ return this._axis;
59
+ }
60
+
61
+ set convexHull(value: boolean) {
62
+ this._convexHull = value;
63
+ if (this.component) {
64
+ this.component.convexHull = value;
65
+ }
66
+ }
67
+
68
+ get convexHull() {
69
+ return this._convexHull;
70
+ }
71
+
72
+ set halfExtents(value: Vec3) {
73
+ this._halfExtents = value;
74
+ if (this.component) {
75
+ this.component.halfExtents = value;
76
+ }
77
+ }
78
+
79
+ get halfExtents() {
80
+ return this._halfExtents;
81
+ }
82
+
83
+ set height(value: number) {
84
+ this._height = value;
85
+ if (this.component) {
86
+ this.component.height = value;
87
+ }
88
+ }
89
+
90
+ get height() {
91
+ return this._height;
92
+ }
93
+
94
+ set radius(value: number) {
95
+ this._radius = value;
96
+ if (this.component) {
97
+ this.component.radius = value;
98
+ }
99
+ }
100
+
101
+ get radius() {
102
+ return this._radius;
103
+ }
104
+
105
+ set type(value: string) {
106
+ this._type = value;
107
+ if (this.component) {
108
+ this.component.type = value;
109
+ }
110
+ }
111
+
112
+ get type() {
113
+ return this._type;
114
+ }
115
+
116
+ static get observedAttributes() {
117
+ return [...super.observedAttributes, 'axis', 'convex-hull', 'half-extents', 'height', 'radius', 'type'];
118
+ }
119
+
120
+ attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
121
+ super.attributeChangedCallback(name, _oldValue, newValue);
122
+
123
+ switch (name) {
124
+ case 'axis':
125
+ this.axis = parseInt(newValue, 10);
126
+ break;
127
+ case 'convex-hull':
128
+ this.convexHull = this.hasAttribute('convex-hull');
129
+ break;
130
+ case 'half-extents':
131
+ this.halfExtents = parseVec3(newValue);
132
+ break;
133
+ case 'height':
134
+ this.height = parseFloat(newValue);
135
+ break;
136
+ case 'radius':
137
+ this.radius = parseFloat(newValue);
138
+ break;
139
+ case 'type':
140
+ this.type = newValue;
141
+ break;
142
+ }
143
+ }
144
+ }
145
+
146
+ customElements.define('pc-collision', CollisionComponentElement);
147
+
148
+ export { CollisionComponentElement };
@@ -0,0 +1,106 @@
1
+ import { Component } from 'playcanvas';
2
+
3
+ import { AppElement } from '../app';
4
+ import { EntityElement } from '../entity';
5
+
6
+ /**
7
+ * Represents a component in the PlayCanvas engine.
8
+ *
9
+ * @category Components
10
+ */
11
+ class ComponentElement extends HTMLElement {
12
+ private _componentName: string;
13
+
14
+ private _enabled = true;
15
+
16
+ private _component: Component | null = null;
17
+
18
+ /**
19
+ * Constructor for the ComponentElement.
20
+ * @param componentName - The name of the component.
21
+ */
22
+ constructor(componentName: string) {
23
+ super();
24
+
25
+ this._componentName = componentName;
26
+ }
27
+
28
+ // Method to be overridden by subclasses to provide initial component data
29
+ getInitialComponentData() {
30
+ return {};
31
+ }
32
+
33
+ async connectedCallback() {
34
+ const appElement = this.closest('pc-app') as AppElement | null;
35
+ if (!appElement) {
36
+ console.error(`${this.tagName.toLowerCase()} should be a descendant of pc-app`);
37
+ return;
38
+ }
39
+
40
+ await appElement.getApplication();
41
+
42
+ this.addComponent();
43
+ }
44
+
45
+ addComponent() {
46
+ // Access the parent pc-entity's 'entity' property
47
+ const entityElement = this.closest('pc-entity') as EntityElement | null;
48
+ if (!entityElement) {
49
+ console.error(`${this.tagName.toLowerCase()} should be a child of pc-entity`);
50
+ return;
51
+ }
52
+
53
+ if (entityElement && entityElement.entity) {
54
+ // Add the component to the entity
55
+ this._component = entityElement.entity.addComponent(
56
+ this._componentName,
57
+ this.getInitialComponentData()
58
+ );
59
+ }
60
+ }
61
+
62
+ disconnectedCallback() {
63
+ // Remove the component when the element is disconnected
64
+ if (this.component && this.component.entity) {
65
+ this._component!.entity.removeComponent(this._componentName);
66
+ this._component = null;
67
+ }
68
+ }
69
+
70
+ get component(): Component | null {
71
+ return this._component;
72
+ }
73
+
74
+ /**
75
+ * Sets the enabled state of the component.
76
+ * @param value - The enabled state of the component.
77
+ */
78
+ set enabled(value: boolean) {
79
+ this._enabled = value;
80
+ if (this.component) {
81
+ this.component.enabled = value;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Gets the enabled state of the component.
87
+ * @returns The enabled state of the component.
88
+ */
89
+ get enabled() {
90
+ return this._enabled;
91
+ }
92
+
93
+ static get observedAttributes() {
94
+ return ['enabled'];
95
+ }
96
+
97
+ attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
98
+ switch (name) {
99
+ case 'enabled':
100
+ this.enabled = newValue !== 'false';
101
+ break;
102
+ }
103
+ }
104
+ }
105
+
106
+ export { ComponentElement };