@preference-sl/pref-viewer 2.10.0-beta.0 → 2.10.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +68 -44
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -6,26 +6,36 @@
|
|
|
6
6
|
* Overview
|
|
7
7
|
* --------
|
|
8
8
|
* `PrefViewer` is a self-contained Web Component built with Babylon.js that:
|
|
9
|
-
* • Renders glTF or GLB
|
|
10
|
-
* • Supports loading via remote
|
|
9
|
+
* • Renders the glTF or GLB model of the product ('model') and merges it with the glTF or GLB model of environment ('scene') into a <canvas> inside its shadow DOM.
|
|
10
|
+
* • Supports loading via remote URL (storage.url), Base64 data URI (storage.url), or from a Base64 stored entry in IndexedDB (storage.db, storage.table, storage.id).
|
|
11
|
+
* • The data for loading both models is provided via the 'config' (both model and scene), 'model' and 'scene' attributes. The 'config' attribute can carry more initial configurations than just the models.
|
|
12
|
+
* • Exposes methods for making changes to the scene that replicate what the attribute observables do: 'loadConfig', 'loadModel', 'loadScene'.
|
|
11
13
|
* • Automatically handles scene creation (engine, camera, lighting) and resource cleanup.
|
|
12
|
-
* • Emits
|
|
14
|
+
* • Emits 'model-loaded' and 'model-error' events for integration.
|
|
13
15
|
*
|
|
14
16
|
* Usage
|
|
15
17
|
* -----
|
|
16
|
-
* Load from
|
|
18
|
+
* Load model from IndexedDB:
|
|
17
19
|
* ```html
|
|
18
20
|
* <pref-viewer
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
+
* model='{ "storage": { "db": "PrefConfiguratorDB", "table": "gltfModels", "id": "1234-1234-1234-1234-1234" }, "visible": true" }'
|
|
22
|
+
* style="width:800px; height:600px;">
|
|
23
|
+
* </pref-viewer>
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Load scene a URL:
|
|
27
|
+
* ```html
|
|
28
|
+
* <pref-viewer
|
|
29
|
+
* scene='{ "storage": { "url" : "https://example.com/scenes/scene1.gltf" }, "visible": true" }'
|
|
30
|
+
* style="width:800px; height:600px;">
|
|
21
31
|
* </pref-viewer>
|
|
22
32
|
* ```
|
|
23
33
|
*
|
|
24
|
-
* Load from Base64 data:
|
|
34
|
+
* Load model from Base64 data:
|
|
25
35
|
* ```html
|
|
26
36
|
* <pref-viewer
|
|
27
|
-
*
|
|
28
|
-
*
|
|
37
|
+
* model='{ "storage": { "url" : "data:model/gltf+json;base64,UEsDB..." }, "visible": true" }'
|
|
38
|
+
* style="width:800px; height:600px;">
|
|
29
39
|
* </pref-viewer>
|
|
30
40
|
* ```
|
|
31
41
|
*/
|
|
@@ -41,14 +51,14 @@ class PrefViewer extends HTMLElement {
|
|
|
41
51
|
|
|
42
52
|
#model = {
|
|
43
53
|
container: null,
|
|
44
|
-
show: true,
|
|
54
|
+
show: true, // Show model by default
|
|
45
55
|
storage: null,
|
|
46
56
|
visible: false,
|
|
47
57
|
};
|
|
48
58
|
|
|
49
59
|
#environment = {
|
|
50
60
|
container: null,
|
|
51
|
-
show: true,
|
|
61
|
+
show: true, // Show environment by default
|
|
52
62
|
storage: null,
|
|
53
63
|
visible: false,
|
|
54
64
|
};
|
|
@@ -89,33 +99,13 @@ class PrefViewer extends HTMLElement {
|
|
|
89
99
|
let data = null;
|
|
90
100
|
switch (name) {
|
|
91
101
|
case "config":
|
|
92
|
-
|
|
93
|
-
if (!data) {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
this.#model.storage = data.model?.storage || null;
|
|
97
|
-
this.#model.show = data.model?.visible || true;
|
|
98
|
-
this.#environment.storage = data.scene?.storage || null;
|
|
99
|
-
this.#environment.show = data.scene?.visible || true;
|
|
100
|
-
this.#initialized && this.#loadContainers(true, true);
|
|
102
|
+
this.loadConfig(value);
|
|
101
103
|
break;
|
|
102
104
|
case "model":
|
|
103
|
-
|
|
104
|
-
if (!data) {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
this.#model.storage = data.model?.storage || null;
|
|
108
|
-
this.#model.show = data.model?.visible || true;
|
|
109
|
-
this.#initialized && this.#loadContainers(true, false);
|
|
105
|
+
this.loadModel(value);
|
|
110
106
|
break;
|
|
111
107
|
case "scene":
|
|
112
|
-
|
|
113
|
-
if (!data) {
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
this.#environment.storage = data.scene?.storage || null;
|
|
117
|
-
this.#environment.show = data.scene?.visible || true;
|
|
118
|
-
this.#initialized && this.#loadContainers(false, true);
|
|
108
|
+
this.loadScene(value);
|
|
119
109
|
break;
|
|
120
110
|
case "show-model":
|
|
121
111
|
data = value.toLowerCase?.() === "true";
|
|
@@ -157,7 +147,7 @@ class PrefViewer extends HTMLElement {
|
|
|
157
147
|
|
|
158
148
|
disconnectedCallback() {
|
|
159
149
|
this.#disposeEngine();
|
|
160
|
-
|
|
150
|
+
this.#canvasResizeObserver.disconnect();
|
|
161
151
|
}
|
|
162
152
|
|
|
163
153
|
// Web Component
|
|
@@ -182,6 +172,7 @@ class PrefViewer extends HTMLElement {
|
|
|
182
172
|
this.shadowRoot.append(wrapper);
|
|
183
173
|
}
|
|
184
174
|
|
|
175
|
+
|
|
185
176
|
// Bbylon.js
|
|
186
177
|
#initializeBabylon() {
|
|
187
178
|
this.#engine = new Engine(this.#canvas, true, { alpha: true });
|
|
@@ -190,17 +181,19 @@ class PrefViewer extends HTMLElement {
|
|
|
190
181
|
this.#createCamera();
|
|
191
182
|
this.#createLights();
|
|
192
183
|
this.#setupInteraction();
|
|
193
|
-
|
|
184
|
+
|
|
194
185
|
this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
|
|
195
|
-
|
|
186
|
+
this.#canvasResizeObserver.observe(this.#canvas);
|
|
196
187
|
}
|
|
197
188
|
|
|
198
|
-
#
|
|
199
|
-
this.#engine && this.#engine.resize();
|
|
200
|
-
}
|
|
189
|
+
#canvasResizeObserver = new ResizeObserver(() => this.#engine && this.#engine.resize());
|
|
201
190
|
|
|
202
191
|
#createCamera() {
|
|
203
|
-
this.#camera = new ArcRotateCamera("camera", Math.PI / 2, Math.PI
|
|
192
|
+
this.#camera = new ArcRotateCamera("camera", 3 * Math.PI / 2, Math.PI * 0.47, 10, Vector3.Zero(), this.#scene);
|
|
193
|
+
this.#camera.upperBetaLimit = Math.PI * 0.48;
|
|
194
|
+
this.#camera.lowerBetaLimit = Math.PI * 0.25;
|
|
195
|
+
this.#camera.lowerRadiusLimit = 5;
|
|
196
|
+
this.#camera.upperRadiusLimit = 20;
|
|
204
197
|
this.#camera.attachControl(this.#canvas, true);
|
|
205
198
|
}
|
|
206
199
|
|
|
@@ -230,8 +223,8 @@ class PrefViewer extends HTMLElement {
|
|
|
230
223
|
this.#canvas.addEventListener("wheel", (event) => {
|
|
231
224
|
if (!this.#scene || !this.#camera) return;
|
|
232
225
|
const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
|
|
233
|
-
this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
|
|
234
|
-
this.#camera.inertialRadiusOffset
|
|
226
|
+
//this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
|
|
227
|
+
this.#camera.inertialRadiusOffset -= event.deltaY * this.#camera.wheelPrecision * 0.001;
|
|
235
228
|
event.preventDefault();
|
|
236
229
|
});
|
|
237
230
|
}
|
|
@@ -377,7 +370,6 @@ class PrefViewer extends HTMLElement {
|
|
|
377
370
|
this.#replaceContainer(this.#environment, environmentContainer.value);
|
|
378
371
|
}
|
|
379
372
|
|
|
380
|
-
this.#scene.createDefaultCamera(true, true, true);
|
|
381
373
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
382
374
|
|
|
383
375
|
this.dispatchEvent(
|
|
@@ -401,6 +393,38 @@ class PrefViewer extends HTMLElement {
|
|
|
401
393
|
}
|
|
402
394
|
|
|
403
395
|
// Public Methods
|
|
396
|
+
loadConfig(config) {
|
|
397
|
+
config = typeof config === "string" ? JSON.parse(config) : config;
|
|
398
|
+
if (!config) {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
this.#model.storage = config.model?.storage || null;
|
|
402
|
+
this.#model.show = config.model?.visible !== undefined ? config.model.visible : this.#model.show;
|
|
403
|
+
this.#environment.storage = config.scene?.storage || null;
|
|
404
|
+
this.#environment.show = config.scene?.visible !== undefined ? config.scene.visible : this.#environment.show;
|
|
405
|
+
this.#initialized && this.#loadContainers(true, true);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
loadModel(model) {
|
|
409
|
+
model = typeof model === "string" ? JSON.parse(model) : model;
|
|
410
|
+
if (!model) {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
this.#model.storage = model.storage || null;
|
|
414
|
+
this.#model.show = model.visible !== undefined ? model.visible : this.#model.show;
|
|
415
|
+
this.#initialized && this.#loadContainers(true, false);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
loadScene(scene) {
|
|
419
|
+
scene = typeof scene === "string" ? JSON.parse(scene) : scene;
|
|
420
|
+
if (!scene) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
this.#environment.storage = scene.storage || null;
|
|
424
|
+
this.#environment.show = scene.visible !== undefined ? scene.visible : this.#environment.show;
|
|
425
|
+
this.#initialized && this.#loadContainers(false, true);
|
|
426
|
+
}
|
|
427
|
+
|
|
404
428
|
showModel() {
|
|
405
429
|
this.#model.show = true;
|
|
406
430
|
this.#addContainer(this.#model);
|